1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.classfile;
20
21 import java.io.DataInput;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.Arrays;
25
26 import org.apache.bcel.Const;
27
28 /**
29 * This class represents a reference to an unknown (i.e., application-specific) attribute of a class. It is instantiated
30 * from the {@link Attribute#readAttribute(java.io.DataInput, ConstantPool)} method. Applications that need to read in
31 * application-specific attributes should create an {@link UnknownAttributeReader} implementation and attach it via
32 * {@link Attribute#addAttributeReader(String, UnknownAttributeReader)}.
33 *
34 * @see Attribute
35 * @see UnknownAttributeReader
36 */
37 public final class Unknown extends Attribute {
38
39 private byte[] bytes;
40
41 private final String name;
42
43 /**
44 * Constructs a new instance for a non-standard attribute.
45 *
46 * @param nameIndex Index in constant pool
47 * @param length Content length in bytes
48 * @param bytes Attribute contents
49 * @param constantPool Array of constants
50 */
51 public Unknown(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) {
52 super(Const.ATTR_UNKNOWN, nameIndex, length, constantPool);
53 this.bytes = bytes;
54 this.name = constantPool.getConstantUtf8(nameIndex).getBytes();
55 }
56
57 /**
58 * Constructs a new instance from an input stream.
59 *
60 * @param nameIndex Index in constant pool
61 * @param length Content length in bytes
62 * @param input Input stream
63 * @param constantPool Array of constants
64 * @throws IOException if an I/O error occurs.
65 */
66 Unknown(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
67 this(nameIndex, length, (byte[]) null, constantPool);
68 if (length > 0) {
69 bytes = new byte[length];
70 input.readFully(bytes);
71 }
72 }
73
74 /**
75 * Constructs a new instance from another instance. Note that both objects use the same references (shallow copy). Use clone() for a physical copy.
76 *
77 * @param unknown Source.
78 */
79 public Unknown(final Unknown unknown) {
80 this(unknown.getNameIndex(), unknown.getLength(), unknown.getBytes(), unknown.getConstantPool());
81 }
82
83 /**
84 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
85 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
86 *
87 * @param v Visitor object
88 */
89 @Override
90 public void accept(final Visitor v) {
91 v.visitUnknown(this);
92 }
93
94 /**
95 * @return deep copy of this attribute
96 */
97 @Override
98 public Attribute copy(final ConstantPool constantPool) {
99 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 }