sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1418613 [2/2] - in /sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util: ./ collection/ resources/ type/
Date Sat, 08 Dec 2012 07:40:18 GMT
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



Mime
View raw message