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;
022
023import org.apache.bcel.Const;
024
025/**
026 * This class represents the type of a local variable or item on stack used in the StackMap entries.
027 *
028 * @see StackMapEntry
029 * @see StackMap
030 * @see Const
031 */
032public final class StackMapType implements Cloneable {
033
034    public static final StackMapType[] EMPTY_ARRAY = {}; // must be public because BCELifier code generator writes calls to it
035
036    private byte type;
037    private int index = -1; // Index to CONSTANT_Class or offset
038    private ConstantPool constantPool;
039
040    /**
041     * @param type type tag as defined in the Constants interface
042     * @param index index to constant pool, or byte code offset
043     */
044    public StackMapType(final byte type, final int index, final ConstantPool constantPool) {
045        this.type = checkType(type);
046        this.index = index;
047        this.constantPool = constantPool;
048    }
049
050    /**
051     * Construct object from file stream.
052     *
053     * @param file Input stream
054     * @throws IOException if an I/O error occurs.
055     */
056    StackMapType(final DataInput file, final ConstantPool constantPool) throws IOException {
057        this(file.readByte(), -1, constantPool);
058        if (hasIndex()) {
059            this.index = file.readUnsignedShort();
060        }
061        this.constantPool = constantPool;
062    }
063
064    private byte checkType(final byte type) {
065        if (type < Const.ITEM_Bogus || type > Const.ITEM_NewObject) {
066            throw new ClassFormatException("Illegal type for StackMapType: " + type);
067        }
068        return type;
069    }
070
071    /**
072     * @return deep copy of this object
073     */
074    public StackMapType copy() {
075        try {
076            return (StackMapType) clone();
077        } catch (final CloneNotSupportedException e) {
078            // TODO should this throw?
079        }
080        return null;
081    }
082
083    /**
084     * Dump type entries to file.
085     *
086     * @param file Output file stream
087     * @throws IOException if an I/O error occurs.
088     */
089    public void dump(final DataOutputStream file) throws IOException {
090        file.writeByte(type);
091        if (hasIndex()) {
092            file.writeShort(getIndex());
093        }
094    }
095
096    /**
097     * @return Constant pool used by this object.
098     */
099    public ConstantPool getConstantPool() {
100        return constantPool;
101    }
102
103    /**
104     * @return index to constant pool if type == ITEM_Object, or offset in byte code, if type == ITEM_NewObject, and -1
105     *         otherwise
106     */
107    public int getIndex() {
108        return index;
109    }
110
111    public byte getType() {
112        return type;
113    }
114
115    /**
116     * @return true, if type is either ITEM_Object or ITEM_NewObject
117     */
118    public boolean hasIndex() {
119        return type == Const.ITEM_Object || type == Const.ITEM_NewObject;
120    }
121
122    private String printIndex() {
123        if (type == Const.ITEM_Object) {
124            if (index < 0) {
125                return ", class=<unknown>";
126            }
127            return ", class=" + constantPool.constantToString(index, Const.CONSTANT_Class);
128        }
129        if (type == Const.ITEM_NewObject) {
130            return ", offset=" + index;
131        }
132        return "";
133    }
134
135    /**
136     * @param constantPool Constant pool to be used for this object.
137     */
138    public void setConstantPool(final ConstantPool constantPool) {
139        this.constantPool = constantPool;
140    }
141
142    public void setIndex(final int index) {
143        this.index = index;
144    }
145
146    public void setType(final byte type) {
147        this.type = checkType(type);
148    }
149
150    /**
151     * @return String representation
152     */
153    @Override
154    public String toString() {
155        return "(type=" + Const.getItemName(type) + printIndex() + ")";
156    }
157}