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.context;
022:
023: import java.util.*;
024:
025: import EDU.purdue.cs.bloat.editor.*;
026: import EDU.purdue.cs.bloat.reflect.*;
027:
028: /**
029: * Maintains all BLOAT data structures as if they were meant to reside in a
030: * persistent store. As a result, it keeps every piece of BLOAT data around
031: * because it might be needed in the future. No fancing cache maintainence is
032: * performed. Because we are going for maximum information we take the closure
033: * of classes when working with the class hierarchy.
034: */
035: public class PersistentBloatContext extends BloatContext {
036:
037: protected final ClassHierarchy hierarchy;
038:
039: protected Map classInfos; // Maps Strings to ClassInfos
040:
041: protected Map methodInfos; // Maps MemberRefs to MethodInfos
042:
043: protected Map fieldInfos; // Maps MemberRefs to FieldInfos
044:
045: protected Map classEditors; // Maps ClassInfos to ClassEditors
046:
047: protected Map methodEditors; // Maps MethodInfos to MethodEditors
048:
049: protected Map fieldEditors; // Maps MethodInfos to FieldEditors
050:
051: public static boolean DB_COMMIT = false;
052:
053: protected static void comm(final String s) {
054: if (PersistentBloatContext.DB_COMMIT || BloatContext.DEBUG) {
055: System.out.println(s);
056: }
057: }
058:
059: /**
060: * Constructor. Each <tt>BloatContext</tt> stems from a
061: * <tt>ClassInfoLoader</tt>. Using the loader it can create an
062: * <tt>Editor</tt> and such. Initially, no classes are loaded.
063: */
064: public PersistentBloatContext(final ClassInfoLoader loader) {
065: this (loader, true);
066: }
067:
068: /**
069: * Constructor. It is the responsibility of the subclasses to add classes to
070: * the hierarchy by calling <tt>addClasses</tt>.
071: *
072: * @param loader
073: * Used to load classes
074: * @param closure
075: * Do we look for the maximum number of classes?
076: */
077: protected PersistentBloatContext(final ClassInfoLoader loader,
078: final boolean closure) {
079: super (loader);
080: BloatContext.db("Creating a new BloatContext");
081:
082: // Create a bunch of the mappings we maintain. Make sure to do
083: // this before anything else!
084: classInfos = new HashMap();
085: methodInfos = new HashMap();
086: fieldInfos = new HashMap();
087:
088: classEditors = new HashMap();
089: methodEditors = new HashMap();
090: fieldEditors = new HashMap();
091:
092: // Have to create an empty class hierarchy then add the classes.
093: // There is a strange circular dependence between the hierarchy
094: // and the context.
095: this .hierarchy = new ClassHierarchy(this , new ArrayList(),
096: closure);
097:
098: }
099:
100: /**
101: * Adds a bunch of (names of) classes to the hierarchy.
102: */
103: protected void addClasses(final Collection classes) {
104: final Iterator iter = classes.iterator();
105: while (iter.hasNext()) {
106: final String className = (String) iter.next();
107: this .hierarchy.addClassNamed(className);
108: }
109: }
110:
111: public ClassInfo loadClass(String className)
112: throws ClassNotFoundException {
113: // Lots of interesting stuff to do here. For the moment, just
114: // load the class from the ClassInfoLoader and add it to the
115: // hierarchy.
116:
117: className = className.replace('.', '/').intern();
118:
119: // Check the cache of ClassInfos
120: ClassInfo info = (ClassInfo) classInfos.get(className);
121:
122: if (info == null) {
123: BloatContext.db("BloatContext: Loading class " + className);
124: info = loader.loadClass(className);
125: hierarchy.addClassNamed(className);
126: BloatContext.db("loadClass: " + className + " -> " + info);
127: classInfos.put(className, info);
128: }
129:
130: return (info);
131: }
132:
133: public ClassInfo newClassInfo(final int modifiers,
134: final int classIndex, final int super ClassIndex,
135: final int[] interfaceIndexes, final List constants) {
136:
137: return this .loader.newClass(modifiers, classIndex,
138: super ClassIndex, interfaceIndexes, constants);
139: }
140:
141: public ClassHierarchy getHierarchy() {
142: return (this .hierarchy);
143: }
144:
145: public ClassEditor newClass(final int modifiers, String className,
146: final Type super Type, final Type[] interfaces) {
147:
148: final ClassEditor ce = new ClassEditor(this , modifiers,
149: className, super Type, interfaces);
150: final ClassInfo info = ce.classInfo();
151:
152: className = ce.name().intern();
153:
154: BloatContext.db("editClass(ClassInfo): " + className + " -> "
155: + info);
156:
157: classInfos.put(className, info);
158: classEditors.put(info, ce);
159:
160: return ce;
161: }
162:
163: public ClassEditor editClass(String className)
164: throws ClassNotFoundException, ClassFormatException {
165: // Only make the name -> classInfo mapping if we edit the class,
166: // this way the mapping will be deleted when the ClassEditor is
167: // released.
168:
169: className = className.intern();
170:
171: ClassInfo info = (ClassInfo) classInfos.get(className);
172:
173: if (info == null) {
174: info = loadClass(className);
175: // db("editClass(String): " + className + " -> " + info);
176: // classInfos.put(className, info);
177: }
178:
179: return (editClass(info));
180: }
181:
182: public ClassEditor editClass(final Type classType)
183: throws ClassNotFoundException, ClassFormatException {
184: return (editClass(classType.className()));
185: }
186:
187: public ClassEditor editClass(final ClassInfo info) {
188: // Check the cache
189: ClassEditor ce = (ClassEditor) classEditors.get(info);
190:
191: if (ce == null) {
192: ce = new ClassEditor(this , info);
193: classEditors.put(info, ce);
194:
195: if (!classInfos.containsValue(info)) {
196: final String className = ce.name().intern();
197: BloatContext.db("editClass(ClassInfo): " + className
198: + " -> " + info);
199: classInfos.put(className, info);
200: }
201: }
202:
203: return (ce);
204: }
205:
206: public MethodEditor editMethod(final MemberRef method)
207: throws NoSuchMethodException {
208:
209: // Check the MethodInfo cache
210: final MethodInfo info = (MethodInfo) methodInfos.get(method);
211:
212: if (info == null) {
213: // Groan, we have to do this the HARD way.
214: BloatContext
215: .db("Creating a new MethodEditor for " + method);
216: final NameAndType nat = method.nameAndType();
217: final String name = nat.name();
218: final Type type = nat.type();
219:
220: try {
221: final ClassEditor ce = editClass(method
222: .declaringClass());
223: final MethodInfo[] methods = ce.methods();
224:
225: for (int i = 0; i < methods.length; i++) {
226: final MethodEditor me = editMethod(methods[i]);
227:
228: if (me.name().equals(name)
229: && me.type().equals(type)) {
230: // The call to editMethod should have already handled
231: // the
232: // methodEditors mapping, but we still need to do
233: // methodInfos.
234: methodInfos.put(method, methods[i]);
235: release(ce.classInfo());
236: return (me);
237: }
238:
239: release(methods[i]);
240: }
241:
242: } catch (final ClassNotFoundException ex1) {
243: } catch (final ClassFormatException ex2) {
244: }
245:
246: throw new NoSuchMethodException(method.toString());
247: }
248:
249: return (editMethod(info));
250: }
251:
252: public MethodEditor editMethod(final MethodInfo info) {
253: // Check methodEditors cache
254: MethodEditor me = (MethodEditor) methodEditors.get(info);
255:
256: if (me == null) {
257: me = new MethodEditor(editClass(info.declaringClass()),
258: info);
259: methodEditors.put(info, me);
260: BloatContext.db("Creating a new MethodEditor for "
261: + me.memberRef());
262: }
263:
264: return (me);
265: }
266:
267: public FieldEditor editField(final MemberRef field)
268: throws NoSuchFieldException {
269:
270: // Just like we had to do with methods
271: final FieldInfo info = (FieldInfo) fieldInfos.get(field);
272:
273: if (info == null) {
274: final NameAndType nat = field.nameAndType();
275: final String name = nat.name();
276: final Type type = nat.type();
277:
278: try {
279: final ClassEditor ce = editClass(field.declaringClass());
280: final FieldInfo[] fields = ce.fields();
281:
282: for (int i = 0; i < fields.length; i++) {
283: final FieldEditor fe = editField(fields[i]);
284:
285: if (fe.name().equals(name)
286: && fe.type().equals(type)) {
287: fieldInfos.put(field, fields[i]);
288: release(ce.classInfo());
289: return (fe);
290: }
291:
292: release(fields[i]);
293: }
294: } catch (final ClassNotFoundException ex1) {
295: } catch (final ClassFormatException ex2) {
296: }
297:
298: throw new NoSuchFieldException(field.toString());
299: }
300:
301: return (editField(info));
302: }
303:
304: public FieldEditor editField(final FieldInfo info) {
305: // Check the cache
306: FieldEditor fe = (FieldEditor) fieldEditors.get(info);
307:
308: if (fe == null) {
309: fe = new FieldEditor(editClass(info.declaringClass()), info);
310: fieldEditors.put(info, fe);
311: BloatContext.db("Creating a new FieldEditor for "
312: + fe.nameAndType());
313: }
314:
315: return (fe);
316: }
317:
318: public void release(final ClassInfo info) {
319: // Since we keep around all data, do nothing
320: }
321:
322: public void release(final ClassEditor ce) {
323: // Since we keep around all data, do nothing
324: }
325:
326: public void release(final MethodInfo info) {
327: // Since we keep around all data, do nothing
328: }
329:
330: public void release(final FieldInfo info) {
331: // Since we keep around all data, do nothing
332: }
333:
334: /**
335: * Classes that are ignored are not committed.
336: *
337: * @see #ignoreClass(Type)
338: */
339: public void commit(final ClassInfo info) {
340: final Type type = Type.getType("L" + info.name() + ";");
341: if (ignoreClass(type)) {
342: return;
343: }
344:
345: final ClassEditor ce = editClass(info);
346:
347: // Commit all of the class's methods and fields
348: final MethodInfo[] methods = ce.methods();
349: for (int i = 0; i < methods.length; i++) {
350: commit(methods[i]);
351: }
352:
353: final FieldInfo[] fields = ce.fields();
354: for (int i = 0; i < fields.length; i++) {
355: commit(fields[i]);
356: }
357:
358: ce.commit();
359:
360: ce.setDirty(false);
361: release(info);
362: }
363:
364: public void commit(final MethodInfo info) {
365: final MethodEditor me = editMethod(info);
366: me.commit();
367:
368: // We make the method's class dirty so it, too, will be committed
369: me.declaringClass().setDirty(true);
370: me.setDirty(false);
371: release(info);
372: }
373:
374: public void commit(final FieldInfo info) {
375: final FieldEditor fe = editField(info);
376: fe.commit();
377:
378: // We make the method's class dirty so it, too, will be committed
379: fe.declaringClass().setDirty(true);
380: fe.setDirty(false);
381: release(info);
382: }
383:
384: public void commit() {
385: Object[] array = fieldEditors.values().toArray();
386: for (int i = 0; i < array.length; i++) {
387: final FieldEditor fe = (FieldEditor) array[i];
388: if (!ignoreField(fe.memberRef())) {
389: commit(fe.fieldInfo());
390: }
391: }
392:
393: array = methodEditors.values().toArray();
394: for (int i = 0; i < array.length; i++) {
395: final MethodEditor me = (MethodEditor) array[i];
396: if (!ignoreMethod(me.memberRef())) {
397: commit(me.methodInfo());
398: }
399: }
400:
401: array = classEditors.values().toArray();
402: for (int i = 0; i < array.length; i++) {
403: final ClassEditor ce = (ClassEditor) array[i];
404: if (!ignoreClass(ce.type())) {
405: commit(ce.classInfo());
406: }
407: }
408: }
409:
410: public void commitDirty() {
411: PersistentBloatContext.comm("Committing dirty data");
412:
413: // Commit all dirty fields
414: Object[] array = this .fieldEditors.values().toArray();
415: for (int i = 0; i < array.length; i++) {
416: final FieldEditor fe = (FieldEditor) array[i];
417: if (fe.isDirty() && !ignoreField(fe.memberRef())) {
418: PersistentBloatContext.comm(" Committing field: "
419: + fe.declaringClass().name() + "." + fe.name());
420: commit(fe.fieldInfo());
421: }
422: }
423:
424: // Commit all dirty methods
425: array = this .methodEditors.values().toArray();
426: for (int i = 0; i < array.length; i++) {
427: final MethodEditor me = (MethodEditor) array[i];
428: if (me.isDirty() && !ignoreMethod(me.memberRef())) {
429: PersistentBloatContext.comm(" Committing method: "
430: + me.declaringClass().name() + "." + me.name()
431: + me.type());
432: commit(me.methodInfo());
433: }
434: }
435:
436: // Commit all dirty classes
437: array = this .classEditors.values().toArray();
438: for (int i = 0; i < array.length; i++) {
439: final ClassEditor ce = (ClassEditor) array[i];
440: if (ce.isDirty() && !ignoreClass(ce.type())) {
441: PersistentBloatContext.comm(" Committing class: "
442: + ce.name());
443: commit(ce.classInfo());
444: }
445: }
446: }
447: }
|