Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFactoryQImpl.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFactoryQImpl.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFactoryQImpl.java Tue May 05 10:34:57 CEST 2009
@@ -37,6 +37,7 @@
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.util.ISO8601;
/**
* This class implements the ValueFactory interface,
@@ -64,6 +65,16 @@
}
/**
+ * The QValueFactory that is wrapped by this ValueFactory
+ * instance.
+ *
+ * @return qfactory The QValueFactory wrapped by this instance.
+ */
+ public QValueFactory getQValueFactory() {
+ return qfactory;
+ }
+
+ /**
* Create a new Value based on an existing
* QValue
* @param qvalue existing QValue
@@ -128,6 +139,7 @@
*/
public Value createValue(Calendar value) {
try {
+ ISO8601.getYear(value);
QValue qvalue = qfactory.create(value);
return new QValueValue(qvalue, resolver);
} catch (RepositoryException ex) {
@@ -168,7 +180,7 @@
Name name = resolver.getQName(value);
qvalue = qfactory.create(name);
} else if (type == PropertyType.PATH) {
- Path path = resolver.getQPath(value);
+ Path path = resolver.getQPath(value, false);
qvalue = qfactory.create(path);
} else {
qvalue = qfactory.create(value, type);
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathFactoryImpl.java Wed May 06 18:55:55 CEST 2009
@@ -137,7 +137,7 @@
}
/**
- * @see PathFactory#create(Path.Element[])
+ * @see PathFactory#create(org.apache.jackrabbit.spi.Path.Element[])
*/
public Path create(Path.Element[] elements) throws IllegalArgumentException {
return new Builder(elements).getPath();
@@ -157,11 +157,11 @@
while (lastPos >= 0) {
Path.Element elem;
if (pos >= 0) {
- elem = createElement(pathString.substring(lastPos, pos));
+ elem = createElementFromString(pathString.substring(lastPos, pos));
lastPos = pos + 1;
pos = pathString.indexOf(Path.DELIMITER, lastPos);
} else {
- elem = createElement(pathString.substring(lastPos));
+ elem = createElementFromString(pathString.substring(lastPos));
lastPos = -1;
}
list.add(elem);
@@ -204,10 +204,18 @@
}
}
+ public Path.Element createElement(String identifier) throws IllegalArgumentException {
+ if (identifier == null) {
+ throw new IllegalArgumentException("The id must not be null.");
+ } else {
+ return new IdentifierElement(identifier);
+ }
+ }
+
/**
* Create an element from the element string
*/
- private Path.Element createElement(String elementString) {
+ private Path.Element createElementFromString(String elementString) {
if (elementString == null) {
throw new IllegalArgumentException("null PathElement literal");
}
@@ -217,6 +225,8 @@
return CURRENT_ELEMENT;
} else if (elementString.equals(PARENT_LITERAL)) {
return PARENT_ELEMENT;
+ } else if (elementString.startsWith("[") && elementString.endsWith("]") && elementString.length() > 2) {
+ return new IdentifierElement(elementString.substring(1, elementString.length()-1));
}
int pos = elementString.indexOf('[');
@@ -301,7 +311,7 @@
throw new IllegalArgumentException("Empty paths are not allowed");
}
this.elements = elements;
- this.absolute = elements[0].denotesRoot();
+ this.absolute = elements[0].denotesRoot() || elements[0].denotesIdentifier();
this.normalized = isNormalized;
}
@@ -309,10 +319,17 @@
* @see Path#denotesRoot()
*/
public boolean denotesRoot() {
- return absolute && elements.length == 1;
+ return absolute && elements.length == 1 && elements[0].denotesRoot();
}
/**
+ * @see Path#denotesIdentifier()
+ */
+ public boolean denotesIdentifier() {
+ return elements.length == 1 && elements[0].denotesIdentifier();
+ }
+
+ /**
* @see Path#isAbsolute()
*/
public boolean isAbsolute() {
@@ -336,10 +353,13 @@
/**
* @see Path#getNormalizedPath()
*/
- public Path getNormalizedPath() {
+ public Path getNormalizedPath() throws RepositoryException {
if (isNormalized()) {
return this;
}
+ if (denotesIdentifier()) {
+ throw new RepositoryException("Identifier-based path cannot be normalized.");
+ }
LinkedList queue = new LinkedList();
Path.Element last = PARENT_ELEMENT;
for (int i = 0; i < elements.length; i++) {
@@ -373,6 +393,9 @@
if (!isAbsolute()) {
throw new RepositoryException("Only an absolute path can be canonicalized.");
}
+ if (denotesIdentifier()) {
+ throw new RepositoryException("Identifier-based path cannot be canonicalized.");
+ }
return getNormalizedPath();
}
@@ -384,10 +407,13 @@
throw new IllegalArgumentException("null argument");
}
- // make sure both paths are absolute
+ // make sure both paths are absolute and not id-based
if (!isAbsolute() || !other.isAbsolute()) {
throw new RepositoryException("Cannot compute relative path from relative paths");
}
+ if (denotesIdentifier() || other.denotesIdentifier()) {
+ throw new RepositoryException("Cannot compute relative path from identifier-based paths");
+ }
// make sure we're comparing canonical paths
Path p0 = getCanonicalPath();
@@ -430,11 +456,11 @@
/**
* @see Path#getAncestor(int)
*/
- public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException {
+ public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException, RepositoryException {
if (degree < 0) {
throw new IllegalArgumentException("degree must be >= 0");
} else if (degree == 0) {
- return this.getNormalizedPath();
+ return getNormalizedPath();
}
if (isAbsolute()) {
@@ -461,8 +487,13 @@
* @see Path#getAncestorCount()
*/
public int getAncestorCount() {
- return (isAbsolute()) ? getDepth() : -1;
+ try {
+ return (isAbsolute() && !denotesIdentifier()) ? getDepth() : -1;
+ } catch (RepositoryException e) {
+ // never gets here.
+ return -1;
- }
+ }
+ }
/**
* @see Path#getLength()
@@ -474,7 +505,10 @@
/**
* @see Path#getDepth()
*/
- public int getDepth() {
+ public int getDepth() throws RepositoryException {
+ if (denotesIdentifier()) {
+ throw new RepositoryException("Cannot determine depth of an identifier based path.");
+ }
int depth = ROOT_DEPTH;
for (int i = 0; i < elements.length; i++) {
if (elements[i].denotesParent()) {
@@ -750,6 +784,14 @@
}
/**
+ * @return always returns false.
+ * @see Path.Element#denotesIdentifier()
+ */
+ public boolean denotesIdentifier() {
+ return false;
+ }
+
+ /**
* @see Path.Element#getString()
*/
public String getString() {
@@ -871,6 +913,86 @@
}
/**
+ *
+ */
+ private static final class IdentifierElement extends Element {
+
+ private final String identifier;
+ /**
+ *
+ * @param identifier
+ */
+ private IdentifierElement(String identifier) {
+ super(null, Path.INDEX_UNDEFINED);
+ this.identifier = identifier;
+ }
+
+ /**
+ * @return Always returns true.
+ * @see Path.Element#denotesIdentifier()
+ */
+ public boolean denotesIdentifier() {
+ return true;
+ }
+
+ /**
+ * @return Always returns false.
+ * @see Path.Element#denotesName()
+ */
+ public boolean denotesName() {
+ return false;
+ }
+
+ /**
+ * Returns a string representation of this path element. Note that
+ * the path element name is expressed using the {uri}name
+ * syntax.
+ *
+ * @return string representation of the path element
+ * @see Object#toString()
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ sb.append(identifier);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ /**
+ * Computes a hash code for this path element.
+ *
+ * @return hash code
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ int h = 37 * 17 + identifier.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 true if the path elements are equal
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof IdentifierElement) {
+ return identifier.equals(((IdentifierElement) obj).identifier);
+ } if (obj instanceof Path.Element) {
+ Path.Element other = (Path.Element) obj;
+ return other.denotesIdentifier() && getString().equals(other.getString());
+ }
+ return false;
+ }
+ }
+ /**
* Builder internal class
*/
private static final class Builder {
@@ -912,7 +1034,7 @@
this.elements = elements;
if (elements.length == 1) {
- isNormalized = true;
+ isNormalized = !elements[0].denotesIdentifier();
} else {
boolean absolute = elements[0].denotesRoot();
isNormalized = true;
@@ -926,6 +1048,8 @@
if (i > 0) {
throw new IllegalArgumentException("Invalid path: The root element may only occur at the beginning.");
}
+ } else if (elem.denotesIdentifier()) {
+ throw new IllegalArgumentException("Invalid path: The identifier element may only occur at the beginning of a single element path.");
} else if (elem.denotesParent()) {
parents++;
if (absolute || named > 0) {
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/DefaultNamePathResolver.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/DefaultNamePathResolver.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/DefaultNamePathResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -42,7 +42,7 @@
}
public DefaultNamePathResolver(Session session) {
- this(new SessionNamespaceResolver(session));
+ this(new SessionNamespaceResolver(session), ((session instanceof IdentifierResolver)? (IdentifierResolver) session : null), false);
}
public DefaultNamePathResolver(NamespaceRegistry registry) {
@@ -50,8 +50,12 @@
}
public DefaultNamePathResolver(NamespaceResolver nsResolver, boolean enableCaching) {
+ this(nsResolver, null, enableCaching);
+ }
+
+ public DefaultNamePathResolver(NamespaceResolver nsResolver, IdentifierResolver idResolver, boolean enableCaching) {
NameResolver nr = new ParsingNameResolver(NameFactoryImpl.getInstance(), nsResolver);
- PathResolver pr = new ParsingPathResolver(PathFactoryImpl.getInstance(), nr);
+ PathResolver pr = new ParsingPathResolver(PathFactoryImpl.getInstance(), nr, idResolver);
if (enableCaching) {
this.nResolver = new CachingNameResolver(nr);
this.pResolver = new CachingPathResolver(pr);
@@ -78,6 +82,10 @@
return pResolver.getQPath(path);
}
+ public Path getQPath(String path, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException {
+ return pResolver.getQPath(path, normalizeIdentifier);
+ }
+
public String getJCRPath(Path path) throws NamespaceException {
return pResolver.getJCRPath(path);
}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValueFactory.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValueFactory.java Fri Apr 17 10:40:20 CEST 2009
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValueFactory.java Fri Apr 17 10:40:20 CEST 2009
@@ -0,0 +1,74 @@
+/*
+ * 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.spi.commons.value;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.uuid.UUID;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import java.util.Calendar;
+
+/**
+ * AbstractQValueFactory...
+ */
+public abstract class AbstractQValueFactory implements QValueFactory {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(AbstractQValueFactory.class);
+
+
+ /**
+ * the default encoding
+ */
+ public static final String DEFAULT_ENCODING = "UTF-8";
+
+ protected static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+ protected static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+
+
+ //------------------------------------------------------< QValueFactory >---
+ /**
+ * @see QValueFactory#computeAutoValues(org.apache.jackrabbit.spi.QPropertyDefinition)
+ */
+ public QValue[] computeAutoValues(QPropertyDefinition propertyDefinition) throws RepositoryException {
+ Name nodeType = propertyDefinition.getDeclaringNodeType();
+ Name name = propertyDefinition.getName();
+
+ if (NameConstants.NT_HIERARCHYNODE.equals(nodeType) && NameConstants.JCR_CREATED.equals(name)) {
+ return new QValue[] { create(Calendar.getInstance()) };
+ } else if (NameConstants.NT_RESOURCE.equals(nodeType) && NameConstants.JCR_LASTMODIFIED.equals(name)) {
+ return new QValue[] { create(Calendar.getInstance()) };
+ } else if (NameConstants.MIX_REFERENCEABLE.equals(nodeType) && NameConstants.JCR_UUID.equals(name)) {
+ return new QValue[] { create(UUID.randomUUID().toString(), PropertyType.STRING) };
+ } else {
+ throw new RepositoryException("createFromDefinition not implemented for: " + name);
+ }
+ }
+}
\ No newline at end of file
Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java (revision 772129)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java Wed May 06 18:51:27 CEST 2009
@@ -16,7 +16,6 @@
*/
package org.apache.jackrabbit.spi.commons.batch;
-import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
@@ -379,7 +378,7 @@
return createNodeId(createPath(nodeId));
}
- public PropertyId createPropertyId(String propertyId) throws PathNotFoundException {
+ public PropertyId createPropertyId(String propertyId) throws RepositoryException {
Path path = createPath(propertyId);
return idFactory.createPropertyId(createNodeId(path.getAncestor(1)), path.getNameElement().getName());
}
Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyIdentifierResolver.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyIdentifierResolver.java Wed Apr 15 17:30:24 CEST 2009
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/DummyIdentifierResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -0,0 +1,117 @@
+/*
+ * 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.spi.commons.conversion;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.uuid.UUID;
+
+import javax.jcr.RepositoryException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * DummyIdentifierResolver...
+ */
+class DummyIdentifierResolver implements IdentifierResolver {
+
+ private static final PathFactory FACTORY = PathFactoryImpl.getInstance();
+ public static final String JCR_PATH = "/a/b/c";
+
+ private final List validIds;
+ private final List invalidFormats;
+ private final List invalidPaths;
+ private final Path path;
+
+ DummyIdentifierResolver() throws RepositoryException {
+ path = FACTORY.create(FACTORY.getRootPath(), FACTORY.create("{}a\t{}b\t{}c"), true);
+ validIds = new ArrayList();
+ validIds.add(UUID.randomUUID().toString());
+ validIds.add("a:b");
+ validIds.add("a[3]");
+ validIds.add("34a[2[");
+ validIds.add("34a/]");
+ validIds.add(" 3-4a/'\"]");
+ validIds.add("/a[3]/b/c:d/");
+ validIds.add("{}\"\"\t{}a[3]\t{}b\t{}c:d");
+
+ String invalidID = UUID.randomUUID().toString();
+ String invalidIdSegment = "["+invalidID+"]";
+ String validSegment = "[" + validIds.get(0).toString() + "]";
+
+ invalidFormats = new ArrayList();
+ invalidPaths = new ArrayList();
+
+ for (Iterator it = validIds.iterator(); it.hasNext();) {
+ String validId = it.next().toString();
+ if (!validId.endsWith("]")) {
+ invalidFormats.add("[" + validId);
+ } else {
+ invalidPaths.add("[" + validId);
+ }
+
+ if (!validId.startsWith("[")) {
+ invalidFormats.add(validId + "]");
+ } else {
+ invalidPaths.add(validId + "]");
+ }
+ }
+ invalidFormats.add(validSegment + "abc/abc");
+ invalidFormats.add(validSegment + "/a/b/c");
+ invalidFormats.add("/" + validSegment);
+ invalidFormats.add("/a/b/" + validSegment + "/c");
+ invalidFormats.add("/a/b/c" + validSegment);
+ invalidFormats.add("/" + invalidIdSegment);
+
+ // path starting with [ and ending with ] -> valid format but
+ // might be invalid path.
+ invalidPaths.add(validSegment + "/a/b[2]");
+ invalidPaths.add(validSegment + "/" + validSegment);
+ invalidPaths.add(validSegment + "[2]");
+ invalidPaths.add(invalidIdSegment);
+ invalidPaths.addAll(invalidFormats);
+ }
+
+ List getValidIdentifiers() {
+ return validIds;
+ }
+
+ List getInvalidIdentifierPaths() {
+ return invalidPaths;
+ }
+
+ List getInvalidIdentifierFormats() {
+ return invalidFormats;
+ }
+
+ public Path getPath(String identifier) throws MalformedPathException {
+ if (validIds.contains(identifier)) {
+ return path;
+ } else {
+ throw new MalformedPathException("Invalid path: identifier '"+ identifier +"' cannot be resolved.");
+ }
+ }
+
+ public void checkFormat(String identifier) throws MalformedPathException {
+ if (validIds.contains(identifier)) {
+ return;
+ }
+ throw new MalformedPathException("Invalid identifier.");
+ }
+}
\ No newline at end of file
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolver.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolver.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -35,20 +35,40 @@
/**
* Name resolver.
*/
- private final NameResolver resolver;
+ private final NameResolver nameResolver;
/**
+ * Identifier resolver.
+ */
+ private final IdentifierResolver idResolver;
+
+ /**
* Creates a parsing path resolver.
*
+ * @param pathFactory path factory.
* @param resolver name resolver
*/
public ParsingPathResolver(PathFactory pathFactory, NameResolver resolver) {
+ this(pathFactory, resolver, null);
+ }
+
+ /**
+ * Creates a parsing path resolver.
+ *
+ * @param pathFactory path factory.
+ * @param nameResolver name resolver.
+ * @param idResolver identifier resolver.
+ * @since JCR 2.0
+ */
+ public ParsingPathResolver(PathFactory pathFactory, NameResolver nameResolver,
+ IdentifierResolver idResolver) {
this.pathFactory = pathFactory;
- this.resolver = resolver;
+ this.nameResolver = nameResolver;
+ this.idResolver = idResolver;
}
/**
- * Parses the prefixed JCR path and returns the resolved qualified path.
+ * Parses the given JCR path and returns the resolved qualified path.
*
* @param path prefixed JCR path
* @return qualified path
@@ -57,12 +77,22 @@
* @throws NamespaceException if a namespace prefix can not be resolved
*/
public Path getQPath(String path) throws MalformedPathException, IllegalNameException, NamespaceException {
- return PathParser.parse(path, resolver, pathFactory);
+ return PathParser.parse(path, nameResolver, idResolver, pathFactory);
}
+ /**
+ * Calls {@link PathParser#parse(String, NameResolver, IdentifierResolver, org.apache.jackrabbit.spi.PathFactory)}
+ * from the given path.
+ *
+ * @see PathResolver#getQPath(String, boolean)
+ */
+ public Path getQPath(String path, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException {
+ return PathParser.parse(path, nameResolver, idResolver, pathFactory, normalizeIdentifier);
+ }
+
/**
- * Returns the prefixed JCR path for the given qualified path.
+ * Returns the given JCR path for the given qualified path.
*
* @param path qualified path
* @return prefixed JCR path
@@ -82,8 +112,10 @@
buffer.append('.');
} else if (elements[i].denotesParent()) {
buffer.append("..");
+ } else if (elements[i].denotesIdentifier()) {
+ buffer.append(elements[i].getString());
} else {
- buffer.append(resolver.getJCRName(elements[i].getName()));
+ buffer.append(nameResolver.getJCRName(elements[i].getName()));
/**
* FIXME the [1] subscript should only be suppressed if the
* item in question can't have same-name siblings.
Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/util/ISO8601.java
===================================================================
--- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/util/ISO8601.java (revision 772129)
+++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/util/ISO8601.java Fri Apr 17 12:12:06 CEST 2009
@@ -197,6 +197,10 @@
* if any of the set values are illegal or out of range
*/
cal.getTime();
+ /**
+ * in addition check the validity of the year
+ */
+ getYear(cal);
} catch (IllegalArgumentException e) {
return null;
}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathResolver.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathResolver.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -26,10 +26,10 @@
public interface PathResolver {
/**
- * Returns the qualified path for the given prefixed JCR path.
+ * Returns the path object for the given JCR path string.
*
* @param path prefixed JCR path
- * @return qualified path
+ * @return a Path object.
* @throws MalformedPathException if the JCR path format is invalid.
* @throws IllegalNameException if any of the JCR names contained in the path are invalid.
* @throws NamespaceException if a namespace prefix can not be resolved.
@@ -37,10 +37,22 @@
Path getQPath(String path) throws MalformedPathException, IllegalNameException, NamespaceException;
/**
- * Returns the prefixed JCR path for the given qualified path.
+ * Returns the path object for the given JCR path string.
*
- * @param path qualified path
- * @return prefixed JCR path
+ * @param path prefixed JCR path
+ * @param normalizeIdentifier
+ * @return a Path object.
+ * @throws MalformedPathException if the JCR path format is invalid.
+ * @throws IllegalNameException if any of the JCR names contained in the path are invalid.
+ * @throws NamespaceException if a namespace prefix can not be resolved.
+ */
+ Path getQPath(String path, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException;
+
+ /**
+ * Returns the given JCR path string for the given path object.
+ *
+ * @param path a Path object.
+ * @return a JCR path string
* @throws NamespaceException if a namespace URI can not be resolved
*/
String getJCRPath(Path path) throws NamespaceException;
Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java (revision 772129)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PathFactoryTest.java Wed Apr 15 17:30:24 CEST 2009
@@ -23,8 +23,10 @@
import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.uuid.UUID;
import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -84,7 +86,7 @@
public void testCreateElementNullName() {
try {
- factory.createElement(null);
+ factory.createElement((Name) null);
fail("Creating element with null name is invalid");
} catch (IllegalArgumentException e) {
// ok
@@ -197,6 +199,87 @@
}
}
+ public void testIdentifier() {
+ String identifier = UUID.randomUUID().toString();
+
+ Path.Element elem = factory.createElement(identifier);
+ assertTrue(elem.denotesIdentifier());
+ assertFalse(elem.denotesCurrent());
+ assertFalse(elem.denotesName());
+ assertFalse(elem.denotesParent());
+ assertFalse(elem.denotesRoot());
+ assertNull(elem.getName());
+ assertNotNull(elem.getString());
+ assertEquals(Path.INDEX_UNDEFINED, elem.getIndex());
+ assertEquals(Path.INDEX_DEFAULT, elem.getNormalizedIndex());
+
+ Path p = factory.create(new Path.Element[] {elem});
+ assertTrue(p.denotesIdentifier());
+ assertTrue(p.isAbsolute());
+
+ assertFalse(p.denotesRoot());
+ assertFalse(p.isCanonical());
+ assertFalse(p.isNormalized());
+
+ assertEquals(1, p.getLength());
+ assertEquals(-1, p.getAncestorCount());
+
+ Path.Element lastElem = p.getNameElement();
+ assertNotNull(lastElem);
+ assertTrue(lastElem.denotesIdentifier());
+
+ assertEquals(1, p.getElements().length);
+
+ try {
+ p.getDepth();
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.getNormalizedPath();
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.getAncestor(1);
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.isAncestorOf(factory.getRootPath());
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.computeRelativePath(factory.getRootPath());
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.getCanonicalPath();
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.isDescendantOf(factory.getRootPath());
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ try {
+ p.isEquivalentTo(factory.getRootPath());
+ fail();
+ } catch (RepositoryException e) {
+ //expected
+ }
+ }
+
public void testCreateInvalidPath() throws NamespaceException {
Path.Element rootEl = factory.getRootElement();
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/CachingPathResolver.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/CachingPathResolver.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/CachingPathResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -72,12 +72,33 @@
* @throws NamespaceException if a namespace prefix can not be resolved
*/
public Path getQPath(String path) throws MalformedPathException, IllegalNameException, NamespaceException {
- Path qpath = (Path) cache.get(path);
+ return getQPath(path, true);
+ }
+
+ /**
+ * @see PathResolver#getQPath(String, boolean)
+ */
+ public Path getQPath(String path, boolean normalizeIdentifier) throws MalformedPathException, IllegalNameException, NamespaceException {
+ Path qpath;
+ /*
+ * Jcr paths consisting of an identifier segment have 2 different
+ * path object representations depending on the given resolution flag:
+ * 1) a normalized absolute path if resolveIdentifier is true
+ * 2) a path denoting an identifier if resolveIdentifier is false.
+ * The latter are not cached in order not to return a wrong resolution
+ * when calling getQPath with the same identifier-jcr-path.
+ */
+ if (path.startsWith("[") && !normalizeIdentifier) {
+ qpath = resolver.getQPath(path, normalizeIdentifier);
+ } else {
+ qpath = (Path) cache.get(path);
- if (qpath == null) {
+ if (qpath == null) {
- qpath = resolver.getQPath(path);
+ qpath = resolver.getQPath(path, normalizeIdentifier);
- cache.put(path, qpath);
- }
+ cache.put(path, qpath);
+ }
+ }
return qpath;
+
}
Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java (revision 772129)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/PathParserTest.java Wed Apr 15 17:30:24 CEST 2009
@@ -28,6 +28,8 @@
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.spi.PathFactory;
+import javax.jcr.RepositoryException;
+
/**
* PathParserTest
*/
@@ -207,4 +209,86 @@
assertFalse("path is canonical: " + path, path.isCanonical());
}
}
+
+ public void testIdentifierParse() throws RepositoryException {
+ DummyIdentifierResolver idResolver = new DummyIdentifierResolver();
+ List valid = idResolver.getValidIdentifiers();
+ for (Iterator it = valid.iterator(); it.hasNext();) {
+ String jcrPath = "[" + it.next() + "]";
+ try {
+ PathParser.parse(jcrPath, resolver, factory);
+ fail("Parsing an identifier-based jcr path needs a IdentifierResolver");
+ } catch (MalformedPathException e) {
+ // success: cannot parse identifier path if idResolver is missing.
-}
+ }
+ try {
+ PathParser.parse(factory.getRootPath(), jcrPath, resolver, factory);
+ fail("Parsing an identifier-based jcr path needs a IdentifierResolver");
+ } catch (MalformedPathException e) {
+ // success: cannot parse identifier path if idResolver is missing.
+ }
+
+ Path p = PathParser.parse(jcrPath, resolver, idResolver, factory, true);
+ assertFalse(p.denotesIdentifier());
+
+ p = PathParser.parse(jcrPath, resolver, idResolver, factory, false);
+ assertTrue(p.denotesIdentifier());
+
+ try {
+ PathParser.parse(factory.getRootPath(), jcrPath, resolver, idResolver, factory);
+ fail("Cannot parser an identifier-based path to a relative path.");
+ } catch (MalformedPathException e) {
+ // success: invalid argument parent-path if the jcr-path is an identifier-based path.
+ }
+
+ try {
+ PathParser.parse(jcrPath, resolver, factory);
+ fail("Parsing an identifier-based jcr path needs a IdentifierResolver");
+ } catch (MalformedPathException e) {
+ // success: cannot parse identifier path if idResolver is missing.
+ }
+ }
+ }
+
+ public void testInvalidIdentifierParse() throws RepositoryException {
+ DummyIdentifierResolver idResolver = new DummyIdentifierResolver();
+
+ List invalid = idResolver.getInvalidIdentifierPaths();
+ for (Iterator it = invalid.iterator(); it.hasNext();) {
+ String jcrPath = it.next().toString();
+ try {
+ Path p = PathParser.parse(jcrPath, resolver, idResolver, factory, true);
+ fail("Invalid identifier based path");
+ } catch (MalformedPathException e) {
+ // ok
+ }
+ try {
+ Path p = PathParser.parse(jcrPath, resolver, idResolver, factory, false);
+ fail("Invalid identifier based path");
+ } catch (MalformedPathException e) {
+ // ok
+ }
+ }
+ }
+
+ public void testIdentifierCheckFormat() throws RepositoryException {
+ DummyIdentifierResolver idResolver = new DummyIdentifierResolver();
+ List valid = idResolver.getValidIdentifiers();
+ for (Iterator it = valid.iterator(); it.hasNext();) {
+ String jcrPath = "[" + it.next() + "]";
+ PathParser.checkFormat(jcrPath);
+ }
+
+ List invalid = idResolver.getInvalidIdentifierFormats();
+ for (Iterator it = invalid.iterator(); it.hasNext();) {
+ String jcrPath = it.next().toString();
+ try {
+ // passing null-nameResolver -> executes check-format only
+ PathParser.checkFormat(jcrPath);
+ fail(jcrPath);
+ } catch (MalformedPathException e) {
+ // success
+ }
+ }
+ }
+}
Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java (revision 772129)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/conversion/ParsingPathResolverTest.java Wed Apr 15 17:30:24 CEST 2009
@@ -18,35 +18,58 @@
import junit.framework.TestCase;
+import javax.jcr.RepositoryException;
import javax.jcr.NamespaceException;
import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
import org.apache.jackrabbit.spi.Path;
+import java.util.List;
+import java.util.Iterator;
+
/**
* Test cases for the {@link ParsingPathResolver} class.
*/
public class ParsingPathResolverTest extends TestCase {
+ private static NameResolver nameResolver = new ParsingNameResolver(NameFactoryImpl.getInstance(), new DummyNamespaceResolver());
+ private DummyIdentifierResolver idResolver;
+
/**
* Path resolver being tested.
*/
- private PathResolver resolver = new ParsingPathResolver(PathFactoryImpl.getInstance(),
- new ParsingNameResolver(NameFactoryImpl.getInstance(), new DummyNamespaceResolver()));
+ private PathResolver resolver = new ParsingPathResolver(PathFactoryImpl.getInstance(), nameResolver);
+ private PathResolver resolverV2;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ idResolver = new DummyIdentifierResolver();
+ resolverV2 = new ParsingPathResolver(PathFactoryImpl.getInstance(), nameResolver, idResolver);
+ }
+
/**
* Checks that the given path resolves properly.
*
* @param path JCR path
*/
private void assertValidPath(String path) {
+ assertValidPath(path, path);
+ }
+
+ private void assertValidPath(String path, String expectedResult) {
try {
Path qpath = resolver.getQPath(path);
- assertEquals(path, path, resolver.getJCRPath(qpath));
- } catch (NameException e) {
+ assertEquals(path, expectedResult, resolver.getJCRPath(qpath));
+ } catch (RepositoryException e) {
fail(path);
- } catch (NamespaceException e) {
+ }
+
+ try {
+ Path qpath = resolverV2.getQPath(path);
+ assertEquals(path, expectedResult, resolver.getJCRPath(qpath));
+ } catch (RepositoryException e) {
fail(path);
}
}
@@ -60,10 +83,16 @@
try {
resolver.getQPath(path);
fail(path);
- } catch (NameException e) {
- } catch (NamespaceException e) {
+ } catch (RepositoryException e) {
+ // success
}
+ try {
+ resolverV2.getQPath(path);
+ fail(path);
+ } catch (RepositoryException e) {
+ // success
- }
+ }
+ }
/**
* Tests that valid paths are properly resolved.
@@ -87,6 +116,12 @@
assertValidPath("name[2]/name[2]");
assertValidPath("prefix:name[2]/prefix:name[2]");
+ // trailing slash is valid
+ assertValidPath("a/", "a");
+ assertValidPath("prefix:name/", "prefix:name");
+ assertValidPath("/prefix:name[1]/", "/prefix:name");
+ assertValidPath("/a/../b/", "/a/../b");
+
assertValidPath("/a/../b");
assertValidPath("./../.");
assertValidPath("/a/./b");
@@ -103,12 +138,9 @@
public void testInvalidPaths() {
assertInvalidPath("");
assertInvalidPath("//");
- //assertInvalidPath("x/"); // TODO: was valid with jcr-commons-path but not with pathresolver? check again
assertInvalidPath("x:");
assertInvalidPath("x:/");
assertInvalidPath("x[]");
- //assertInvalidPath("x:y/"); // TODO: was valid with jcr-commons-path but not with pathresolver? check again
- //assertInvalidPath("x:y[1]/"); // TODO: was valid with jcr-commons-path but not with pathresolver? check again
assertInvalidPath("x:y[");
assertInvalidPath("x:y[]");
assertInvalidPath("x:y[1");
@@ -129,4 +161,38 @@
assertInvalidPath("prefix:name[2]foo/prefix:name[2]");
}
+ public void testValidIdentifierPaths() throws MalformedPathException, IllegalNameException, NamespaceException {
+ for (Iterator it = idResolver.getValidIdentifiers().iterator(); it.hasNext();) {
+ String jcrPath = "[" + it.next().toString() + "]";
+
+ Path p = resolverV2.getQPath(jcrPath, true);
+ assertFalse(p.denotesIdentifier());
+ assertTrue(p.isAbsolute());
+ assertTrue(p.isNormalized());
+ assertTrue(p.isCanonical());
+ assertEquals(DummyIdentifierResolver.JCR_PATH, resolverV2.getJCRPath(p));
+
+ p = resolverV2.getQPath(jcrPath, false);
+ assertTrue(p.denotesIdentifier());
+ assertEquals(1, p.getLength());
+ assertTrue(p.isAbsolute());
+ assertFalse(p.isNormalized());
+ assertFalse(p.isCanonical());
+ assertEquals(jcrPath, resolverV2.getJCRPath(p));
-}
+ }
+ }
+
+ public void testInvalidIdentifierPaths() throws MalformedPathException, IllegalNameException, NamespaceException {
+ List l = idResolver.getInvalidIdentifierPaths();
+
+ for (Iterator it = l.iterator(); it.hasNext();) {
+ String path = it.next().toString();
+ try {
+ resolverV2.getQPath(path);
+ fail(path);
+ } catch (MalformedPathException e) {
+ // success
+ }
+ }
+ }
+}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathParser.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathParser.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/PathParser.java Wed Apr 15 17:30:24 CEST 2009
@@ -39,15 +39,20 @@
private static final int STATE_INDEX_END = 5;
private static final int STATE_DOT = 6;
private static final int STATE_DOTDOT = 7;
+ private static final int STATE_IDENTIFIER = 8;
+ private static final int STATE_URI = 9;
+ private static final int STATE_URI_END = 10;
/**
* Parses jcrPath into a qualified path using
- * resolver to convert prefixes into namespace URI's.
+ * resolver to convert prefixes into namespace URI's. If
+ * resolver is null this method only checks the format of the
+ * passed String and returns null.
*
* @param jcrPath the jcr path.
* @param resolver the namespace resolver.
* @param factory
- * @return qualified path.
+ * @return A path object.
* @throws MalformedPathException If the jcrPath is malformed.
* @throws IllegalNameException if any of the jcrNames is malformed.
* @throws NamespaceException If an unresolvable prefix is encountered.
@@ -58,17 +63,76 @@
}
/**
- * Parses the give jcrPath and returns a Path. If
+ * Parses jcrPath into a qualified path using
+ * resolver to convert prefixes into namespace URI's. If the
+ * specified jcrPath is an identifier based absolute path
+ * beginning with an identifier segment the specified
+ * IdentifierResolver will be used to resolve it to an
+ * absolute path.
namResolver is null or if identifierResolver
+ * is null and the path starts with an identifier segment, this
+ * method only checks the format of the string and returns null.
+ *
+ * @param jcrPath the jcr path.
+ * @param nameResolver the namespace resolver.
+ * @param identifierResolver the resolver to validate any trailing identifier
+ * segment and resolve to an absolute path.
+ * @param factory
+ * @return A path object.
+ * @throws MalformedPathException If the jcrPath is malformed.
+ * @throws IllegalNameException if any of the jcrNames is malformed.
+ * @throws NamespaceException If an unresolvable prefix is encountered.
+ * @since JCR 2.0
+ */
+ public static Path parse(String jcrPath, NameResolver nameResolver,
+ IdentifierResolver identifierResolver, PathFactory factory)
+ throws MalformedPathException, IllegalNameException, NamespaceException {
+ return parse(null, jcrPath, nameResolver, identifierResolver, factory);
+ }
+
+ /**
+ * Parses jcrPath into a qualified path using
+ * resolver to convert prefixes into namespace URI's. If the
+ * specified jcrPath is an identifier based absolute path
+ * beginning with an identifier segment the specified
+ * IdentifierResolver will be used to resolve it to an
+ * absolute path.
+ * If namResolver is null or if identifierResolver
+ * is null and the path starts with an identifier segment, this
+ * method only checks the format of the string and returns null.
+ *
+ * @param jcrPath the jcr path.
+ * @param nameResolver the namespace resolver.
+ * @param identifierResolver the resolver to validate any trailing identifier
+ * segment and resolve to an absolute path.
+ * @param factory
+ * @param normalizeIdentifier
+ * @return A path object.
+ * @throws MalformedPathException If the jcrPath is malformed.
+ * @throws IllegalNameException if any of the jcrNames is malformed.
+ * @throws NamespaceException If an unresolvable prefix is encountered.
+ * @since JCR 2.0
+ */
+ public static Path parse(String jcrPath, NameResolver nameResolver,
+ IdentifierResolver identifierResolver,
+ PathFactory factory, boolean normalizeIdentifier)
+ throws MalformedPathException, IllegalNameException, NamespaceException {
+ return parse(null, jcrPath, nameResolver, identifierResolver, factory, normalizeIdentifier);
+ }
+
+ /**
+ * Parses the given jcrPath and returns a Path. If
* parent is not null, it is prepended to the
- * returned list. If resolver is null, this method
- * only checks the format of the string and returns null.
+ * built path before it is returned. If resolver is
+ * null, this method only checks the format of the string and
+ * returns null.
*
* @param parent the parent path
* @param jcrPath the JCR path
* @param resolver the namespace resolver to get prefixes for namespace
* URI's.
* @param factory
- * @return the fully qualified Path.
+ * @return the Path object.
* @throws MalformedPathException If the jcrPath is malformed.
* @throws IllegalNameException if any of the jcrNames is malformed.
* @throws NamespaceException If an unresolvable prefix is encountered.
@@ -76,6 +140,65 @@
public static Path parse(Path parent, String jcrPath,
NameResolver resolver,
PathFactory factory) throws MalformedPathException, IllegalNameException, NamespaceException {
+ return parse(parent, jcrPath, resolver, null, factory);
+ }
+
+ /**
+ * Parses the given jcrPath and returns a Path. If
+ * parent is not null, it is prepended to the
+ * built path before it is returned. If the specifed jcrPath
+ * is an identifier based absolute path beginning with an identifier segment
+ * the given identifierResolver will be used to resolve it to an
+ * absolute path.
+ * If nameResolver is null or if identifierResolver
+ * is null and the path starts with an identifier segment, this
+ * method only checks the format of the string and returns null.
+ *
+ * @param parent the parent path.
+ * @param jcrPath the jcr path.
+ * @param nameResolver the namespace resolver.
+ * @param identifierResolver the resolver to validate any trailing identifier
+ * segment and resolve it to an absolute path.
+ * @param factory The path factory.
+ * @return the Path object.
+ * @throws MalformedPathException
+ * @throws IllegalNameException
+ * @throws NamespaceException
+ */
+ public static Path parse(Path parent, String jcrPath, NameResolver nameResolver,
+ IdentifierResolver identifierResolver, PathFactory factory)
+ throws MalformedPathException, IllegalNameException, NamespaceException {
+ return parse(parent, jcrPath, nameResolver, identifierResolver, factory, true);
+ }
+
+ /**
+ * Parses the given jcrPath and returns a Path. If
+ * parent is not null, it is prepended to the
+ * built path before it is returned. If the specifed jcrPath
+ * is an identifier based absolute path beginning with an identifier segment
+ * the given identifierResolver will be used to resolve it to an
+ * absolute path.
+ * If nameResolver is null or if identifierResolver
+ * is null and the path starts with an identifier segment, this
+ * method only checks the format of the string and returns null.
+ *
+ * @param parent the parent path.
+ * @param jcrPath the jcr path.
+ * @param nameResolver the namespace resolver.
+ * @param identifierResolver the resolver to validate any trailing identifier
+ * segment and resolve it to an absolute path.
+ * @param factory The path factory.
+ * @param normalizeIdentifier
+ * @return the Path object.
+ * @throws MalformedPathException
+ * @throws IllegalNameException
+ * @throws NamespaceException
+ */
+ private static Path parse(Path parent, String jcrPath, NameResolver nameResolver,
+ IdentifierResolver identifierResolver, PathFactory factory,
+ boolean normalizeIdentifier)
+ throws MalformedPathException, IllegalNameException, NamespaceException {
+
final char EOF = (char) -1;
// check for length
@@ -95,7 +218,7 @@
int pos = 0;
if (jcrPath.charAt(0) == '/') {
if (parent != null) {
- throw new MalformedPathException("'" + jcrPath + "' is not a relative path");
+ throw new MalformedPathException("'" + jcrPath + "' is not a relative path.");
}
builder.addRoot();
pos++;
@@ -107,12 +230,26 @@
}
// parse the path
- int state = STATE_PREFIX_START;
+ int state;
+ if (jcrPath.charAt(0) == '[') {
+ if (parent != null) {
+ throw new MalformedPathException("'" + jcrPath + "' is not a relative path.");
+ }
+ state = STATE_IDENTIFIER;
+ pos++;
+ } else {
+ state = STATE_PREFIX_START;
+ }
+
int lastPos = pos;
+
String name = null;
+
int index = Path.INDEX_UNDEFINED;
boolean wasSlash = false;
+ boolean checkFormat = (nameResolver == null);
+
while (pos <= len) {
char c = pos == len ? EOF : jcrPath.charAt(pos);
pos++;
@@ -128,7 +265,8 @@
}
if (state == STATE_PREFIX
|| state == STATE_NAME
- || state == STATE_INDEX_END) {
+ || state == STATE_INDEX_END
+ || state == STATE_URI_END) {
// eof pathelement
if (name == null) {
@@ -140,14 +278,38 @@
// only add element if resolver not null. otherwise this
// is just a check for valid format.
- if (resolver != null) {
- Name qName = resolver.getQName(name);
+ if (checkFormat) {
+ NameParser.checkFormat(name);
+ } else {
+ Name qName = nameResolver.getQName(name);
builder.addLast(qName, index);
}
state = STATE_PREFIX_START;
lastPos = pos;
name = null;
index = Path.INDEX_UNDEFINED;
+ } else if (state == STATE_IDENTIFIER) {
+ if (c == EOF) {
+ // eof identifier reached
+ if (jcrPath.charAt(pos - 2) != ']') {
+ throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Unterminated identifier segment.");
+ }
+ String identifier = jcrPath.substring(lastPos, pos - 2);
+ if (checkFormat) {
+ if (identifierResolver != null) {
+ identifierResolver.checkFormat(identifier);
+ } // else ignore. TODO: rather throw?
+ } else if (identifierResolver == null) {
+ throw new MalformedPathException("'" + jcrPath + "' is not a valid path: Identifier segments are not supported.");
+ } else if (normalizeIdentifier) {
+ builder.addAll(identifierResolver.getPath(identifier).getElements());
+ } else {
+ identifierResolver.checkFormat(identifier);
+ builder.addLast(factory.createElement(identifier));
+ }
+ state = STATE_PREFIX_START;
+ lastPos = pos;
+ }
} else if (state == STATE_DOT) {
builder.addLast(factory.getCurrentElement());
lastPos = pos;
@@ -158,7 +320,7 @@
state = STATE_PREFIX_START;
} else if (state == STATE_PREFIX_START && c == EOF) {
// ignore trailing slash
- } else {
+ } else if (state != STATE_URI) {
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character.");
}
break;
@@ -184,6 +346,8 @@
}
state = STATE_NAME_START;
// don't reset the lastPos/pos since prefix+name are passed together to the NameResolver
+ } else if (state == STATE_IDENTIFIER || state == STATE_URI) {
+ // nothing do
} else {
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid name character");
}
@@ -197,6 +361,8 @@
state = STATE_INDEX;
name = jcrPath.substring(lastPos, pos - 1);
lastPos = pos;
+ } else if (state == STATE_IDENTIFIER) {
+ // nothing do
} else {
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character.");
}
@@ -213,6 +379,8 @@
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. Index number invalid: " + index);
}
state = STATE_INDEX_END;
+ } else if (state == STATE_IDENTIFIER) {
+ // nothing do
} else {
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character.");
}
@@ -225,19 +393,32 @@
throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid after index. '/' expected.");
} else if (state == STATE_DOT || state == STATE_DOTDOT) {
state = STATE_PREFIX;
- } else if (state == STATE_INDEX_END) {
- throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not valid after index. '/' expected.");
}
break;
case '\t':
+ if (state != STATE_IDENTIFIER) {
- throw new MalformedPathException("'" + jcrPath + "' is not a valid path. Whitespace not a allowed in name.");
+ throw new MalformedPathException("'" + jcrPath + "' is not a valid path. Whitespace not a allowed in name.");
-
+ }
case '*':
case '\'':
case '\"':
+ if (state != STATE_IDENTIFIER) {
+ // TODO for JCR 2.0 remove limitation of ' and "
- throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character.");
+ throw new MalformedPathException("'" + jcrPath + "' is not a valid path. '" + c + "' not a valid name character.");
+ }
+ case '{':
+ if (state == STATE_PREFIX_START) {
+ state = STATE_URI;
+ }
+ break;
+ case '}':
+ if (state == STATE_URI) {
+ state = STATE_URI_END;
+ }
+ break;
+
default:
if (state == STATE_PREFIX_START || state == STATE_DOT || state == STATE_DOTDOT) {
state = STATE_PREFIX;
@@ -250,7 +431,7 @@
wasSlash = c == ' ';
}
- if (resolver == null) {
+ if (checkFormat) {
// this was only for checking the format
return null;
} else {
@@ -269,7 +450,7 @@
public static void checkFormat(String jcrPath) throws MalformedPathException {
try {
// since no path is created -> use default factory
- parse(jcrPath, null, PathFactoryImpl.getInstance());
+ parse(jcrPath, null, null, PathFactoryImpl.getInstance());
} catch (NamespaceException e) {
// will never occur
} catch (IllegalNameException e) {
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValue.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValue.java Fri Apr 17 11:59:18 CEST 2009
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/AbstractQValue.java Fri Apr 17 11:59:18 CEST 2009
@@ -0,0 +1,247 @@
+/*
+ * 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.spi.commons.value;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.util.ISO8601;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * AbstractQValue...
+ */
+public abstract class AbstractQValue implements QValue {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(AbstractQValue.class);
+
+ protected Object val;
+ protected final int type;
+
+ /**
+ * Create a new AbstractQValue.
+ *
+ * @param value The value.
+ * @param type The property type.
+ */
+ protected AbstractQValue(Object value, int type) {
+ val = value;
+ this.type = type;
+ }
+
+ protected AbstractQValue(String value, int type) {
+ if (!(type == PropertyType.STRING || type == PropertyType.REFERENCE)) {
+ throw new IllegalArgumentException();
+ }
+ val = value;
+ this.type = type;
+ }
+
+ protected AbstractQValue(Long value) {
+ val = value;
+ type = PropertyType.LONG;
+ }
+
+ protected AbstractQValue(Double value) {
+ val = value;
+ type = PropertyType.DOUBLE;
+ }
+
+ protected AbstractQValue(Boolean value) {
+ val = value;
+ type = PropertyType.BOOLEAN;
+ }
+
+ protected AbstractQValue(Name value) {
+ val = value;
+ type = PropertyType.NAME;
+ }
+
+ protected AbstractQValue(Path value) {
+ val = value;
+ type = PropertyType.PATH;
+ }
+
+ //---------------------------------------------------------< QValue >---
+ /**
+ * @see QValue#getType()
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * @see QValue#getLength()
+ */
+ public long getLength() throws RepositoryException {
+ return getString().length();
+ }
+
+ /**
+ * @see QValue#getName()
+ */
+ public Name getName() throws RepositoryException {
+ if (type == PropertyType.NAME) {
+ return (Name) val;
+ } else {
+ try {
+ return AbstractQValueFactory.NAME_FACTORY.create(getString());
+ } catch (IllegalArgumentException e) {
+ throw new ValueFormatException("not a valid Name value: " + getString(), e);
+ }
+ }
+ }
+
+ /**
+ * @see QValue#getCalendar()
+ */
+ public Calendar getCalendar() throws RepositoryException {
+ if (type == PropertyType.DATE) {
+ return (Calendar) ((Calendar) val).clone();
+ } else if (type == PropertyType.DOUBLE) {
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
+ cal.setTimeInMillis(((Double) val).longValue());
+ return cal;
+ } else if (type == PropertyType.LONG) {
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
+ cal.setTimeInMillis(((Long) val).longValue());
+ return cal;
+ } else {
+ Calendar cal = ISO8601.parse(getString());
+ if (cal == null) {
+ throw new ValueFormatException("not a date string: " + getString());
+ } else {
+ return cal;
+ }
+ }
+ }
+
+ /**
+ * @see QValue#getDouble()
+ */
+ public double getDouble() throws RepositoryException {
+ if (type == PropertyType.DOUBLE) {
+ return ((Double) val).doubleValue();
+ } else if (type == PropertyType.DATE) {
+ return ((Calendar) val).getTimeInMillis();
+ } else {
+ try {
+ return Double.parseDouble(getString());
+ } catch (NumberFormatException ex) {
+ throw new ValueFormatException("not a double: " + getString(), ex);
+ }
+ }
+ }
+
+ /**
+ * @see QValue#getLong()
+ */
+ public long getLong() throws RepositoryException {
+ if (type == PropertyType.LONG) {
+ return ((Long) val).longValue();
+ } else if (type == PropertyType.DOUBLE) {
+ return ((Double) val).longValue();
+ } else if (type == PropertyType.DATE) {
+ return ((Calendar) val).getTimeInMillis();
+ } else {
+ try {
+ return Long.parseLong(getString());
+ } catch (NumberFormatException ex) {
+ throw new ValueFormatException("not a long: " + getString(), ex);
+ }
+ }
+ }
+
+ /**
+ * @throws RepositoryException
+ * @see QValue#getBoolean()
+ */
+ public boolean getBoolean() throws RepositoryException {
+ if (type == PropertyType.BOOLEAN) {
+ return ((Boolean) val).booleanValue();
+ } else {
+ return Boolean.valueOf(getString()).booleanValue();
+ }
+ }
+
+ /**
+ * @see QValue#getPath()
+ */
+ public Path getPath() throws RepositoryException {
+ if (type == PropertyType.PATH) {
+ return (Path) val;
+ } else {
+ try {
+ return AbstractQValueFactory.PATH_FACTORY.create(getString());
+ } catch (IllegalArgumentException e) {
+ throw new ValueFormatException("not a valid Path: " + getString(), e);
+ }
+ }
+ }
+
+ /**
+ * @see QValue#discard()
+ */
+ public void discard() {
+ // nothing to do
+ }
+
+ //---------------------------------------------------------< Object >---
+ /**
+ * Returns the string representation of this internal value.
+ *
+ * @return string representation of this internal value
+ */
+ public String toString() {
+ return val.toString();
+ }
+
+ /**
+ * @param obj
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AbstractQValue) {
+ AbstractQValue other = (AbstractQValue) obj;
+ return type == other.type && val.equals(other.val);
+ }
+ return false;
+ }
+
+ /**
+ * @return the hashCode of the internal value object.
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ return val.hashCode();
+ }
+
+
+}
\ No newline at end of file
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFormat.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFormat.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/ValueFormat.java Wed May 06 18:53:34 CEST 2009
@@ -119,7 +119,7 @@
qValue = factory.create(qName);
break;
case PropertyType.PATH:
- Path qPath = resolver.getQPath(jcrValue).getNormalizedPath();
+ Path qPath = resolver.getQPath(jcrValue, false);
qValue = factory.create(qPath);
break;
default:
@@ -131,7 +131,8 @@
/**
* @param qualifiedValue
* @param resolver
- * @return
+ * @param factory
+ * @return the JCR value created from the given qualifiedValue.
* @throws RepositoryException
*/
public static Value getJCRValue(QValue qualifiedValue,
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueFactoryImpl.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueFactoryImpl.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueFactoryImpl.java Fri Apr 17 13:17:04 CEST 2009
@@ -16,6 +16,16 @@
*/
package org.apache.jackrabbit.spi.commons.value;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.util.TransientFileFactory;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -31,41 +41,14 @@
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Calendar;
-import java.util.TimeZone;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.ValueFormatException;
-
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.NameFactory;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.PathFactory;
-import org.apache.jackrabbit.spi.QPropertyDefinition;
-import org.apache.jackrabbit.spi.QValue;
-import org.apache.jackrabbit.spi.QValueFactory;
-import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
-import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
-import org.apache.jackrabbit.util.ISO8601;
-import org.apache.jackrabbit.util.TransientFileFactory;
-import org.apache.jackrabbit.uuid.UUID;
-
/**
* QValueFactoryImpl...
*/
-public final class QValueFactoryImpl implements QValueFactory {
+public final class QValueFactoryImpl extends AbstractQValueFactory {
private static final QValueFactory INSTANCE = new QValueFactoryImpl();
- private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
- private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
-
- /**
- * the default encoding
- */
- private static final String DEFAULT_ENCODING = "UTF-8";
-
private QValueFactoryImpl() {
}
@@ -207,93 +190,49 @@
return new BinaryQValue(value);
}
- /**
- * @see QValueFactory#computeAutoValues(QPropertyDefinition)
- */
- public QValue[] computeAutoValues(QPropertyDefinition propertyDefinition) throws RepositoryException {
- Name nodeType = propertyDefinition.getDeclaringNodeType();
- Name name = propertyDefinition.getName();
- if (NameConstants.NT_HIERARCHYNODE.equals(nodeType) && NameConstants.JCR_CREATED.equals(name)) {
- return new QValue[] { create(Calendar.getInstance()) };
- } else if (NameConstants.NT_RESOURCE.equals(nodeType) && NameConstants.JCR_LASTMODIFIED.equals(name)) {
- return new QValue[] { create(Calendar.getInstance()) };
- } else if (NameConstants.MIX_REFERENCEABLE.equals(nodeType) && NameConstants.JCR_UUID.equals(name)) {
- return new QValue[] { create(UUID.randomUUID().toString(), PropertyType.STRING) };
- } else {
- throw new RepositoryException("createFromDefinition not implemented for: " + name);
- }
- }
-
//--------------------------------------------------------< Inner Class >---
/**
* QValue implementation for all valid PropertyTypes
- * except for BINARY.
+ * except for BINARY and DATE.
* @see QValueFactoryImpl.BinaryQValue
*/
- private static class QValueImpl implements QValue, Serializable {
+ private static class QValueImpl extends AbstractQValue implements Serializable {
private static final QValue TRUE = new QValueImpl(Boolean.TRUE);
-
private static final QValue FALSE = new QValueImpl(Boolean.FALSE);
- private final Object val;
- private final int type;
private QValueImpl(Object value, int type) {
- val = value;
- this.type = type;
+ super(value, type);
}
private QValueImpl(String value, int type) {
- if (!(type == PropertyType.STRING || type == PropertyType.REFERENCE)) {
- throw new IllegalArgumentException();
+ super(value, type);
- }
+ }
- val = value;
- this.type = type;
- }
private QValueImpl(Long value) {
- val = value;
- type = PropertyType.LONG;
+ super(value);
}
private QValueImpl(Double value) {
- val = value;
- type = PropertyType.DOUBLE;
+ super(value);
}
private QValueImpl(Boolean value) {
- val = value;
- type = PropertyType.BOOLEAN;
+ super(value);
}
private QValueImpl(Name value) {
- val = value;
- type = PropertyType.NAME;
+ super(value);
}
private QValueImpl(Path value) {
- val = value;
- type = PropertyType.PATH;
+ super(value);
}
//---------------------------------------------------------< QValue >---
/**
- * @see QValue#getType()
- */
- public int getType() {
- return type;
- }
-
- /**
- * @see QValue#getLength()
- */
- public long getLength() throws RepositoryException {
- return getString().length();
- }
-
- /**
* @see QValue#getString()
*/
public String getString() throws RepositoryException {
@@ -306,158 +245,13 @@
public InputStream getStream() throws RepositoryException {
try {
// convert via string
- return new ByteArrayInputStream(getString().getBytes(QValueFactoryImpl.DEFAULT_ENCODING));
+ return new ByteArrayInputStream(getString().getBytes(DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
throw new RepositoryException(QValueFactoryImpl.DEFAULT_ENCODING + " is not supported encoding on this platform", e);
}
- }
+ }
-
- /**
- * @see QValue#getName()
- */
- public Name getName() throws RepositoryException {
- if (type == PropertyType.NAME) {
- return (Name) val;
- } else {
- try {
- return NAME_FACTORY.create(getString());
- } catch (IllegalArgumentException e) {
- throw new ValueFormatException("not a valid Name value: " + getString(), e);
- }
+ }
- }
- }
- /**
- * @see QValue#getCalendar()
- */
- public Calendar getCalendar() throws RepositoryException {
- if (type == PropertyType.DATE) {
- return (Calendar) ((Calendar) val).clone();
- } else if (type == PropertyType.DOUBLE) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
- cal.setTimeInMillis(((Double) val).longValue());
- return cal;
- } else if (type == PropertyType.LONG) {
- Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+00:00"));
- cal.setTimeInMillis(((Long) val).longValue());
- return cal;
- } else {
- Calendar cal = ISO8601.parse(getString());
- if (cal == null) {
- throw new ValueFormatException("not a date string: " + getString());
- }
- else {
- return cal;
- }
- }
- }
-
- /**
- * @see QValue#getDouble()
- */
- public double getDouble() throws RepositoryException {
- if (type == PropertyType.DOUBLE) {
- return ((Double) val).doubleValue();
- } else if (type == PropertyType.DATE) {
- return ((Calendar) val).getTimeInMillis();
- } else {
- try {
- return Double.parseDouble(getString());
- } catch (NumberFormatException ex) {
- throw new ValueFormatException("not a double: " + getString(), ex);
- }
- }
- }
-
- /**
- * @see QValue#getLong()
- */
- public long getLong() throws RepositoryException {
- if (type == PropertyType.LONG) {
- return ((Long) val).longValue();
- } else if (type == PropertyType.DOUBLE) {
- return ((Double) val).longValue();
- } else if (type == PropertyType.DATE) {
- return ((Calendar) val).getTimeInMillis();
- } else {
- try {
- return Long.parseLong(getString());
- } catch (NumberFormatException ex) {
- throw new ValueFormatException("not a long: " + getString(), ex);
- }
- }
- }
-
- /**
- * @throws RepositoryException
- * @see QValue#getBoolean()
- */
- public boolean getBoolean() throws RepositoryException {
- if (type == PropertyType.BOOLEAN) {
- return ((Boolean) val).booleanValue();
- } else {
- return Boolean.valueOf(getString()).booleanValue();
- }
- }
-
- /**
- * @see QValue#getPath()
- */
- public Path getPath() throws RepositoryException {
- if (type == PropertyType.PATH) {
- return (Path) val;
- } else {
- try {
- return PATH_FACTORY.create(getString());
- } catch (IllegalArgumentException e) {
- throw new ValueFormatException("not a valid Path: " + getString(), e);
- }
- }
- }
-
- /**
- * @see QValue#discard()
- */
- public void discard() {
- // nothing to do
- }
-
- //---------------------------------------------------------< Object >---
- /**
- * Returns the string representation of this internal value.
- *
- * @return string representation of this internal value
- */
- public String toString() {
- return val.toString();
- }
-
- /**
- *
- * @param obj
- * @see Object#equals(Object)
- */
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj instanceof QValueImpl) {
- QValueImpl other = (QValueImpl) obj;
- return type == other.type && val.equals(other.val);
- }
- return false;
- }
-
- /**
- * @return the hashCode of the internal value object.
- * @see Object#hashCode()
- */
- public int hashCode() {
- return val.hashCode();
- }
-
- }
-
//--------------------------------------------------------< Inner Class >---
/**
* Extension for values of type {@link PropertyType#DATE}.
@@ -494,7 +288,14 @@
if (obj instanceof DateQValue) {
DateQValue other = (DateQValue) obj;
return formattedStr.equals(other.formattedStr);
+ } else if (obj instanceof QValue) {
+ QValue other = (QValue) obj;
+ try {
+ return PropertyType.DATE == other.getType() && formattedStr.equals(other.getString());
+ } catch (RepositoryException e) {
+ // should never get here -> ignore.
- }
+ }
+ }
return false;
}
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/PathFactoryLogger.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/PathFactoryLogger.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/logging/PathFactoryLogger.java Wed Apr 15 17:30:24 CEST 2009
@@ -117,6 +117,13 @@
}}, "createElement(Name)", new Object[]{name, new Integer(index)});
}
+ public Element createElement(final String identifier) throws IllegalArgumentException {
+ return (Element) execute(new SafeCallable() {
+ public Object call() {
+ return pathFactory.createElement(identifier);
+ }}, "createElement(String)", new Object[]{identifier});
+ }
+
public Element getCurrentElement() {
return (Element) execute(new SafeCallable() {
public Object call() {
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/IdentifierResolver.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/IdentifierResolver.java Wed Apr 15 17:30:24 CEST 2009
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/conversion/IdentifierResolver.java Wed Apr 15 17:30:24 CEST 2009
@@ -0,0 +1,29 @@
+/*
+ * 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.spi.commons.conversion;
+
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * IdentifierResolver ....
+ */
+public interface IdentifierResolver {
+
+ public Path getPath(String identifier) throws MalformedPathException;
+
+ public void checkFormat(String identifier) throws MalformedPathException;
+}
Index: jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/PathFactory.java
===================================================================
--- jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/PathFactory.java (revision 772129)
+++ jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/PathFactory.java Wed Apr 15 17:30:24 CEST 2009
@@ -152,6 +152,16 @@
public Path.Element createElement(Name name, int index) throws IllegalArgumentException;
/**
+ * Creates a path element from the given identifier.
+ *
+ * @param identifier Node identifier for which the path element should be created.
+ * @return a path element.
+ * @throws IllegalArgumentException If the identifier is null.
+ * @since JCR 2.0
+ */
+ public Path.Element createElement(String identifier) throws IllegalArgumentException;
+
+ /**
* Return the current element.
*
* @return the current element.
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathBuilder.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathBuilder.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/PathBuilder.java Wed Apr 15 17:30:24 CEST 2009
@@ -46,16 +46,6 @@
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
@@ -121,13 +111,6 @@
* @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);
}
@@ -157,8 +140,6 @@
*/
public void addLast(Path.Element elem) {
queue.addLast(elem);
- leadingParent &= elem.denotesParent();
- isNormalized &= !elem.denotesCurrent() && (leadingParent || !elem.denotesParent());
}
/**
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueValue.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueValue.java (revision 772129)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/value/QValueValue.java Tue May 05 10:34:57 CEST 2009
@@ -176,9 +176,9 @@
*/
public boolean equals(Object obj) {
if (obj instanceof QValueValue) {
+ QValue qv = ((QValueValue) obj).qvalue;
return qvalue.equals(((QValueValue) obj).qvalue);
- }
- else {
+ } else {
return false;
}
}
Index: jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java
===================================================================
--- jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java (revision 772129)
+++ jackrabbit-spi/src/main/java/org/apache/jackrabbit/spi/Path.java Wed Apr 15 17:30:24 CEST 2009
@@ -138,8 +138,17 @@
public boolean denotesRoot();
/**
- * Tests whether this path is absolute, i.e. whether it starts with "/".
+ * Test if this path represents an unresolved identifier-based path.
*
+ * @return true if this path represents an unresolved
+ * identifier-based path.
+ */
+ public boolean denotesIdentifier();
+
+ /**
+ * Tests whether this path is absolute, i.e. whether it starts with "/" or
+ * is an identifier based path.
+ *
* @return true if this path is absolute; false otherwise.
*/
public boolean isAbsolute();
@@ -229,8 +238,10 @@
* that there is no ancestor of the specified degree. In case of this
* being an absolute path, this would be the case if degree is
* greater that the {@link #getDepth() depth} of this path.
+ * @throws RepositoryException If the implementation is not able to determine
+ * the ancestor of the specified degree for some other reason.
*/
- public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException;
+ public Path getAncestor(int degree) throws IllegalArgumentException, PathNotFoundException, RepositoryException;
/**
* Returns the number of ancestors of this path. This is the equivalent
@@ -273,10 +284,11 @@
* canonical, e.g. the depth of "../../a" is -1.
*
* @return the depth this path
+ * @throws RepositoryException If the depths cannot be determined.
* @see #getLength()
* @see #getAncestorCount()
*/
- public int getDepth();
+ public int getDepth() throws RepositoryException;
/**
* Determines if the the other path would be equal to this
@@ -447,6 +459,14 @@
public boolean denotesName();
/**
+ * Returns true if this element represents an identifier element.
+ *
+ * @return true if this element represents an identifier element.
+ * @since JCR 2.0
+ */
+ public boolean denotesIdentifier();
+
+ /**
* Return the String presentation of a {@link Path.Element}. It must be
* in the format "{namespaceURI}localPart" or
* "{namespaceURI}localPart[index]" in case of an index