diff options
author | Eike Stepper | 2013-04-20 06:25:26 +0000 |
---|---|---|
committer | Eike Stepper | 2013-04-20 06:25:26 +0000 |
commit | 38ddb33c40630f799efa9fb3af1d1a3754e9201f (patch) | |
tree | dacf35c9ec5bec6a9231acb5e6f61652ab318a39 /plugins/org.eclipse.net4j.util/src | |
parent | cf6c29f8a8c059c6b8d9d8449700b5c807641c87 (diff) | |
parent | b0ce7501cb2566d97733dd7a07d73b5d72f72134 (diff) | |
download | cdo-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')
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; + } + } +} |