/******************************************************************************* * 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.lang.reflect.Modifier; 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; /** * This class models a class file class declaration: * u2 access_flags; * u2 this_class; * u2 super_class; * u2 interfaces_count; * u2[interfaces_count] interfaces; * * See "The Java Virtual Machine Specification" Chapter 4. */ public class ClassDeclaration { /** the constants referenced by the indexes */ private ConstantPool constantPool; /** these flags can be interrogated by the java.lang.reflect.Modifier static methods */ private short accessFlags; /** these flags will match those returned by java.lang.Class#getModifiers() */ private short standardAccessFlags; private short thisClassIndex; private short superClassIndex; private short interfacesCount; private short[] interfaceIndexes; /** constants defined in "The Java Virtual Machine Specification" */ public static final short ACC_SUPER = 0x0020; public static final short ACC_SYNTHETIC = 0x1000; public static final short ACC_ANNOTATION = 0x2000; public static final short ACC_ENUM = 0x4000; static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * cleared bits: * 0x8000 reserved for future use - ignore it * (although the Eclipse compiler sets it for some reason...) * 0x4000 unrecognized by Modifier * 0x2000 unrecognized by Modifier * 0x1000 unrecognized by Modifier * 0x0100 native * 0x0080 transient * x00040 volatile * x00020 synchronized (re-used by "super") */ public static final int VISIBLE_ACCESS_FLAGS_MASK = 0x0E1F; /** * Construct a class file declaration from the specified stream * of byte codes. */ ClassDeclaration(ClassFileDataInputStream stream, ConstantPool constantPool) throws IOException { super(); this.constantPool = constantPool; this.initialize(stream); } private void initialize(ClassFileDataInputStream stream) throws IOException { this.accessFlags = stream.readU2(); this.standardAccessFlags = this.buildStandardAccessFlags(); this.thisClassIndex = stream.readU2(); this.superClassIndex = stream.readU2(); this.interfacesCount = stream.readU2(); short cnt = this.interfacesCount; this.interfaceIndexes = new short[cnt]; short[] indexes = this.interfaceIndexes; for (short i = 0; i < cnt; i++) { indexes[i] = stream.readU2(); } } private short buildStandardAccessFlags() { short result = this.accessFlags; result &= VISIBLE_ACCESS_FLAGS_MASK; return result; } void setStandardAccessFlagsForNestedClass(short flags) { this.standardAccessFlags = flags; } public String displayString() { StringWriter sw = new StringWriter(); IndentingPrintWriter writer = new IndentingPrintWriter(sw); this.displayStringOn(writer); return sw.toString(); } public void displayStringOn(IndentingPrintWriter writer) { writer.println("Declaration:"); writer.indent(); this.printDeclarationOn(writer); writer.undent(); writer.println(); } public void printDeclarationOn(PrintWriter writer) { String modifierString = this.modifierString(); if (modifierString.length() != 0) { writer.print(modifierString); writer.print(' '); } if (this.isClass()) { // as opposed to an interface writer.print("class "); } writer.print(this.thisClassName()); if (this.isClass()) { // as opposed to an interface writer.print(" extends "); writer.print(this.superClassName()); } short cnt = this.interfacesCount; if (cnt != 0) { if (this.isClass()) { // as opposed to an interface writer.print(" implements "); } else { writer.print(" extends "); } for (short i = 0; i < cnt; i++) { if (i != 0) { writer.write(", "); } writer.write(this.interfaceName(i)); } } } public String modifierString() { return Modifier.toString(this.standardAccessFlags); } /** * Return the set of flags that will match those * returned by java.lang.Class#getModifiers() */ public short standardAccessFlags() { return this.standardAccessFlags; } /** * Return whether the class is an interface. */ public boolean isInterface() { return Modifier.isInterface(this.accessFlags); } /** * Return whether the class is a class, * as opposed to an interface. */ public boolean isClass() { return ! this.isInterface(); } /** * Check a bit that cannot be interpreted by the * Modifier static methods. This bit is used by the JVM * for backward-compatibility purposes: * "Treat superclass methods specially when invoked * by the invokespecial instruction." */ public boolean isSuper() { return (this.accessFlags & ACC_SUPER) != 0; } /** * Check a bit that cannot (yet?) be interpreted by the * Modifier static methods. This bit indicates the class * is "not present in the source code". */ public boolean isSynthetic() { return (this.accessFlags & ACC_SYNTHETIC) != 0; } /** * Check a bit that cannot (yet?) be interpreted by the * Modifier static methods. This bit indicates the class * is "declared as an annotation type". */ public boolean isAnnotation() { return (this.accessFlags & ACC_ANNOTATION) != 0; } /** * Check a bit that cannot (yet?) be interpreted by the * Modifier static methods. This bit indicates the class * is "declared as an enumerated type". */ public boolean isEnum() { return (this.accessFlags & ACC_ENUM) != 0; } public String thisClassName() { return this.className(this.thisClassIndex); } public String superClassName() { if (this.isInterface()) { return null; } short index = this.superClassIndex; return (index == 0) ? null : this.className(index); } public String interfaceName(int index) { return this.className(this.interfaceIndexes[index]); } public String[] interfaceNames() { short count = this.interfacesCount; if (count == 0) { return EMPTY_STRING_ARRAY; } String[] interfaceNames = new String[count]; for (short i = 0; i < count; i++) { interfaceNames[i] = this.interfaceName(i); } return interfaceNames; } private String className(short index) { return this.constantPool.getClassConstant(index).name(); } public void accept(Visitor visitor) { visitor.visit(this); } public ConstantPool getConstantPool() { return this.constantPool; } public short getAccessFlags() { return this.accessFlags; } public short getThisClassIndex() { return this.thisClassIndex; } public short getSuperClassIndex() { return this.superClassIndex; } public short getInterfacesCount() { return this.interfacesCount; } public short[] getInterfaceIndexes() { return this.interfaceIndexes; } public short getInterfaceIndex(int index) { return this.interfaceIndexes[index]; } public String toString() { StringWriter sw = new StringWriter(200); PrintWriter pw = new PrintWriter(sw); pw.print(ClassTools.shortClassNameForObject(this)); pw.print('('); this.printDeclarationOn(pw); pw.print(')'); return sw.toString(); } }