001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.bcel.classfile;
018
019import java.io.DataInput;
020import java.io.IOException;
021import java.util.Objects;
022
023import org.apache.bcel.Const;
024import org.apache.bcel.generic.Type;
025import org.apache.bcel.util.BCELComparator;
026
027/**
028 * This class represents the field info structure, i.e., the representation for a variable in the class. See JVM
029 * specification for details.
030 */
031public final class Field extends FieldOrMethod {
032
033    /**
034     * Empty array constant.
035     *
036     * @since 6.6.0
037     */
038    public static final Field[] EMPTY_ARRAY = {};
039
040    private static BCELComparator<Field> bcelComparator = new BCELComparator<Field>() {
041
042        @Override
043        public boolean equals(final Field a, final Field b) {
044            return a == b || a != null && b != null && Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getSignature(), b.getSignature());
045        }
046
047        @Override
048        public int hashCode(final Field o) {
049            return o != null ? Objects.hash(o.getSignature(), o.getName()) : 0;
050        }
051    };
052
053    /**
054     * Empty array.
055     */
056    static final Field[] EMPTY_FIELD_ARRAY = {};
057
058    /**
059     * @return Comparison strategy object.
060     */
061    public static BCELComparator<Field> getComparator() {
062        return bcelComparator;
063    }
064
065    /**
066     * @param comparator Comparison strategy object.
067     */
068    public static void setComparator(final BCELComparator<Field> comparator) {
069        bcelComparator = comparator;
070    }
071
072    /**
073     * Constructs object from file stream.
074     *
075     * @param file Input stream.
076     */
077    Field(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
078        super(file, constantPool);
079    }
080
081    /**
082     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
083     * physical copy.
084     *
085     * @param c Source to copy.
086     */
087    public Field(final Field c) {
088        super(c);
089    }
090
091    /**
092     * @param accessFlags Access rights of field
093     * @param nameIndex Points to field name in constant pool
094     * @param signatureIndex Points to encoded signature
095     * @param attributes Collection of attributes
096     * @param constantPool Array of constants
097     */
098    public Field(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
099        super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
100    }
101
102    /**
103     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
104     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
105     *
106     * @param v Visitor object
107     */
108    @Override
109    public void accept(final Visitor v) {
110        v.visitField(this);
111    }
112
113    /**
114     * @return deep copy of this field
115     */
116    public Field copy(final ConstantPool constantPool) {
117        return (Field) copy_(constantPool);
118    }
119
120    /**
121     * Return value as defined by given BCELComparator strategy. By default two Field objects are said to be equal when
122     * their names and signatures are equal.
123     *
124     * @see Object#equals(Object)
125     */
126    @Override
127    public boolean equals(final Object obj) {
128        return obj instanceof Field && bcelComparator.equals(this, (Field) obj);
129    }
130
131    /**
132     * @return constant value associated with this field (may be null)
133     */
134    public ConstantValue getConstantValue() {
135        for (final Attribute attribute : super.getAttributes()) {
136            if (attribute.getTag() == Const.ATTR_CONSTANT_VALUE) {
137                return (ConstantValue) attribute;
138            }
139        }
140        return null;
141    }
142
143    /**
144     * See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.2.2
145     *
146     * @return type of field
147     */
148    public Type getType() {
149        return Type.getType(getSignature());
150    }
151
152    /**
153     * Return value as defined by given BCELComparator strategy. By default return the hash code of the field's name XOR
154     * signature.
155     *
156     * @see Object#hashCode()
157     */
158    @Override
159    public int hashCode() {
160        return bcelComparator.hashCode(this);
161    }
162
163    /**
164     * Return string representation close to declaration format, 'public static final short MAX = 100', e.g..
165     *
166     * @return String representation of field, including the signature.
167     */
168    @Override
169    public String toString() {
170        String name;
171        String signature;
172        String access; // Short cuts to constant pool
173
174        // Get names from constant pool
175        access = Utility.accessToString(super.getAccessFlags());
176        access = access.isEmpty() ? "" : access + " ";
177        signature = Utility.signatureToString(getSignature());
178        name = getName();
179        final StringBuilder buf = new StringBuilder(64); // CHECKSTYLE IGNORE MagicNumber
180        buf.append(access).append(signature).append(" ").append(name);
181        final ConstantValue cv = getConstantValue();
182        if (cv != null) {
183            buf.append(" = ").append(cv);
184        }
185        for (final Attribute attribute : super.getAttributes()) {
186            if (!(attribute instanceof ConstantValue)) {
187                buf.append(" [").append(attribute).append("]");
188            }
189        }
190        return buf.toString();
191    }
192}