/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.utility.classfile; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import org.eclipse.persistence.tools.workbench.utility.ClassTools; import org.eclipse.persistence.tools.workbench.utility.classfile.tools.ClassFileDataInputStream; import org.eclipse.persistence.tools.workbench.utility.io.IndentingPrintWriter; import org.eclipse.persistence.tools.workbench.utility.io.StringBufferWriter; /** * This class models a class file attribute: * u2 attribute_name_index; * u4 attribute_length; * u1[attribute_length] info; * * See "The Java Virtual Machine Specification" Chapter 4. */ public abstract class Attribute { private AttributePool pool; private short nameIndex; private int length; static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * Construct a class file attribute from the specified stream * of byte codes. */ static Attribute read(ClassFileDataInputStream stream, AttributePool pool) throws IOException { short nameIndex = stream.readU2(); String name = pool.constantPool().getUTF8String(nameIndex); if (name.equals("ConstantValue")) { return new ConstantValueAttribute(stream, nameIndex, pool); } else if (name.equals("Code")) { return new CodeAttribute(stream, nameIndex, pool); } else if (name.equals("Exceptions")) { return new ExceptionsAttribute(stream, nameIndex, pool); } else if (name.equals("InnerClasses")) { return new InnerClassesAttribute(stream, nameIndex, pool); } else if (name.equals("EnclosingMethod")) { return new EnclosingMethodAttribute(stream, nameIndex, pool); } else if (name.equals("Synthetic")) { return new SyntheticAttribute(stream, nameIndex, pool); } else if (name.equals("Signature")) { return new SignatureAttribute(stream, nameIndex, pool); } else if (name.equals("SourceFile")) { return new SourceFileAttribute(stream, nameIndex, pool); } else if (name.equals("SourceDebugExtension")) { return new SourceDebugExtensionAttribute(stream, nameIndex, pool); } else if (name.equals("LineNumberTable")) { return new LineNumberTableAttribute(stream, nameIndex, pool); } else if (name.equals("LocalVariableTable")) { return new LocalVariableTableAttribute(stream, nameIndex, pool); } else if (name.equals("LocalVariableTypeTable")) { return new LocalVariableTypeTableAttribute(stream, nameIndex, pool); } else if (name.equals("Deprecated")) { return new DeprecatedAttribute(stream, nameIndex, pool); } else if (name.equals("RuntimeVisibleAnnotations")) { return new RuntimeVisibleAnnotationsAttribute(stream, nameIndex, pool); } else if (name.equals("RuntimeInvisibleAnnotations")) { return new RuntimeInvisibleAnnotationsAttribute(stream, nameIndex, pool); } else if (name.equals("RuntimeVisibleParameterAnnotations")) { return new RuntimeVisibleParameterAnnotationsAttribute(stream, nameIndex, pool); } else if (name.equals("RuntimeInvisibleParameterAnnotations")) { return new RuntimeInvisibleParameterAnnotationsAttribute(stream, nameIndex, pool); } else if (name.equals("AnnotationDefault")) { return new AnnotationDefaultAttribute(stream, nameIndex, pool); } else { return new UnknownAttribute(stream, nameIndex, pool); } } /** * Construct a class file attribute from the specified stream * of byte codes. */ Attribute(ClassFileDataInputStream stream, short nameIndex, AttributePool pool) throws IOException { super(); this.pool = pool; this.nameIndex = nameIndex; this.initialize(stream); } void initialize(ClassFileDataInputStream stream) throws IOException { this.length = stream.readU4(); this.initializeInfo(stream); } /** * Read in the info. */ abstract void initializeInfo(ClassFileDataInputStream stream) throws IOException; public String displayString() { StringWriter sw = new StringWriter(1000); IndentingPrintWriter writer = new IndentingPrintWriter(sw); this.displayStringOn(writer); return sw.toString(); } public void displayStringOn(IndentingPrintWriter writer) { this.displayNameOn(writer); writer.println(); writer.indent(); this.displayInfoStringOn(writer); writer.undent(); } public void displayNameOn(IndentingPrintWriter writer) { writer.print(this.name()); } /** * Display the info. */ abstract void displayInfoStringOn(IndentingPrintWriter writer); public Object fieldConstantValue() { return null; } public void printFieldInitializationClauseOn(PrintWriter writer) { // the default is to do nothing } public String[] exceptionClassNames() { return null; } public String sourceFileName() { return null; } public InnerClass innerClassNamed(String className) { return null; } public String declaringClassName() { return null; } public String nestedClassName() { return null; } public short nestedClassAccessFlags() { return 0; } public String[] nestedClassNames() { return null; } public String[] declaredMemberClassNames() { return null; } public void printThrowsClauseOn(PrintWriter writer) { // the default is to do nothing } public boolean isDeprecated() { return false; } public boolean isSynthetic() { return false; } public boolean isNestedClass() { return false; } public boolean isMemberClass() { return false; } public boolean isLocalClass() { return false; } public boolean isAnonymousClass() { return false; } public ClassFile classFile() { return this.pool.getClassFile(); } public ConstantPool constantPool() { return this.classFile().getConstantPool(); } public String name() { return this.constantPool().getUTF8String(this.nameIndex); } public AttributePool getPool() { return this.pool; } public short getNameIndex() { return this.nameIndex; } public int getLength() { return this.length; } /** * Convert the byte array to a HEX string. * HEX allows for binary data to be printed. */ void appendHexStringTo(byte[] bytes, StringBuffer sb) { this.writeHexStringOn(bytes, new StringBufferWriter(sb)); } /** * Convert the byte array to a HEX string. * HEX allows for binary data to be printed. */ void writeHexStringOn(byte[] bytes, Writer writer) { try { this.writeHexStringOnInternal(bytes, writer); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Convert the byte array to a HEX string. * HEX allows for binary data to be printed. */ private static final char[] HEX_CHARS = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; private void writeHexStringOnInternal(byte[] bytes, Writer writer) throws IOException { int len = bytes.length; for (int i = 0; i < len; i++) { int b = bytes[i]; if (b < 0) { b = b + 256; // compensate for the fact that byte is signed in Java } b = (byte) (b / 16); // get the first digit writer.write(HEX_CHARS[b]); b = bytes[i]; if (b < 0) { b = b + 256; } b = (byte) (b % 16); // get the second digit writer.write(HEX_CHARS[b]); } } public abstract void accept(Visitor visitor); public final String toString() { StringBuffer sb = new StringBuffer(); sb.append(ClassTools.shortClassNameForObject(this)); sb.append('('); this.toString(sb); sb.append(')'); return sb.toString(); } void toString(StringBuffer sb) { // allow subclasses to optionally override } }