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.io.*;
024: import java.util.*;
025:
026: import EDU.purdue.cs.bloat.editor.*;
027: import EDU.purdue.cs.bloat.reflect.*;
028:
029: /**
030: * Does a lot of the same stuff as <tt>PersistentBloatContext</tt> except that
031: * it manages the chaches of BLOAT objects. For example, when a
032: * <tt>MethodEditor</tt> is no longer needed, it is removed from the cache if
033: * it is not dirty. This context is meant to used in volatile memory.
034: */
035: public class CachingBloatContext extends PersistentBloatContext {
036:
037: // Keep track of reference counts in a manner reminiscent of the old
038: // Editor class.
039: protected Map classRC;
040:
041: protected Map methodRC;
042:
043: protected Map fieldRC;
044:
045: /**
046: * Constructor.
047: *
048: * @param loader
049: * Used to load classes
050: * @param classes
051: * Some initial classes in the context
052: * @param closure
053: * Do we look for the maximum number of classes?
054: */
055: public CachingBloatContext(final ClassInfoLoader loader,
056: final Collection classes, final boolean closure) {
057: super (loader, closure);
058:
059: classRC = new HashMap();
060: methodRC = new HashMap();
061: fieldRC = new HashMap();
062:
063: addClasses(classes);
064: }
065:
066: public ClassEditor newClass(final int modifiers,
067: final String className, final Type super Type,
068: final Type[] interfaces) {
069:
070: final ClassEditor ce = super .newClass(modifiers, className,
071: super Type, interfaces);
072: final ClassInfo info = ce.classInfo();
073: classRC.put(info, new Integer(1));
074:
075: return ce;
076: }
077:
078: public ClassEditor editClass(final ClassInfo info) {
079: // Check the cache
080: ClassEditor ce = (ClassEditor) classEditors.get(info);
081:
082: if (ce == null) {
083: ce = new ClassEditor(this , info);
084: classEditors.put(info, ce);
085: classRC.put(info, new Integer(1));
086:
087: if (!classInfos.containsValue(info)) {
088: final String className = ce.name().intern();
089: BloatContext.db("editClass(ClassInfo): " + className
090: + " -> " + info);
091: classInfos.put(className, info);
092: }
093:
094: } else {
095: final Integer rc = (Integer) classRC.get(info);
096: classRC.put(info, new Integer(rc.intValue() + 1));
097: }
098:
099: return (ce);
100: }
101:
102: public MethodEditor editMethod(final MemberRef method)
103: throws NoSuchMethodException {
104:
105: // Check the MethodInfo cache
106: final MethodInfo info = (MethodInfo) methodInfos.get(method);
107:
108: if (info == null) {
109: // Groan, we have to do this the HARD way.
110: BloatContext
111: .db("Creating a new MethodEditor for " + method);
112: final NameAndType nat = method.nameAndType();
113: final String name = nat.name();
114: final Type type = nat.type();
115:
116: try {
117: final ClassEditor ce = editClass(method
118: .declaringClass());
119: final MethodInfo[] methods = ce.methods();
120:
121: for (int i = 0; i < methods.length; i++) {
122: final MethodEditor me = editMethod(methods[i]);
123:
124: if (me.name().equals(name)
125: && me.type().equals(type)) {
126: // The call to editMethod should have already handled
127: // the
128: // methodEditors mapping, but we still need to do
129: // methodInfos.
130: methodInfos.put(method, methods[i]);
131: release(ce.classInfo());
132: return (me);
133: }
134: }
135:
136: release(ce.classInfo());
137:
138: } catch (final ClassNotFoundException ex1) {
139: throw new NoSuchMethodException(method.toString() + "("
140: + ex1.getMessage() + ")");
141:
142: } catch (final ClassFormatException ex2) {
143: throw new NoSuchMethodException(method.toString() + "("
144: + ex2.getMessage() + ")");
145:
146: }
147:
148: throw new NoSuchMethodException(method.toString());
149: }
150:
151: return (editMethod(info));
152: }
153:
154: public MethodEditor editMethod(final MethodInfo info) {
155: // Check methodEditors cache
156: MethodEditor me = (MethodEditor) methodEditors.get(info);
157:
158: if (me == null) {
159: final ClassInfo classInfo = info.declaringClass();
160: me = new MethodEditor(editClass(classInfo), info);
161: release(classInfo);
162:
163: methodEditors.put(info, me);
164: methodRC.put(info, new Integer(1));
165: BloatContext.db("Creating a new MethodEditor for "
166: + me.memberRef());
167:
168: } else {
169: final Integer rc = (Integer) methodRC.get(info);
170: methodRC.put(info, new Integer(rc.intValue() + 1));
171: }
172:
173: return (me);
174: }
175:
176: public FieldEditor editField(final MemberRef field)
177: throws NoSuchFieldException {
178:
179: // Just like we had to do with methods
180: final FieldInfo info = (FieldInfo) fieldInfos.get(field);
181:
182: if (info == null) {
183: final NameAndType nat = field.nameAndType();
184: final String name = nat.name();
185: final Type type = nat.type();
186:
187: try {
188: final ClassEditor ce = editClass(field.declaringClass());
189: final FieldInfo[] fields = ce.fields();
190:
191: for (int i = 0; i < fields.length; i++) {
192: final FieldEditor fe = editField(fields[i]);
193:
194: if (fe.name().equals(name)
195: && fe.type().equals(type)) {
196: fieldInfos.put(field, fields[i]);
197: release(ce.classInfo());
198: return (fe);
199: }
200:
201: release(fields[i]);
202: }
203:
204: release(ce.classInfo());
205: } catch (final ClassNotFoundException ex1) {
206: } catch (final ClassFormatException ex2) {
207: }
208:
209: throw new NoSuchFieldException(field.toString());
210: }
211:
212: return (editField(info));
213: }
214:
215: public FieldEditor editField(final FieldInfo info) {
216: // Check the cache
217: FieldEditor fe = (FieldEditor) fieldEditors.get(info);
218:
219: BloatContext.db("Editing " + info);
220:
221: if (fe == null) {
222: final ClassInfo classInfo = info.declaringClass();
223: fe = new FieldEditor(editClass(classInfo), info);
224: release(classInfo);
225:
226: fieldEditors.put(info, fe);
227: fieldRC.put(info, new Integer(0));
228: BloatContext.db("Creating a new FieldEditor for "
229: + fe.nameAndType());
230:
231: } else {
232: final Integer rc = (Integer) fieldRC.get(info);
233: fieldRC.put(info, new Integer(rc.intValue() + 1));
234:
235: }
236:
237: return (fe);
238: }
239:
240: public void release(final ClassInfo info) {
241: final Integer rc = (Integer) classRC.get(info);
242:
243: if ((rc != null) && (rc.intValue() > 1)) {
244: // Not done yet;
245: classRC.put(info, new Integer(rc.intValue() - 1));
246: return;
247:
248: }
249:
250: ClassEditor ce = (ClassEditor) classEditors.get(info);
251: if ((ce != null) && ce.isDirty()) {
252: return;
253: }
254:
255: // We're done with this class, remove all traces of it
256: ce = (ClassEditor) classEditors.remove(info);
257: classRC.remove(info);
258: classEditors.remove(info);
259:
260: final Iterator iter = classInfos.keySet().iterator();
261: while (iter.hasNext()) {
262: final String name = (String) iter.next();
263: final ClassInfo info2 = (ClassInfo) classInfos.get(name);
264: if (info2 == info) {
265: BloatContext.db("Removing ClassInfo: " + name + " -> "
266: + info2);
267: classInfos.remove(name);
268: break;
269: }
270: }
271:
272: if (ce != null) {
273: // Remove all of the class's fields and methods also
274: final MethodInfo[] methods = ce.methods();
275: for (int i = 0; i < methods.length; i++) {
276: release(methods[i]);
277: }
278:
279: final FieldInfo[] fields = ce.fields();
280: for (int i = 0; i < fields.length; i++) {
281: release(fields[i]);
282: }
283: }
284:
285: }
286:
287: public void release(final MethodInfo info) {
288: final Integer rc = (Integer) classRC.get(info);
289:
290: if ((rc != null) && (rc.intValue() > 1)) {
291: methodRC.put(info, new Integer(rc.intValue() - 1));
292: return;
293: }
294:
295: final MethodEditor me = (MethodEditor) methodEditors.get(info);
296:
297: // We should keep dirty methods around. My original thought was
298: // that if we committed dirty methods when they were released, we
299: // risk having MethodEditors editing different versions of the
300: // same method. So, if we don't release dirty methods, we'll only
301: // have ONE MethodEditor.
302: if ((me != null) && me.isDirty()) {
303: return;
304: }
305:
306: // We're done with this method, remove all traces of it
307: methodRC.remove(info);
308: methodEditors.remove(info);
309:
310: final Iterator iter = methodInfos.keySet().iterator();
311: while (iter.hasNext()) {
312: final MemberRef ref = (MemberRef) iter.next();
313: final MethodInfo info2 = (MethodInfo) methodInfos.get(ref);
314: if (info2 == info) {
315: methodInfos.remove(ref);
316: break;
317: }
318: }
319: }
320:
321: public void release(final FieldInfo info) {
322: final Integer rc = (Integer) fieldRC.get(info);
323:
324: BloatContext.db("Releasing " + info);
325:
326: if ((rc != null) && (rc.intValue() > 1)) {
327: fieldRC.put(info, new Integer(rc.intValue() - 1));
328: return;
329: }
330:
331: final FieldEditor fe = (FieldEditor) fieldEditors.get(info);
332: if ((fe != null) && fe.isDirty()) {
333: return;
334: }
335:
336: // We're done with this field, remove all traces of it
337: fieldRC.remove(info);
338: fieldEditors.remove(info);
339:
340: final Iterator iter = fieldInfos.keySet().iterator();
341: while (iter.hasNext()) {
342: final MemberRef ref = (MemberRef) iter.next();
343: final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref);
344: if (info2 == info) {
345: fieldInfos.remove(ref);
346: break;
347: }
348: }
349: }
350:
351: public void commit(final ClassInfo info) {
352: super .commit(info);
353:
354: classEditors.remove(info);
355: classRC.remove(info);
356: }
357:
358: public void commit(final MethodInfo info) {
359: super .commit(info);
360:
361: methodEditors.remove(info);
362: methodRC.remove(info);
363: }
364:
365: public void commit(final FieldInfo info) {
366: super .commit(info);
367:
368: fieldEditors.remove(info);
369: fieldRC.remove(info);
370: }
371:
372: public void commit() {
373: Iterator iter = fieldEditors.values().iterator();
374: while (iter.hasNext()) {
375: final FieldEditor fe = (FieldEditor) iter.next();
376: commit(fe.fieldInfo());
377: }
378:
379: iter = methodEditors.values().iterator();
380: while (iter.hasNext()) {
381: final MethodEditor me = (MethodEditor) iter.next();
382: commit(me.methodInfo());
383: }
384:
385: iter = classEditors.values().iterator();
386: while (iter.hasNext()) {
387: final ClassEditor ce = (ClassEditor) iter.next();
388: commit(ce.classInfo());
389: }
390: }
391:
392: /**
393: * Return a textual description of all of the caches. Useful if we run out
394: * of memory.
395: */
396: public String toString() {
397: final StringWriter sw = new StringWriter();
398: final PrintWriter pw = new PrintWriter(sw, true);
399:
400: pw.println("Context of caches in CachingBloatContext...");
401:
402: pw.println(" Class Infos");
403: Iterator iter = classInfos.keySet().iterator();
404: while (iter.hasNext()) {
405: final Object key = iter.next();
406: pw.println(" " + key + " -> " + classInfos.get(key));
407: }
408:
409: pw.println(" Class Editors");
410: iter = classEditors.keySet().iterator();
411: while (iter.hasNext()) {
412: final Object key = iter.next();
413: pw.println(" " + key + " -> " + classEditors.get(key));
414: }
415:
416: pw.println(" Class RC");
417: iter = classRC.keySet().iterator();
418: while (iter.hasNext()) {
419: final Object key = iter.next();
420: pw.println(" " + key + " -> " + classRC.get(key));
421: }
422:
423: pw.println(" Method Infos");
424: iter = methodInfos.keySet().iterator();
425: while (iter.hasNext()) {
426: final Object key = iter.next();
427: pw.println(" " + key + " -> " + methodInfos.get(key));
428: }
429:
430: pw.println(" Method Editors");
431: iter = methodEditors.keySet().iterator();
432: while (iter.hasNext()) {
433: final Object key = iter.next();
434: pw.println(" " + key + " -> " + methodEditors.get(key));
435: }
436:
437: pw.println(" Method RC");
438: iter = methodRC.keySet().iterator();
439: while (iter.hasNext()) {
440: final Object key = iter.next();
441: pw.println(" " + key + " -> " + methodRC.get(key));
442: }
443:
444: pw.println(" Field Infos");
445: iter = fieldInfos.keySet().iterator();
446: while (iter.hasNext()) {
447: final Object key = iter.next();
448: pw.println(" " + key + " -> " + fieldInfos.get(key));
449: }
450:
451: pw.println(" Field Editors");
452: iter = fieldEditors.keySet().iterator();
453: while (iter.hasNext()) {
454: final Object key = iter.next();
455: pw.println(" " + key + " -> " + fieldEditors.get(key));
456: }
457:
458: pw.println(" Field RC");
459: iter = fieldRC.keySet().iterator();
460: while (iter.hasNext()) {
461: final Object key = iter.next();
462: pw.println(" " + key + " -> " + fieldRC.get(key));
463: }
464:
465: return (sw.toString());
466: }
467: }
|