/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o 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 General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package EDU.purdue.cs.bloat.context;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Does a lot of the same stuff as <tt>PersistentBloatContext</tt> except that
* it manages the chaches of BLOAT objects. For example, when a
* <tt>MethodEditor</tt> is no longer needed, it is removed from the cache if
* it is not dirty. This context is meant to used in volatile memory.
*/
public class CachingBloatContext extends PersistentBloatContext {
// Keep track of reference counts in a manner reminiscent of the old
// Editor class.
protected Map classRC;
protected Map methodRC;
protected Map fieldRC;
/**
* Constructor.
*
* @param loader
* Used to load classes
* @param classes
* Some initial classes in the context
* @param closure
* Do we look for the maximum number of classes?
*/
public CachingBloatContext(final ClassInfoLoader loader,
final Collection classes, final boolean closure) {
super(loader, closure);
classRC = new HashMap();
methodRC = new HashMap();
fieldRC = new HashMap();
addClasses(classes);
}
public ClassEditor newClass(final int modifiers, final String className,
final Type superType, final Type[] interfaces) {
final ClassEditor ce = super.newClass(modifiers, className, superType,
interfaces);
final ClassInfo info = ce.classInfo();
classRC.put(info, new Integer(1));
return ce;
}
public ClassEditor editClass(final ClassInfo info) {
// Check the cache
ClassEditor ce = (ClassEditor) classEditors.get(info);
if (ce == null) {
ce = new ClassEditor(this, info);
classEditors.put(info, ce);
classRC.put(info, new Integer(1));
if (!classInfos.containsValue(info)) {
final String className = ce.name().intern();
BloatContext.db("editClass(ClassInfo): " + className + " -> "
+ info);
classInfos.put(className, info);
}
} else {
final Integer rc = (Integer) classRC.get(info);
classRC.put(info, new Integer(rc.intValue() + 1));
}
return (ce);
}
public MethodEditor editMethod(final MemberRef method)
throws NoSuchMethodException {
// Check the MethodInfo cache
final MethodInfo info = (MethodInfo) methodInfos.get(method);
if (info == null) {
// Groan, we have to do this the HARD way.
BloatContext.db("Creating a new MethodEditor for " + method);
final NameAndType nat = method.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(method.declaringClass());
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = editMethod(methods[i]);
if (me.name().equals(name) && me.type().equals(type)) {
// The call to editMethod should have already handled
// the
// methodEditors mapping, but we still need to do
// methodInfos.
methodInfos.put(method, methods[i]);
release(ce.classInfo());
return (me);
}
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
throw new NoSuchMethodException(method.toString() + "("
+ ex1.getMessage() + ")");
} catch (final ClassFormatException ex2) {
throw new NoSuchMethodException(method.toString() + "("
+ ex2.getMessage() + ")");
}
throw new NoSuchMethodException(method.toString());
}
return (editMethod(info));
}
public MethodEditor editMethod(final MethodInfo info) {
// Check methodEditors cache
MethodEditor me = (MethodEditor) methodEditors.get(info);
if (me == null) {
final ClassInfo classInfo = info.declaringClass();
me = new MethodEditor(editClass(classInfo), info);
release(classInfo);
methodEditors.put(info, me);
methodRC.put(info, new Integer(1));
BloatContext
.db("Creating a new MethodEditor for " + me.memberRef());
} else {
final Integer rc = (Integer) methodRC.get(info);
methodRC.put(info, new Integer(rc.intValue() + 1));
}
return (me);
}
public FieldEditor editField(final MemberRef field)
throws NoSuchFieldException {
// Just like we had to do with methods
final FieldInfo info = (FieldInfo) fieldInfos.get(field);
if (info == null) {
final NameAndType nat = field.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(field.declaringClass());
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = editField(fields[i]);
if (fe.name().equals(name) && fe.type().equals(type)) {
fieldInfos.put(field, fields[i]);
release(ce.classInfo());
return (fe);
}
release(fields[i]);
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
} catch (final ClassFormatException ex2) {
}
throw new NoSuchFieldException(field.toString());
}
return (editField(info));
}
public FieldEditor editField(final FieldInfo info) {
// Check the cache
FieldEditor fe = (FieldEditor) fieldEditors.get(info);
BloatContext.db("Editing " + info);
if (fe == null) {
final ClassInfo classInfo = info.declaringClass();
fe = new FieldEditor(editClass(classInfo), info);
release(classInfo);
fieldEditors.put(info, fe);
fieldRC.put(info, new Integer(0));
BloatContext.db("Creating a new FieldEditor for "
+ fe.nameAndType());
} else {
final Integer rc = (Integer) fieldRC.get(info);
fieldRC.put(info, new Integer(rc.intValue() + 1));
}
return (fe);
}
public void release(final ClassInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
// Not done yet;
classRC.put(info, new Integer(rc.intValue() - 1));
return;
}
ClassEditor ce = (ClassEditor) classEditors.get(info);
if ((ce != null) && ce.isDirty()) {
return;
}
// We're done with this class, remove all traces of it
ce = (ClassEditor) classEditors.remove(info);
classRC.remove(info);
classEditors.remove(info);
final Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
final ClassInfo info2 = (ClassInfo) classInfos.get(name);
if (info2 == info) {
BloatContext.db("Removing ClassInfo: " + name + " -> " + info2);
classInfos.remove(name);
break;
}
}
if (ce != null) {
// Remove all of the class's fields and methods also
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
release(methods[i]);
}
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
release(fields[i]);
}
}
}
public void release(final MethodInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
methodRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final MethodEditor me = (MethodEditor) methodEditors.get(info);
// We should keep dirty methods around. My original thought was
// that if we committed dirty methods when they were released, we
// risk having MethodEditors editing different versions of the
// same method. So, if we don't release dirty methods, we'll only
// have ONE MethodEditor.
if ((me != null) && me.isDirty()) {
return;
}
// We're done with this method, remove all traces of it
methodRC.remove(info);
methodEditors.remove(info);
final Iterator iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final MethodInfo info2 = (MethodInfo) methodInfos.get(ref);
if (info2 == info) {
methodInfos.remove(ref);
break;
}
}
}
public void release(final FieldInfo info) {
final Integer rc = (Integer) fieldRC.get(info);
BloatContext.db("Releasing " + info);
if ((rc != null) && (rc.intValue() > 1)) {
fieldRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final FieldEditor fe = (FieldEditor) fieldEditors.get(info);
if ((fe != null) && fe.isDirty()) {
return;
}
// We're done with this field, remove all traces of it
fieldRC.remove(info);
fieldEditors.remove(info);
final Iterator iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref);
if (info2 == info) {
fieldInfos.remove(ref);
break;
}
}
}
public void commit(final ClassInfo info) {
super.commit(info);
classEditors.remove(info);
classRC.remove(info);
}
public void commit(final MethodInfo info) {
super.commit(info);
methodEditors.remove(info);
methodRC.remove(info);
}
public void commit(final FieldInfo info) {
super.commit(info);
fieldEditors.remove(info);
fieldRC.remove(info);
}
public void commit() {
Collection fieldValues = fieldEditors.values();
FieldEditor[] fieldArray = (FieldEditor[]) fieldValues.toArray(new FieldEditor[fieldValues.size()]);
for (int i = 0; i < fieldArray.length; i++) {
final FieldEditor fe = fieldArray[i];
commit(fe.fieldInfo());
}
Collection methodValues = methodEditors.values();
MethodEditor[] methodArray = (MethodEditor[]) methodValues.toArray(new MethodEditor[methodValues.size()]);
for (int i = 0; i < methodArray.length; i++) {
final MethodEditor me = methodArray[i];
commit(me.methodInfo());
}
Collection classValues = classEditors.values();
ClassEditor[] classArray = (ClassEditor[]) classValues.toArray(new ClassEditor[classValues.size()]);
for (int i = 0; i < classArray.length; i++) {
final ClassEditor ce = classArray[i];
commit(ce.classInfo());
}
}
/**
* Return a textual description of all of the caches. Useful if we run out
* of memory.
*/
public String toString() {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
pw.println("Context of caches in CachingBloatContext...");
pw.println(" Class Infos");
Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classInfos.get(key));
}
pw.println(" Class Editors");
iter = classEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classEditors.get(key));
}
pw.println(" Class RC");
iter = classRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classRC.get(key));
}
pw.println(" Method Infos");
iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodInfos.get(key));
}
pw.println(" Method Editors");
iter = methodEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodEditors.get(key));
}
pw.println(" Method RC");
iter = methodRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodRC.get(key));
}
pw.println(" Field Infos");
iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldInfos.get(key));
}
pw.println(" Field Editors");
iter = fieldEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldEditors.get(key));
}
pw.println(" Field RC");
iter = fieldRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldRC.get(key));
}
return (sw.toString());
}
}