001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.classfile;
020
021import java.io.DataInput;
022import java.io.DataOutputStream;
023import java.io.IOException;
024import java.util.Arrays;
025
026import org.apache.bcel.Const;
027
028/**
029 * This class represents a reference to an unknown (i.e., application-specific) attribute of a class. It is instantiated
030 * from the {@link Attribute#readAttribute(java.io.DataInput, ConstantPool)} method. Applications that need to read in
031 * application-specific attributes should create an {@link UnknownAttributeReader} implementation and attach it via
032 * {@link Attribute#addAttributeReader(String, UnknownAttributeReader)}.
033 *
034 * @see Attribute
035 * @see UnknownAttributeReader
036 */
037public final class Unknown extends Attribute {
038
039    private byte[] bytes;
040
041    private final String name;
042
043    /**
044     * Constructs a new instance for a non-standard attribute.
045     *
046     * @param nameIndex Index in constant pool
047     * @param length Content length in bytes
048     * @param bytes Attribute contents
049     * @param constantPool Array of constants
050     */
051    public Unknown(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) {
052        super(Const.ATTR_UNKNOWN, nameIndex, length, constantPool);
053        this.bytes = bytes;
054        this.name = constantPool.getConstantUtf8(nameIndex).getBytes();
055    }
056
057    /**
058     * Constructs a new instance from an input stream.
059     *
060     * @param nameIndex Index in constant pool
061     * @param length Content length in bytes
062     * @param input Input stream
063     * @param constantPool Array of constants
064     * @throws IOException if an I/O error occurs.
065     */
066    Unknown(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
067        this(nameIndex, length, (byte[]) null, constantPool);
068        if (length > 0) {
069            bytes = new byte[length];
070            input.readFully(bytes);
071        }
072    }
073
074    /**
075     * Constructs a new instance from another instance. Note that both objects use the same references (shallow copy). Use clone() for a physical copy.
076     *
077     * @param unknown Source.
078     */
079    public Unknown(final Unknown unknown) {
080        this(unknown.getNameIndex(), unknown.getLength(), unknown.getBytes(), unknown.getConstantPool());
081    }
082
083    /**
084     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
085     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
086     *
087     * @param v Visitor object
088     */
089    @Override
090    public void accept(final Visitor v) {
091        v.visitUnknown(this);
092    }
093
094    /**
095     * @return deep copy of this attribute
096     */
097    @Override
098    public Attribute copy(final ConstantPool constantPool) {
099        final Unknown c = (Unknown) clone();
100        if (bytes != null) {
101            c.bytes = bytes.clone();
102        }
103        c.setConstantPool(constantPool);
104        return c;
105    }
106
107    /**
108     * Dumps unknown bytes to file stream.
109     *
110     * @param file Output file stream
111     * @throws IOException if an I/O error occurs.
112     */
113    @Override
114    public void dump(final DataOutputStream file) throws IOException {
115        super.dump(file);
116        if (super.getLength() > 0) {
117            file.write(bytes, 0, super.getLength());
118        }
119    }
120
121    /**
122     * @return data bytes.
123     */
124    public byte[] getBytes() {
125        return bytes;
126    }
127
128    /**
129     * @return name of attribute.
130     */
131    @Override
132    public String getName() {
133        return name;
134    }
135
136    /**
137     * @param bytes the bytes to set
138     */
139    public void setBytes(final byte[] bytes) {
140        this.bytes = bytes;
141    }
142
143    /**
144     * @return String representation.
145     */
146    @Override
147    public String toString() {
148        if (super.getLength() == 0 || bytes == null) {
149            return "(Unknown attribute " + name + ")";
150        }
151        final String hex;
152        final int limit = 10;
153        if (super.getLength() > limit) {
154            final byte[] tmp = Arrays.copyOf(bytes, limit);
155            hex = Utility.toHexString(tmp) + "... (truncated)";
156        } else {
157            hex = Utility.toHexString(bytes);
158        }
159        return "(Unknown attribute " + name + ": " + hex + ")";
160    }
161}