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