Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java?rev=1418613&view=auto ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java (added) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java Sat Dec 8 07:40:17 2012 @@ -0,0 +1,518 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.util.type; + +import java.util.Map; +import java.util.List; +import java.util.ListIterator; +import java.io.Serializable; +import java.io.ObjectStreamException; +import net.jcip.annotations.Immutable; +import org.opengis.util.NameSpace; +import org.opengis.util.LocalName; +import org.opengis.util.ScopedName; +import org.opengis.util.GenericName; +import org.opengis.util.InternationalString; +import org.apache.sis.util.collection.WeakValueHashMap; +import org.apache.sis.util.collection.UnmodifiableArrayList; + +import static org.apache.sis.util.ArgumentChecks.ensureNonNull; + +// Related to JDK7 +import java.util.Objects; + + +/** + * A domain in which {@linkplain AbstractName names} given by character strings are defined. + * This implementation does not support localization in order to avoid ambiguity when testing + * two namespaces for {@linkplain #equals(Object) equality}. + * + * <p>{@code DefaultNameSpace} can be instantiated by any of the following methods:</p> + * <ul> + * <li>{@link DefaultNameFactory#createNameSpace(GenericName)}</li> + * <li>{@link DefaultNameFactory#createNameSpace(GenericName, Map)}</li> + * </ul> + * + * @author Martin Desruisseaux (IRD, Geomatys) + * @since 0.3 (derived from geotk-3.00) + * @version 0.3 + * @module + */ +@Immutable +public class DefaultNameSpace implements NameSpace, Serializable { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -3064358267398624306L; + + /** + * The default separator, which is {@value}. The separator is inserted between the + * namespace and any {@linkplain GenericName generic name} in that namespace. + */ + public static final char DEFAULT_SEPARATOR = ':'; + + /** + * {@link #DEFAULT_SEPARATOR} as a {@link String}. + */ + static final String DEFAULT_SEPARATOR_STRING = ":"; + + /** + * The parent namespace, or {@code null} if the parent is the unique {@code GLOBAL} instance. + * We don't use direct reference to {@code GLOBAL} because {@code null} is used as a sentinel + * value for stopping iterative searches (using GLOBAL would have higher risk of never-ending + * loops in case of bug), and in order to reduce the stream size during serialization. + */ + final DefaultNameSpace parent; + + /** + * The name of this namespace, usually as a {@link String} or an {@link InternationalString}. + */ + private final CharSequence name; + + /** + * The separator to insert between the namespace and the {@linkplain AbstractName#head() head} + * of any name in that namespace. + */ + final String headSeparator; + + /** + * The separator to insert between the {@linkplain AbstractName#getParsedNames() parsed names} + * of any name in that namespace. + */ + final String separator; + + /** + * The fully qualified name for this namespace. + * Will be created when first needed. + */ + private transient AbstractName path; + + /** + * The children created in this namespace. The values are restricted to the following types: + * + * <ul> + * <li>{@link DefaultNameSpace}</li> + * <li>{@link DefaultLocalName}</li> + * </ul> + * + * No other type should be allowed. The main purpose of this map is to hold child namespaces. + * However we can (in an opportunist way) handles local names as well. In case of conflict, + * the namespace will have precedence. + * + * <p>This field is initialized soon after {@code DefaultNameSpace} and should be treated + * like a final field from that point.</p> + */ + private transient WeakValueHashMap<String,Object> childs; + + /** + * Creates the global namespace. This constructor can be invoked by {@link GlobalNameSpace} + * only. + */ + DefaultNameSpace() { + this.parent = null; + this.name = "global"; + this.headSeparator = DEFAULT_SEPARATOR_STRING; + this.separator = DEFAULT_SEPARATOR_STRING; + init(); + } + + /** + * Creates a new namespace with the given separator. + * + * @param parent + * The parent namespace, or {@code null} if none. + * @param name + * The name of the new namespace, usually as a {@link String} + * or an {@link InternationalString}. + * @param headSeparator + * The separator to insert between the namespace and the + * {@linkplain AbstractName#head() head} of any name in that namespace. + * @param separator + * The separator to insert between the {@linkplain AbstractName#getParsedNames() + * parsed names} of any name in that namespace. + */ + protected DefaultNameSpace(final DefaultNameSpace parent, CharSequence name, + final String headSeparator, final String separator) + { + this.parent = parent; + ensureNonNull("name", name); + ensureNonNull("headSeparator", headSeparator); + ensureNonNull("separator", separator); + if (!(name instanceof InternationalString)) { + name = name.toString(); + } + this.name = name; + this.headSeparator = headSeparator; + this.separator = separator; + init(); + } + + /** + * Initializes the transient fields. + */ + private void init() { + childs = new WeakValueHashMap<>(String.class); + } + + /** + * Wraps the given namespace in a {@code DefaultNameSpace} implementation. + * This method returns an existing instance when possible. + * + * @param ns The namespace to wrap, or {@code null} for the global one. + * @return The given namespace as a {@code DefaultNameSpace} implementation. + */ + static DefaultNameSpace castOrCopy(final NameSpace ns) { + if (ns == null) { + return GlobalNameSpace.GLOBAL; + } + if (ns instanceof DefaultNameSpace) { + return (DefaultNameSpace) ns; + } + return forName(ns.name(), DEFAULT_SEPARATOR_STRING, DEFAULT_SEPARATOR_STRING); + } + + /** + * Returns a namespace having the given name and separators. + * This method returns an existing instance when possible. + * + * @param name + * The name for the namespace to obtain, or {@code null}. + * @param headSeparator + * The separator to insert between the namespace and the + * {@linkplain AbstractName#head() head} of any name in that namespace. + * @param separator + * The separator to insert between the {@linkplain AbstractName#getParsedNames() + * parsed names} of any name in that namespace. + * @return A namespace having the given name, or {@code null} if name was null. + */ + static DefaultNameSpace forName(final GenericName name, + final String headSeparator, final String separator) + { + if (name == null) { + return null; + } + final List<? extends LocalName> parsedNames = name.getParsedNames(); + final ListIterator<? extends LocalName> it = parsedNames.listIterator(parsedNames.size()); + NameSpace scope; + /* + * Searches for the last parsed name having a DefaultNameSpace implementation as its + * scope. It should be the tip in most cases. If we don't find any, we will recreate + * the whole chain starting with the global scope. + */ + do { + if (!it.hasPrevious()) { + scope = GlobalNameSpace.GLOBAL; + break; + } + scope = it.previous().scope(); + } while (!(scope instanceof DefaultNameSpace)); + /* + * We have found a scope. Adds to it the supplemental names. + * In most cases we should have only the tip to add. + */ + DefaultNameSpace ns = (DefaultNameSpace) scope; + while (it.hasNext()) { + final LocalName tip = it.next(); + ns = ns.child(tip.toString(), tip.toInternationalString(), headSeparator, separator); + } + return ns; + } + + /** + * Indicates whether this namespace is a "top level" namespace. Global, or top-level + * namespaces are not contained within another namespace. The global namespace has no + * parent. + * + * @return {@code true} if this namespace is the global namespace. + */ + @Override + public boolean isGlobal() { + return false; // To be overridden by GlobalNameSpace. + } + + /** + * Returns the depth of the given namespace. + * + * @param ns The namespace for which to get the depth, or {@code null}. + * @return The depth of the given namespace. + */ + private static int depth(DefaultNameSpace ns) { + int depth = 0; + if (ns != null) do { + depth++; + ns = ns.parent; + } while (ns != null && !ns.isGlobal()); + return depth; + } + + /** + * Represents the identifier of this namespace. Namespace identifiers shall be + * {@linkplain AbstractName#toFullyQualifiedName() fully-qualified names} where + * the following condition holds: + * + * {@preformat java + * assert name.scope().isGlobal() == true; + * } + * + * @return The identifier of this namespace. + */ + @Override + public GenericName name() { + final int depth; + synchronized (this) { + if (path != null) { + return path; + } + depth = depth(this); + final DefaultLocalName[] names = new DefaultLocalName[depth]; + DefaultNameSpace scan = this; + for (int i=depth; --i>=0;) { + names[i] = new DefaultLocalName(scan.parent, scan.name); + scan = scan.parent; + } + assert depth(scan) == 0 || scan.isGlobal(); + path = DefaultScopedName.create(UnmodifiableArrayList.wrap(names)); + GenericName truncated = path; + for (int i=depth; --i>=0;) { + names[i].fullyQualified = truncated; + truncated = (truncated instanceof ScopedName) ? ((ScopedName) truncated).path() : null; + } + } + /* + * At this point the name is created and ready to be returned. As an optimization, + * defines the name of parents now in order to share subarea of the array we just + * created. The goal is to have less objects in memory. + */ + AbstractName truncated = path; + DefaultNameSpace scan = parent; + while (scan != null && !scan.isGlobal()) { + /* + * If we have a parent, then depth >= 2 and consequently the name is a ScopedName. + * Actually it should be an instance of DefaultScopedName - we known that since we + * created it ourself with the DefaultScopedName.create(...) method call - and we + * know that its tail() implementation creates instance of AbstractName. Given all + * the above, none of the casts on the line below should ever fails, unless there + * is bug in this package. + */ + truncated = (AbstractName) ((ScopedName) truncated).path(); + synchronized (scan) { + if (scan.path == null || scan.path.arraySize() < depth) { + scan.path = truncated; + } + } + scan = scan.parent; + } + return path; + } + + /** + * Returns a child namespace of the given name. The returned namespace will + * have this namespace as its parent, and will use the same separator. + * + * <p>The {@link #headSeparator} is not inherited by the children on intend, because this + * method is used only by {@link DefaultScopedName} constructors in order to create a + * sequence of parsed local names. For example in {@code "http://www.opengeospatial.org"} + * the head separator is {@code "://"} for {@code "www"} (which is having this namespace), + * but it is {@code "."} for all children ({@code "opengeospatial"} and {@code "org"}).</p> + * + * @param name The name of the child namespace. + * @return The child namespace. It may be an existing instance. + */ + final DefaultNameSpace child(final CharSequence name) { + return child(key(name), name, separator, separator); + } + + /** + * Returns a key to be used in the {@linkplain #pool} from the given name. + * The key must be the unlocalized version of the given string. + * + * @param name The name. + * @return A key from the given name. + */ + private static String key(final CharSequence name) { + return (name instanceof InternationalString) ? + ((InternationalString) name).toString(null) : name.toString(); + } + + /** + * Returns a child namespace of the given name and separator. + * The returned namespace will have this namespace as its parent. + * + * @param key + * The unlocalized name of the child namespace, to be used as a key in the cache. + * @param name + * The name of the child namespace, or {@code null} if same than key. + * @param headSeparator + * The separator to insert between the namespace and the + * {@linkplain AbstractName#head() head} of any name in that namespace. + * @param separator + * The separator to insert between the {@linkplain AbstractName#getParsedNames() + * parsed names} of any name in that namespace. + * @return The child namespace. It may be an existing instance. + */ + private DefaultNameSpace child(final String key, CharSequence name, + final String headSeparator, final String separator) + { + ensureNonNull("key", key); + if (name == null) { + name = key; + } + DefaultNameSpace child; + synchronized (childs) { + final Object existing = childs.get(key); + if (existing instanceof DefaultNameSpace) { + child = (DefaultNameSpace) existing; + if (!child.separator .equals(separator) || + !child.headSeparator.equals(headSeparator) || + !child.name .equals(name)) // Same test than equalsIgnoreParent. + { + child = new DefaultNameSpace(this, name, headSeparator, separator); + /* + * Do not cache that instance. Actually we can't guess if that instance + * would be more appropriate for caching purpose than the old one. We + * just assume that keeping the oldest one is more conservative. + */ + } + } else { + child = new DefaultNameSpace(this, name, headSeparator, separator); + if (childs.put(key, child) != existing) { + throw new AssertionError(); // Paranoiac check. + } + } + } + assert child.parent == this; + return child; + } + + /** + * Returns a name which is local in this namespace. The returned name will have this + * namespace as its {@linkplain DefaultLocalName#scope() scope}. This method may returns + * an existing instance on a "best effort" basis, but this is not guaranteed. + * + * @param name The name of the instance to create. + * @param candidate The instance to cache if no instance was found for the given name, + * or {@code null} if none. + * @return A name which is local in this namespace. + */ + final DefaultLocalName local(final CharSequence name, final DefaultLocalName candidate) { + ensureNonNull("name", name); + final String key = name.toString(); + DefaultLocalName child; + synchronized (childs) { + final Object existing = childs.get(key); + if (existing instanceof DefaultLocalName) { + child = (DefaultLocalName) existing; + if (name.equals(child.name)) { + assert (child.scope != null ? child.scope : GlobalNameSpace.GLOBAL) == this; + return child; + } + } + if (candidate != null) { + child = candidate; + } else { + child = new DefaultLocalName(this, name); + } + // Cache only if the slot is not already occupied by a NameSpace. + if (!(existing instanceof DefaultNameSpace)) { + if (childs.put(key, child) != existing) { + throw new AssertionError(); // Paranoiac check. + } + } + } + return child; + } + + /** + * Returns a string representation of this namespace. + * + * @return A string representation of this namespace. + */ + @Override + public String toString() { + return "NameSpace[\"" + name() + "\"]"; + } + + /** + * Returns {@code true} if this namespace is equal to the given object. + * + * @param object The object to compare with this namespace. + * @return {@code true} if the given object is equal to this namespace. + */ + @Override + public boolean equals(final Object object) { + if (object != null && object.getClass() == getClass()) { + final DefaultNameSpace that = (DefaultNameSpace) object; + return equalsIgnoreParent(that) && Objects.equals(this.parent, that.parent); + } + return false; + } + + /** + * Returns {@code true} if the namespace is equal to the given one, ignoring the parent. + * + * @param that The namespace to compare with this one. + * @return {@code true} if both namespaces are equal, ignoring the parent. + */ + private boolean equalsIgnoreParent(final DefaultNameSpace that) { + return Objects.equals(this.headSeparator, that.headSeparator) && + Objects.equals(this.separator, that.separator) && + Objects.equals(this.name, that.name); // Most expensive test last. + } + + /** + * Returns a hash code value for this namespace. + */ + @Override + public int hashCode() { + return Objects.hash(parent, name, separator); + } + + /** + * If an instance already exists for the deserialized namespace, returns that instance. + * Otherwise completes the initialization of the deserialized instance. + * + * <p>Because of its package-private access, this method is <strong>not</strong> invoked if + * the deserialized class is a subclass defined in an other package. This is the intended + * behavior since we don't want to replace an instance of a user-defined class.</p> + * + * @return The unique instance. + * @throws ObjectStreamException Should never happen. + */ + Object readResolve() throws ObjectStreamException { + final DefaultNameSpace p = (parent != null) ? parent : GlobalNameSpace.GLOBAL; + final String key = key(name); + final WeakValueHashMap<String,Object> pool = p.childs; + synchronized (pool) { + final Object existing = pool.get(key); + if (existing instanceof DefaultNameSpace) { + if (equalsIgnoreParent((DefaultNameSpace) existing)) { + return existing; + } else { + // Exit from the synchronized block. + } + } else { + init(); + if (pool.put(key, this) != existing) { + throw new AssertionError(); // Paranoiac check. + } + return this; + } + } + init(); + return this; + } +}
Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultNameSpace.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java?rev=1418613&view=auto ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java (added) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java Sat Dec 8 07:40:17 2012 @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.util.type; + +import java.util.List; +import java.util.Iterator; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import net.jcip.annotations.Immutable; +import org.opengis.util.NameSpace; +import org.opengis.util.LocalName; +import org.opengis.util.ScopedName; +import org.opengis.util.GenericName; +import org.opengis.util.InternationalString; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.collection.UnmodifiableArrayList; + + + +/** + * A composite of a {@linkplain DefaultNameSpace name space} (as a {@linkplain DefaultLocalName local name}) + * and a {@linkplain AbstractName generic name} valid in that name space. + * See the {@linkplain ScopedName GeoAPI javadoc} for more information. + * + * <p>{@code DefaultScopedName} can be instantiated by any of the following methods:</p> + * <ul> + * <li>{@link DefaultNameFactory#createGenericName(NameSpace, CharSequence[])} with an array of length 2 or more</li> + * <li>{@link DefaultNameFactory#parseGenericName(NameSpace, CharSequence)} with at least one separator</li> + * </ul> + * + * @author Martin Desruisseaux (IRD, Geomatys) + * @since 0.3 (derived from geotk-2.1) + * @version 0.3 + * @module + */ +@Immutable +@XmlRootElement(name = "ScopedName") +public class DefaultScopedName extends AbstractName implements ScopedName { + /** + * Serial number for inter-operability with different versions. + */ + private static final long serialVersionUID = -5215955533541748481L; + + /** + * The immutable list of parsed names. + */ + private final UnmodifiableArrayList<? extends LocalName> parsedNames; + + /** + * The tail or path, computed when first needed. + */ + private transient GenericName tail, path; + + /** + * Creates a new scoped names from the given list of local names. This constructor is + * not public because we do not check if the given local names have the proper scope. + * + * @param names The names to gives to the new scoped name. + */ + static AbstractName create(final UnmodifiableArrayList<? extends DefaultLocalName> names) { + ArgumentChecks.ensureNonNull("names", names); + switch (names.size()) { + default: return new DefaultScopedName(names); + case 1: return names.get(0); + case 0: throw new IllegalArgumentException(Errors.format(Errors.Keys.EmptyArgument_1, "names")); + } + } + + /** + * Empty constructor to be used by JAXB only. Despite its "final" declaration, + * the {@link #parsedNames} field will be set by JAXB during unmarshalling. + */ + private DefaultScopedName() { + parsedNames = null; + } + + /** + * Creates a new scoped names from the given list of local names. This constructor is + * not public because it does not check if the given local names have the proper scope. + * + * @param names The names to gives to the new scoped name. + */ + private DefaultScopedName(final UnmodifiableArrayList<? extends LocalName> names) { + parsedNames = names; + } + + /** + * Constructs a scoped name from the specified list of strings. + * If any of the given names is an instance of {@link InternationalString}, then its + * {@link InternationalString#toString(java.util.Locale) toString(null)} method will + * be invoked for fetching an unlocalized name. + * Otherwise the {@link CharSequence#toString()} method will be used. + * + * @param scope The scope of this name, or {@code null} for the global scope. + * @param names The local names. This list must have at least two elements. + */ + protected DefaultScopedName(final NameSpace scope, final List<? extends CharSequence> names) { + ArgumentChecks.ensureNonNull("names", names); + final int size = names.size(); + ArgumentChecks.ensureSizeBetween("names", 2, Integer.MAX_VALUE, size); + DefaultNameSpace ns = DefaultNameSpace.castOrCopy(scope); + final boolean global = ns.isGlobal(); + int i = 0; + final LocalName[] locals = new LocalName[size]; + final Iterator<? extends CharSequence> it = names.iterator(); + /* + * Builds the parsed name list by creating DefaultLocalName instances now. + * Note that we expect at least 2 valid entries (because of the check we + * did before), so we don't check hasNext() for the two first entries. + */ + CharSequence name = it.next(); + do { + ArgumentChecks.ensureNonNullElement("names", i, name); + locals[i++] = new DefaultLocalName(ns, name); + ns = ns.child(name); + name = it.next(); + } while (it.hasNext()); + /* + * At this point, we have almost finished to build the parsed names array. + * The last name is the tip, which we want to live in the given namespace. + * If this namespace is global, then the fully qualified name is this name. + * In this case we assign the reference now in order to avoid letting + * tip.toFullyQualifiedName() creates a new object later. + */ + final DefaultLocalName tip = ns.local(name, null); + if (global) { + tip.fullyQualified = fullyQualified = this; + } + locals[i++] = tip; + assert (i == size); // Paranoiac check. + parsedNames = UnmodifiableArrayList.wrap(locals); + } + + /** + * Constructs a scoped name as the concatenation of the given generic names. + * The scope of the new name will be the scope of the {@code path} argument. + * + * @param path The first part to concatenate. + * @param tail The second part to concatenate. + */ + protected DefaultScopedName(final GenericName path, final GenericName tail) { + ArgumentChecks.ensureNonNull("path", path); + ArgumentChecks.ensureNonNull("tail", tail); + final List<? extends LocalName> parsedPath = path.getParsedNames(); + final List<? extends LocalName> parsedTail = tail.getParsedNames(); + int index = parsedPath.size(); + LocalName[] locals = new LocalName[index + parsedTail.size()]; + locals = parsedPath.toArray(locals); + /* + * We have copied the LocalNames from the path unconditionally. Now we need to process the + * LocalNames from the tail. If the tail scope follows the path scope, we can just copy the + * names without further processing (easy case). Otherwise we need to create new instances. + * + * Note that by contract, GenericName shall contain at least 1 element. This assumption + * appears in two places: it.next() invoked once before any it.hasNext(), and testing for + * locals[index-1] element (so we assume index > 0). + */ + final Iterator<? extends LocalName> it = parsedTail.iterator(); + LocalName name = it.next(); + final LocalName lastName = locals[index-1]; + final NameSpace lastScope = lastName.scope(); + final NameSpace tailScope = name.scope(); + if (tailScope instanceof DefaultNameSpace && ((DefaultNameSpace) tailScope).parent == lastScope) { + /* + * If the tail is actually the tip (a LocalName), remember the tail so we + * don't need to create it again later. Then copy the tail after the path. + */ + if (path instanceof LocalName) { + this.tail = tail; + } + while (true) { + locals[index++] = name; + if (!it.hasNext()) break; + name = it.next(); + } + } else { + /* + * There is no continuity in the chain of scopes, so we need to create new + * LocalName instances. + */ + DefaultNameSpace scope = DefaultNameSpace.castOrCopy(lastScope); + CharSequence label = name(lastName); + while (true) { + scope = scope.child(label); + label = name(name); + name = new DefaultLocalName(scope, label); + locals[index++] = name; + if (!it.hasNext()) break; + name = it.next(); + } + } + assert (index == locals.length); // Paranoiac check. + parsedNames = UnmodifiableArrayList.wrap(locals); + if (tail instanceof LocalName) { + this.path = path; + } + } + + /** + * Returns the name to be given to {@link DefaultLocalName} constructors. + */ + private static CharSequence name(final GenericName name) { + if (name instanceof DefaultLocalName) { + return ((DefaultLocalName) name).name; + } + final InternationalString label = name.toInternationalString(); + return (label != null) ? label : name.toString(); + } + + /** + * Returns the size of the backing array. This is used only has a hint for optimizations + * in attempts to share internal arrays. + */ + @Override + final int arraySize() { + return parsedNames.arraySize(); + } + + /** + * {@inheritDoc} + */ + @Override + public NameSpace scope() { + return head().scope(); + } + + /** + * Returns every elements of the {@linkplain #getParsedNames() parsed names list} + * except for the {@linkplain #head() head}. + */ + @Override + public synchronized GenericName tail() { + if (tail == null) { + final int size = parsedNames.size(); + switch (size) { + default: tail = new DefaultScopedName(parsedNames.subList(1, size)); break; + case 2: tail = parsedNames.get(1); break; + case 1: // fall through + case 0: throw new AssertionError(size); + } + } + return tail; + } + + /** + * Returns every element of the {@linkplain #getParsedNames() parsed names list} + * except for the {@linkplain #tip() tip}. + */ + @Override + public synchronized GenericName path() { + if (path == null) { + final int size = parsedNames.size(); + switch (size) { + default: path = new DefaultScopedName(parsedNames.subList(0, size-1)); break; + case 2: path = parsedNames.get(0); break; + case 1: // fall through + case 0: throw new AssertionError(size); + } + } + return path; + } + + /** + * Returns the sequence of local name for this generic name. + */ + @Override + @XmlElement(name = "parsedName", required = true) + public List<? extends LocalName> getParsedNames() { + return parsedNames; + } +} Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultScopedName.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java?rev=1418613&view=auto ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java (added) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java Sat Dec 8 07:40:17 2012 @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.util.type; + +import javax.xml.bind.annotation.XmlRootElement; +import net.jcip.annotations.Immutable; + +import org.opengis.util.TypeName; +import org.opengis.util.NameSpace; + + +/** + * The name of an attribute type associated to a {@linkplain DefaultMemberName member name}. + * {@code DefaultTypeName} can be instantiated by any of the following methods: + * + * <ul> + * <li>{@link DefaultNameFactory#createTypeName(NameSpace, CharSequence)}</li> + * </ul> + * + * @author Guilhem Legal (Geomatys) + * @author Cédric Briançon (Geomatys) + * @since 0.3 (derived from geotk-3.00) + * @version 0.3 + * @module + */ +@Immutable +@XmlRootElement(name = "TypeName") +public class DefaultTypeName extends DefaultLocalName implements TypeName { + /** + * Serial number for inter-operability with different versions. + */ + private static final long serialVersionUID = -7985388992575173993L; + + /** + * Empty constructor to be used by JAXB only. Despite its "final" declaration, + * the {@link #name} field will be set by JAXB during unmarshalling. + */ + private DefaultTypeName() { + } + + /** + * Constructs a type name from the given character sequence. The argument are given unchanged + * to the {@linkplain DefaultLocalName#DefaultLocalName(NameSpace,CharSequence) super-class + * constructor}. + * + * @param scope The scope of this name, or {@code null} for a global scope. + * @param name The local name (never {@code null}). + */ + protected DefaultTypeName(final NameSpace scope, final CharSequence name) { + super(scope, name); + } +} Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/DefaultTypeName.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java?rev=1418613&view=auto ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java (added) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java Sat Dec 8 07:40:17 2012 @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.util.type; + +import java.io.ObjectStreamException; +import net.jcip.annotations.Immutable; + + +/** + * The global namespace. Only one instance of this class is allowed to exists. We do not expose + * any global namespace in public API since ISO 19103 does not define them and users should not + * need to handle them explicitely. + * + * @author Martin Desruisseaux (IRD, Geomatys) + * @since 0.3 (derived from geotk-3.00) + * @version 0.3 + * @module + */ +@Immutable +final class GlobalNameSpace extends DefaultNameSpace { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 5276591595735817024L; + + /** + * The unique global namespace. + */ + public static final GlobalNameSpace GLOBAL = new GlobalNameSpace(); + + /** + * Creates the global namespace. + */ + private GlobalNameSpace() { + } + + /** + * Indicates that this namespace is a "top level" namespace. + */ + @Override + public boolean isGlobal() { + return true; + } + + /** + * Returns the unique instance of global name space on deserialization. + * + * @return The unique instance. + * @throws ObjectStreamException Should never happen. + */ + @Override + Object readResolve() throws ObjectStreamException { + return GLOBAL; + } +} Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/type/GlobalNameSpace.java ------------------------------------------------------------------------------ svn:mime-type = text/plain