Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2013-04-20 06:25:26 +0000
committerEike Stepper2013-04-20 06:25:26 +0000
commit38ddb33c40630f799efa9fb3af1d1a3754e9201f (patch)
treedacf35c9ec5bec6a9231acb5e6f61652ab318a39 /plugins/org.eclipse.net4j.util/src
parentcf6c29f8a8c059c6b8d9d8449700b5c807641c87 (diff)
parentb0ce7501cb2566d97733dd7a07d73b5d72f72134 (diff)
downloadcdo-38ddb33c40630f799efa9fb3af1d1a3754e9201f.tar.gz
cdo-38ddb33c40630f799efa9fb3af1d1a3754e9201f.tar.xz
cdo-38ddb33c40630f799efa9fb3af1d1a3754e9201f.zip
Merge branch 'master' into committers/estepper/merger-2
Conflicts: plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionData.java plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/revision/AbstractCDORevision.java plugins/org.eclipse.emf.cdo.dawn.codegen.dawngenmodel.emf/src/templates/EMFFragmentTemplate.xpt plugins/org.eclipse.emf.cdo.ui/plugin.properties plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/DefaultCDOMerger.java
Diffstat (limited to 'plugins/org.eclipse.net4j.util/src')
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/container/PluginContainer.java3
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/factory/PluginFactoryRegistry.java17
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java47
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java52
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/MoveableArrayList.java4
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java24
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/PositionProvider.java20
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/QueueWorker.java11
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/TrackableTimerTask.java123
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/IManagedContainerFactory.java35
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/ManagedContainer.java28
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/IFactory.java9
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/monitor/AbstractMonitor.java18
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/Interner.java277
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap.java361
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap2.java453
16 files changed, 1118 insertions, 364 deletions
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/container/PluginContainer.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/container/PluginContainer.java
index e4fc8603a7..6df16832d2 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/container/PluginContainer.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/container/PluginContainer.java
@@ -8,6 +8,7 @@
* Contributors:
* Eike Stepper - initial API and implementation
* Christian W. Damus (CEA) - private plug-in container instances
+ * Christian W. Damus (CEA) - bug 399641: container-aware factories
*/
package org.eclipse.net4j.internal.util.container;
@@ -37,7 +38,7 @@ public class PluginContainer extends ManagedContainer implements IPluginContaine
@Override
protected IRegistry<IFactoryKey, IFactory> createFactoryRegistry()
{
- return new PluginFactoryRegistry();
+ return new PluginFactoryRegistry(this);
}
@Override
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/factory/PluginFactoryRegistry.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/factory/PluginFactoryRegistry.java
index 19491cd3e0..0469d7ab5a 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/factory/PluginFactoryRegistry.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/internal/util/factory/PluginFactoryRegistry.java
@@ -7,10 +7,12 @@
*
* Contributors:
* Eike Stepper - initial API and implementation
+ * Christian W. Damus (CEA) - bug 399641: container-aware factories
*/
package org.eclipse.net4j.internal.util.factory;
import org.eclipse.net4j.internal.util.bundle.OM;
+import org.eclipse.net4j.util.container.IManagedContainer;
import org.eclipse.net4j.util.factory.FactoryDescriptor;
import org.eclipse.net4j.util.factory.IFactory;
import org.eclipse.net4j.util.factory.IFactoryKey;
@@ -25,10 +27,13 @@ public class PluginFactoryRegistry extends HashMapRegistry<IFactoryKey, IFactory
public static final String EXT_POINT = "factories"; //$NON-NLS-1$
+ private final IManagedContainer container;
+
private Object extensionRegistryListener;
- public PluginFactoryRegistry()
+ public PluginFactoryRegistry(IManagedContainer container)
{
+ this.container = container;
}
@Override
@@ -39,6 +44,10 @@ public class PluginFactoryRegistry extends HashMapRegistry<IFactoryKey, IFactory
{
FactoryDescriptor descriptor = (FactoryDescriptor)factory;
factory = descriptor.createFactory();
+ if (factory != null && factory != descriptor)
+ {
+ put(factory.getKey(), factory);
+ }
}
return factory;
@@ -53,6 +62,7 @@ public class PluginFactoryRegistry extends HashMapRegistry<IFactoryKey, IFactory
protected void doActivate() throws Exception
{
super.doActivate();
+
try
{
doActivateOSGi();
@@ -79,6 +89,11 @@ public class PluginFactoryRegistry extends HashMapRegistry<IFactoryKey, IFactory
super.doDeactivate();
}
+ protected final IManagedContainer getManagedContainer()
+ {
+ return container;
+ }
+
private void doActivateOSGi()
{
org.eclipse.core.runtime.IExtensionRegistry extensionRegistry = org.eclipse.core.runtime.Platform
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java
index b2572ea3ce..c0c0320444 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ObjectUtil.java
@@ -4,19 +4,21 @@
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
- *
+ *
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util;
+import org.eclipse.net4j.util.collection.Closeable;
+
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
/**
* Various static helper methods.
- *
+ *
* @author Eike Stepper
*/
public final class ObjectUtil
@@ -47,7 +49,7 @@ public final class ObjectUtil
/**
* A collision-free hash code for small sets (<=4) of small, positive integers (<=128)
- *
+ *
* @since 3.2
*/
public static int hashCode(int... values)
@@ -107,4 +109,43 @@ public final class ObjectUtil
{
return string == null || string.length() == 0;
}
+
+ /**
+ * @since 3.3
+ */
+ public static Exception close(Object object)
+ {
+ try
+ {
+ if (object instanceof Closeable)
+ {
+ Closeable closeable = (Closeable)object;
+ closeable.close();
+ }
+ else if (object instanceof java.io.Closeable)
+ {
+ java.io.Closeable closeable = (java.io.Closeable)object;
+ closeable.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ return ex;
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 3.3
+ */
+ public static <T> T notNull(T object)
+ {
+ if (object == null)
+ {
+ throw new NullPointerException();
+ }
+
+ return object;
+ }
}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
index 0bb6ac07d1..c113d81ad2 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
@@ -7,6 +7,7 @@
*
* Contributors:
* Eike Stepper - initial API and implementation
+ * Christian W. Damus (CEA) - bug 376620: switch on primitive types
*/
package org.eclipse.net4j.util;
@@ -368,7 +369,7 @@ public final class ReflectUtil
for (Field field : fields)
{
Object value = getValue(field, object);
- result[i++] = new Pair<Field, Object>(field, value);
+ result[i++] = Pair.create(field, value);
}
return result;
@@ -625,4 +626,53 @@ public final class ReflectUtil
public @interface ExcludeFromDump
{
}
+
+ /**
+ * @author Christian W. Damus (CEA)
+ * @since 3.3
+ */
+ public static enum PrimitiveType
+ {
+ BOOLEAN(boolean.class), //
+ BYTE(byte.class), //
+ CHAR(char.class), //
+ SHORT(short.class), //
+ INT(int.class), //
+ LONG(long.class), //
+ FLOAT(float.class), //
+ DOUBLE(double.class), //
+ VOID(void.class), //
+ NONE(null);
+
+ private static final Map<Class<?>, PrimitiveType> INSTANCES = new HashMap<Class<?>, PrimitiveType>();
+
+ private final Class<?> type;
+
+ static
+ {
+ for (PrimitiveType primitiveType : values())
+ {
+ if (primitiveType.type != null)
+ {
+ INSTANCES.put(primitiveType.type, primitiveType);
+ }
+ }
+ }
+
+ private PrimitiveType(Class<?> type)
+ {
+ this.type = type;
+ }
+
+ public Class<?> type()
+ {
+ return type;
+ }
+
+ public static PrimitiveType forClass(Class<?> clazz)
+ {
+ PrimitiveType result = INSTANCES.get(clazz);
+ return result == null ? NONE : result;
+ }
+ }
}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/MoveableArrayList.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/MoveableArrayList.java
index 825f796aa2..84f539fd9a 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/MoveableArrayList.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/MoveableArrayList.java
@@ -14,7 +14,9 @@ import java.util.ArrayList;
/**
* A list with O(1) effort for random access.
- *
+ * <p>
+ * Whenever possible EMF's BasicEList should be used in favour of this list implementation.
+ *
* @author Eike Stepper
*/
public class MoveableArrayList<E> extends ArrayList<E> implements MoveableList<E>
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java
index f22a62398c..9826988662 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/Pair.java
@@ -97,4 +97,28 @@ public class Pair<T1, T2>
{
return "Pair[" + element1 + ", " + element2 + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
+
+ /**
+ * @since 3.3
+ */
+ public Pair<T1, T2> copy()
+ {
+ return new Pair<T1, T2>(this);
+ }
+
+ /**
+ * @since 3.3
+ */
+ public static <T1, T2> Pair<T1, T2> create()
+ {
+ return new Pair<T1, T2>();
+ }
+
+ /**
+ * @since 3.3
+ */
+ public static <T1, T2> Pair<T1, T2> create(T1 element1, T2 element2)
+ {
+ return new Pair<T1, T2>(element1, element2);
+ }
}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/PositionProvider.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/PositionProvider.java
new file mode 100644
index 0000000000..110cead45d
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/collection/PositionProvider.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.collection;
+
+/**
+ * @author Eike Stepper
+ * @since 3.3
+ */
+public interface PositionProvider
+{
+ public int getPosition();
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/QueueWorker.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/QueueWorker.java
index caec3355fa..1094072337 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/QueueWorker.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/QueueWorker.java
@@ -87,6 +87,10 @@ public abstract class QueueWorker<E> extends Worker
{
work(context, element);
}
+ else
+ {
+ noWork(context);
+ }
}
/**
@@ -99,6 +103,13 @@ public abstract class QueueWorker<E> extends Worker
protected abstract void work(WorkContext context, E element);
+ /**
+ * @since 3.3
+ */
+ protected void noWork(WorkContext context)
+ {
+ }
+
protected BlockingQueue<E> createQueue()
{
return new LinkedBlockingQueue<E>();
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/TrackableTimerTask.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/TrackableTimerTask.java
new file mode 100644
index 0000000000..14fb116af3
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/TrackableTimerTask.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.concurrent;
+
+import org.eclipse.net4j.internal.util.bundle.OM;
+import org.eclipse.net4j.util.om.OMPlatform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TimerTask;
+import java.util.WeakHashMap;
+
+/**
+ * @author Eike Stepper
+ * @since 3.3
+ */
+public abstract class TrackableTimerTask extends TimerTask
+{
+ /**
+ * The boolean value of the system property <code>org.eclipse.net4j.util.concurrent.TrackTimerTasks</code>.
+ */
+ public static final boolean TRACK_TIMER_TASKS = Boolean.parseBoolean(OMPlatform.INSTANCE.getProperty(
+ "org.eclipse.net4j.util.concurrent.TrackTimerTasks", "false"));
+
+ private static final Map<TrackableTimerTask, ConstructionInfo> CONSTRUCTION_INFOS = TRACK_TIMER_TASKS ? new WeakHashMap<TrackableTimerTask, ConstructionInfo>()
+ : null;
+
+ protected TrackableTimerTask()
+ {
+ if (TRACK_TIMER_TASKS)
+ {
+ synchronized (CONSTRUCTION_INFOS)
+ {
+ CONSTRUCTION_INFOS.put(this, new ConstructionInfo());
+ }
+ }
+ }
+
+ @Override
+ public boolean cancel()
+ {
+ if (TRACK_TIMER_TASKS)
+ {
+ synchronized (CONSTRUCTION_INFOS)
+ {
+ CONSTRUCTION_INFOS.remove(this);
+ }
+ }
+
+ return super.cancel();
+ }
+
+ public static Collection<Exception> getConstructionStackTraces(long minLifeTimeMillis)
+ {
+ if (!TRACK_TIMER_TASKS)
+ {
+ return Collections.emptyList();
+ }
+
+ long maxTimeStamp = System.currentTimeMillis() - minLifeTimeMillis;
+ Collection<Exception> result = new ArrayList<Exception>();
+
+ synchronized (CONSTRUCTION_INFOS)
+ {
+ for (ConstructionInfo constructionInfo : CONSTRUCTION_INFOS.values())
+ {
+ if (constructionInfo.timeStamp < maxTimeStamp)
+ {
+ result.add(constructionInfo.stackTrace);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static void logConstructionStackTraces(long minLifeTimeMillis)
+ {
+ if (TRACK_TIMER_TASKS)
+ {
+ Collection<Exception> constructionStackTraces = getConstructionStackTraces(minLifeTimeMillis);
+ if (!constructionStackTraces.isEmpty())
+ {
+ for (Exception exception : constructionStackTraces)
+ {
+ OM.LOG.info(exception);
+ }
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ConstructionInfo
+ {
+ public final long timeStamp = System.currentTimeMillis();
+
+ public final Exception stackTrace = getStackTrace();
+
+ private Exception getStackTrace()
+ {
+ try
+ {
+ throw new Exception("The timer task " + TrackableTimerTask.this + " has been constructed here:");
+ }
+ catch (Exception ex)
+ {
+ return ex;
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/IManagedContainerFactory.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/IManagedContainerFactory.java
new file mode 100644
index 0000000000..4987d3481a
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/IManagedContainerFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2004 - 2013 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - initial API and implementation
+ */
+package org.eclipse.net4j.util.container;
+
+import org.eclipse.net4j.util.factory.IFactory;
+
+/**
+ * An extension of the {@link IFactory} protocol that provides an
+ * awareness of the container that instantiated it, so that the
+ * factory may reach back into that container for dependencies.
+ *
+ * @since 3.3
+ */
+public interface IManagedContainerFactory extends IFactory
+{
+ /**
+ * Obtains the container that I should use to get my dependencies.
+ */
+ public IManagedContainer getManagedContainer();
+
+ /**
+ * Assigns the container that I should use to get my dependencies.
+ *
+ * @param container the container in which I am created/registered
+ */
+ public void setManagedContainer(IManagedContainer container);
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/ManagedContainer.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/ManagedContainer.java
index 70c9945b0f..85836480b6 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/ManagedContainer.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/container/ManagedContainer.java
@@ -7,6 +7,7 @@
*
* Contributors:
* Eike Stepper - initial API and implementation
+ * Christian W. Damus (CEA) - bug 399641: container-aware factories
*/
package org.eclipse.net4j.util.container;
@@ -40,6 +41,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -88,6 +90,32 @@ public class ManagedContainer extends Lifecycle implements IManagedContainer
if (factoryRegistry == null)
{
factoryRegistry = createFactoryRegistry();
+ factoryRegistry.addListener(new ContainerEventAdapter<Map.Entry<IFactoryKey, IFactory>>()
+ {
+ @Override
+ protected void onAdded(IContainer<Map.Entry<IFactoryKey, IFactory>> container,
+ Map.Entry<IFactoryKey, IFactory> entry)
+ {
+ updateFactory(entry, ManagedContainer.this);
+ }
+
+ @Override
+ protected void onRemoved(IContainer<Map.Entry<IFactoryKey, IFactory>> container,
+ Map.Entry<IFactoryKey, IFactory> entry)
+ {
+ updateFactory(entry, null);
+ }
+
+ private void updateFactory(Map.Entry<IFactoryKey, IFactory> entry, IManagedContainer container)
+ {
+ IFactory factory = entry.getValue();
+ if (factory instanceof IManagedContainerFactory)
+ {
+ IManagedContainerFactory f = (IManagedContainerFactory)factory;
+ f.setManagedContainer(container);
+ }
+ }
+ });
}
return factoryRegistry;
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/IFactory.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/IFactory.java
index 23d408d54b..f95aa05fcc 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/IFactory.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/factory/IFactory.java
@@ -7,15 +7,24 @@
*
* Contributors:
* Eike Stepper - initial API and implementation
+ * Christian W. Damus (CEA) - bug 399641: container-aware factories
*/
package org.eclipse.net4j.util.factory;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IManagedContainerFactory;
+
/**
* {@link #create(String) Creates} objects from a string {@link #getDescriptionFor(Object) description}.
+ * <p>
+ * If a factory is registered in an {@link IManagedContainer}, the {@link IManagedContainerFactory} extension interface
+ * injects that container into the factory for it to reach back into to obtain dependencies.
*
* @author Eike Stepper
* @apiviz.landmark
* @apiviz.has {@link IFactoryKey}
+ *
+ * @see IManagedContainerFactory
*/
public interface IFactory
{
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/monitor/AbstractMonitor.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/monitor/AbstractMonitor.java
index 579e4b54ac..84fbdc893d 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/monitor/AbstractMonitor.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/om/monitor/AbstractMonitor.java
@@ -11,6 +11,8 @@
package org.eclipse.net4j.util.om.monitor;
import org.eclipse.net4j.internal.util.bundle.OM;
+import org.eclipse.net4j.util.concurrent.TrackableTimerTask;
+import org.eclipse.net4j.util.om.OMPlatform;
import java.util.Timer;
import java.util.TimerTask;
@@ -21,6 +23,9 @@ import java.util.TimerTask;
*/
public abstract class AbstractMonitor implements OMMonitor
{
+ private static final boolean CHECK_BEGIN = Boolean.parseBoolean(OMPlatform.INSTANCE.getProperty(
+ "org.eclipse.net4j.util.om.monitor.CheckBegin", "false"));
+
private static final long NOT_BEGUN = -1;
private double totalWork = NOT_BEGUN;
@@ -138,7 +143,7 @@ public abstract class AbstractMonitor implements OMMonitor
private void checkBegun() throws MonitorCanceledException
{
- if (!hasBegun())
+ if (CHECK_BEGIN && !hasBegun())
{
throw new IllegalStateException("begin() has not been called"); //$NON-NLS-1$
}
@@ -155,7 +160,7 @@ public abstract class AbstractMonitor implements OMMonitor
/**
* @author Eike Stepper
*/
- public static class AsyncTimerTask extends TimerTask implements Async
+ public static class AsyncTimerTask extends TrackableTimerTask implements Async
{
private OMMonitor monitor;
@@ -172,7 +177,7 @@ public abstract class AbstractMonitor implements OMMonitor
{
try
{
- if (!canceled)
+ if (!canceled && monitor != null)
{
double work = 1 - monitor.getWork();
monitor.worked(work / TEN);
@@ -188,7 +193,11 @@ public abstract class AbstractMonitor implements OMMonitor
{
try
{
- monitor.done();
+ if (monitor != null)
+ {
+ monitor.done();
+ }
+
cancel();
}
catch (Exception ex)
@@ -201,6 +210,7 @@ public abstract class AbstractMonitor implements OMMonitor
public boolean cancel()
{
canceled = true;
+ monitor = null;
return super.cancel();
}
}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/Interner.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/Interner.java
new file mode 100644
index 0000000000..3247428250
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/Interner.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ed Merks - initial API and implementation
+ */
+package org.eclipse.net4j.util.ref;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+/**
+ * @author Ed Merks
+ * @since 3.3
+ */
+public class Interner<E>
+{
+ private static final int[] PRIME_CAPACITIES = new int[] { 17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411,
+ 32771, 65537, 131101, 262147, 524309, 1048583, 2097169, 4194319, 8388617, 16777259, 33554467, 67108879,
+ 134217757, 268435459, 536870923, 1073741827, 2147483629 };
+
+ private int size;
+
+ private int capacityIndex;
+
+ private Entry<E>[] entries;
+
+ private ReferenceQueue<E> queue = new ReferenceQueue<E>();
+
+ public Interner()
+ {
+ }
+
+ public Interner(int minimumCapacity)
+ {
+ grow(minimumCapacity);
+ }
+
+ /**
+ * Ensures that the set has at least the specifies capacity.
+ * Higher capacity ensures fewer collisions hence faster lookup.
+ * Does nothing if the specified capacity is smaller than the current capacity.
+ */
+ public void grow(int minimumCapacity)
+ {
+ int currentCapacity = PRIME_CAPACITIES[capacityIndex];
+ if (currentCapacity < minimumCapacity)
+ {
+ for (int i = 0, length = PRIME_CAPACITIES.length; i < length; ++i)
+ {
+ int capacity = PRIME_CAPACITIES[i];
+ if (capacity > minimumCapacity)
+ {
+ capacityIndex = i;
+ rehash(newEntries(capacity));
+ break;
+ }
+ }
+ }
+ }
+
+ public E intern(E object)
+ {
+ cleanup();
+
+ int hashCode = hashCode(object);
+ if (entries != null)
+ {
+ int index = index(hashCode);
+ for (Entry<E> entry = entries[index]; entry != null; entry = entry.next)
+ {
+ if (hashCode == entry.hashCode)
+ {
+ E otherObject = entry.get();
+ if (equals(object, otherObject))
+ {
+ return otherObject;
+ }
+ }
+ }
+ }
+
+ Entry<E> entry = createEntry(object, hashCode);
+ addEntry(entry);
+ return object;
+ }
+
+ /**
+ * Gets the first entry in the table with exactly the given hash code.
+ * It's very useful to call {@link Entry#getNextEntry()} to yield the next entry with exactly this same hash code.
+ */
+ protected Entry<E> getEntry(int hashCode)
+ {
+ cleanup();
+
+ if (entries != null)
+ {
+ int index = index(hashCode);
+ for (Entry<E> entry = entries[index]; entry != null; entry = entry.next)
+ {
+ if (hashCode == entry.hashCode)
+ {
+ return entry;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected int hashCode(E object)
+ {
+ return object.hashCode();
+ }
+
+ /**
+ * Returns true if the two objects are to be considered equal.
+ * The first object will always be the one passed in as an argument to {@link #add(Object) add},
+ * {@link #contains(Object) contains}, {@link #get(Object) get}, {@link #intern(Object)}, {@link #remove(Object)}.
+ */
+ protected boolean equals(E object, E otherObject)
+ {
+ return object == otherObject || object.equals(otherObject);
+ }
+
+ protected Entry<E> createEntry(E object, int hashCode)
+ {
+ return new Entry<E>(object, hashCode, queue);
+ }
+
+ /**
+ * Adds a new entry, {@link #ensureCapacity() ensures} the capacity is sufficient and increases the {@link #size}.
+ */
+ protected void addEntry(Entry<E> entry)
+ {
+ ensureCapacity();
+ ++size;
+ putEntry(entry);
+ }
+
+ private Entry<E>[] newEntries(int capacity)
+ {
+ @SuppressWarnings("unchecked")
+ Entry<E>[] newEntries = new Entry[capacity];
+ return newEntries;
+ }
+
+ private void ensureCapacity()
+ {
+ int capacity = PRIME_CAPACITIES[capacityIndex];
+ if (entries == null)
+ {
+ entries = newEntries(capacity);
+ }
+ else if (size > (capacity >> 2) * 3)
+ {
+ // The current size is more the 3/4 of the current capacity
+ ++capacityIndex;
+ rehash(newEntries(PRIME_CAPACITIES[capacityIndex]));
+ }
+ }
+
+ private void rehash(Entry<E>[] newEntries)
+ {
+ Entry<E>[] oldEntries = entries;
+ entries = newEntries;
+ if (oldEntries != null)
+ {
+ for (int i = 0, length = oldEntries.length; i < length; ++i)
+ {
+ Entry<E> entry = oldEntries[i];
+ while (entry != null)
+ {
+ Entry<E> nextEntry = entry.next;
+ putEntry(entry);
+ entry = nextEntry;
+ }
+ }
+ }
+ }
+
+ private int index(int hashCode)
+ {
+ return (hashCode & 0x7FFFFFFF) % entries.length;
+ }
+
+ private void putEntry(Entry<E> entry)
+ {
+ int index = index(entry.hashCode);
+ Entry<E> otherEntry = entries[index];
+ entries[index] = entry;
+ entry.next = otherEntry;
+ }
+
+ private void cleanup()
+ {
+ for (;;)
+ {
+ @SuppressWarnings("unchecked")
+ Entry<E> entry = (Entry<E>)queue.poll();
+ if (entry == null)
+ {
+ return;
+ }
+
+ removeEntry(entry);
+ }
+ }
+
+ private void removeEntry(Entry<E> entry)
+ {
+ int index = index(entry.hashCode);
+ Entry<E> otherEntry = entries[index];
+ --size;
+ if (entry == otherEntry)
+ {
+ entries[index] = entry.next;
+ }
+ else
+ {
+ for (Entry<E> nextOtherEntry = otherEntry.next; nextOtherEntry != null; otherEntry = nextOtherEntry, nextOtherEntry = nextOtherEntry.next)
+ {
+ if (nextOtherEntry == entry)
+ {
+ otherEntry.next = entry.next;
+ break;
+ }
+ }
+ }
+
+ // Make life easier for the garbage collector.
+ entry.next = null;
+ entry.clear();
+ }
+
+ /**
+ * A weak reference holder that caches the hash code of the referent and is chained in the {@link Interner#entries} to handle collisions.
+ *
+ * @author Ed Merks
+ */
+ protected static class Entry<E> extends WeakReference<E>
+ {
+ public final int hashCode;
+
+ public Entry<E> next;
+
+ public Entry(E object, int hashCode, ReferenceQueue<? super E> queue)
+ {
+ super(object, queue);
+ this.hashCode = hashCode;
+ }
+
+ public Entry<E> getNextEntry()
+ {
+ for (Entry<E> entry = next; entry != null; entry = entry.next)
+ {
+ if (entry.hashCode == hashCode)
+ {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString()
+ {
+ E object = get();
+ return object == null ? "null" : object.toString();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap.java
index 75bff38f4b..0ea271d86a 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap.java
@@ -4,21 +4,13 @@
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
- *
+ *
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util.ref;
-import org.eclipse.net4j.util.collection.MapEntry;
-
import java.lang.ref.ReferenceQueue;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -31,17 +23,11 @@ import java.util.concurrent.ConcurrentMap;
* <p>
* <b>Note:</b> This map is not synchronized. If it is to be used by multiple threads concurrently the user is
* responsible for applying proper external synchronization!
- *
+ *
* @author Eike Stepper
*/
-public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>
+public abstract class ReferenceValueMap<K, V> extends ReferenceValueMap2<K, V> implements ConcurrentMap<K, V>
{
- private ConcurrentMap<K, KeyedReference<K, V>> map;
-
- private ReferenceQueue<V> queue;
-
- private EntrySet entrySet;
-
public ReferenceValueMap()
{
this(new ConcurrentHashMap<K, KeyedReference<K, V>>());
@@ -49,93 +35,7 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
public ReferenceValueMap(ConcurrentMap<K, KeyedReference<K, V>> map)
{
- if (!map.isEmpty())
- {
- throw new IllegalArgumentException("!map.isEmpty()"); //$NON-NLS-1$
- }
-
- this.map = map;
- queue = createQueue();
- }
-
- @Override
- public int size()
- {
- purgeQueue();
- return map.size();
- }
-
- @Override
- public boolean isEmpty()
- {
- purgeQueue();
- return map.isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key)
- {
- KeyedReference<K, V> ref = map.get(key);
- if (ref != null)
- {
- if (ref.get() == null)
- {
- // ref.enqueue();
- return false;
- }
-
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean containsValue(Object value)
- {
- if (value == null)
- {
- throw new IllegalArgumentException("value == null"); //$NON-NLS-1$
- }
-
- for (KeyedReference<K, V> ref : map.values())
- {
- V v = ref.get();
- if (v == null)
- {
- // ref.enqueue();
- return false;
- }
-
- if (value.equals(v))
- {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public V get(Object key)
- {
- KeyedReference<K, V> ref = map.get(key);
- return dereference(ref);
- }
-
- @Override
- public V put(K key, V value)
- {
- try
- {
- KeyedReference<K, V> ref = createReference(key, value, queue);
- KeyedReference<K, V> oldRef = map.put(key, ref);
- return dereference(oldRef);
- }
- finally
- {
- purgeQueue();
- }
+ super(map);
}
public V putIfAbsent(K key, V value)
@@ -143,7 +43,7 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
try
{
KeyedReference<K, V> ref = createReference(key, value, queue);
- KeyedReference<K, V> oldRef = map.putIfAbsent(key, ref);
+ KeyedReference<K, V> oldRef = ((ConcurrentMap<K, KeyedReference<K, V>>)map).putIfAbsent(key, ref);
return dereference(oldRef);
}
finally
@@ -157,7 +57,7 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
try
{
KeyedReference<K, V> ref = createReference(key, value, queue);
- KeyedReference<K, V> oldRef = map.replace(key, ref);
+ KeyedReference<K, V> oldRef = ((ConcurrentMap<K, KeyedReference<K, V>>)map).replace(key, ref);
return dereference(oldRef);
}
finally
@@ -173,7 +73,7 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
// TODO Consider a dummy KeyedReference class for oldRef
KeyedReference<K, V> oldRef = createReference(key, oldValue, queue);
KeyedReference<K, V> newRef = createReference(key, newValue, queue);
- return map.replace(key, oldRef, newRef);
+ return ((ConcurrentMap<K, KeyedReference<K, V>>)map).replace(key, oldRef, newRef);
}
finally
{
@@ -181,75 +81,12 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
}
}
- @Override
- public V remove(Object key)
- {
- KeyedReference<K, V> ref = map.remove(key);
- return dereference(ref);
- }
-
public boolean remove(Object key, Object value)
{
// TODO Consider a dummy KeyedReference class for value
- return map.remove(key, value);
- }
-
- @Override
- public void clear()
- {
- purgeQueue();
- map.clear();
- }
-
- @Override
- public Set<Map.Entry<K, V>> entrySet()
- {
- if (entrySet == null)
- {
- purgeQueue();
- entrySet = new EntrySet();
- }
-
- return entrySet;
- }
-
- protected ReferenceQueue<V> createQueue()
- {
- return new ReferenceQueue<V>();
- }
-
- @SuppressWarnings("unchecked")
- protected void purgeQueue()
- {
- if (queue != null)
- {
- KeyedReference<K, V> ref;
- while ((ref = (KeyedReference<K, V>)queue.poll()) != null)
- {
- // Slightly faster than map.get() + map.remove()
- K key = ref.getKey();
- map.remove(key, ref);
- purged(key);
- }
- }
- }
-
- protected void purged(K key)
- {
- }
-
- protected V dereference(KeyedReference<K, V> ref)
- {
- if (ref == null)
- {
- return null;
- }
-
- return ref.get();
+ return ((ConcurrentMap<K, KeyedReference<K, V>>)map).remove(key, value);
}
- protected abstract KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue);
-
/**
* @author Eike Stepper
*/
@@ -318,186 +155,4 @@ public abstract class ReferenceValueMap<K, V> extends AbstractMap<K, V> implemen
return new KeyedWeakReference<K, V>(key, value, queue);
}
}
-
- /**
- * @author Eike Stepper
- */
- private class EntrySet extends AbstractSet<Map.Entry<K, V>>
- {
- public EntrySet()
- {
- }
-
- @Override
- public int size()
- {
- return map.size();
- }
-
- @Override
- public boolean isEmpty()
- {
- return map.isEmpty();
- }
-
- @Override
- public boolean contains(Object object)
- {
- if (object == null)
- {
- throw new IllegalArgumentException("object == null"); //$NON-NLS-1$
- }
-
- if (object instanceof Map.Entry<?, ?>)
- {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
- Object key = entry.getKey();
- Object value = entry.getValue();
- return key != null && value != null && value.equals(get(key));
- }
-
- return false;
- }
-
- @Override
- public Iterator<Map.Entry<K, V>> iterator()
- {
- return new EntrySetIterator();
- }
-
- @Override
- public Object[] toArray()
- {
- Object[] a = new Object[size()];
- int i = 0;
- for (Map.Entry<K, V> entry : this)
- {
- a[i++] = entry;
- }
-
- return a;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <T> T[] toArray(T[] a)
- {
- if (a == null)
- {
- throw new IllegalArgumentException("array == null"); //$NON-NLS-1$
- }
-
- int size = size();
- if (a.length < size)
- {
- a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
- }
-
- int i = 0;
- for (Map.Entry<K, V> entry : this)
- {
- a[i++] = (T)entry;
- }
-
- if (a.length > size)
- {
- a[size] = null;
- }
-
- return a;
- }
-
- @Override
- public boolean remove(Object object)
- {
- if (object == null)
- {
- throw new IllegalArgumentException("object == null"); //$NON-NLS-1$
- }
-
- if (object instanceof Map.Entry<?, ?>)
- {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
- return map.remove(entry.getKey(), entry.getValue());
- }
-
- return false;
- }
-
- @Override
- public void clear()
- {
- map.clear();
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private class EntrySetIterator implements Iterator<Map.Entry<K, V>>
- {
- private Iterator<Entry<K, KeyedReference<K, V>>> it = map.entrySet().iterator();
-
- private MapEntry<K, V> nextEntry;
-
- private K lastKey;
-
- public EntrySetIterator()
- {
- }
-
- public boolean hasNext()
- {
- if (nextEntry != null)
- {
- return true;
- }
-
- while (it.hasNext())
- {
- Entry<K, KeyedReference<K, V>> entry = it.next();
- lastKey = entry.getKey();
- V value = dereference(entry.getValue());
- if (value != null)
- {
- nextEntry = new MapEntry<K, V>(lastKey, value);
- return true;
- }
- }
-
- return false;
- }
-
- public Entry<K, V> next()
- {
- if (nextEntry == null)
- {
- if (!hasNext())
- {
- throw new NoSuchElementException();
- }
- }
-
- try
- {
- return nextEntry;
- }
- finally
- {
- nextEntry = null;
- }
- }
-
- public void remove()
- {
- if (lastKey == null)
- {
- throw new IllegalStateException("lastKey == null"); //$NON-NLS-1$
- }
-
- map.remove(lastKey);
- lastKey = null;
- nextEntry = null;
- }
- }
}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap2.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap2.java
new file mode 100644
index 0000000000..e57f92dce0
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ref/ReferenceValueMap2.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.ref;
+
+import org.eclipse.net4j.util.collection.MapEntry;
+
+import java.lang.ref.ReferenceQueue;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A {@link Map} implementation that uses {@link KeyedReference} instances ({@link KeyedStrongReference},
+ * {@link KeyedSoftReference}, {@link KeyedWeakReference} or {@link KeyedPhantomReference}) as its values.
+ * <p>
+ * A <code>ReferenceValueMap</code> can be used to cache mappings until the <em>value</em> of the mapping is no longer
+ * reachable from outside of the map
+ * <p>
+ * <b>Note:</b> This map is not synchronized. If it is to be used by multiple threads concurrently the user is
+ * responsible for applying proper external synchronization!
+ *
+ * @author Eike Stepper
+ * @since 3.3
+ */
+public abstract class ReferenceValueMap2<K, V> extends AbstractMap<K, V>
+{
+ Map<K, KeyedReference<K, V>> map;
+
+ ReferenceQueue<V> queue;
+
+ private EntrySet entrySet;
+
+ public ReferenceValueMap2()
+ {
+ this(new HashMap<K, KeyedReference<K, V>>());
+ }
+
+ public ReferenceValueMap2(Map<K, KeyedReference<K, V>> map)
+ {
+ if (!map.isEmpty())
+ {
+ throw new IllegalArgumentException("!map.isEmpty()"); //$NON-NLS-1$
+ }
+
+ this.map = map;
+ queue = createQueue();
+ }
+
+ @Override
+ public int size()
+ {
+ purgeQueue();
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ purgeQueue();
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ KeyedReference<K, V> ref = map.get(key);
+ if (ref != null)
+ {
+ if (ref.get() == null)
+ {
+ // ref.enqueue();
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean containsValue(Object value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("value == null"); //$NON-NLS-1$
+ }
+
+ for (KeyedReference<K, V> ref : map.values())
+ {
+ V v = ref.get();
+ if (v == null)
+ {
+ // ref.enqueue();
+ return false;
+ }
+
+ if (value.equals(v))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public V get(Object key)
+ {
+ KeyedReference<K, V> ref = map.get(key);
+ return dereference(ref);
+ }
+
+ @Override
+ public V put(K key, V value)
+ {
+ try
+ {
+ KeyedReference<K, V> ref = createReference(key, value, queue);
+ KeyedReference<K, V> oldRef = map.put(key, ref);
+ return dereference(oldRef);
+ }
+ finally
+ {
+ purgeQueue();
+ }
+ }
+
+ @Override
+ public V remove(Object key)
+ {
+ KeyedReference<K, V> ref = map.remove(key);
+ return dereference(ref);
+ }
+
+ @Override
+ public void clear()
+ {
+ purgeQueue();
+ map.clear();
+ }
+
+ @Override
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entrySet == null)
+ {
+ purgeQueue();
+ entrySet = new EntrySet();
+ }
+
+ return entrySet;
+ }
+
+ protected ReferenceQueue<V> createQueue()
+ {
+ return new ReferenceQueue<V>();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void purgeQueue()
+ {
+ if (queue != null)
+ {
+ KeyedReference<K, V> ref;
+ while ((ref = (KeyedReference<K, V>)queue.poll()) != null)
+ {
+ K key = ref.getKey();
+ map.remove(key);
+ purged(key);
+ }
+ }
+ }
+
+ protected void purged(K key)
+ {
+ }
+
+ protected V dereference(KeyedReference<K, V> ref)
+ {
+ if (ref == null)
+ {
+ return null;
+ }
+
+ return ref.get();
+ }
+
+ protected abstract KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue);
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Strong<K, V> extends ReferenceValueMap2<K, V>
+ {
+ public Strong()
+ {
+ }
+
+ public Strong(Map<K, KeyedReference<K, V>> map)
+ {
+ super(map);
+ }
+
+ @Override
+ protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
+ {
+ return new KeyedStrongReference<K, V>(key, value);
+ }
+
+ @Override
+ protected ReferenceQueue<V> createQueue()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Soft<K, V> extends ReferenceValueMap2<K, V>
+ {
+ public Soft()
+ {
+ }
+
+ public Soft(Map<K, KeyedReference<K, V>> map)
+ {
+ super(map);
+ }
+
+ @Override
+ protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
+ {
+ return new KeyedSoftReference<K, V>(key, value, queue);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static class Weak<K, V> extends ReferenceValueMap2<K, V>
+ {
+ public Weak()
+ {
+ }
+
+ public Weak(Map<K, KeyedReference<K, V>> map)
+ {
+ super(map);
+ }
+
+ @Override
+ protected KeyedReference<K, V> createReference(K key, V value, ReferenceQueue<V> queue)
+ {
+ return new KeyedWeakReference<K, V>(key, value, queue);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private class EntrySet extends AbstractSet<Map.Entry<K, V>>
+ {
+ public EntrySet()
+ {
+ }
+
+ @Override
+ public int size()
+ {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object object)
+ {
+ if (object == null)
+ {
+ throw new IllegalArgumentException("object == null"); //$NON-NLS-1$
+ }
+
+ if (object instanceof Map.Entry<?, ?>)
+ {
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ return key != null && value != null && value.equals(get(key));
+ }
+
+ return false;
+ }
+
+ @Override
+ public Iterator<Map.Entry<K, V>> iterator()
+ {
+ return new EntrySetIterator();
+ }
+
+ @Override
+ public Object[] toArray()
+ {
+ Object[] a = new Object[size()];
+ int i = 0;
+ for (Map.Entry<K, V> entry : this)
+ {
+ a[i++] = entry;
+ }
+
+ return a;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T[] toArray(T[] a)
+ {
+ if (a == null)
+ {
+ throw new IllegalArgumentException("array == null"); //$NON-NLS-1$
+ }
+
+ int size = size();
+ if (a.length < size)
+ {
+ a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
+ }
+
+ int i = 0;
+ for (Map.Entry<K, V> entry : this)
+ {
+ a[i++] = (T)entry;
+ }
+
+ if (a.length > size)
+ {
+ a[size] = null;
+ }
+
+ return a;
+ }
+
+ @Override
+ public boolean remove(Object object)
+ {
+ if (object == null)
+ {
+ throw new IllegalArgumentException("object == null"); //$NON-NLS-1$
+ }
+
+ if (object instanceof Map.Entry<?, ?>)
+ {
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>)object;
+ return map.remove(entry.getKey()) != null;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void clear()
+ {
+ map.clear();
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private class EntrySetIterator implements Iterator<Map.Entry<K, V>>
+ {
+ private Iterator<Entry<K, KeyedReference<K, V>>> it = map.entrySet().iterator();
+
+ private MapEntry<K, V> nextEntry;
+
+ private K lastKey;
+
+ public EntrySetIterator()
+ {
+ }
+
+ public boolean hasNext()
+ {
+ if (nextEntry != null)
+ {
+ return true;
+ }
+
+ while (it.hasNext())
+ {
+ Entry<K, KeyedReference<K, V>> entry = it.next();
+ lastKey = entry.getKey();
+ V value = dereference(entry.getValue());
+ if (value != null)
+ {
+ nextEntry = new MapEntry<K, V>(lastKey, value);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public Entry<K, V> next()
+ {
+ if (nextEntry == null)
+ {
+ if (!hasNext())
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ try
+ {
+ return nextEntry;
+ }
+ finally
+ {
+ nextEntry = null;
+ }
+ }
+
+ public void remove()
+ {
+ if (lastKey == null)
+ {
+ throw new IllegalStateException("lastKey == null"); //$NON-NLS-1$
+ }
+
+ map.remove(lastKey);
+ lastKey = null;
+ nextEntry = null;
+ }
+ }
+}

Back to the top