org.geotoolkit.internal.jaxb.UUIDs Maven / Gradle / Ivy
Show all versions of geotk-xml-base Show documentation
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2010-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.internal.jaxb;
import java.util.Map;
import java.util.UUID;
import java.util.Random;
import java.util.HashMap;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
import org.geotoolkit.resources.Errors;
import org.geotoolkit.internal.ReferenceQueueConsumer;
import org.geotoolkit.util.Disposable;
/**
* Maps {@link UUID} to arbitrary objects. The objects are typically instances of ISO 19115
* (metadata) and ISO 19111 (referencing) types. This {@code UUIDs} class works as a map with
* the following properties:
*
*
* Each UUID is associated to a specific instance, i.e. the underlying map uses
* {@linkplain System#identityHashCode identity hash code} and the {@code ==} comparison
* operator (not the {@link Object#equals(Object)} method) when looking for the {@link UUID}
* of a given object.
*
* The object instances are referenced by {@linkplain WeakReference weak references}.
* Consequently, assigning a {@link UUID} to an object will not prevent the object from being
* garbage collected.
*
*
* This class is convenient at XML marshalling and unmarshalling time for handling the {@code uuid}
* and {@code uuidref} attributes. The {@code uuidref} attribute is used to refer to an XML element
* that has a corresponding {@code uuid} attribute.
*
* @param The type of UUID objects, typically {@link String} or {@link UUID}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.19
*
* @see GML identifiers
*
* @since 3.13
* @module
*/
@ThreadSafe
public abstract class UUIDs {
/**
* An implementation of {@link UUIDs} using {@link UUID}.
*/
public static final class Standard extends UUIDs {
/** Generates a random UUID. */
@Override protected UUID createUUID() {
return UUID.randomUUID();
}
}
/**
* An implementation of {@link UUIDs} using strings.
*/
public static final class AsString extends UUIDs {
private final Random random = new Random();
/** Generates a random ID as an alpha-numeric code beginning with a letter. */
@Override protected String createUUID() {
final char[] id = new char[6];
for (int i=0; i uuidToObject = new HashMap();
/**
* The UUID for existing objects.
* Every access to this map must be synchronized on {@code this}.
*/
@GuardedBy("this")
private final Map objectToUUID = new HashMap();
/**
* Creates a new, initially empty, map of UUIDs.
*/
public UUIDs() {
}
/**
* Returns the object associated to the given UUID, or {@code null} if none.
*
* @param uuid The UUID for which to look for an object (can be {@code null}).
* @return The object associated to the given UUID, or {@code null} if none.
*/
public final Object lookup(final T uuid) {
final Reference> ref;
synchronized (this) {
ref = uuidToObject.get(uuid);
}
return (ref != null) ? ref.get() : null;
}
/**
* Returns the UUID associated to the given object, or {@code null} if none.
*
* @param object The object for which to get the UUID (can be {@code null}).
* @return The UUID of the given object, or {@code null} if none.
*/
public final T getUUID(final Object object) {
final StrongRef check = new StrongRef(object);
synchronized (this) {
return objectToUUID.get(check);
}
}
/**
* Sets the UUID for the given object. A UUID can be associated only once for any new
* object, and the same UUID can not be associated to 2 different objects.
*
* if an object is already mapped to the given UUID, then the mapping is not
* modified and the currently mapped object is returned. The returned object doesn't need to
* be equals to the object given in argument to this method.
*
* @param object The object for which to set the UUID.
* @param uuid The UUID to associate to the object.
* @return If an object is already mapped to the given UUID, that object. Otherwise {@code null}.
* @throws IllegalArgumentException If the object is already associated to an other UUID value.
*/
public final Object setUUID(final Object object, final T uuid) throws IllegalArgumentException {
final StrongRef check = new StrongRef(object);
synchronized (this) {
final T oldUUID = objectToUUID.get(check);
if (oldUUID != null) {
if (!oldUUID.equals(uuid)) {
throw new IllegalArgumentException(Errors.format(
Errors.Keys.VALUE_ALREADY_DEFINED_$1, object));
}
} else {
final WeakRef ref = new WeakRef(object);
final WeakRef oldRef = uuidToObject.put(uuid, ref);
if (oldRef != null) {
final Object old = oldRef.get();
if (old != null) {
// An object was already assigned to the UUID.
// Restore the previous object and return it.
uuidToObject.put(uuid, oldRef);
return old;
}
}
if (objectToUUID.put(ref, uuid) != null) {
throw new AssertionError(ref); // Should never happen.
}
}
}
return null;
}
/**
* Removes the UUID associated to the given object.
*
* @param object The object for which to remove the UUID (can be {@code null}).
* @return The removed UUID, or {@code null} if no UUID were associated to the given object.
*
* @since 3.18
*/
public final T removeUUID(final Object object) {
final T uuid;
final StrongRef check = new StrongRef(object);
synchronized (this) {
uuid = objectToUUID.remove(check);
if (uuid != null && uuidToObject.remove(uuid) == null) {
throw new AssertionError(uuid); // Should never happen.
}
}
return uuid;
}
/**
* Returns the UUID associated to the given object, or {@linkplain UUID#randomUUID()
* create a random UUID} if the given object does not have one.
*
* @param object The object for which to get the UUID.
* @return The UUID of the given object.
*/
public final T getOrCreateUUID(final Object object) {
final StrongRef check = new StrongRef(object);
synchronized (this) {
T uuid = objectToUUID.get(check);
if (uuid == null) {
final WeakRef ref = new WeakRef(object);
while (true) {
uuid = createUUID();
final WeakRef old = uuidToObject.put(uuid, ref);
if (old == null) break;
// We are very unlikely to get a UUID clash. But if we do,
// restore the previous mapping and get an other UUID.
uuidToObject.put(uuid, old);
}
if (objectToUUID.put(ref, uuid) != null) {
throw new AssertionError(ref); // Should never happen.
}
}
return uuid;
}
}
/**
* Creates a new UUID. This method is invoked by {@link #getOrCreateUUID(Object)} when it
* has determined that a new UUID is required. Implementations will typically delegate the
* work to some library function like {@link UUID#randomUUID()}.
*
* @return A new UUID.
*
* @since 3.18
*/
protected abstract T createUUID();
/**
* A weak reference to an object, which remove itself from the maps when the
* object is garbage collected.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.13
*
* @since 3.13
* @module
*/
private final class WeakRef extends WeakReference