/**
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.objectweb.speedo.generation.mivisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.CodeVisitor;
import org.objectweb.asm.Constants;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Attribute;
import org.objectweb.speedo.metadata.SpeedoClass;
import org.objectweb.speedo.metadata.SpeedoField;
import org.objectweb.speedo.api.SpeedoRuntimeException;
import org.objectweb.speedo.generation.api.SpeedoCompilerParameter;
import org.objectweb.speedo.generation.enhancer.common.AbstractEnhancerComponent;
import org.objectweb.speedo.generation.enhancer.common.LoggedClassVisitor;
import org.objectweb.speedo.generation.enhancer.common.Util;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;
import java.io.ObjectStreamClass;
import java.util.Iterator;
import java.util.Map;
import java.util.Collection;
/**
* Analyzes a class and updates the Speedo meta information accordingly.
*
* Adapted from storeClassInfo, verify and getField methods in EnhancerTool.
*/
public class ClassInfoAnalyzer extends LoggedClassVisitor implements ClassVisitor {
/**
* The access enhancer, used to load the super class(es) of the class, if
* necessary.
*/
final AbstractEnhancerComponent enhancer;
/**
* The Speedo meta information for the visited class.
*/
final SpeedoClass moClass;
/**
* A collection of SpeedoXMLDescriptor describing known persistent classes
*/
private final Collection xmlDescriptors;
/**
* Creates a new {@link ClassInfoAnalyzer}.
*
* @param enhancer the access enhancer, used to load the super class(es) of
* the class, if necessary.
* @param sc the Speedo meta information for the visited class.
*/
public ClassInfoAnalyzer(final AbstractEnhancerComponent enhancer,
final SpeedoClass sc,
final Collection xmlDescriptors,
Logger logger) {
super(logger);
this.enhancer = enhancer;
this.moClass = sc;
this.xmlDescriptors = xmlDescriptors;
}
protected void manageInheritance(String superName) {
// Verifies inheritance
String superClass = moClass.getSuperClassName();
if (superClass != null && !superClass.equals(superName.replace('/', '.'))) {
throw new SpeedoRuntimeException("Class " + moClass.getFQName() + " has actually " +
superName + " as super class, whereas the .jdo file spcifies '"
+ moClass.getSuperClassName() + "'");
}
}
protected void managedInterfaces(String[] interfaces) {
// Gets implemented interfaces
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals("javax/jdo/InstanceCallbacks"))
moClass.isInstanceCallbacks = true;
if (interfaces[i].equals("java/io/Serializable"))
moClass.isSerializable = true;
}
}
// IMPLEMENTATION OF THE ClassVisitor INTERFACE //
// ---------------------------------------------//
public void visit(final int version, final int access,
final String name,
final String superName,
final String[] interfaces,
final String sourceFile) {
// Stores class's signature
moClass.isAbstract = (access & Constants.ACC_ABSTRACT) != 0;
manageInheritance(superName);
managedInterfaces(interfaces);
// Looks for fields defined in the speedoClass in the Java class and
// assigns the corresponding modifier if missing
ClassFieldFinder finder = new ClassFieldFinder();
if (debug) {
logger.log(BasicLevel.DEBUG, "field names: "
+ moClass.fields.keySet());
logger.log(BasicLevel.DEBUG, "fields ["
+ moClass.fields.values().size() + "]: "
+ moClass.fields.values());
}
for(Iterator it = moClass.fields.values().iterator(); it.hasNext();) {
SpeedoField current = (SpeedoField) it.next();
if (debug) {
logger.log(BasicLevel.DEBUG, "treat field " + current.name);
}
if (!finder.fillInfo(current)) {
throw new SpeedoRuntimeException(
"Persistent Field " + current.name +
" not defined in class " + name);
}
if (current.persistenceStatus == SpeedoField.UNKNOWN) {
current.persistenceStatus =
Util.isPersistentType(current.type, xmlDescriptors)
? SpeedoField.PERSISTENT
: SpeedoField.NONE;
}
}
// Stores the serial version uid for serializable classes
if (moClass.isSerializable) {
try {
ObjectStreamClass oserial = ObjectStreamClass.lookup(
Class.forName(name.replace('/', '.')));
} catch (Exception e) {
logger.log(BasicLevel.WARN,
"Impossible to find the VersionUID of the class", e);
}
}
}
public void visitInnerClass(final String name,
final String outerName,
final String innerName,
final int access) {
// does nothing
}
public void visitField(final int access,
final String name,
final String desc,
final Object value,
final Attribute attrs) {
// Adds in the object model fields whose type is known as
// Persistence Capable
if ((access & Constants.ACC_STATIC) == 0
&& (access & Constants.ACC_FINAL) == 0
&& (access & Constants.ACC_SYNTHETIC) == 0) {
SpeedoField jdoField = (SpeedoField) moClass.fields.get(name);
if (jdoField == null
&& Util.isAutomaticPersistentType(desc, xmlDescriptors)) {
jdoField = new SpeedoField();
jdoField.persistenceStatus = SpeedoField.PERSISTENT;
jdoField.name = name;
jdoField.visibility = access;
jdoField.type = desc;
moClass.add(jdoField);
}
}
}
public CodeVisitor visitMethod(final int access,
final String name,
final String desc,
final String[] exceptions,
final Attribute attrs) {
if ("<init>".equals(name) && "()V".equals(desc)) {
moClass.noArgConstructorStatus = ((access & Constants.ACC_PUBLIC) == 0
? SpeedoClass.NON_PUBLIC_NO_ARG_CONSTRUCTOR
: SpeedoClass.PUBLIC_NO_ARG_CONSTRUCTOR);
if (logger.isLoggable(BasicLevel.DEBUG)) {
logger.log(BasicLevel.DEBUG, moClass.getFQName()
+ ".noArgConstructorStatus=" + moClass.noArgConstructorStatus);
}
}
return null;
}
public void visitAttribute(Attribute attribute) {
// does nothing
}
public void visitEnd() {
// Removes from the object model all fields that are not defined as
// persistent
for (Iterator it = moClass.fields.entrySet().iterator();it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
SpeedoField field = (SpeedoField) entry.getValue();
if (field.persistenceStatus != SpeedoField.PERSISTENT) {
it.remove();
}
}
}
/**
* Looks for a field in a class, and in its (persistent) super classes.
*/
class ClassFieldFinder implements ClassVisitor {
/**
* Information about this field, if found.
*/
private SpeedoField result;
boolean find = false;
public boolean fillInfo(SpeedoField sf) {
this.result = sf;
String className = sf.moClass.getFQName();
SpeedoCompilerParameter scp = enhancer.getSpeedoCompilerParameter();
try {
find = false;
while (true) {
// lookup in the class if the field exists
ClassReader cr = enhancer.loadJavaClass(enhancer.isSrcJar,
className, scp.output, false);
cr.accept(this, true);
if (find) {
return true;
}
// lookup in the super class, if applicable
className = moClass.getSuperClassName();
if (className == null) {
// The current class has not a superclasses and the
// field has not been found
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void visit(final int version,
final int access,
final String name,
final String superName,
final String[] interfaces,
final String sourceFile) {
// does nothing
}
public void visitInnerClass(final String name,
final String outerName,
final String innerName,
final int access) {
// does nothing
}
public void visitField(final int access,
final String name,
final String desc,
final Object value,
final Attribute attrs) {
if (name.equals(result.name)) {
// we have found the field we are looking for
result.visibility = access;
result.type = desc;
find = true;
}
}
public CodeVisitor visitMethod(final int access,
final String name,
final String desc,
final String[] exceptions,
final Attribute attrs) {
// this visitor does not need to visit the code of methods
return null;
}
public void visitAttribute(Attribute attribute) {
// does nothing
}
public void visitEnd() {
// does nothing
}
}
}