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     * @return Comparison strategy object.
055     */
056    public static BCELComparator<Field> getComparator() {
057        return bcelComparator;
058    }
059
060    /**
061     * @param comparator Comparison strategy object.
062     */
063    public static void setComparator(final BCELComparator<Field> comparator) {
064        bcelComparator = comparator;
065    }
066
067    /**
068     * Constructs object from file stream.
069     *
070     * @param file Input stream.
071     */
072    Field(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
073        super(file, constantPool);
074    }
075
076    /**
077     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
078     * physical copy.
079     *
080     * @param c Source to copy.
081     */
082    public Field(final Field c) {
083        super(c);
084    }
085
086    /**
087     * @param accessFlags Access rights of field
088     * @param nameIndex Points to field name in constant pool
089     * @param signatureIndex Points to encoded signature
090     * @param attributes Collection of attributes
091     * @param constantPool Array of constants
092     */
093    public Field(final int accessFlags, final int nameIndex, final int signatureIndex, final Attribute[] attributes, final ConstantPool constantPool) {
094        super(accessFlags, nameIndex, signatureIndex, attributes, constantPool);
095    }
096
097    /**
098     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
099     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
100     *
101     * @param v Visitor object
102     */
103    @Override
104    public void accept(final Visitor v) {
105        v.visitField(this);
106    }
107
108    /**
109     * @return deep copy of this field
110     */
111    public Field copy(final ConstantPool constantPool) {
112        return (Field) copy_(constantPool);
113    }
114
115    /**
116     * Return value as defined by given BCELComparator strategy. By default two Field objects are said to be equal when
117     * their names and signatures are equal.
118     *
119     * @see Object#equals(Object)
120     */
121    @Override
122    public boolean equals(final Object obj) {
123        return obj instanceof Field && bcelComparator.equals(this, (Field) obj);
124    }
125
126    /**
127     * @return constant value associated with this field (may be null)
128     */
129    public ConstantValue getConstantValue() {
130        for (final Attribute attribute : super.getAttributes()) {
131            if (attribute.getTag() == Const.ATTR_CONSTANT_VALUE) {
132                return (ConstantValue) attribute;
133            }
134        }
135        return null;
136    }
137
138    /**
139     * See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.2.2
140     *
141     * @return type of field
142     */
143    public Type getType() {
144        return Type.getType(getSignature());
145    }
146
147    /**
148     * Return value as defined by given BCELComparator strategy. By default return the hash code of the field's name XOR
149     * signature.
150     *
151     * @see Object#hashCode()
152     */
153    @Override
154    public int hashCode() {
155        return bcelComparator.hashCode(this);
156    }
157
158    /**
159     * Return string representation close to declaration format, 'public static final short MAX = 100', e.g..
160     *
161     * @return String representation of field, including the signature.
162     */
163    @Override
164    public String toString() {
165        String name;
166        String signature;
167        String access; // Short cuts to constant pool
168
169        // Get names from constant pool
170        access = Utility.accessToString(super.getAccessFlags());
171        access = access.isEmpty() ? "" : access + " ";
172        signature = Utility.signatureToString(getSignature());
173        name = getName();
174        final StringBuilder buf = new StringBuilder(64); // CHECKSTYLE IGNORE MagicNumber
175        buf.append(access).append(signature).append(" ").append(name);
176        final ConstantValue cv = getConstantValue();
177        if (cv != null) {
178            buf.append(" = ").append(cv);
179        }
180        for (final Attribute attribute : super.getAttributes()) {
181            if (!(attribute instanceof ConstantValue)) {
182                buf.append(" [").append(attribute).append("]");
183            }
184        }
185        return buf.toString();
186    }
187}