git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@289 30ef5769-5b8d-40dd-aea6-55b5d6557bb3tags/rel_3_17_1_ga
@@ -2,7 +2,7 @@ Manifest-Version: 1.1 | |||
Specification-Title: Javassist | |||
Created-By: Shigeru Chiba, Tokyo Institute of Technology | |||
Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology | |||
Specification-Version: 3.2 | |||
Specification-Version: 3.3.0.snapshot | |||
Main-Class: javassist.CtClass | |||
Name: javassist/ |
@@ -0,0 +1,279 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import java.lang.ref.WeakReference; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import javassist.CannotCompileException; | |||
import javassist.ClassPool; | |||
import javassist.CtClass; | |||
import javassist.LoaderClassPath; | |||
import javassist.NotFoundException; | |||
/** | |||
* A scoped class pool | |||
* | |||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | |||
* @author <a href="adrian@jboss.com">Adrian Brock</a> | |||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | |||
* @version $Revision: 1.1 $ | |||
*/ | |||
public class ScopedClassPool extends ClassPool | |||
{ | |||
protected ScopedClassPoolRepository repository; | |||
protected WeakReference classLoader; | |||
protected LoaderClassPath classPath; | |||
protected SoftValueHashMap softcache = new SoftValueHashMap(); | |||
static | |||
{ | |||
ClassPool.doPruning = false; | |||
ClassPool.releaseUnmodifiedClassFile = false; | |||
} | |||
/** | |||
* Create a new ScopedClassPool. | |||
* | |||
* @param cl the classloader | |||
* @param src the original class pool | |||
* @param repository the repository | |||
*/ | |||
protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) | |||
{ | |||
super(src); | |||
this.repository = repository; | |||
this.classLoader = new WeakReference(cl); | |||
if (cl != null) | |||
{ | |||
classPath = new LoaderClassPath(cl); | |||
this.insertClassPath(classPath); | |||
} | |||
childFirstLookup = true; | |||
} | |||
/** | |||
* Get the class loader | |||
* | |||
* @return the class loader | |||
*/ | |||
public ClassLoader getClassLoader() | |||
{ | |||
return getClassLoader0(); | |||
} | |||
private ClassLoader getClassLoader0() | |||
{ | |||
ClassLoader cl = (ClassLoader) classLoader.get(); | |||
if (cl == null) | |||
throw new IllegalStateException("ClassLoader has been garbage collected"); | |||
return cl; | |||
} | |||
/** | |||
* Close the class pool | |||
*/ | |||
public void close() | |||
{ | |||
this.removeClassPath(classPath); | |||
classPath.close(); | |||
classes.clear(); | |||
softcache.clear(); | |||
} | |||
/** | |||
* Flush a class | |||
* | |||
* @param classname the class to flush | |||
*/ | |||
public synchronized void flushClass(String classname) | |||
{ | |||
classes.remove(classname); | |||
softcache.remove(classname); | |||
} | |||
/** | |||
* Soften a class | |||
* | |||
* @param clazz the class | |||
*/ | |||
public synchronized void soften(CtClass clazz) | |||
{ | |||
if (repository.isPrune()) clazz.prune(); | |||
classes.remove(clazz.getName()); | |||
softcache.put(clazz.getName(), clazz); | |||
} | |||
/** | |||
* Whether the classloader is loader | |||
* | |||
* @return false always | |||
*/ | |||
public boolean isUnloadedClassLoader() | |||
{ | |||
return false; | |||
} | |||
/** | |||
* Get the cached class | |||
* | |||
* @param classname the class name | |||
* @return the class | |||
*/ | |||
protected CtClass getCached(String classname) | |||
{ | |||
CtClass clazz = getCachedLocally(classname); | |||
if (clazz == null) | |||
{ | |||
boolean isLocal = false; | |||
ClassLoader dcl = getClassLoader0(); | |||
if (dcl != null) | |||
{ | |||
final int lastIndex = classname.lastIndexOf('$'); | |||
String classResourceName = null; | |||
if (lastIndex < 0) | |||
{ | |||
classResourceName = classname.replaceAll("[\\.]", "/") + ".class"; | |||
} | |||
else | |||
{ | |||
classResourceName = classname.substring(0, lastIndex).replaceAll("[\\.]", "/") + classname.substring(lastIndex) + ".class"; | |||
} | |||
isLocal = dcl.getResource(classResourceName) != null; | |||
} | |||
if (!isLocal) | |||
{ | |||
Map registeredCLs = repository.getRegisteredCLs(); | |||
synchronized (registeredCLs) | |||
{ | |||
Iterator it = registeredCLs.values().iterator(); | |||
while (it.hasNext()) | |||
{ | |||
ScopedClassPool pool = (ScopedClassPool) it.next(); | |||
if (pool.isUnloadedClassLoader()) | |||
{ | |||
repository.unregisterClassLoader(pool.getClassLoader()); | |||
continue; | |||
} | |||
clazz = pool.getCachedLocally(classname); | |||
if (clazz != null) | |||
{ | |||
return clazz; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
// *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! | |||
return clazz; | |||
} | |||
/** | |||
* Cache a class | |||
* | |||
* @param classname the class name | |||
* @param c the ctClass | |||
* @param dynamic whether the class is dynamically generated | |||
*/ | |||
protected void cacheCtClass(String classname, CtClass c, boolean dynamic) | |||
{ | |||
if (dynamic) | |||
{ | |||
super.cacheCtClass(classname, c, dynamic); | |||
} | |||
else | |||
{ | |||
if (repository.isPrune()) c.prune(); | |||
softcache.put(classname, c); | |||
} | |||
} | |||
/** | |||
* Lock a class into the cache | |||
* | |||
* @param c the class | |||
*/ | |||
public void lockInCache(CtClass c) | |||
{ | |||
super.cacheCtClass(c.getName(), c, false); | |||
} | |||
/** | |||
* Whether the class is cached in this pooled | |||
* | |||
* @param classname the class name | |||
* @return the cached class | |||
*/ | |||
protected CtClass getCachedLocally(String classname) | |||
{ | |||
CtClass cached = (CtClass) classes.get(classname); | |||
if (cached != null) return cached; | |||
synchronized (softcache) | |||
{ | |||
return (CtClass) softcache.get(classname); | |||
} | |||
} | |||
/** | |||
* Get any local copy of the class | |||
* | |||
* @param classname the class name | |||
* @return the class | |||
* @throws NotFoundException when the class is not found | |||
*/ | |||
public synchronized CtClass getLocally(String classname) throws NotFoundException | |||
{ | |||
softcache.remove(classname); | |||
CtClass clazz = (CtClass) classes.get(classname); | |||
if (clazz == null) | |||
{ | |||
clazz = createCtClass(classname, true); | |||
if (clazz == null) throw new NotFoundException(classname); | |||
super.cacheCtClass(classname, clazz, false); | |||
} | |||
return clazz; | |||
} | |||
/** | |||
* Convert a javassist class to a java class | |||
* | |||
* @param ct the javassist class | |||
* @param loader the loader | |||
* @throws CannotCompileException for any error | |||
*/ | |||
public Class toClass(CtClass ct, ClassLoader loader) throws CannotCompileException | |||
{ | |||
//We need to pass up the classloader stored in this pool, as the default implementation uses the Thread context cl. | |||
//In the case of JSP's in Tomcat, org.apache.jasper.servlet.JasperLoader will be stored here, while it's parent | |||
//org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread context cl. The invocation class needs to | |||
// be generated in the JasperLoader classloader since in the case of method invocations, the package name will be | |||
//the same as for the class generated from the jsp, i.e. org.apache.jsp. For classes belonging to org.apache.jsp, | |||
//JasperLoader does NOT delegate to its parent if it cannot find them. | |||
lockInCache(ct); | |||
return super.toClass(ct, getClassLoader0()); | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import javassist.ClassPool; | |||
/** | |||
* | |||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | |||
* @version $Revision: 1.1 $ | |||
*/ | |||
public interface ScopedClassPoolFactory | |||
{ | |||
ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository); | |||
ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository); | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import javassist.ClassPool; | |||
/** | |||
* | |||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | |||
* @version $Revision: 1.1 $ | |||
*/ | |||
public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory | |||
{ | |||
public ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) | |||
{ | |||
return new ScopedClassPool(cl, src, repository); | |||
} | |||
public ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository) | |||
{ | |||
return new ScopedClassPool(null, src, repository); | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import java.util.Map; | |||
import javassist.ClassPool; | |||
/** | |||
* | |||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | |||
* @version $Revision: 1.1 $ | |||
*/ | |||
public interface ScopedClassPoolRepository | |||
{ | |||
void setClassPoolFactory(ScopedClassPoolFactory factory); | |||
ScopedClassPoolFactory getClassPoolFactory(); | |||
/** | |||
* Get the prune. | |||
* | |||
* @return the prune. | |||
*/ | |||
boolean isPrune(); | |||
/** | |||
* Set the prune. | |||
* | |||
* @param prune the prune. | |||
*/ | |||
void setPrune(boolean prune); | |||
/** | |||
* Create a scoped classpool | |||
* | |||
* @param cl the classloader | |||
* @param src the original classpool | |||
* @return the classpool | |||
*/ | |||
ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); | |||
/** | |||
* Finds a scoped classpool registered under the passed in classloader | |||
* @param the classloader | |||
* @return the classpool | |||
*/ | |||
ClassPool findClassPool(ClassLoader cl); | |||
/** | |||
* Register a classloader | |||
* | |||
* @param ucl the classloader | |||
* @return the classpool | |||
*/ | |||
ClassPool registerClassLoader(ClassLoader ucl); | |||
/** | |||
* Get the registered classloaders | |||
* | |||
* @return the registered classloaders | |||
*/ | |||
Map getRegisteredCLs(); | |||
/** | |||
* This method will check to see if a register classloader has been undeployed (as in JBoss) | |||
*/ | |||
void clearUnregisteredClassLoaders(); | |||
/** | |||
* Unregisters a classpool and unregisters its classloader. | |||
* @ClassLoader the classloader the pool is stored under | |||
*/ | |||
void unregisterClassLoader(ClassLoader cl); | |||
} |
@@ -0,0 +1,212 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.WeakHashMap; | |||
import javassist.ClassPool; | |||
import javassist.LoaderClassPath; | |||
/** | |||
* | |||
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a> | |||
* @version $Revision: 1.1 $ | |||
*/ | |||
public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository | |||
{ | |||
/** The instance */ | |||
private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); | |||
/** Whether to prune */ | |||
private boolean prune = true; | |||
/** Whether to prune when added to the classpool's cache */ | |||
boolean pruneWhenCached; | |||
/** The registered classloaders */ | |||
protected Map registeredCLs = Collections.synchronizedMap(new WeakHashMap()); | |||
/** The default class pool */ | |||
protected ClassPool classpool; | |||
/** The factory for creating class pools */ | |||
protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); | |||
/** | |||
* Get the instance | |||
* | |||
* @return the instance | |||
*/ | |||
public static ScopedClassPoolRepository getInstance() | |||
{ | |||
return instance; | |||
} | |||
/** | |||
* Singleton | |||
*/ | |||
private ScopedClassPoolRepositoryImpl() | |||
{ | |||
classpool = ClassPool.getDefault(); | |||
// FIXME This doesn't look correct | |||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||
classpool.insertClassPath(new LoaderClassPath(cl)); | |||
} | |||
/** | |||
* Get the prune. | |||
* | |||
* @return the prune. | |||
*/ | |||
public boolean isPrune() | |||
{ | |||
return prune; | |||
} | |||
/** | |||
* Set the prune. | |||
* | |||
* @param prune the prune. | |||
*/ | |||
public void setPrune(boolean prune) | |||
{ | |||
this.prune = prune; | |||
} | |||
/** | |||
* Create a scoped classpool | |||
* | |||
* @param cl the classloader | |||
* @param src the original classpool | |||
* @return the classpool | |||
*/ | |||
public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) | |||
{ | |||
return factory.create(cl, src, this); | |||
} | |||
public ClassPool findClassPool(ClassLoader cl) | |||
{ | |||
if (cl == null) | |||
return registerClassLoader(ClassLoader.getSystemClassLoader()); | |||
return registerClassLoader(cl); | |||
} | |||
/** | |||
* Register a classloader | |||
* | |||
* @param ucl the classloader | |||
* @return the classpool | |||
*/ | |||
public ClassPool registerClassLoader(ClassLoader ucl) | |||
{ | |||
synchronized (registeredCLs) | |||
{ | |||
// FIXME: Probably want to take this method out later | |||
// so that AOP framework can be independent of JMX | |||
// This is in here so that we can remove a UCL from the ClassPool as a | |||
// ClassPool.classpath | |||
if (registeredCLs.containsKey(ucl)) | |||
{ | |||
return (ClassPool) registeredCLs.get(ucl); | |||
} | |||
ScopedClassPool pool = createScopedClassPool(ucl, classpool); | |||
registeredCLs.put(ucl, pool); | |||
return pool; | |||
} | |||
} | |||
/** | |||
* Get the registered classloaders | |||
* | |||
* @return the registered classloaders | |||
*/ | |||
public Map getRegisteredCLs() | |||
{ | |||
clearUnregisteredClassLoaders(); | |||
return registeredCLs; | |||
} | |||
/** | |||
* This method will check to see if a register classloader has been undeployed (as in JBoss) | |||
*/ | |||
public void clearUnregisteredClassLoaders() | |||
{ | |||
ArrayList toUnregister = null; | |||
synchronized (registeredCLs) | |||
{ | |||
Iterator it = registeredCLs.values().iterator(); | |||
while (it.hasNext()) | |||
{ | |||
ScopedClassPool pool = (ScopedClassPool) it.next(); | |||
if (pool.isUnloadedClassLoader()) | |||
{ | |||
it.remove(); | |||
ClassLoader cl = pool.getClassLoader(); | |||
if (cl != null) | |||
{ | |||
if (toUnregister == null) | |||
{ | |||
toUnregister = new ArrayList(); | |||
} | |||
toUnregister.add(cl); | |||
} | |||
} | |||
} | |||
if (toUnregister != null) | |||
{ | |||
for (int i = 0; i < toUnregister.size(); i++) | |||
{ | |||
unregisterClassLoader((ClassLoader) toUnregister.get(i)); | |||
} | |||
} | |||
} | |||
} | |||
public void unregisterClassLoader(ClassLoader cl) | |||
{ | |||
synchronized (registeredCLs) | |||
{ | |||
ScopedClassPool pool = (ScopedClassPool) registeredCLs.remove(cl); | |||
if (pool != null) pool.close(); | |||
} | |||
} | |||
public void insertDelegate(ScopedClassPoolRepository delegate) | |||
{ | |||
//Noop - this is the end | |||
} | |||
public void setClassPoolFactory(ScopedClassPoolFactory factory) | |||
{ | |||
this.factory = factory; | |||
} | |||
public ScopedClassPoolFactory getClassPoolFactory() | |||
{ | |||
return factory; | |||
} | |||
} |
@@ -0,0 +1,247 @@ | |||
/* | |||
* JBoss, Home of Professional Open Source | |||
* Copyright 2005, JBoss Inc., and individual contributors as indicated | |||
* by the @authors tag. See the copyright.txt in the distribution for a | |||
* full listing of individual contributors. | |||
* | |||
* This is free software; you can redistribute it and/or modify it | |||
* under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation; either version 2.1 of | |||
* the License, or (at your option) any later version. | |||
* | |||
* This software 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 | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public | |||
* License along with this software; if not, write to the Free | |||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | |||
*/ | |||
package javassist.scopedpool; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.SoftReference; | |||
import java.util.AbstractMap; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
* This Map will remove entries when the value in the map has been | |||
* cleaned from garbage collection | |||
* | |||
* @version <tt>$Revision: 1.1 $</tt> | |||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | |||
*/ | |||
public class SoftValueHashMap | |||
extends AbstractMap | |||
implements Map | |||
{ | |||
private static class SoftValueRef extends SoftReference | |||
{ | |||
public Object key; | |||
private SoftValueRef(Object key, Object val, ReferenceQueue q) | |||
{ | |||
super(val, q); | |||
this.key = key; | |||
} | |||
private static SoftValueRef create(Object key, Object val, ReferenceQueue q) | |||
{ | |||
if (val == null) return null; | |||
else return new SoftValueRef(key, val, q); | |||
} | |||
} | |||
public Set entrySet() | |||
{ | |||
processQueue(); | |||
return hash.entrySet(); | |||
} | |||
/* Hash table mapping WeakKeys to values */ | |||
private Map hash; | |||
/* Reference queue for cleared WeakKeys */ | |||
private ReferenceQueue queue = new ReferenceQueue(); | |||
/* Remove all invalidated entries from the map, that is, remove all entries | |||
whose values have been discarded. | |||
*/ | |||
private void processQueue() | |||
{ | |||
SoftValueRef ref; | |||
while ((ref = (SoftValueRef)queue.poll()) != null) { | |||
if (ref == (SoftValueRef) hash.get(ref.key)) { | |||
// only remove if it is the *exact* same WeakValueRef | |||
// | |||
hash.remove(ref.key); | |||
} | |||
} | |||
} | |||
/* -- Constructors -- */ | |||
/** | |||
* Constructs a new, empty <code>WeakHashMap</code> with the given | |||
* initial capacity and the given load factor. | |||
* | |||
* @param initialCapacity The initial capacity of the | |||
* <code>WeakHashMap</code> | |||
* | |||
* @param loadFactor The load factor of the <code>WeakHashMap</code> | |||
* | |||
* @throws IllegalArgumentException If the initial capacity is less than | |||
* zero, or if the load factor is | |||
* nonpositive | |||
*/ | |||
public SoftValueHashMap(int initialCapacity, float loadFactor) | |||
{ | |||
hash = new HashMap(initialCapacity, loadFactor); | |||
} | |||
/** | |||
* Constructs a new, empty <code>WeakHashMap</code> with the given | |||
* initial capacity and the default load factor, which is | |||
* <code>0.75</code>. | |||
* | |||
* @param initialCapacity The initial capacity of the | |||
* <code>WeakHashMap</code> | |||
* | |||
* @throws IllegalArgumentException If the initial capacity is less than | |||
* zero | |||
*/ | |||
public SoftValueHashMap(int initialCapacity) | |||
{ | |||
hash = new HashMap(initialCapacity); | |||
} | |||
/** | |||
* Constructs a new, empty <code>WeakHashMap</code> with the default | |||
* initial capacity and the default load factor, which is | |||
* <code>0.75</code>. | |||
*/ | |||
public SoftValueHashMap() | |||
{ | |||
hash = new HashMap(); | |||
} | |||
/** | |||
* Constructs a new <code>WeakHashMap</code> with the same mappings as the | |||
* specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with an | |||
* initial capacity of twice the number of mappings in the specified map | |||
* or 11 (whichever is greater), and a default load factor, which is | |||
* <tt>0.75</tt>. | |||
* | |||
* @param t the map whose mappings are to be placed in this map. | |||
* @since 1.3 | |||
*/ | |||
public SoftValueHashMap(Map t) | |||
{ | |||
this(Math.max(2*t.size(), 11), 0.75f); | |||
putAll(t); | |||
} | |||
/* -- Simple queries -- */ | |||
/** | |||
* Returns the number of key-value mappings in this map. | |||
* <strong>Note:</strong> <em>In contrast with most implementations of the | |||
* <code>Map</code> interface, the time required by this operation is | |||
* linear in the size of the map.</em> | |||
*/ | |||
public int size() | |||
{ | |||
processQueue(); | |||
return hash.size(); | |||
} | |||
/** | |||
* Returns <code>true</code> if this map contains no key-value mappings. | |||
*/ | |||
public boolean isEmpty() | |||
{ | |||
processQueue(); | |||
return hash.isEmpty(); | |||
} | |||
/** | |||
* Returns <code>true</code> if this map contains a mapping for the | |||
* specified key. | |||
* | |||
* @param key The key whose presence in this map is to be tested | |||
*/ | |||
public boolean containsKey(Object key) | |||
{ | |||
processQueue(); | |||
return hash.containsKey(key); | |||
} | |||
/* -- Lookup and modification operations -- */ | |||
/** | |||
* Returns the value to which this map maps the specified <code>key</code>. | |||
* If this map does not contain a value for this key, then return | |||
* <code>null</code>. | |||
* | |||
* @param key The key whose associated value, if any, is to be returned | |||
*/ | |||
public Object get(Object key) | |||
{ | |||
processQueue(); | |||
SoftReference ref = (SoftReference)hash.get(key); | |||
if (ref != null) return ref.get(); | |||
return null; | |||
} | |||
/** | |||
* Updates this map so that the given <code>key</code> maps to the given | |||
* <code>value</code>. If the map previously contained a mapping for | |||
* <code>key</code> then that mapping is replaced and the previous value is | |||
* returned. | |||
* | |||
* @param key The key that is to be mapped to the given | |||
* <code>value</code> | |||
* @param value The value to which the given <code>key</code> is to be | |||
* mapped | |||
* | |||
* @return The previous value to which this key was mapped, or | |||
* <code>null</code> if if there was no mapping for the key | |||
*/ | |||
public Object put(Object key, Object value) | |||
{ | |||
processQueue(); | |||
Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); | |||
if (rtn != null) rtn = ((SoftReference)rtn).get(); | |||
return rtn; | |||
} | |||
/** | |||
* Removes the mapping for the given <code>key</code> from this map, if | |||
* present. | |||
* | |||
* @param key The key whose mapping is to be removed | |||
* | |||
* @return The value to which this key was mapped, or <code>null</code> if | |||
* there was no mapping for the key | |||
*/ | |||
public Object remove(Object key) | |||
{ | |||
processQueue(); | |||
return hash.remove(key); | |||
} | |||
/** | |||
* Removes all mappings from this map. | |||
*/ | |||
public void clear() | |||
{ | |||
processQueue(); | |||
hash.clear(); | |||
} | |||
} |