001: /**
002: * Copyright (C) 2001-2004 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.generation.mivisitor;
018:
019: import org.objectweb.asm.ClassVisitor;
020: import org.objectweb.asm.CodeVisitor;
021: import org.objectweb.asm.Constants;
022: import org.objectweb.asm.ClassReader;
023: import org.objectweb.asm.Attribute;
024: import org.objectweb.speedo.metadata.SpeedoClass;
025: import org.objectweb.speedo.metadata.SpeedoField;
026: import org.objectweb.speedo.api.SpeedoRuntimeException;
027: import org.objectweb.speedo.generation.api.SpeedoCompilerParameter;
028: import org.objectweb.speedo.generation.enhancer.common.AbstractEnhancerComponent;
029: import org.objectweb.speedo.generation.enhancer.common.LoggedClassVisitor;
030: import org.objectweb.speedo.generation.enhancer.common.Util;
031: import org.objectweb.util.monolog.api.Logger;
032: import org.objectweb.util.monolog.api.BasicLevel;
033:
034: import java.io.ObjectStreamClass;
035: import java.util.Iterator;
036: import java.util.Map;
037: import java.util.Collection;
038:
039: /**
040: * Analyzes a class and updates the Speedo meta information accordingly.
041: *
042: * Adapted from storeClassInfo, verify and getField methods in EnhancerTool.
043: */
044: public class ClassInfoAnalyzer extends LoggedClassVisitor implements
045: ClassVisitor {
046:
047: /**
048: * The access enhancer, used to load the super class(es) of the class, if
049: * necessary.
050: */
051: final AbstractEnhancerComponent enhancer;
052:
053: /**
054: * The Speedo meta information for the visited class.
055: */
056: final SpeedoClass moClass;
057:
058: /**
059: * A collection of SpeedoXMLDescriptor describing known persistent classes
060: */
061: private final Collection xmlDescriptors;
062:
063: /**
064: * Creates a new {@link ClassInfoAnalyzer}.
065: *
066: * @param enhancer the access enhancer, used to load the super class(es) of
067: * the class, if necessary.
068: * @param sc the Speedo meta information for the visited class.
069: */
070: public ClassInfoAnalyzer(final AbstractEnhancerComponent enhancer,
071: final SpeedoClass sc, final Collection xmlDescriptors,
072: Logger logger) {
073: super (logger);
074: this .enhancer = enhancer;
075: this .moClass = sc;
076: this .xmlDescriptors = xmlDescriptors;
077: }
078:
079: protected void manageInheritance(String super Name) {
080: // Verifies inheritance
081: String super Class = moClass.getSuperClassName();
082: if (super Class != null
083: && !super Class.equals(super Name.replace('/', '.'))) {
084: throw new SpeedoRuntimeException(
085: "Class "
086: + moClass.getFQName()
087: + " has actually "
088: + super Name
089: + " as super class, whereas the .jdo file spcifies '"
090: + moClass.getSuperClassName() + "'");
091: }
092: }
093:
094: protected void managedInterfaces(String[] interfaces) {
095: // Gets implemented interfaces
096: for (int i = 0; i < interfaces.length; i++) {
097: if (interfaces[i].equals("javax/jdo/InstanceCallbacks"))
098: moClass.isInstanceCallbacks = true;
099: if (interfaces[i].equals("java/io/Serializable"))
100: moClass.isSerializable = true;
101: }
102: }
103:
104: // IMPLEMENTATION OF THE ClassVisitor INTERFACE //
105: // ---------------------------------------------//
106:
107: public void visit(final int version, final int access,
108: final String name, final String super Name,
109: final String[] interfaces, final String sourceFile) {
110: // Stores class's signature
111: moClass.isAbstract = (access & Constants.ACC_ABSTRACT) != 0;
112: manageInheritance(super Name);
113: managedInterfaces(interfaces);
114:
115: // Looks for fields defined in the speedoClass in the Java class and
116: // assigns the corresponding modifier if missing
117: ClassFieldFinder finder = new ClassFieldFinder();
118: if (debug) {
119: logger.log(BasicLevel.DEBUG, "field names: "
120: + moClass.fields.keySet());
121: logger.log(BasicLevel.DEBUG, "fields ["
122: + moClass.fields.values().size() + "]: "
123: + moClass.fields.values());
124: }
125: for (Iterator it = moClass.fields.values().iterator(); it
126: .hasNext();) {
127: SpeedoField current = (SpeedoField) it.next();
128: if (debug) {
129: logger.log(BasicLevel.DEBUG, "treat field "
130: + current.name);
131: }
132: if (!finder.fillInfo(current)) {
133: throw new SpeedoRuntimeException("Persistent Field "
134: + current.name + " not defined in class "
135: + name);
136: }
137: if (current.persistenceStatus == SpeedoField.UNKNOWN) {
138: current.persistenceStatus = Util.isPersistentType(
139: current.type, xmlDescriptors) ? SpeedoField.PERSISTENT
140: : SpeedoField.NONE;
141: }
142: }
143: // Stores the serial version uid for serializable classes
144: if (moClass.isSerializable) {
145: try {
146: ObjectStreamClass oserial = ObjectStreamClass
147: .lookup(Class.forName(name.replace('/', '.')));
148: } catch (Exception e) {
149: logger
150: .log(
151: BasicLevel.WARN,
152: "Impossible to find the VersionUID of the class",
153: e);
154: }
155: }
156: }
157:
158: public void visitInnerClass(final String name,
159: final String outerName, final String innerName,
160: final int access) {
161: // does nothing
162: }
163:
164: public void visitField(final int access, final String name,
165: final String desc, final Object value, final Attribute attrs) {
166: // Adds in the object model fields whose type is known as
167: // Persistence Capable
168: if ((access & Constants.ACC_STATIC) == 0
169: && (access & Constants.ACC_FINAL) == 0
170: && (access & Constants.ACC_SYNTHETIC) == 0) {
171: SpeedoField jdoField = (SpeedoField) moClass.fields
172: .get(name);
173: if (jdoField == null
174: && Util.isAutomaticPersistentType(desc,
175: xmlDescriptors)) {
176: jdoField = new SpeedoField();
177: jdoField.persistenceStatus = SpeedoField.PERSISTENT;
178: jdoField.name = name;
179: jdoField.visibility = access;
180: jdoField.type = desc;
181: moClass.add(jdoField);
182: }
183: }
184: }
185:
186: public CodeVisitor visitMethod(final int access, final String name,
187: final String desc, final String[] exceptions,
188: final Attribute attrs) {
189: if ("<init>".equals(name) && "()V".equals(desc)) {
190: moClass.noArgConstructorStatus = ((access & Constants.ACC_PUBLIC) == 0 ? SpeedoClass.NON_PUBLIC_NO_ARG_CONSTRUCTOR
191: : SpeedoClass.PUBLIC_NO_ARG_CONSTRUCTOR);
192: if (logger.isLoggable(BasicLevel.DEBUG)) {
193: logger.log(BasicLevel.DEBUG, moClass.getFQName()
194: + ".noArgConstructorStatus="
195: + moClass.noArgConstructorStatus);
196: }
197: }
198: return null;
199: }
200:
201: public void visitAttribute(Attribute attribute) {
202: // does nothing
203: }
204:
205: public void visitEnd() {
206: // Removes from the object model all fields that are not defined as
207: // persistent
208: for (Iterator it = moClass.fields.entrySet().iterator(); it
209: .hasNext();) {
210: Map.Entry entry = (Map.Entry) it.next();
211: SpeedoField field = (SpeedoField) entry.getValue();
212: if (field.persistenceStatus != SpeedoField.PERSISTENT) {
213: it.remove();
214: }
215: }
216: }
217:
218: /**
219: * Looks for a field in a class, and in its (persistent) super classes.
220: */
221: class ClassFieldFinder implements ClassVisitor {
222:
223: /**
224: * Information about this field, if found.
225: */
226: private SpeedoField result;
227: boolean find = false;
228:
229: public boolean fillInfo(SpeedoField sf) {
230: this .result = sf;
231: String className = sf.moClass.getFQName();
232: SpeedoCompilerParameter scp = enhancer
233: .getSpeedoCompilerParameter();
234: try {
235: find = false;
236: while (true) {
237: // lookup in the class if the field exists
238: ClassReader cr = enhancer.loadJavaClass(
239: enhancer.isSrcJar, className, scp.output,
240: false);
241: cr.accept(this , true);
242: if (find) {
243: return true;
244: }
245:
246: // lookup in the super class, if applicable
247: className = moClass.getSuperClassName();
248: if (className == null) {
249: // The current class has not a superclasses and the
250: // field has not been found
251: return false;
252: }
253: }
254: } catch (Exception e) {
255: e.printStackTrace();
256: return false;
257: }
258: }
259:
260: public void visit(final int version, final int access,
261: final String name, final String super Name,
262: final String[] interfaces, final String sourceFile) {
263: // does nothing
264: }
265:
266: public void visitInnerClass(final String name,
267: final String outerName, final String innerName,
268: final int access) {
269: // does nothing
270: }
271:
272: public void visitField(final int access, final String name,
273: final String desc, final Object value,
274: final Attribute attrs) {
275: if (name.equals(result.name)) {
276: // we have found the field we are looking for
277: result.visibility = access;
278: result.type = desc;
279: find = true;
280: }
281: }
282:
283: public CodeVisitor visitMethod(final int access,
284: final String name, final String desc,
285: final String[] exceptions, final Attribute attrs) {
286: // this visitor does not need to visit the code of methods
287: return null;
288: }
289:
290: public void visitAttribute(Attribute attribute) {
291: // does nothing
292: }
293:
294: public void visitEnd() {
295: // does nothing
296: }
297: }
298: }
|