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