/*******************************************************************************
* 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.StringWriter;
import java.lang.reflect.Modifier;
import java.util.List;
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 an inner class:
* u2 inner_class_info_index;
* u2 outer_class_info_index;
* u2 inner_name_index;
* u2 inner_class_access_flags;
*
* See "The Java Virtual Machine Specification" Chapter 4.
*/
public class InnerClass {
private InnerClassesAttribute pool;
private short innerClassInfoIndex; // this is the fully-qualified class name
private short outerClassInfoIndex; // this is only present for "member" classes
private short innerClassNameIndex; // this is the "local" name
private short innerClassAccessFlags;
/** constants defined in "The Java Virtual Machine Specification" */
public static final short ACC_SYNTHETIC = 0x1000;
public static final short ACC_ANNOTATION = 0x2000;
public static final short ACC_ENUM = 0x4000;
private static final String ANONYMOUS = "<anonymous>";
private static final String NOT_APPLICABLE = "<N/A>";
/**
* Construct an inner class from the specified stream
* of byte codes.
*/
InnerClass(ClassFileDataInputStream stream, InnerClassesAttribute pool) throws IOException {
super();
this.pool = pool;
this.initialize(stream);
}
void initialize(ClassFileDataInputStream stream) throws IOException {
this.innerClassInfoIndex = stream.readU2();
this.outerClassInfoIndex = stream.readU2();
this.innerClassNameIndex = stream.readU2();
this.innerClassAccessFlags = stream.readU2();
}
public String displayString() {
StringWriter sw = new StringWriter(1000);
IndentingPrintWriter writer = new IndentingPrintWriter(sw);
this.displayStringOn(writer);
return sw.toString();
}
public void displayStringOn(IndentingPrintWriter writer) {
writer.println(this.innerClassName());
writer.indent();
writer.print("inner class info name: ");
writer.println(this.innerClassInfoName());
writer.print("outer class info name: ");
writer.println(this.outerClassInfoName());
writer.print("inner class name: ");
writer.println(this.innerClassName());
writer.print("modifier string: ");
writer.println(this.modifierString());
writer.undent();
}
boolean isNamed(short nameIndex) {
return this.innerClassInfoIndex == nameIndex;
}
public boolean isNamed(String name) {
return this.innerClassInfoName().equals(name);
// String innerClassName = this.innerClassName();
// return innerClassName != ANONYMOUS
// && innerClassName.equals(name);
}
/**
* only "member" classes have "declaring" classes
* ("local" and "anonymous" classes do not)
*/
String declaringClassName() {
short index = this.outerClassInfoIndex;
return (index == 0) ? null : this.className(index);
}
/**
* only a "member" class has an outer class of
* the class file's main class
*/
void addDeclaredMemberClassTo(List memberClassNames) {
if (this.isSynthetic()) {
return;
}
// String className = this.classFile().className();
// if (this.outerClassInfoName().equals(className)) {
short classNameIndex = this.classFile().getDeclaration().getThisClassIndex();
if (this.outerClassInfoIndex == classNameIndex) {
memberClassNames.add(this.innerClassInfoName());
}
}
public String innerClassInfoName() {
return this.className(this.innerClassInfoIndex);
}
public String outerClassInfoName() {
short index = this.outerClassInfoIndex;
return (index == 0) ? NOT_APPLICABLE : this.className(index);
}
private String className(short index) {
return this.constantPool().getClassConstant(index).name();
}
public String innerClassName() {
short index = this.innerClassNameIndex;
return (index == 0) ? ANONYMOUS : this.constantPool().getUTF8String(index);
}
public String modifierString() {
return Modifier.toString(this.innerClassAccessFlags);
}
public ConstantPool constantPool() {
return this.pool.constantPool();
}
public ClassFile classFile() {
return this.pool.classFile();
}
/**
* 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.innerClassAccessFlags & 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.innerClassAccessFlags & 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 enum type".
*/
public boolean isEnum() {
return (this.innerClassAccessFlags & ACC_ENUM) != 0;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
public InnerClassesAttribute getPool() {
return this.pool;
}
public short getInnerClassInfoIndex() {
return this.innerClassInfoIndex;
}
public short getOuterClassInfoIndex() {
return this.outerClassInfoIndex;
}
public short getInnerClassNameIndex() {
return this.innerClassNameIndex;
}
public short getInnerClassAccessFlags() {
return this.innerClassAccessFlags;
}
public String toString() {
return ClassTools.shortClassNameForObject(this) + '(' + this.innerClassName() + ')';
}
}