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