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.DataOutputStream;
021import java.io.IOException;
022import java.util.Objects;
023
024import org.apache.bcel.Const;
025import org.apache.bcel.util.BCELComparator;
026
027/**
028 * Abstract superclass for classes to represent the different constant types in the constant pool of a class file. The
029 * classes keep closely to the JVM specification.
030 */
031public abstract class Constant implements Cloneable, Node {
032
033    static final Constant[] EMPTY_ARRAY = {};
034
035    private static BCELComparator<Constant> bcelComparator = new BCELComparator<Constant>() {
036
037        @Override
038        public boolean equals(final Constant a, final Constant b) {
039            return a == b || a != null && b != null && Objects.equals(a.toString(), b.toString());
040        }
041
042        @Override
043        public int hashCode(final Constant o) {
044            return o != null ? Objects.hashCode(o.toString()) : 0;
045        }
046    };
047
048    /**
049     * @return Comparison strategy object.
050     */
051    public static BCELComparator<Constant> getComparator() {
052        return bcelComparator;
053    }
054
055    /**
056     * Reads one constant from the given input, the type depends on a tag byte.
057     *
058     * @param dataInput Input stream
059     * @return Constant object
060     * @throws IOException if an I/O error occurs reading from the given {@code dataInput}.
061     * @throws ClassFormatException if the next byte is not recognized
062     * @since 6.0 made public
063     */
064    public static Constant readConstant(final DataInput dataInput) throws IOException, ClassFormatException {
065        final byte b = dataInput.readByte(); // Read tag byte
066        switch (b) {
067        case Const.CONSTANT_Class:
068            return new ConstantClass(dataInput);
069        case Const.CONSTANT_Fieldref:
070            return new ConstantFieldref(dataInput);
071        case Const.CONSTANT_Methodref:
072            return new ConstantMethodref(dataInput);
073        case Const.CONSTANT_InterfaceMethodref:
074            return new ConstantInterfaceMethodref(dataInput);
075        case Const.CONSTANT_String:
076            return new ConstantString(dataInput);
077        case Const.CONSTANT_Integer:
078            return new ConstantInteger(dataInput);
079        case Const.CONSTANT_Float:
080            return new ConstantFloat(dataInput);
081        case Const.CONSTANT_Long:
082            return new ConstantLong(dataInput);
083        case Const.CONSTANT_Double:
084            return new ConstantDouble(dataInput);
085        case Const.CONSTANT_NameAndType:
086            return new ConstantNameAndType(dataInput);
087        case Const.CONSTANT_Utf8:
088            return ConstantUtf8.getInstance(dataInput);
089        case Const.CONSTANT_MethodHandle:
090            return new ConstantMethodHandle(dataInput);
091        case Const.CONSTANT_MethodType:
092            return new ConstantMethodType(dataInput);
093        case Const.CONSTANT_Dynamic:
094            return new ConstantDynamic(dataInput);
095        case Const.CONSTANT_InvokeDynamic:
096            return new ConstantInvokeDynamic(dataInput);
097        case Const.CONSTANT_Module:
098            return new ConstantModule(dataInput);
099        case Const.CONSTANT_Package:
100            return new ConstantPackage(dataInput);
101        default:
102            throw new ClassFormatException("Invalid byte tag in constant pool: " + b);
103        }
104    }
105
106    /**
107     * @param comparator Comparison strategy object
108     */
109    public static void setComparator(final BCELComparator<Constant> comparator) {
110        bcelComparator = comparator;
111    }
112
113    /*
114     * In fact this tag is redundant since we can distinguish different 'Constant' objects by their type, i.e., via
115     * 'instanceof'. In some places we will use the tag for switch()es anyway.
116     *
117     * First, we want match the specification as closely as possible. Second we need the tag as an index to select the
118     * corresponding class name from the 'CONSTANT_NAMES' array.
119     */
120    /**
121     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
122     */
123    @java.lang.Deprecated
124    protected byte tag; // TODO should be private & final
125
126    Constant(final byte tag) {
127        this.tag = tag;
128    }
129
130    /**
131     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
132     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
133     *
134     * @param v Visitor object
135     */
136    @Override
137    public abstract void accept(Visitor v);
138
139    @Override
140    public Object clone() {
141        try {
142            return super.clone();
143        } catch (final CloneNotSupportedException e) {
144            throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
145        }
146    }
147
148    /**
149     * @return deep copy of this constant
150     */
151    public Constant copy() {
152        try {
153            return (Constant) super.clone();
154        } catch (final CloneNotSupportedException e) {
155            // TODO should this throw?
156        }
157        return null;
158    }
159
160    public abstract void dump(DataOutputStream file) throws IOException;
161
162    /**
163     * Returns value as defined by given BCELComparator strategy. By default two Constant objects are said to be equal when
164     * the result of toString() is equal.
165     *
166     * @see Object#equals(Object)
167     */
168    @Override
169    public boolean equals(final Object obj) {
170        return obj instanceof Constant && bcelComparator.equals(this, (Constant) obj);
171    }
172
173    /**
174     * @return Tag of constant, i.e., its type. No setTag() method to avoid confusion.
175     */
176    public final byte getTag() {
177        return tag;
178    }
179
180    /**
181     * Returns value as defined by given BCELComparator strategy. By default return the hash code of the result of
182     * toString().
183     *
184     * @see Object#hashCode()
185     */
186    @Override
187    public int hashCode() {
188        return bcelComparator.hashCode(this);
189    }
190
191    /**
192     * @return String representation.
193     */
194    @Override
195    public String toString() {
196        return Const.getConstantName(tag) + "[" + tag + "]";
197    }
198}