You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2007/10/18 20:35:01 UTC
svn commit: r586058 [2/4] - in /jackrabbit/trunk/jackrabbit-spi-commons: ./
src/main/java/org/apache/jackrabbit/conversion/
src/main/java/org/apache/jackrabbit/identifier/
src/main/java/org/apache/jackrabbit/lock/
src/main/java/org/apache/jackrabbit/na...
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java?rev=586058&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java Thu Oct 18 11:34:57 2007
@@ -0,0 +1,270 @@
+/*
+ * 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.jackrabbit.lock;
+
+import org.apache.jackrabbit.namespace.SessionNamespaceResolver;
+import org.apache.jackrabbit.name.NameConstants;
+import org.apache.jackrabbit.conversion.NamePathResolver;
+import org.apache.jackrabbit.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.conversion.NameException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Node;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Session;
+import javax.jcr.Repository;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.Event;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+
+/**
+ * <code>Locked</code> is a utility to synchronize modifications on a lockable
+ * node. The modification is applied while the lock on the node is held, thus
+ * ensuring that the modification will never fail with an {@link
+ * javax.jcr.InvalidItemStateException}. This utility can be used with any
+ * JCR Repository, not just Jackrabbit.
+ * <p/>
+ * The following example shows how this utility can be used to implement
+ * a persistent counter:
+ * <pre>
+ * Node counter = ...;
+ * long nextValue = ((Long) new Locked() {
+ * protected Object run(Node counter) throws RepositoryException {
+ * Property seqProp = counter.getProperty("value");
+ * long value = seqProp.getLong();
+ * seqProp.setValue(++value);
+ * seqProp.save();
+ * return new Long(value);
+ * }
+ * }.with(counter, false)).longValue();
+ * </pre>
+ * If you specify a <code>timeout</code> you need to check the return value
+ * whether the <code>run</code> method could be executed within the timeout
+ * period:
+ * <pre>
+ * Node counter = ...;
+ * Object ret = new Locked() {
+ * protected Object run(Node counter) throws RepositoryException {
+ * Property seqProp = counter.getProperty("value");
+ * long value = seqProp.getLong();
+ * seqProp.setValue(++value);
+ * seqProp.save();
+ * return new Long(value);
+ * }
+ * }.with(counter, false);
+ * if (ret == Locked.TIMED_OUT) {
+ * // do whatever you think is appropriate in this case
+ * } else {
+ * // get the value
+ * long nextValue = ((Long) ret).longValue();
+ * }
+ * </pre>
+ */
+public abstract class Locked {
+
+ /**
+ * Object returned when timeout is reached without being able to call
+ * {@link #run} while holding the lock.
+ */
+ public static final Object TIMED_OUT = new Object();
+
+ /**
+ * Executes {@link #run} while the lock on <code>lockable</code> is held.
+ * This method will block until {@link #run} is executed while holding the
+ * lock on node <code>lockable</code>.
+ *
+ * @param lockable a lockable node.
+ * @param isDeep <code>true</code> if <code>lockable</code> will be locked
+ * deep.
+ * @return the object returned by {@link #run}.
+ * @throws IllegalArgumentException if <code>lockable</code> is not
+ * <i>mix:lockable</i>.
+ * @throws RepositoryException if {@link #run} throws an exception.
+ * @throws InterruptedException if this thread is interrupted while waiting
+ * for the lock on node <code>lockable</code>.
+ */
+ public Object with(Node lockable, boolean isDeep)
+ throws RepositoryException, InterruptedException {
+ return with(lockable, isDeep, Long.MAX_VALUE);
+ }
+
+ /**
+ * Executes the method {@link #run} within the scope of a lock held on
+ * <code>lockable</code>.
+ *
+ * @param lockable the node where the lock is obtained from.
+ * @param isDeep <code>true</code> if <code>lockable</code> will be locked
+ * deep.
+ * @param timeout time in milliseconds to wait at most to aquire the lock.
+ * @return the object returned by {@link #run} or {@link #TIMED_OUT} if the
+ * lock on <code>lockable</code> could not be aquired within the
+ * specified timeout.
+ * @throws IllegalArgumentException if <code>timeout</code> is negative or
+ * <code>lockable</code> is not
+ * <i>mix:lockable</i>.
+ * @throws RepositoryException if {@link #run} throws an exception.
+ * @throws UnsupportedRepositoryOperationException
+ * if this repository does not support
+ * locking.
+ * @throws InterruptedException if this thread is interrupted while
+ * waiting for the lock on node
+ * <code>lockable</code>.
+ */
+ public Object with(Node lockable, boolean isDeep, long timeout)
+ throws UnsupportedRepositoryOperationException, RepositoryException, InterruptedException {
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout must be >= 0");
+ }
+
+ Session session = lockable.getSession();
+ NamePathResolver resolver = new DefaultNamePathResolver(new SessionNamespaceResolver(session));
+
+ Lock lock;
+ EventListener listener = null;
+ try {
+ // check whether the lockable can be locked at all
+ if (!lockable.isNodeType(resolver.getJCRName(NameConstants.MIX_LOCKABLE))) {
+ throw new IllegalArgumentException("Node is not lockable");
+ }
+
+ lock = tryLock(lockable, isDeep);
+ if (lock != null) {
+ return runAndUnlock(lock);
+ }
+
+ if (timeout == 0) {
+ return TIMED_OUT;
+ }
+
+ long timelimit;
+ if (timeout == Long.MAX_VALUE) {
+ timelimit = Long.MAX_VALUE;
+ } else {
+ timelimit = System.currentTimeMillis() + timeout;
+ }
+
+ // node is locked by other session -> register event listener if possible
+ if (isObservationSupported(session)) {
+ ObservationManager om = session.getWorkspace().getObservationManager();
+ listener = new EventListener() {
+ public void onEvent(EventIterator events) {
+ synchronized (this) {
+ this.notify();
+ }
+ }
+ };
+ om.addEventListener(listener, Event.PROPERTY_REMOVED,
+ lockable.getPath(), false, null, null, true);
+ }
+
+ // now keep trying to aquire the lock
+ // using 'this' as a monitor allows the event listener to notify
+ // the current thread when the lockable node is possibly unlocked
+ for (; ;) {
+ synchronized (this) {
+ lock = tryLock(lockable, isDeep);
+ if (lock != null) {
+ return runAndUnlock(lock);
+ } else {
+ // check timeout
+ if (System.currentTimeMillis() > timelimit) {
+ return TIMED_OUT;
+ }
+ if (listener != null) {
+ // event listener *should* wake us up, however
+ // there is a chance that removal of the lockOwner
+ // property is notified before the node is acutally
+ // unlocked. therefore we use a safety net to wait
+ // at most 1000 millis.
+ this.wait(Math.min(1000, timeout));
+ } else {
+ // repository does not support observation
+ // wait at most 50 millis then retry
+ this.wait(Math.min(50, timeout));
+ }
+ }
+ }
+ }
+ } catch (NameException e) {
+ throw new RepositoryException(e);
+ } finally {
+ if (listener != null) {
+ session.getWorkspace().getObservationManager().removeEventListener(listener);
+ }
+ }
+ }
+
+ /**
+ * This method is executed while holding the lock.
+ * @param node The <code>Node</code> on which the lock is placed.
+ * @return an object which is then returned by {@link #with with()}.
+ * @throws RepositoryException if an error occurs.
+ */
+ protected abstract Object run(Node node) throws RepositoryException;
+
+ /**
+ * Executes {@link #run} and unlocks the lockable node in any case, even
+ * when an exception is thrown.
+ *
+ * @param lock The <code>Lock</code> to unlock in any case before returning.
+ *
+ * @return the object returned by {@link #run}.
+ * @throws RepositoryException if an error occurs.
+ */
+ private Object runAndUnlock(Lock lock) throws RepositoryException {
+ try {
+ return run(lock.getNode());
+ } finally {
+ lock.getNode().unlock();
+ }
+ }
+
+ /**
+ * Tries to aquire a session scoped lock on <code>lockable</code>.
+ *
+ * @param lockable the lockable node
+ * @param isDeep <code>true</code> if the lock should be deep
+ * @return The <code>Lock</code> or <code>null</code> if the
+ * <code>lockable</code> cannot be locked.
+ * @throws UnsupportedRepositoryOperationException
+ * if this repository does not support locking.
+ * @throws RepositoryException if an error occurs
+ */
+ private static Lock tryLock(Node lockable, boolean isDeep)
+ throws UnsupportedRepositoryOperationException, RepositoryException {
+ try {
+ return lockable.lock(isDeep, true);
+ } catch (LockException e) {
+ // locked by some other session
+ }
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> if the repository supports observation.
+ *
+ * @param s a session of the repository.
+ * @return <code>true</code> if the repository supports observation.
+ */
+ private static boolean isObservationSupported(Session s) {
+ return "true".equalsIgnoreCase(s.getRepository().getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED));
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/lock/Locked.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java?rev=586058&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java Thu Oct 18 11:34:57 2007
@@ -0,0 +1,445 @@
+/*
+ * 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.jackrabbit.name;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+
+/**
+ * <code>NameConstants</code>...
+ */
+public class NameConstants {
+
+ private static final NameFactory FACTORY = NameFactoryImpl.getInstance();
+
+ /**
+ * Extra Name for the root node
+ */
+ public static final Name ROOT = FACTORY.create(Name.NS_DEFAULT_URI,"");
+
+ /**
+ * jcr:system
+ */
+ public static final Name JCR_SYSTEM = FACTORY.create(Name.NS_JCR_URI, "system");
+
+ /**
+ * jcr:nodeTypes
+ */
+ public static final Name JCR_NODETYPES = FACTORY.create(Name.NS_JCR_URI, "nodeTypes");
+
+ /**
+ * jcr:uuid
+ */
+ public static final Name JCR_UUID = FACTORY.create(Name.NS_JCR_URI, "uuid");
+
+ /**
+ * jcr:primaryType
+ */
+ public static final Name JCR_PRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "primaryType");
+
+ /**
+ * jcr:mixinTypes
+ */
+ public static final Name JCR_MIXINTYPES = FACTORY.create(Name.NS_JCR_URI, "mixinTypes");
+
+ /**
+ * jcr:created
+ */
+ public static final Name JCR_CREATED = FACTORY.create(Name.NS_JCR_URI, "created");
+
+ /**
+ * jcr:lastModified
+ */
+ public static final Name JCR_LASTMODIFIED = FACTORY.create(Name.NS_JCR_URI, "lastModified");
+
+ /**
+ * jcr:encoding
+ */
+ public static final Name JCR_ENCODING = FACTORY.create(Name.NS_JCR_URI, "encoding");
+
+ /**
+ * jcr:mimeType
+ */
+ public static final Name JCR_MIMETYPE = FACTORY.create(Name.NS_JCR_URI, "mimeType");
+
+ /**
+ * jcr:data
+ */
+ public static final Name JCR_DATA = FACTORY.create(Name.NS_JCR_URI, "data");
+
+ /**
+ * jcr:content
+ */
+ public static final Name JCR_CONTENT = FACTORY.create(Name.NS_JCR_URI, "content");
+
+ //--------------------------------------< xml related item name constants >
+
+ /**
+ * jcr:root (dummy name for root node used in XML serialization)
+ */
+ public static final Name JCR_ROOT = FACTORY.create(Name.NS_JCR_URI, "root");
+
+ /**
+ * jcr:xmltext
+ */
+ public static final Name JCR_XMLTEXT = FACTORY.create(Name.NS_JCR_URI, "xmltext");
+
+ /**
+ * jcr:xmlcharacters
+ */
+ public static final Name JCR_XMLCHARACTERS = FACTORY.create(Name.NS_JCR_URI, "xmlcharacters");
+
+ //-----------------------------------------< query related name constants >
+
+ /**
+ * jcr:score
+ */
+ public static final Name JCR_SCORE = FACTORY.create(Name.NS_JCR_URI, "score");
+
+ /**
+ * jcr:path
+ */
+ public static final Name JCR_PATH = FACTORY.create(Name.NS_JCR_URI, "path");
+
+ /**
+ * jcr:statement
+ */
+ public static final Name JCR_STATEMENT = FACTORY.create(Name.NS_JCR_URI, "statement");
+
+ /**
+ * jcr:language
+ */
+ public static final Name JCR_LANGUAGE = FACTORY.create(Name.NS_JCR_URI, "language");
+
+ //----------------------------------< locking related item name constants >
+
+ /**
+ * jcr:lockOwner
+ */
+ public static final Name JCR_LOCKOWNER = FACTORY.create(Name.NS_JCR_URI, "lockOwner");
+
+ /**
+ * jcr:lockIsDeep
+ */
+ public static final Name JCR_LOCKISDEEP = FACTORY.create(Name.NS_JCR_URI, "lockIsDeep");
+
+ //-------------------------------< versioning related item name constants >
+
+ /**
+ * jcr:versionStorage
+ */
+ public static final Name JCR_VERSIONSTORAGE = FACTORY.create(Name.NS_JCR_URI, "versionStorage");
+
+ /**
+ * jcr:mergeFailed
+ */
+ public static final Name JCR_MERGEFAILED = FACTORY.create(Name.NS_JCR_URI, "mergeFailed");
+
+ /**
+ * jcr:frozenNode
+ */
+ public static final Name JCR_FROZENNODE = FACTORY.create(Name.NS_JCR_URI, "frozenNode");
+
+ /**
+ * jcr:frozenUuid
+ */
+ public static final Name JCR_FROZENUUID = FACTORY.create(Name.NS_JCR_URI, "frozenUuid");
+
+ /**
+ * jcr:frozenPrimaryType
+ */
+ public static final Name JCR_FROZENPRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "frozenPrimaryType");
+
+ /**
+ * jcr:frozenMixinTypes
+ */
+ public static final Name JCR_FROZENMIXINTYPES = FACTORY.create(Name.NS_JCR_URI, "frozenMixinTypes");
+
+ /**
+ * jcr:predecessors
+ */
+ public static final Name JCR_PREDECESSORS = FACTORY.create(Name.NS_JCR_URI, "predecessors");
+
+ /**
+ * jcr:versionLabels
+ */
+ public static final Name JCR_VERSIONLABELS = FACTORY.create(Name.NS_JCR_URI, "versionLabels");
+
+ /**
+ * jcr:successors
+ */
+ public static final Name JCR_SUCCESSORS = FACTORY.create(Name.NS_JCR_URI, "successors");
+
+ /**
+ * jcr:isCheckedOut
+ */
+ public static final Name JCR_ISCHECKEDOUT = FACTORY.create(Name.NS_JCR_URI, "isCheckedOut");
+
+ /**
+ * jcr:versionHistory
+ */
+ public static final Name JCR_VERSIONHISTORY = FACTORY.create(Name.NS_JCR_URI, "versionHistory");
+
+ /**
+ * jcr:baseVersion
+ */
+ public static final Name JCR_BASEVERSION = FACTORY.create(Name.NS_JCR_URI, "baseVersion");
+
+ /**
+ * jcr:childVersionHistory
+ */
+ public static final Name JCR_CHILDVERSIONHISTORY = FACTORY.create(Name.NS_JCR_URI, "childVersionHistory");
+
+ /**
+ * jcr:rootVersion
+ */
+ public static final Name JCR_ROOTVERSION = FACTORY.create(Name.NS_JCR_URI, "rootVersion");
+
+ /**
+ * jcr:versionableUuid
+ */
+ public static final Name JCR_VERSIONABLEUUID = FACTORY.create(Name.NS_JCR_URI, "versionableUuid");
+
+ //--------------------------------< node type related item name constants >
+
+ /**
+ * jcr:nodeTypeName
+ */
+ public static final Name JCR_NODETYPENAME = FACTORY.create(Name.NS_JCR_URI, "nodeTypeName");
+
+ /**
+ * jcr:hasOrderableChildNodes
+ */
+ public static final Name JCR_HASORDERABLECHILDNODES = FACTORY.create(Name.NS_JCR_URI, "hasOrderableChildNodes");
+
+ /**
+ * jcr:isMixin
+ */
+ public static final Name JCR_ISMIXIN = FACTORY.create(Name.NS_JCR_URI, "isMixin");
+
+ /**
+ * jcr:supertypes
+ */
+ public static final Name JCR_SUPERTYPES = FACTORY.create(Name.NS_JCR_URI, "supertypes");
+
+ /**
+ * jcr:propertyDefinition
+ */
+ public static final Name JCR_PROPERTYDEFINITION = FACTORY.create(Name.NS_JCR_URI, "propertyDefinition");
+
+ /**
+ * jcr:name
+ */
+ public static final Name JCR_NAME = FACTORY.create(Name.NS_JCR_URI, "name");
+
+ /**
+ * jcr:mandatory
+ */
+ public static final Name JCR_MANDATORY = FACTORY.create(Name.NS_JCR_URI, "mandatory");
+
+ /**
+ * jcr:protected
+ */
+ public static final Name JCR_PROTECTED = FACTORY.create(Name.NS_JCR_URI, "protected");
+
+ /**
+ * jcr:requiredType
+ */
+ public static final Name JCR_REQUIREDTYPE = FACTORY.create(Name.NS_JCR_URI, "requiredType");
+
+ /**
+ * jcr:onParentVersion
+ */
+ public static final Name JCR_ONPARENTVERSION = FACTORY.create(Name.NS_JCR_URI, "onParentVersion");
+
+ /**
+ * jcr:primaryItemName
+ */
+ public static final Name JCR_PRIMARYITEMNAME = FACTORY.create(Name.NS_JCR_URI, "primaryItemName");
+
+ /**
+ * jcr:multiple
+ */
+ public static final Name JCR_MULTIPLE = FACTORY.create(Name.NS_JCR_URI, "multiple");
+
+ /**
+ * jcr:valueConstraints
+ */
+ public static final Name JCR_VALUECONSTRAINTS = FACTORY.create(Name.NS_JCR_URI, "valueConstraints");
+
+ /**
+ * jcr:defaultValues
+ */
+ public static final Name JCR_DEFAULTVALUES = FACTORY.create(Name.NS_JCR_URI, "defaultValues");
+
+ /**
+ * jcr:autoCreated
+ */
+ public static final Name JCR_AUTOCREATED = FACTORY.create(Name.NS_JCR_URI, "autoCreated");
+
+ /**
+ * jcr:childNodeDefinition
+ */
+ public static final Name JCR_CHILDNODEDEFINITION = FACTORY.create(Name.NS_JCR_URI, "childNodeDefinition");
+
+ /**
+ * jcr:sameNameSiblings
+ */
+ public static final Name JCR_SAMENAMESIBLINGS = FACTORY.create(Name.NS_JCR_URI, "sameNameSiblings");
+
+ /**
+ * jcr:defaultPrimaryType
+ */
+ public static final Name JCR_DEFAULTPRIMARYTYPE = FACTORY.create(Name.NS_JCR_URI, "defaultPrimaryType");
+
+ /**
+ * jcr:requiredPrimaryTypes
+ */
+ public static final Name JCR_REQUIREDPRIMARYTYPES = FACTORY.create(Name.NS_JCR_URI, "requiredPrimaryTypes");
+
+ //-------------------------------------------< node type name constants >---
+ /**
+ * nt:unstructured
+ */
+ public static final Name NT_UNSTRUCTURED = FACTORY.create(Name.NS_NT_URI, "unstructured");
+
+ /**
+ * nt:base
+ */
+ public static final Name NT_BASE = FACTORY.create(Name.NS_NT_URI, "base");
+
+ /**
+ * nt:hierarchyNode
+ */
+ public static final Name NT_HIERARCHYNODE = FACTORY.create(Name.NS_NT_URI, "hierarchyNode");
+
+ /**
+ * nt:resource
+ */
+ public static final Name NT_RESOURCE = FACTORY.create(Name.NS_NT_URI, "resource");
+
+ /**
+ * nt:file
+ */
+ public static final Name NT_FILE = FACTORY.create(Name.NS_NT_URI, "file");
+
+ /**
+ * nt:folder
+ */
+ public static final Name NT_FOLDER = FACTORY.create(Name.NS_NT_URI, "folder");
+
+ /**
+ * nt:query
+ */
+ public static final Name NT_QUERY = FACTORY.create(Name.NS_NT_URI, "query");
+
+ /**
+ * mix:referenceable
+ */
+ public static final Name MIX_REFERENCEABLE = FACTORY.create(Name.NS_MIX_URI, "referenceable");
+ /**
+ * mix:referenceable
+ */
+ public static final Name MIX_LOCKABLE = FACTORY.create(Name.NS_MIX_URI, "lockable");
+ /**
+ * mix:versionable
+ */
+ public static final Name MIX_VERSIONABLE = FACTORY.create(Name.NS_MIX_URI, "versionable");
+ /**
+ * nt:versionHistory
+ */
+ public static final Name NT_VERSIONHISTORY = FACTORY.create(Name.NS_NT_URI, "versionHistory");
+ /**
+ * nt:version
+ */
+ public static final Name NT_VERSION = FACTORY.create(Name.NS_NT_URI, "version");
+ /**
+ * nt:versionLabels
+ */
+ public static final Name NT_VERSIONLABELS = FACTORY.create(Name.NS_NT_URI, "versionLabels");
+ /**
+ * nt:versionedChild
+ */
+ public static final Name NT_VERSIONEDCHILD = FACTORY.create(Name.NS_NT_URI, "versionedChild");
+ /**
+ * nt:frozenNode
+ */
+ public static final Name NT_FROZENNODE = FACTORY.create(Name.NS_NT_URI, "frozenNode");
+ /**
+ * nt:nodeType
+ */
+ public static final Name NT_NODETYPE = FACTORY.create(Name.NS_NT_URI, "nodeType");
+ /**
+ * nt:propertyDefinition
+ */
+ public static final Name NT_PROPERTYDEFINITION = FACTORY.create(Name.NS_NT_URI, "propertyDefinition");
+ /**
+ * nt:childNodeDefinition
+ */
+ public static final Name NT_CHILDNODEDEFINITION = FACTORY.create(Name.NS_NT_URI, "childNodeDefinition");
+
+
+ //--------------------------------------------------------------------------
+ /**
+ * rep:root
+ */
+ public static final Name REP_ROOT = FACTORY.create(Name.NS_REP_URI, "root");
+
+ /**
+ * rep:system
+ */
+ public static final Name REP_SYSTEM = FACTORY.create(Name.NS_REP_URI, "system");
+
+ /**
+ * rep:versionStorage
+ */
+ public static final Name REP_VERSIONSTORAGE = FACTORY.create(Name.NS_REP_URI, "versionStorage");
+
+ /**
+ * rep:versionStorage
+ */
+ public static final Name REP_NODETYPES = FACTORY.create(Name.NS_REP_URI, "nodeTypes");
+
+ /**
+ * The special wildcard name used as the name of residual item definitions.
+ */
+ public static final Name ANY_NAME = FACTORY.create("", "*");
+
+ //------------------------------------------< system view name constants >
+ /**
+ * sv:node
+ */
+ public static final Name SV_NODE = FACTORY.create(Name.NS_SV_URI, "node");
+ /**
+ * sv:property
+ */
+ public static final Name SV_PROPERTY = FACTORY.create(Name.NS_SV_URI, "property");
+ /**
+ * sv:value
+ */
+ public static final Name SV_VALUE = FACTORY.create(Name.NS_SV_URI, "value");
+ /**
+ * sv:type
+ */
+ public static final Name SV_TYPE = FACTORY.create(Name.NS_SV_URI, "type");
+ /**
+ * sv:name
+ */
+ public static final Name SV_NAME = FACTORY.create(Name.NS_SV_URI, "name");
+
+
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameConstants.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java?rev=586058&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java Thu Oct 18 11:34:57 2007
@@ -0,0 +1,227 @@
+/*
+ * 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.jackrabbit.name;
+
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.Name;
+
+/**
+ * <code>NameFactoryImpl</code>...
+ */
+public class NameFactoryImpl implements NameFactory {
+
+ private static NameFactory FACTORY;
+
+ private NameFactoryImpl() {};
+
+ public static NameFactory getInstance() {
+ if (FACTORY == null) {
+ FACTORY = new NameFactoryImpl();
+ }
+ return FACTORY;
+ }
+
+ //--------------------------------------------------------< NameFactory >---
+ /**
+ * @see NameFactory#create(String, String)
+ */
+ public Name create(String namespaceURI, String localName) throws IllegalArgumentException {
+ // NOTE: an empty localName and/or URI is valid (e.g. the root node name)
+ if (namespaceURI == null) {
+ throw new IllegalArgumentException("invalid namespaceURI specified");
+ }
+ if (localName == null) {
+ throw new IllegalArgumentException("invalid localName specified");
+ }
+ return new NameImpl(namespaceURI, localName);
+ }
+
+ /**
+ * @see NameFactory#create(String)
+ */
+ public Name create(String nameString) throws IllegalArgumentException {
+ if (nameString == null || "".equals(nameString)) {
+ throw new IllegalArgumentException("Invalid Name literal.");
+ }
+ if (nameString.charAt(0) != '{') {
+ throw new IllegalArgumentException("Invalid Name literal");
+ }
+ int i = nameString.indexOf('}');
+ if (i == -1) {
+ throw new IllegalArgumentException("Invalid Name literal");
+ }
+ if (i == nameString.length() - 1) {
+ throw new IllegalArgumentException("Invalid Name literal");
+ } else {
+ return new NameImpl(nameString.substring(1, i), nameString.substring(i + 1));
+ }
+ }
+
+ //--------------------------------------------------------< inner class >---
+ /**
+ * Inner class implementing the <code>Name</code> interface.
+ */
+ private static class NameImpl implements Name {
+
+ /** The memorized hash code of this qualified name. */
+ private transient int hash;
+
+ /** The memorized string representation of this qualified name. */
+ private transient String string;
+
+ /** The internalized namespace URI of this qualified name. */
+ private final String namespaceURI;
+
+ /** The local part of this qualified name. */
+ private final String localName;
+
+ private NameImpl(String namespaceURI, String localName) {
+ // internalize namespaceURI to improve performance of comparisons.
+ this.namespaceURI = namespaceURI.intern();
+ // localName is not internalized in order not to risk huge perm
+ // space for large repositories
+ this.localName = localName;
+ hash = 0;
+ }
+
+ //-----------------------------------------------------------< Name >---
+ /**
+ * @see Name#getLocalName()
+ */
+ public String getLocalName() {
+ return localName;
+ }
+
+ /**
+ * @see Name#getNamespaceURI()
+ */
+ public String getNamespaceURI() {
+ return namespaceURI;
+ }
+
+ //---------------------------------------------------------< Object >---
+ /**
+ * Returns the string representation of this <code>Name</code> in the
+ * following format:
+ * <p/>
+ * <code><b>{</b>namespaceURI<b>}</b>localName</code>
+ *
+ * @return the string representation of this <code>Name</code>.
+ * @see NameFactory#create(String)
+ * @see Object#toString()
+ */
+ public String toString() {
+ // Name is immutable, we can store the string representation
+ if (string == null) {
+ string = '{' + namespaceURI + '}' + localName;
+ }
+ return string;
+ }
+
+ /**
+ * Compares two qualified names for equality. Returns <code>true</code>
+ * if the given object is a qualified name and has the same namespace URI
+ * and local part as this qualified name.
+ *
+ * @param obj the object to compare this qualified name with
+ * @return <code>true</code> if the object is equal to this qualified name,
+ * <code>false</code> otherwise
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof NameImpl) {
+ NameImpl other = (NameImpl) obj;
+ // we can use == operator for namespaceURI since it is internalized
+ return namespaceURI == other.namespaceURI && localName.equals(other.localName);
+ }
+ // some other Name implementation
+ if (obj instanceof Name) {
+ Name other = (Name) obj;
+ return namespaceURI.equals(other.getNamespaceURI()) && localName.equals(other.getLocalName());
+ }
+ return false;
+ }
+
+ /**
+ * Returns the hash code of this qualified name. The hash code is
+ * computed from the namespace URI and local part of the qualified
+ * name and memorized for better performance.
+ *
+ * @return hash code
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ // Name is immutable, we can store the computed hash code value
+ int h = hash;
+ if (h == 0) {
+ h = 17;
+ h = 37 * h + namespaceURI.hashCode();
+ h = 37 * h + localName.hashCode();
+ hash = h;
+ }
+ return h;
+ }
+
+ //------------------------------------------------------< Cloneable >---
+ /**
+ * Creates a clone of this qualified name.
+ * Overriden in order to make <code>clone()</code> public.
+ *
+ * @return a clone of this instance
+ * @throws CloneNotSupportedException never thrown
+ * @see Object#clone()
+ */
+ public Object clone() throws CloneNotSupportedException {
+ // Name is immutable, no special handling required
+ return super.clone();
+ }
+
+ //-----------------------------------------------------< Comparable >---
+ /**
+ * Compares two qualified names.
+ *
+ * @param o the object to compare this qualified name with
+ * @return comparison result
+ * @throws ClassCastException if the given object is not a qualified name
+ * @see Comparable#compareTo(Object)
+ */
+ public int compareTo(Object o) {
+ if (this == o) {
+ return 0;
+ }
+ Name other = (Name) o;
+ if (namespaceURI.equals(other.getNamespaceURI())) {
+ return localName.compareTo(other.getLocalName());
+ } else {
+ return namespaceURI.compareTo(other.getNamespaceURI());
+ }
+ }
+
+ //---------------------------------------------------< Serializable >---
+ /**
+ * Creates a new <code>Name</code> instance using the proper constructor
+ * during deserialization in order to make sure that internalized strings
+ * are used where appropriate.
+ */
+ private Object readResolve() {
+ return new NameImpl(namespaceURI, localName);
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/NameFactoryImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java?rev=586058&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java Thu Oct 18 11:34:57 2007
@@ -0,0 +1,196 @@
+/*
+ * 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.jackrabbit.name;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.conversion.MalformedPathException;
+
+import java.util.LinkedList;
+
+/**
+ * Helper class used to build a path from pre-parsed path elements.
+ * <p/>
+ * Note that this class does neither validate the format of the path elements nor
+ * does it validate the format of the entire path.
+ * This class should therefore only be used in situations, where the elements
+ * and the resulting path are known to be valid. The regular way of creating
+ * a <code>Path</code> object is by calling any of the
+ * <code>PathFactory.create()</code>methods.
+ */
+public final class PathBuilder {
+
+ /**
+ * The path factory
+ */
+ private final PathFactory factory;
+
+ /**
+ * the list of path elements of the constructed path
+ */
+ private final LinkedList queue;
+
+ /**
+ * flag indicating if the current path is normalized
+ */
+ boolean isNormalized = true;
+
+ /**
+ * flag indicating if the current path has leading parent '..' elements
+ */
+ boolean leadingParent = true;
+
+ /**
+ * Creates a new PathBuilder to create a Path using the
+ * {@link PathFactoryImpl default PathFactory}. See
+ * {@link PathBuilder#PathBuilder(PathFactory)} for a constructor explicitely
+ * specifying the factory to use.
+ */
+ public PathBuilder() {
+ this(PathFactoryImpl.getInstance());
+ }
+
+ /**
+ * Creates a new PathBuilder.
+ *
+ * @param factory The PathFactory used to create the elements and the final path.
+ */
+ public PathBuilder(PathFactory factory) {
+ this.factory = (factory != null) ? factory : PathFactoryImpl.getInstance();
+ queue = new LinkedList();
+ }
+
+ /**
+ * Creates a new PathBuilder and initialized it with the given path
+ * elements.
+ *
+ * @param elements
+ */
+ public PathBuilder(Path.Element[] elements) {
+ this();
+ addAll(elements);
+ }
+
+ /**
+ * Creates a new PathBuilder and initialized it with elements of the
+ * given path.
+ *
+ * @param parent
+ */
+ public PathBuilder(Path parent) {
+ this();
+ addAll(parent.getElements());
+ }
+
+ /**
+ * Adds the {@link org.apache.jackrabbit.spi.PathFactory#getRootElement()}.
+ */
+ public void addRoot() {
+ addFirst(factory.getRootElement());
+ }
+
+ /**
+ * Adds the given elemenets
+ *
+ * @param elements
+ */
+ public void addAll(Path.Element[] elements) {
+ for (int i = 0; i < elements.length; i++) {
+ addLast(elements[i]);
+ }
+ }
+
+ /**
+ * Inserts the element at the beginning of the path to be built.
+ *
+ * @param elem
+ */
+ public void addFirst(Path.Element elem) {
+ if (queue.isEmpty()) {
+ isNormalized &= !elem.denotesCurrent();
+ leadingParent = elem.denotesParent();
+ } else {
+ isNormalized &= !elem.denotesCurrent() && (!leadingParent || elem.denotesParent());
+ leadingParent |= elem.denotesParent();
+ }
+ queue.addFirst(elem);
+ }
+
+ /**
+ * Inserts the element at the beginning of the path to be built.
+ *
+ * @param name
+ */
+ public void addFirst(Name name) {
+ addFirst(factory.createElement(name));
+ }
+
+ /**
+ * Inserts the element at the beginning of the path to be built.
+ *
+ * @param name
+ * @param index
+ */
+ public void addFirst(Name name, int index) {
+ addFirst(factory.createElement(name, index));
+ }
+
+ /**
+ * Inserts the element at the end of the path to be built.
+ *
+ * @param elem
+ */
+ public void addLast(Path.Element elem) {
+ queue.addLast(elem);
+ leadingParent &= elem.denotesParent();
+ isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
+ }
+
+ /**
+ * Inserts the element at the end of the path to be built.
+ *
+ * @param name
+ */
+ public void addLast(Name name) {
+ addLast(factory.createElement(name));
+ }
+
+ /**
+ * Inserts the element at the end of the path to be built.
+ *
+ * @param name
+ * @param index
+ */
+ public void addLast(Name name, int index) {
+ addLast(factory.createElement(name, index));
+ }
+
+ /**
+ * Assembles the built path and returns a new {@link Path}.
+ *
+ * @return a new {@link Path}
+ * @throws MalformedPathException if the internal path element queue is empty.
+ */
+ public Path getPath() throws MalformedPathException {
+ if (queue.size() == 0) {
+ throw new MalformedPathException("empty path");
+ }
+ Path.Element[] elements = (Path.Element[]) queue.toArray(new Path.Element[queue.size()]);
+ return factory.create(elements);
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathBuilder.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java?rev=586058&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java Thu Oct 18 11:34:57 2007
@@ -0,0 +1,895 @@
+/*
+ * 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.jackrabbit.name;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.NameFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PathNotFoundException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * <code>PathFactoryImpl</code>...
+ */
+public class PathFactoryImpl implements PathFactory {
+
+ private static PathFactory FACTORY = new PathFactoryImpl();
+
+ private static final String CURRENT_LITERAL = ".";
+ private static final String PARENT_LITERAL = "..";
+
+ private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+ private final static Name CURRENT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, CURRENT_LITERAL);
+ private final static Name PARENT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, PARENT_LITERAL);
+ private final static Name ROOT_NAME = NAME_FACTORY.create(Name.NS_DEFAULT_URI, "");
+
+ private static final Path.Element CURRENT_ELEMENT = new SpecialElement(CURRENT_NAME);
+ private static final Path.Element PARENT_ELEMENT = new SpecialElement(PARENT_NAME);
+ private static final Path.Element ROOT_ELEMENT = new SpecialElement(ROOT_NAME);
+
+ /**
+ * the root path
+ */
+ private static final Path ROOT = new PathImpl(new Path.Element[]{ROOT_ELEMENT}, true);
+
+ private PathFactoryImpl() {}
+
+ public static PathFactory getInstance() {
+ return FACTORY;
+ }
+
+ //--------------------------------------------------------< PathFactory >---
+ /**
+ * @see PathFactory#create(Path, Path, boolean)
+ */
+ public Path create(Path parent, Path relPath, boolean normalize) throws IllegalArgumentException, RepositoryException {
+ if (relPath.isAbsolute()) {
+ throw new IllegalArgumentException("relPath is not a relative path");
+ }
+ List l = new ArrayList();
+ l.addAll(Arrays.asList(parent.getElements()));
+ l.addAll(Arrays.asList(relPath.getElements()));
+
+ Builder pb = new Builder(l);
+ Path path = pb.getPath();
+ if (normalize) {
+ return path.getNormalizedPath();
+ } else {
+ return path;
+ }
+ }
+
+ /**
+ * @see PathFactory#create(Path, Name, boolean)
+ */
+ public Path create(Path parent, Name name, boolean normalize) throws RepositoryException {
+ List elements = new ArrayList();
+ elements.addAll(Arrays.asList(parent.getElements()));
+ elements.add(createElement(name));
+
+ Builder pb = new Builder(elements);
+ Path path = pb.getPath();
+ if (normalize) {
+ return path.getNormalizedPath();
+ } else {
+ return path;
+ }
+ }
+
+ /**
+ * @see PathFactory#create(Path, Name, int, boolean)
+ */
+ public Path create(Path parent, Name name, int index, boolean normalize) throws IllegalArgumentException, RepositoryException {
+ List elements = new ArrayList();
+ elements.addAll(Arrays.asList(parent.getElements()));
+ elements.add(createElement(name, index));
+
+ Builder pb = new Builder(elements);
+ Path path = pb.getPath();
+ if (normalize) {
+ return path.getNormalizedPath();
+ } else {
+ return path;
+ }
+ }
+
+ /**
+ * @see PathFactory#create(Name)
+ */
+ public Path create(Name name) throws IllegalArgumentException {
+ Path.Element elem = createElement(name);
+ return new Builder(new Path.Element[]{elem}).getPath();
+ }
+
+ /**
+ * @see PathFactory#create(Name, int)
+ */
+ public Path create(Name name, int index) throws IllegalArgumentException {
+ if (index < Path.INDEX_UNDEFINED) {
+ throw new IllegalArgumentException("Index must not be negative: " + index);
+ }
+ Path.Element elem = createElement(name, index);
+ return new Builder(new Path.Element[]{elem}).getPath();
+ }
+
+ /**
+ * @see PathFactory#create(Path.Element[])
+ */
+ public Path create(Path.Element[] elements) throws IllegalArgumentException {
+ return new Builder(elements).getPath();
+ }
+
+ /**
+ * @see PathFactory#create(String)
+ */
+ public Path create(String pathString) throws IllegalArgumentException {
+ if (pathString == null || "".equals(pathString)) {
+ throw new IllegalArgumentException("Invalid Path literal");
+ }
+ // split into path elements
+ int lastPos = 0;
+ int pos = pathString.indexOf(Path.DELIMITER);
+ ArrayList list = new ArrayList();
+ while (lastPos >= 0) {
+ Path.Element elem;
+ if (pos >= 0) {
+ elem = createElement(pathString.substring(lastPos, pos));
+ lastPos = pos + 1;
+ pos = pathString.indexOf(Path.DELIMITER, lastPos);
+ } else {
+ elem = createElement(pathString.substring(lastPos));
+ lastPos = -1;
+ }
+ list.add(elem);
+ }
+ return new Builder(list).getPath();
+ }
+
+ /**
+ * @see PathFactory#createElement(Name)
+ */
+ public Path.Element createElement(Name name) throws IllegalArgumentException {
+ if (name == null) {
+ throw new IllegalArgumentException("name must not be null");
+ } else if (name.equals(PARENT_NAME)) {
+ return PARENT_ELEMENT;
+ } else if (name.equals(CURRENT_NAME)) {
+ return CURRENT_ELEMENT;
+ } else if (name.equals(ROOT_NAME)) {
+ return ROOT_ELEMENT;
+ } else {
+ return new Element(name, Path.INDEX_UNDEFINED);
+ }
+ }
+
+ /**
+ * @see PathFactory#createElement(Name, int)
+ */
+ public Path.Element createElement(Name name, int index) throws IllegalArgumentException {
+ if (index < Path.INDEX_UNDEFINED) {
+ throw new IllegalArgumentException("The index may not be negative.");
+ } else if (name == null) {
+ throw new IllegalArgumentException("The name must not be null");
+ } else if (name.equals(PARENT_NAME)
+ || name.equals(CURRENT_NAME)
+ || name.equals(ROOT_NAME)) {
+ throw new IllegalArgumentException(
+ "Special path elements (root, '.' and '..') can not have an explicit index.");
+ } else {
+ return new Element(name, index);
+ }
+ }
+
+ /**
+ * Create an element from the element string
+ */
+ private Path.Element createElement(String elementString) {
+ if (elementString == null) {
+ throw new IllegalArgumentException("null PathElement literal");
+ }
+ if (elementString.equals(ROOT_NAME.toString())) {
+ return ROOT_ELEMENT;
+ } else if (elementString.equals(CURRENT_LITERAL)) {
+ return CURRENT_ELEMENT;
+ } else if (elementString.equals(PARENT_LITERAL)) {
+ return PARENT_ELEMENT;
+ }
+
+ int pos = elementString.indexOf('[');
+ if (pos == -1) {
+ Name name = NAME_FACTORY.create(elementString);
+ return new Element(name, Path.INDEX_UNDEFINED);
+ }
+ Name name = NAME_FACTORY.create(elementString.substring(0, pos));
+ int pos1 = elementString.indexOf(']');
+ if (pos1 == -1) {
+ throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (missing ']')");
+ }
+ try {
+ int index = Integer.valueOf(elementString.substring(pos + 1, pos1)).intValue();
+ if (index < 1) {
+ throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (index is 1-based)");
+ }
+ return new Element(name, index);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid PathElement literal: " + elementString + " (" + e.getMessage() + ")");
+ }
+ }
+
+ /**
+ * @see PathFactory#getCurrentElement()
+ */
+ public Path.Element getCurrentElement() {
+ return CURRENT_ELEMENT;
+ }
+
+ /**
+ * @see PathFactory#getParentElement()
+ */
+ public Path.Element getParentElement() {
+ return PARENT_ELEMENT;
+ }
+
+ /**
+ * @see PathFactory#getRootElement()
+ */
+ public Path.Element getRootElement() {
+ return ROOT_ELEMENT;
+ }
+
+ /**
+ * @see PathFactory#getRootPath()
+ */
+ public Path getRootPath() {
+ return ROOT;
+ }
+
+ //--------------------------------------------------------------------------
+ private static final class PathImpl implements Path {
+
+ /**
+ * the elements of this path
+ */
+ private final Path.Element[] elements;
+
+ /**
+ * flag indicating if this path is normalized
+ */
+ private final boolean normalized;
+
+ /**
+ * flag indicating if this path is absolute
+ */
+ private final boolean absolute;
+
+ /**
+ * the cached hashcode of this path
+ */
+ private transient int hash = 0;
+
+ /**
+ * the cached 'toString' of this path
+ */
+ private transient String string;
+
+ private PathImpl(Path.Element[] elements, boolean isNormalized) {
+ if (elements == null || elements.length == 0) {
+ throw new IllegalArgumentException("Empty paths are not allowed");
+ }
+ this.elements = elements;
+ this.absolute = elements[0].denotesRoot();
+ this.normalized = isNormalized;
+ }
+
+ /**
+ * @see Path#denotesRoot()
+ */
+ public boolean denotesRoot() {
+ return absolute && elements.length == 1;
+ }
+
+ /**
+ * @see Path#isAbsolute()
+ */
+ public boolean isAbsolute() {
+ return absolute;
+ }
+
+ /**
+ * @see Path#isCanonical()
+ */
+ public boolean isCanonical() {
+ return absolute && normalized;
+ }
+
+ /**
+ * @see Path#isNormalized()
+ */
+ public boolean isNormalized() {
+ return normalized;
+ }
+
+ /**
+ * @see Path#getNormalizedPath()
+ */
+ public Path getNormalizedPath() throws RepositoryException {
+ if (isNormalized()) {
+ return this;
+ }
+ LinkedList queue = new LinkedList();
+ Path.Element last = PARENT_ELEMENT;
+ for (int i = 0; i < elements.length; i++) {
+ Path.Element elem = elements[i];
+ if (elem.denotesParent() && !last.denotesParent()) {
+ if (last.denotesRoot()) {
+ // the first element is the root element;
+ // ".." would refer to the parent of root
+ throw new RepositoryException("Path can not be canonicalized: unresolvable '..' element");
+ }
+ queue.removeLast();
+ if (queue.isEmpty()) {
+ last = PARENT_ELEMENT;
+ } else {
+ last = (Path.Element) queue.getLast();
+ }
+ } else if (!elem.denotesCurrent()) {
+ last = elem;
+ queue.add(last);
+ }
+ }
+ if (queue.isEmpty()) {
+ throw new RepositoryException("Path can not be normalized: would result in an empty path.");
+ }
+ boolean isNormalized = true;
+ return new PathImpl((Path.Element[]) queue.toArray(new Element[queue.size()]), isNormalized);
+ }
+
+ /**
+ * @see Path#getCanonicalPath()
+ */
+ public Path getCanonicalPath() throws RepositoryException {
+ if (isCanonical()) {
+ return this;
+ }
+ if (!isAbsolute()) {
+ throw new RepositoryException("Only an absolute path can be canonicalized.");
+ }
+ return getNormalizedPath();
+ }
+
+ /**
+ * @see Path#computeRelativePath(Path)
+ */
+ public Path computeRelativePath(Path other) throws RepositoryException {
+ if (other == null) {
+ throw new IllegalArgumentException("null argument");
+ }
+
+ // make sure both paths are absolute
+ if (!isAbsolute() || !other.isAbsolute()) {
+ throw new RepositoryException("Cannot compute relative path from relative paths");
+ }
+
+ // make sure we're comparing canonical paths
+ Path p0 = getCanonicalPath();
+ Path p1 = other.getCanonicalPath();
+
+ if (p0.equals(p1)) {
+ // both paths are equal, the relative path is therefore '.'
+ Builder pb = new Builder(new Path.Element[] {CURRENT_ELEMENT});
+ return pb.getPath();
+ }
+
+ // determine length of common path fragment
+ int lengthCommon = 0;
+ Path.Element[] elems0 = p0.getElements();
+ Path.Element[] elems1 = p1.getElements();
+ for (int i = 0; i < elems0.length && i < elems1.length; i++) {
+ if (!elems0[i].equals(elems1[i])) {
+ break;
+ }
+ lengthCommon++;
+ }
+ List l = new ArrayList();
+ if (lengthCommon < elems0.length) {
+ /**
+ * the common path fragment is an ancestor of this path;
+ * this has to be accounted for by prepending '..' elements
+ * to the relative path
+ */
+ int tmp = elems0.length - lengthCommon;
+ while (tmp-- > 0) {
+ l.add(0, PARENT_ELEMENT);
+ }
+ }
+ // add remainder of other path
+ for (int i = lengthCommon; i < elems1.length; i++) {
+ l.add(elems1[i]);
+ }
+ return new Builder(l).getPath();
+ }
+
+ /**
+ * @see Path#getAncestor(int)
+ */
+ public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException {
+ if (degree < 0) {
+ throw new IllegalArgumentException("degree must be >= 0");
+ } else if (degree == 0) {
+ return this;
+ }
+ int length = elements.length - degree;
+ if (length < 1) {
+ throw new PathNotFoundException("no such ancestor path of degree " + degree);
+ }
+ Path.Element[] elements = new Element[length];
+ System.arraycopy(this.elements, 0, elements, 0, length);
+ return new PathImpl(elements, normalized);
+ }
+
+ /**
+ * @see Path#getAncestorCount()
+ */
+ public int getAncestorCount() {
+ return getDepth() - 1;
+ }
+
+ /**
+ * @see Path#getLength()
+ */
+ public int getLength() {
+ return elements.length;
+ }
+
+ /**
+ * @see Path#getDepth()
+ */
+ public int getDepth() {
+ int depth = ROOT_DEPTH;
+ for (int i = 0; i < elements.length; i++) {
+ if (elements[i].denotesParent()) {
+ depth--;
+ } else if (!elements[i].denotesCurrent()) {
+ depth++;
+ }
+ }
+ return depth;
+ }
+
+ /**
+ * @see Path#isAncestorOf(Path)
+ */
+ public boolean isAncestorOf(Path other) throws IllegalArgumentException, RepositoryException {
+ if (other == null) {
+ throw new IllegalArgumentException("null argument");
+ }
+ // make sure both paths are either absolute or relative
+ if (isAbsolute() != other.isAbsolute()) {
+ throw new IllegalArgumentException("Cannot compare a relative path with an absolute path");
+ }
+ // make sure we're comparing normalized paths
+ Path p0 = getNormalizedPath();
+ Path p1 = other.getNormalizedPath();
+
+ if (p0.equals(p1)) {
+ return false;
+ }
+ // calculate depth of paths (might be negative)
+ if (p0.getDepth() >= p1.getDepth()) {
+ return false;
+ }
+ Path.Element[] elems0 = p0.getElements();
+ Path.Element[] elems1 = p1.getElements();
+ for (int i = 0; i < elems0.length; i++) {
+ if (!elems0[i].equals(elems1[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @see Path#isDescendantOf(Path)
+ */
+ public boolean isDescendantOf(Path other) throws IllegalArgumentException, RepositoryException {
+ if (other == null) {
+ throw new IllegalArgumentException("Null argument");
+ }
+ return other.isAncestorOf(this);
+ }
+
+ /**
+ * @see Path#subPath(int, int)
+ */
+ public Path subPath(int from, int to) throws IllegalArgumentException, RepositoryException {
+ if (from < 0 || to >= elements.length || from >= to) {
+ throw new IllegalArgumentException();
+ }
+ if (!isNormalized()) {
+ throw new RepositoryException("Cannot extract sub-Path from a non-normalized Path.");
+ }
+ Path.Element[] dest = new Path.Element[to-from];
+ System.arraycopy(elements, from, dest, 0, dest.length);
+ Builder pb = new Builder(dest);
+ return pb.getPath();
+ }
+
+ /**
+ * @see Path#getNameElement()
+ */
+ public Element getNameElement() {
+ return elements[elements.length - 1];
+ }
+
+ /**
+ * @see Path#getString()
+ */
+ public String getString() {
+ return toString();
+ }
+
+ /**
+ * @see Path#getElements()
+ */
+ public Element[] getElements() {
+ return elements;
+ }
+
+ //---------------------------------------------------------< Object >---
+ /**
+ * Returns the internal string representation of this <code>Path</code>.
+ * <p/>
+ * Note that the returned string is not a valid JCR path, i.e. the
+ * namespace URI's of the individual path elements are not replaced with
+ * their mapped prefixes.
+ *
+ * @return the internal string representation of this <code>Path</code>.
+ */
+ public String toString() {
+ // Path is immutable, we can store the string representation
+ if (string == null) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < elements.length; i++) {
+ if (i > 0) {
+ sb.append(Path.DELIMITER);
+ }
+ Path.Element element = elements[i];
+ String elem = element.toString();
+ sb.append(elem);
+ }
+ string = sb.toString();
+ }
+ return string;
+ }
+
+ /**
+ * Returns a hash code value for this path.
+ *
+ * @return a hash code value for this path.
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ // Path is immutable, we can store the computed hash code value
+ int h = hash;
+ if (h == 0) {
+ h = 17;
+ for (int i = 0; i < elements.length; i++) {
+ h = 37 * h + elements[i].hashCode();
+ }
+ hash = h;
+ }
+ return h;
+ }
+
+ /**
+ * Compares the specified object with this path for equality.
+ *
+ * @param obj the object to be compared for equality with this path.
+ * @return <tt>true</tt> if the specified object is equal to this path.
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof PathImpl) {
+ Path other = (Path) obj;
+ return Arrays.equals(elements, other.getElements());
+ }
+ return false;
+ }
+ }
+
+ //-------------------------------------------------------< Path.Element >---
+ /**
+ * Object representation of a single JCR path element.
+ *
+ * @see Path.Element
+ */
+ private static class Element implements Path.Element {
+
+ /**
+ * Qualified name of the path element.
+ */
+ private final Name name;
+
+ /**
+ * Optional index of the path element. Set to zero, if not
+ * explicitly specified, otherwise contains the 1-based index.
+ */
+ private final int index;
+
+ /**
+ * Private constructor for creating a path element with the given
+ * qualified name and index. Instead of using this constructor directly
+ * the factory methods {@link PathFactory#createElement(Name)} and
+ * {@link PathFactory#create(Name, int)} should be used.
+ *
+ * @param name qualified name
+ * @param index index
+ */
+ private Element(Name name, int index) {
+ this.index = index;
+ this.name = name;
+ }
+
+ /**
+ * @see Path.Element#getName()
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * @see Path.Element#getIndex()
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @see Path.Element#getNormalizedIndex()
+ */
+ public int getNormalizedIndex() {
+ if (index == Path.INDEX_UNDEFINED) {
+ return Path.INDEX_DEFAULT;
+ } else {
+ return index;
+ }
+ }
+
+ /**
+ * @return always returns false.
+ * @see Path.Element#denotesRoot()
+ */
+ public boolean denotesRoot() {
+ return false;
+ }
+
+ /**
+ * @return always returns false.
+ * @see Path.Element#denotesParent()
+ */
+ public boolean denotesParent() {
+ return false;
+ }
+
+ /**
+ * @return always returns false.
+ * @see Path.Element#denotesCurrent()
+ */
+ public boolean denotesCurrent() {
+ return false;
+ }
+
+ /**
+ * @return always returns true.
+ * @see Path.Element#denotesName()
+ */
+ public boolean denotesName() {
+ return true;
+ }
+
+ /**
+ * @see Path.Element#getString()
+ */
+ public String getString() {
+ return toString();
+ }
+
+ /**
+ * Returns a string representation of this path element. Note that
+ * the path element name is expressed using the <code>{uri}name</code>
+ * syntax.
+ *
+ * @return string representation of the path element
+ * @see Object#toString()
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ // name
+ sb.append(name.toString());
+ // index
+ if (index > Path.INDEX_DEFAULT) {
+ sb.append('[');
+ sb.append(index);
+ sb.append(']');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Computes a hash code for this path element.
+ *
+ * @return hash code
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ int h = 17;
+ h = 37 * h + getNormalizedIndex();
+ h = 37 * h + name.hashCode();
+ return h;
+ }
+
+ /**
+ * Check for path element equality. Returns true if the given
+ * object is a PathElement and contains the same name and index
+ * as this one.
+ *
+ * @param obj the object to compare with
+ * @return <code>true</code> if the path elements are equal
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Path.Element) {
+ Path.Element other = (Path.Element) obj;
+ return name.equals(other.getName())
+ && getNormalizedIndex() == other.getNormalizedIndex();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Object representation of a special JCR path element notably the root, the
+ * current and the parent element.
+ */
+ private static final class SpecialElement extends Element {
+
+ private final static int ROOT = 1;
+ private final static int CURRENT = 2;
+ private final static int PARENT = 4;
+
+ private final int type;
+
+ private SpecialElement(Name name) {
+ super(name, Path.INDEX_UNDEFINED);
+ if (ROOT_NAME.equals(name)) {
+ type = ROOT;
+ } else if (CURRENT_NAME.equals(name)) {
+ type = CURRENT;
+ } else if (PARENT_NAME.equals(name)) {
+ type = PARENT;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * @return true if this is the {@link #ROOT root element}.
+ * @see Path.Element#denotesRoot()
+ */
+ public boolean denotesRoot() {
+ return type == ROOT;
+ }
+
+ /**
+ * @return true if this is the {@link #PARENT parent element}.
+ * @see Path.Element#denotesParent()
+ */
+ public boolean denotesParent() {
+ return type == PARENT;
+ }
+
+ /**
+ * @return true if this is the {@link #CURRENT current element}.
+ * @see Path.Element#denotesCurrent()
+ */
+ public boolean denotesCurrent() {
+ return type == CURRENT;
+ }
+
+ /**
+ * @return Always returns false.
+ * @see Path.Element#denotesParent()
+ */
+ public boolean denotesName() {
+ return false;
+ }
+ }
+
+ /**
+ * Builder internal class
+ */
+ private static final class Builder {
+
+ /**
+ * the lpath elements of the constructed path
+ */
+ private final Path.Element[] elements;
+
+ /**
+ * flag indicating if the current path is normalized
+ */
+ private boolean isNormalized = true;
+
+ /**
+ * flag indicating if the current path has leading parent '..' elements
+ */
+ private boolean leadingParent = true;
+
+ /**
+ * Creates a new Builder and initialized it with the given path
+ * elements.
+ *
+ * @param elemList
+ * @throws IllegalArgumentException if the given elements array is null
+ * or has a length less than 1;
+ */
+ private Builder(List elemList) {
+ this((Path.Element[]) elemList.toArray(new Path.Element[elemList.size()]));
+ }
+
+ /**
+ * Creates a new Builder and initialized it with the given path
+ * elements.
+ *
+ * @param elements
+ * @throws IllegalArgumentException if the given elements array is null
+ * or has a length less than 1;
+ */
+ private Builder(Path.Element[] elements) {
+ if (elements == null || elements.length == 0) {
+ throw new IllegalArgumentException("Cannot build path from null or 0 elements.");
+ }
+ this.elements = elements;
+ for (int i = 0; i < elements.length; i++) {
+ Path.Element elem = elements[i];
+ leadingParent &= elem.denotesParent();
+ isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
+ }
+ }
+
+ /**
+ * Assembles the built path and returns a new {@link Path}.
+ *
+ * @return a new {@link Path}
+ */
+ private Path getPath() {
+ // no need to check the path format, assuming all names correct
+ return new PathImpl(elements, isNormalized);
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/name/PathFactoryImpl.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url