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