001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.editor;
022:
023: import java.util.*;
024:
025: import EDU.purdue.cs.bloat.reflect.*;
026: import EDU.purdue.cs.bloat.util.*;
027:
028: /**
029: * A ClassEditor provides finer-grain access to a class than a CLassInfo object
030: * does. A ClassEditor takes a ClassInfo and extracts the class's constant pool,
031: * type, super class type, and the types of its interfaces. When editing is
032: * finished, changes are committed with the commit method.
033: *
034: * @see ClassInfo
035: * @see MethodEditor
036: *
037: * @author Nate Nystrom (<a
038: * href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
039: */
040: public class ClassEditor {
041: public static boolean DEBUG = Boolean
042: .getBoolean("ClassEditor.DEBUG");
043:
044: private ConstantPool constants; // A copy of the constant pool of the class
045:
046: // being edited.
047: private ClassInfo classInfo; // (A representation of) the class being
048: // edited
049:
050: private Type type; // An index into constant pool (descriptors) that
051:
052: private Type super class; // specifies the class, superclass, and
053: // interfaces
054:
055: private Type[] interfaces; // of the class being edited.
056:
057: private EditorContext context; // Use to edit classes and methods
058:
059: private boolean dirty; // Has the class been modified?
060:
061: /**
062: * Constructor. Create a new ClassEditor based on information in a ClassInfo
063: * object. It extracts the class's constant pool, and the types of the
064: * class, its superclass, and any interfaces it implements.
065: *
066: * @param context
067: * The <tt>EditorContext</tt> used to edit fields and methods.
068: * @param classInfo
069: * The ClassInfo structure of the class to edit.
070: *
071: * @see EDU.purdue.cs.bloat.reflect.ClassInfo
072: * @see ConstantPool
073: * @see Type
074: */
075: public ClassEditor(final EditorContext context,
076: final ClassInfo classInfo) {
077: this .context = context;
078: this .classInfo = classInfo;
079: this .dirty = false;
080:
081: // Extract the constant pool from the ClassInfo
082: constants = new ConstantPool(classInfo.constants());
083:
084: int index;
085:
086: // Load information (such as the indices of the class, superclass,
087: // and the interfaces) about the class being edited from its
088: // constant pool.
089:
090: index = classInfo.classIndex();
091: type = (Type) constants.constantAt(index);
092:
093: index = classInfo.super classIndex();
094: super class = (Type) constants.constantAt(index);
095:
096: final int ifs[] = classInfo.interfaceIndices();
097: interfaces = new Type[ifs.length];
098:
099: for (int i = 0; i < ifs.length; i++) {
100: interfaces[i] = (Type) constants.constantAt(ifs[i]);
101: }
102:
103: if (ClassEditor.DEBUG) {
104: System.out.println("Editing class " + type);
105: }
106:
107: this .setDirty(false);
108: }
109:
110: /**
111: * Creates a new <code>ClassEditor</code> for editing a class (or
112: * interface) from scratch. This constructor should not be invoked direcly.
113: * Use {@link EditorContext#newClass(int, String, Type, Type[])} instead.
114: */
115: public ClassEditor(final EditorContext context,
116: final int modifiers, final String className,
117: Type super Type, Type[] interfaces) {
118:
119: if (className == null) {
120: final String s = "Cannot have a null class name";
121: throw new IllegalArgumentException(s);
122: }
123:
124: if (super Type == null) {
125: super Type = Type.OBJECT;
126: }
127:
128: if (interfaces == null) {
129: interfaces = new Type[0];
130: }
131:
132: if (ClassEditor.DEBUG) {
133: System.out.println("Creating new class " + className
134: + " extends " + super Type.className());
135: }
136:
137: this .context = context;
138: this .super class = super Type;
139: this .interfaces = interfaces;
140:
141: final ConstantPool cp = new ConstantPool();
142: this .constants = cp;
143:
144: this .type = Type.getType(Type.classDescriptor(className));
145: final int classNameIndex = cp.getClassIndex(this .type);
146: final int super TypeIndex = cp.getClassIndex(super Type);
147: final int[] interfaceIndexes = new int[interfaces.length];
148: for (int i = 0; i < interfaces.length; i++) {
149: interfaceIndexes[i] = cp.getClassIndex(interfaces[i]);
150: }
151:
152: this .classInfo = context.newClassInfo(modifiers,
153: classNameIndex, super TypeIndex, interfaceIndexes, cp
154: .getConstantsList());
155:
156: this .dirty = true;
157: }
158:
159: /**
160: * Returns <tt>true</tt> if the class has been modified.
161: */
162: public boolean isDirty() {
163: return (this .dirty);
164: }
165:
166: /**
167: * Sets this class's dirty flag. The dirty flag is <tt>true</tt> if the
168: * class has been modified.
169: */
170: public void setDirty(final boolean dirty) {
171: this .dirty = dirty;
172: }
173:
174: /**
175: * Returns the name of the class represented by this <tt>ClassEditor</tt>.
176: */
177: public String name() {
178: return (this .classInfo().name());
179: }
180:
181: /**
182: * Obtain the <tt>EditorContext</tt> for this ClassEditor.
183: */
184: public EditorContext context() {
185: return context;
186: }
187:
188: /**
189: * Get the ClassInfo object representing the class that being edited.
190: */
191: public ClassInfo classInfo() {
192: return classInfo;
193: }
194:
195: public boolean isPublic() {
196: return (classInfo.modifiers() & Modifiers.PUBLIC) != 0;
197: }
198:
199: public boolean isPrivate() {
200: return (classInfo.modifiers() & Modifiers.PRIVATE) != 0;
201: }
202:
203: public boolean isProtected() {
204: return (classInfo.modifiers() & Modifiers.PROTECTED) != 0;
205: }
206:
207: public boolean isStatic() {
208: return (classInfo.modifiers() & Modifiers.STATIC) != 0;
209: }
210:
211: public boolean isFinal() {
212: return (classInfo.modifiers() & Modifiers.FINAL) != 0;
213: }
214:
215: public boolean isSuper() {
216: return (classInfo.modifiers() & Modifiers.SUPER) != 0;
217: }
218:
219: public boolean isAbstract() {
220: return (classInfo.modifiers() & Modifiers.ABSTRACT) != 0;
221: }
222:
223: public boolean isInterface() {
224: return (classInfo.modifiers() & Modifiers.INTERFACE) != 0;
225: }
226:
227: public void setPublic(final boolean flag) {
228: int modifiers = classInfo.modifiers();
229:
230: if (flag) {
231: modifiers |= Modifiers.PUBLIC;
232: } else {
233: modifiers &= ~Modifiers.PUBLIC;
234: }
235:
236: classInfo.setModifiers(modifiers);
237: this .setDirty(true);
238: }
239:
240: public void setPrivate(final boolean flag) {
241: int modifiers = classInfo.modifiers();
242:
243: if (flag) {
244: modifiers |= Modifiers.PRIVATE;
245: } else {
246: modifiers &= ~Modifiers.PRIVATE;
247: }
248:
249: classInfo.setModifiers(modifiers);
250: this .setDirty(true);
251: }
252:
253: public void setProtected(final boolean flag) {
254: int modifiers = classInfo.modifiers();
255:
256: if (flag) {
257: modifiers |= Modifiers.PROTECTED;
258: } else {
259: modifiers &= ~Modifiers.PROTECTED;
260: }
261:
262: classInfo.setModifiers(modifiers);
263: this .setDirty(true);
264: }
265:
266: public void setStatic(final boolean flag) {
267: int modifiers = classInfo.modifiers();
268:
269: if (flag) {
270: modifiers |= Modifiers.STATIC;
271: } else {
272: modifiers &= ~Modifiers.STATIC;
273: }
274:
275: classInfo.setModifiers(modifiers);
276: this .setDirty(true);
277: }
278:
279: public void setFinal(final boolean flag) {
280: int modifiers = classInfo.modifiers();
281:
282: if (flag) {
283: modifiers |= Modifiers.FINAL;
284: } else {
285: modifiers &= ~Modifiers.FINAL;
286: }
287:
288: classInfo.setModifiers(modifiers);
289: this .setDirty(true);
290: }
291:
292: public void setSuper(final boolean flag) {
293: int modifiers = classInfo.modifiers();
294:
295: if (flag) {
296: modifiers |= Modifiers.SUPER;
297: } else {
298: modifiers &= ~Modifiers.SUPER;
299: }
300:
301: classInfo.setModifiers(modifiers);
302: this .setDirty(true);
303: }
304:
305: public void setAbstract(final boolean flag) {
306: int modifiers = classInfo.modifiers();
307:
308: if (flag) {
309: modifiers |= Modifiers.ABSTRACT;
310: } else {
311: modifiers &= ~Modifiers.ABSTRACT;
312: }
313:
314: classInfo.setModifiers(modifiers);
315: this .setDirty(true);
316: }
317:
318: public void setInterface(final boolean flag) {
319: int modifiers = classInfo.modifiers();
320:
321: if (flag) {
322: modifiers |= Modifiers.INTERFACE;
323: } else {
324: modifiers &= ~Modifiers.INTERFACE;
325: }
326:
327: classInfo.setModifiers(modifiers);
328: this .setDirty(true);
329: }
330:
331: /**
332: * Sets the Type (descriptor) object for the class.
333: *
334: * @param type
335: * A Type.
336: */
337: public void setType(final Type type) {
338: this .type = type;
339: Assert.isTrue(type.isObject(), "Cannot set class type to "
340: + type);
341: this .setDirty(true);
342: }
343:
344: /**
345: * Returns the Type (descriptor) for the class.
346: */
347: public Type type() {
348: return type;
349: }
350:
351: /**
352: * Returns a Type object for the class's superclass.
353: */
354: public Type super class() {
355: return super class;
356: }
357:
358: /**
359: * Adds an interface of the given class to the set of interfaces that the
360: * class implements.
361: *
362: * @throws IllegalArgumentException <code>interfaceClass</code> is not an
363: * interface
364: */
365: public void addInterface(final Class interfaceClass) {
366: if (!interfaceClass.isInterface()) {
367: final String s = "Cannot add non-interface type: "
368: + interfaceClass.getName();
369: throw new IllegalArgumentException(s);
370: }
371:
372: addInterface(Type.getType(interfaceClass));
373: }
374:
375: /**
376: * Adds an interface of a given Type to the set of interfaces that the class
377: * implements.
378: */
379: public void addInterface(final Type interfaceType) {
380: // // The interface must have an index in the constant pool
381: // this.constants().getClassIndex(interfaceType);
382:
383: final Type[] interfaces = new Type[this .interfaces.length + 1];
384: for (int i = 0; i < this .interfaces.length; i++) {
385: interfaces[i] = this .interfaces[i];
386: }
387: interfaces[interfaces.length - 1] = interfaceType;
388: this .setInterfaces(interfaces);
389: }
390:
391: /**
392: * Returns the interfaces the class implements.
393: *
394: * @param interfaces
395: * An array of Types.
396: */
397: public void setInterfaces(final Type[] interfaces) {
398: this .interfaces = interfaces;
399: this .setDirty(true);
400: }
401:
402: /**
403: * Returns the interfaces the class implements.
404: */
405: public Type[] interfaces() {
406: return interfaces;
407: }
408:
409: /**
410: * Returns the modifiers of the class. The values correspond to the
411: * constants in the <tt>Modifiers</tt> class.
412: *
413: * @return A bit vector of modifier flags for the class.
414: * @see Modifiers
415: */
416: public int modifiers() {
417: return classInfo.modifiers();
418: }
419:
420: /**
421: * Returns an array of <tt>FieldInfo</tt> structures for each field in the
422: * class.
423: */
424: public FieldInfo[] fields() {
425: return classInfo.fields();
426: }
427:
428: /**
429: * Returns an array of MethodInfo structures for each method in the class.
430: */
431: public MethodInfo[] methods() {
432: return classInfo.methods();
433: }
434:
435: /**
436: * Returns the constant pool for the class.
437: */
438: public ConstantPool constants() {
439: return constants;
440: }
441:
442: /**
443: * Commit any changes to the class since creation time. Note that committal
444: * will occur regardless of whether or not the class is dirty.
445: */
446: public void commit() {
447: commitOnly(null, null);
448: }
449:
450: /**
451: * Commits only certain methods and fields. Note that committal will occur
452: * regardless of whether or not the class is dirty.
453: *
454: * @param methods
455: * Methods (<tt>MethodInfo</tt>s) to commit. If <tt>null</tt>,
456: * all methods are committed.
457: * @param fields
458: * Fields (<tt>FieldInfo</tt>s) to commit. If <tt>null</tt>,
459: * all fields are committed.
460: */
461: public void commitOnly(final Set methods, final Set fields) {
462: classInfo.setClassIndex(constants.addConstant(Constant.CLASS,
463: type));
464: classInfo.setSuperclassIndex(constants.addConstant(
465: Constant.CLASS, super class));
466:
467: final int ifs[] = new int[interfaces.length];
468:
469: for (int i = 0; i < ifs.length; i++) {
470: ifs[i] = constants.addConstant(Constant.CLASS,
471: interfaces[i]);
472: }
473:
474: classInfo.setInterfaceIndices(ifs);
475:
476: classInfo.setConstants(constants.constants());
477:
478: classInfo.commitOnly(methods, fields);
479:
480: // This class is no longer dirty
481: this .setDirty(false);
482: }
483:
484: /**
485: * This class is visited by an <tt>EditorVisitor</tt>. First, this
486: * <tt>ClassEditor</tt> itself is visited. Then, all of this class's
487: * fields (<tt>FieldEditor</tt>s) are visited. Finally, each of this
488: * class's methods (<tt>MethodEditor</tt>s) are visited. Constructors
489: * are visited before regular methods.
490: */
491: public void visit(final EditorVisitor visitor) {
492: // First visit ourself
493: visitor.visitClassEditor(this );
494:
495: final EditorContext context = this .context();
496:
497: // Visit each field
498: final FieldInfo[] fields = this .fields();
499: for (int i = 0; i < fields.length; i++) {
500: final FieldEditor fieldEditor = context
501: .editField(fields[i]);
502: visitor.visitFieldEditor(fieldEditor);
503: context.release(fields[i]);
504: }
505:
506: // Visit each method
507: final ArrayList regularMethods = new ArrayList();
508: final MethodInfo[] methods = this .methods();
509: for (int i = 0; i < methods.length; i++) {
510: final MethodEditor methodEditor = context
511: .editMethod(methods[i]);
512:
513: if (methodEditor.name().charAt(0) != '<') {
514: regularMethods.add(methods[i]);
515:
516: } else {
517: visitor.visitMethodEditor(methodEditor);
518: }
519:
520: context.release(methods[i]);
521: }
522:
523: final Iterator iter = regularMethods.iterator();
524: while (iter.hasNext()) {
525: final MethodInfo info = (MethodInfo) iter.next();
526: final MethodEditor me = context.editMethod(info);
527: visitor.visitMethodEditor(me);
528: context.release(info);
529: }
530: }
531:
532: /**
533: * Two <tt>ClassEditor</tt>s are equal if they edit the same class.
534: */
535: public boolean equals(final Object o) {
536: if (o instanceof ClassEditor) {
537: final ClassEditor other = (ClassEditor) o;
538: if (!other.type().equals(this .type())) {
539: return (false);
540: }
541:
542: return (true);
543: }
544:
545: return (false);
546: }
547:
548: /**
549: * A <tt>ClassEditor</tt>'s hash code is based upon the hash code of the
550: * name of the class it edits.
551: */
552: public int hashCode() {
553: return (this .name().hashCode());
554: }
555:
556: public String toString() {
557: return (this.type().toString());
558: }
559:
560: }
|