Author: nbelaevski
Date: 2008-12-03 17:30:09 -0500 (Wed, 03 Dec 2008)
New Revision: 11536
Added:
trunk/framework/impl/src/main/java/org/ajax4jsf/el/
trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELContextWrapper.java
trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELResolverWrapper.java
trunk/framework/impl/src/main/java/org/ajax4jsf/util/CapturingELResolver.java
trunk/framework/impl/src/main/java/org/ajax4jsf/util/GenericsIntrospectionCache.java
Modified:
trunk/framework/impl/src/main/java/org/ajax4jsf/util/ELUtils.java
trunk/framework/impl/src/main/java/org/richfaces/util/ReferenceMap.java
Log:
RF-4540
Copied: trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELContextWrapper.java (from rev
11455,
trunk/ui/beanValidator/src/main/java/org/richfaces/validator/ELContextWrapper.java)
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELContextWrapper.java
(rev 0)
+++ trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELContextWrapper.java 2008-12-03
22:30:09 UTC (rev 11536)
@@ -0,0 +1,98 @@
+/**
+ * License Agreement.
+ *
+ * Rich Faces - Natural Ajax for Java Server Faces (JSF)
+ *
+ * Copyright (C) 2007 Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package org.ajax4jsf.el;
+
+import java.util.Locale;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.FunctionMapper;
+import javax.el.VariableMapper;
+
+/**
+ * @author asmirnov
+ *
+ */
+public class ELContextWrapper extends ELContext {
+
+ private final ELContext parent;
+
+ private final ELResolver resolver;
+
+ /**
+ * @param parent
+ */
+ public ELContextWrapper(ELContext parent,ELResolver resolver) {
+ super();
+ this.resolver = resolver;
+ this.parent = parent;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.el.ELContext#getELResolver()
+ */
+ @Override
+ public ELResolver getELResolver() {
+ return resolver;
+ }
+
+ /**
+ * @return
+ * @see javax.el.ELContext#getFunctionMapper()
+ */
+ public FunctionMapper getFunctionMapper() {
+ return parent.getFunctionMapper();
+ }
+
+ /**
+ * @return
+ * @see javax.el.ELContext#getVariableMapper()
+ */
+ public VariableMapper getVariableMapper() {
+ return parent.getVariableMapper();
+ }
+
+ /**
+ * @param key
+ * @return
+ * @see javax.el.ELContext#getContext(java.lang.Class)
+ */
+ public Object getContext(Class key) {
+ return parent.getContext(key);
+ }
+
+ /**
+ * @param key
+ * @param contextObject
+ * @see javax.el.ELContext#putContext(java.lang.Class, java.lang.Object)
+ */
+ public void putContext(Class key, Object contextObject) {
+ parent.putContext(key, contextObject);
+ }
+
+ public Locale getLocale() {
+ return parent.getLocale();
+ }
+
+ public void setLocale(Locale locale) {
+ parent.setLocale(locale);
+ }
+}
Property changes on:
trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELContextWrapper.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:mergeinfo
+
Added: trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELResolverWrapper.java
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELResolverWrapper.java
(rev 0)
+++ trunk/framework/impl/src/main/java/org/ajax4jsf/el/ELResolverWrapper.java 2008-12-03
22:30:09 UTC (rev 11536)
@@ -0,0 +1,109 @@
+/**
+ * License Agreement.
+ *
+ * JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007 Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.ajax4jsf.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+/**
+ * @author Nick Belaevski
+ * @since 3.3.0
+ */
+
+public class ELResolverWrapper extends ELResolver {
+
+ public ELResolverWrapper(ELResolver resolver) {
+ super();
+ this.resolver = resolver;
+ }
+
+ private ELResolver resolver;
+
+ /**
+ * @param context
+ * @param base
+ * @return
+ * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
+ */
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ return resolver.getCommonPropertyType(context, base);
+ }
+
+ /**
+ * @param context
+ * @param base
+ * @return
+ * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
+ */
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
+ Object base) {
+ return resolver.getFeatureDescriptors(context, base);
+ }
+
+ /**
+ * @param context
+ * @param base
+ * @param property
+ * @return
+ * @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object,
java.lang.Object)
+ */
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ return resolver.getType(context, base, property);
+ }
+
+ /**
+ * @param context
+ * @param base
+ * @param property
+ * @return
+ * @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object,
java.lang.Object)
+ */
+ public Object getValue(ELContext context, Object base, Object property) {
+ return resolver.getValue(context, base, property);
+ }
+
+ /**
+ * @param context
+ * @param base
+ * @param property
+ * @return
+ * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object,
java.lang.Object)
+ */
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ return resolver.isReadOnly(context, base, property);
+ }
+
+ /**
+ * @param context
+ * @param base
+ * @param property
+ * @param value
+ * @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object,
java.lang.Object, java.lang.Object)
+ */
+ public void setValue(ELContext context, Object base, Object property,
+ Object value) {
+ resolver.setValue(context, base, property, value);
+ }
+}
Added: trunk/framework/impl/src/main/java/org/ajax4jsf/util/CapturingELResolver.java
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/util/CapturingELResolver.java
(rev 0)
+++
trunk/framework/impl/src/main/java/org/ajax4jsf/util/CapturingELResolver.java 2008-12-03
22:30:09 UTC (rev 11536)
@@ -0,0 +1,71 @@
+/**
+ * License Agreement.
+ *
+ * JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007 Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.ajax4jsf.util;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+import org.ajax4jsf.el.ELResolverWrapper;
+
+/**
+ * @author Nick Belaevski
+ * @since 3.3.0
+ */
+
+class CapturingELResolver extends ELResolverWrapper {
+
+ private Object base;
+
+ private Object property;
+
+ public CapturingELResolver(ELResolver resolver) {
+ super(resolver);
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (base != null && property != null) {
+ this.base = base;
+ this.property = property;
+ }
+
+ return super.getValue(context, base, property);
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if (base != null && property != null) {
+ this.base = base;
+ this.property = property;
+ }
+
+ return super.getType(context, base, property);
+ }
+
+ public Object getBase() {
+ return base;
+ }
+
+ public Object getProperty() {
+ return property;
+ }
+}
Modified: trunk/framework/impl/src/main/java/org/ajax4jsf/util/ELUtils.java
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/util/ELUtils.java 2008-12-03 22:29:10
UTC (rev 11535)
+++ trunk/framework/impl/src/main/java/org/ajax4jsf/util/ELUtils.java 2008-12-03 22:30:09
UTC (rev 11536)
@@ -20,6 +20,25 @@
*/
package org.ajax4jsf.util;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.context.FacesContext;
+
+import org.ajax4jsf.el.ELContextWrapper;
+import org.ajax4jsf.util.GenericsIntrospectionCache.GenericsCacheEntry;
+
/**
* @author asmirnov
*
@@ -54,4 +73,98 @@
return false;
}
+ private static Class<?> resolveType(Type type) {
+ Class<?> result = Object.class;
+
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Type[] types = parameterizedType.getActualTypeArguments();
+ if (types != null && types.length != 0) {
+ Type actualType = types[0];
+ if (actualType instanceof Class) {
+ result = (Class<?>) actualType;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static BeanInfo getBeanInfo(Class<?> beanClass, GenericsCacheEntry entry)
{
+ BeanInfo beanInfo = null;
+
+ SoftReference<BeanInfo> beanInfoReference = entry.beanInfoReference;
+ if (beanInfoReference != null) {
+ beanInfo = beanInfoReference.get();
+ }
+
+ if (beanInfo == null) {
+ try {
+ beanInfo = Introspector.getBeanInfo(beanClass);
+ entry.beanInfoReference = new SoftReference<BeanInfo>(beanInfo);
+ } catch (IntrospectionException e) {
+ throw new FacesException(e.getMessage(), e);
+ }
+ }
+
+ return beanInfo;
+ }
+
+ private static Class<?> getGenericCollectionType(FacesContext context, Object
base, String propertyName) {
+ Class<?> genericPropertyClass = null;
+
+ GenericsIntrospectionCache introspectionCache =
GenericsIntrospectionCache.getInstance(context);
+ if (base != null && propertyName != null) {
+ Class<? extends Object> beanClass = base.getClass();
+
+ synchronized (introspectionCache) {
+ GenericsCacheEntry cacheEntry = introspectionCache.getGenericCacheEntry(beanClass);
+
+ if (cacheEntry.genericPropertiesClasses == null) {
+ cacheEntry.genericPropertiesClasses = new HashMap<String, Class<?>>();
+ } else {
+ genericPropertyClass = cacheEntry.genericPropertiesClasses.get(propertyName);
+ }
+
+ if (genericPropertyClass == null) {
+ BeanInfo beanInfo = getBeanInfo(beanClass, cacheEntry);
+
+ PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
+ for (PropertyDescriptor pd : descriptors) {
+ if (propertyName.equals(pd.getName())) {
+ Method readMethod = pd.getReadMethod();
+
+ genericPropertyClass = resolveType(readMethod.getGenericReturnType());
+ break;
+ }
+ }
+
+ cacheEntry.genericPropertiesClasses.put(propertyName, genericPropertyClass);
+ }
+ }
+ }
+
+ return genericPropertyClass;
+ }
+
+ public static Class<?> getContainerClass(FacesContext facesContext,
ValueExpression expression) {
+ ELContext initialELContext = facesContext.getELContext();
+
+ CapturingELResolver capturingELResolver = new
CapturingELResolver(initialELContext.getELResolver());
+ Class<?> type = expression.getType(new ELContextWrapper(initialELContext,
capturingELResolver));
+
+ Class<?> containerType = type.getComponentType();
+ if (containerType == null && type != null) {
+ if (Collection.class.isAssignableFrom(type)) {
+ Object base = capturingELResolver.getBase();
+ Object property = capturingELResolver.getProperty();
+
+ if (base != null && property != null) {
+ containerType = getGenericCollectionType(facesContext, base, property.toString());
+ }
+ }
+ }
+
+ return containerType;
+ }
}
Added:
trunk/framework/impl/src/main/java/org/ajax4jsf/util/GenericsIntrospectionCache.java
===================================================================
--- trunk/framework/impl/src/main/java/org/ajax4jsf/util/GenericsIntrospectionCache.java
(rev 0)
+++
trunk/framework/impl/src/main/java/org/ajax4jsf/util/GenericsIntrospectionCache.java 2008-12-03
22:30:09 UTC (rev 11536)
@@ -0,0 +1,101 @@
+/**
+ * License Agreement.
+ *
+ * JBoss RichFaces - Ajax4jsf Component Library
+ *
+ * Copyright (C) 2007 Exadel, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.ajax4jsf.util;
+
+import java.beans.BeanInfo;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.Map;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import org.richfaces.util.ReferenceMap;
+
+/**
+ * @author Nick Belaevski
+ * @since 3.3.0
+ */
+
+class GenericsIntrospectionCache {
+
+ private static final String INSTANCE_ATTRIBUTE_NAME =
GenericsIntrospectionCache.class.getName();
+
+ private static final String CACHE_SIZE_PARAMETER =
"org.richfaces.GenericsIntrospectionCacheSize";
+
+ private static final int DEFAULT_CACHE_SIZE = 256;
+
+ static final class GenericsCacheEntry {
+ SoftReference<BeanInfo> beanInfoReference;
+ Map<String, Class<?>> genericPropertiesClasses;
+ };
+
+ private Map<Class<?>, GenericsCacheEntry> genericsCache;
+
+ private static int getSize(ExternalContext externalContext) {
+ int cacheSize = DEFAULT_CACHE_SIZE;
+
+ String cacheSizeParameter = externalContext.getInitParameter(CACHE_SIZE_PARAMETER);
+ if (cacheSizeParameter != null && cacheSizeParameter.length() != 0) {
+ try {
+ cacheSize = Integer.valueOf(cacheSizeParameter);
+ } catch (NumberFormatException e) {
+ externalContext.log("Error converting " + CACHE_SIZE_PARAMETER + "
init parameter to int: " + e.getMessage(),
+ e);
+ }
+ }
+
+ return cacheSize;
+ }
+
+ private GenericsIntrospectionCache(int cacheSize) {
+ genericsCache = new ReferenceMap<Class<?>, GenericsCacheEntry>(
+ new LRUMap<Class<?>, Reference<GenericsCacheEntry>>(cacheSize));
+ }
+
+ public GenericsCacheEntry getGenericCacheEntry(Class<?> beanClass) {
+ GenericsCacheEntry cacheEntry = genericsCache.get(beanClass);
+ if (cacheEntry == null) {
+ cacheEntry = new GenericsCacheEntry();
+ genericsCache.put(beanClass, cacheEntry);
+ }
+
+ return cacheEntry;
+ }
+
+ static GenericsIntrospectionCache getInstance(FacesContext facesContext) {
+ ExternalContext externalContext = facesContext.getExternalContext();
+ Map<String, Object> applicationMap = externalContext.getApplicationMap();
+
+ GenericsIntrospectionCache instance;
+ synchronized (applicationMap) {
+ instance = (GenericsIntrospectionCache) applicationMap.get(INSTANCE_ATTRIBUTE_NAME);
+ if (instance == null) {
+ instance = new GenericsIntrospectionCache(getSize(externalContext));
+ applicationMap.put(INSTANCE_ATTRIBUTE_NAME, instance);
+ }
+ }
+
+ return instance;
+ }
+
+}
Modified: trunk/framework/impl/src/main/java/org/richfaces/util/ReferenceMap.java
===================================================================
--- trunk/framework/impl/src/main/java/org/richfaces/util/ReferenceMap.java 2008-12-03
22:29:10 UTC (rev 11535)
+++ trunk/framework/impl/src/main/java/org/richfaces/util/ReferenceMap.java 2008-12-03
22:30:09 UTC (rev 11536)
@@ -37,123 +37,138 @@
public class ReferenceMap<K, V> implements Map<K, V> {
- private Map<K, ReferenceMapSoftReference<K, V>> map = new HashMap<K,
ReferenceMapSoftReference<K, V>>();
+ private Map<K, Reference<V>> map;
- private ReferenceQueue<V> queue = new ReferenceQueue<V>();
-
- private void purge() {
- Reference<? extends V> reference = null;
- while ((reference = queue.poll()) != null) {
- ReferenceMapSoftReference<?, ?> entry = (ReferenceMapSoftReference<?,
?>) reference;
- entry.clear();
- map.remove(entry.getKey());
+ private ReferenceQueue<V> queue = new ReferenceQueue<V>();
+
+ protected static class ReferenceMapSoftReference<K, V> extends
SoftReference<V> {
+ private K key;
+
+ public K getKey() {
+ return key;
+ }
+
+ public ReferenceMapSoftReference(K key, V value, ReferenceQueue<? super V> queue)
{
+ super(value, queue);
+ this.key = key;
+ }
+ }
+
+ public ReferenceMap() {
+ this(new HashMap<K, Reference<V>>());
}
- }
-
- public void clear() {
- map.clear();
- while (queue.poll() != null) {
- //release queue entries
+
+ public ReferenceMap(Map<K, Reference<V>> map) {
+ super();
+
+ this.map = map;
}
- }
-
- public boolean containsKey(Object key) {
- purge();
- return map.containsKey(key);
- }
+ private void purge() {
+ Reference<? extends V> reference = null;
+ while ((reference = queue.poll()) != null) {
+ ReferenceMapSoftReference<?, ?> entry = (ReferenceMapSoftReference<?, ?>)
reference;
+ entry.clear();
+ map.remove(entry.getKey());
+ }
+ }
- public boolean containsValue(Object value) {
- throw new UnsupportedOperationException();
- }
+ public void clear() {
+ map.clear();
+
+ Reference<? extends V> reference = null;
+ while ((reference = queue.poll()) != null) {
+ //release queue entries
+ reference.clear();
+ }
+ }
- public Set<java.util.Map.Entry<K, V>> entrySet() {
- throw new UnsupportedOperationException();
- }
+ public boolean containsKey(Object key) {
+ purge();
- public V get(Object key) {
- purge();
-
- ReferenceMapSoftReference<K,V> reference = map.get(key);
- if (reference != null) {
- return reference.get();
+ return map.containsKey(key);
}
-
- return null;
- }
- public boolean isEmpty() {
- purge();
-
- return map.isEmpty();
- }
+ public boolean containsValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
- public Set<K> keySet() {
- purge();
-
- return map.keySet();
- }
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
- private V doPut(K key, V value) {
- ReferenceMapSoftReference<K,V> reference = map.put(key,
- new ReferenceMapSoftReference<K, V>(key, value, queue));
-
- if (reference != null) {
- return reference.get();
+ public V get(Object key) {
+ purge();
+
+ Reference<V> reference = map.get(key);
+ if (reference != null) {
+ return reference.get();
+ }
+
+ return null;
}
-
- return null;
- }
-
- public V put(K key, V value) {
- purge();
-
- return doPut(key, value);
- }
- public void putAll(Map<? extends K, ? extends V> t) {
- purge();
-
- for (Map.Entry<? extends K, ? extends V> entry: t.entrySet()) {
- doPut(entry.getKey(), entry.getValue());
+ public boolean isEmpty() {
+ purge();
+
+ return map.isEmpty();
}
- }
- public V remove(Object key) {
- purge();
-
- ReferenceMapSoftReference<K,V> reference = map.remove(key);
- if (reference != null) {
- return reference.get();
+ public Set<K> keySet() {
+ purge();
+
+ return map.keySet();
}
- return null;
- }
+ private V doPut(K key, V value) {
+ Reference<V> reference = map.put(key, new ReferenceMapSoftReference<K,
V>(key, value, queue));
- public int size() {
- purge();
+ if (reference != null) {
+ return reference.get();
+ }
+
+ return null;
+ }
+
+ public V put(K key, V value) {
+ purge();
+
+ V v = doPut(key, value);
- return map.size();
- }
+ purge();
+
+ return v;
+ }
- public Collection<V> values() {
- throw new UnsupportedOperationException();
- }
-}
+ public void putAll(Map<? extends K, ? extends V> t) {
+ purge();
-class ReferenceMapSoftReference<K, V> extends SoftReference<V> {
- private K key;
+ for (Map.Entry<? extends K, ? extends V> entry: t.entrySet()) {
+ doPut(entry.getKey(), entry.getValue());
+ }
+
+ purge();
+ }
- public K getKey() {
- return key;
- }
+ public V remove(Object key) {
+ purge();
- public ReferenceMapSoftReference(K key, V value) {
- this(key, value, null);
- }
+ Reference<V> reference = map.remove(key);
+ if (reference != null) {
+ return reference.get();
+ }
- public ReferenceMapSoftReference(K key, V value, ReferenceQueue<? super V>
queue) {
- super(value, queue);
- this.key = key;
- }
+ return null;
+ }
+
+ public int size() {
+ purge();
+
+ return map.size();
+ }
+
+ public Collection<V> values() {
+ throw new UnsupportedOperationException();
+ }
+
}
\ No newline at end of file