You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2009/02/24 12:26:41 UTC
svn commit: r747347 - in /jackrabbit/trunk/jackrabbit-spi-commons/src:
main/java/org/apache/jackrabbit/spi/commons/batch/
test/java/org/apache/jackrabbit/spi/commons/batch/
Author: mduerig
Date: Tue Feb 24 11:26:41 2009
New Revision: 747347
URL: http://svn.apache.org/viewvc?rev=747347&view=rev
Log:
JCR-1983: Provide change log consolidator
Added:
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java (with props)
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,40 @@
+/*
+ * 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.batch;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Batch;
+
+
+/**
+ * A <code>ChangeLog</code> is a specialized {@link Batch} which
+ * keeps a list of {@link Operation}s. The {@link #apply(Batch)} method
+ * applies these operations to another batch.
+ */
+public interface ChangeLog extends Batch {
+
+ /**
+ * Applies the {@link Operation}s contained in this change log to
+ * the passed <code>batch</code>.
+ * @param batch
+ * @return The <code>batch</code> passed in as argument with the
+ * operations from this change log applied.
+ * @throws RepositoryException
+ */
+ public Batch apply(Batch batch) throws RepositoryException;
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLog.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,143 @@
+/*
+ * 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.batch;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Batch;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.QValue;
+
+/**
+ * This {@link ChangeLog} implementation simply keeps back all calls to its {@link Batch} methods as
+ * a list of {@link #operations} (with item of type {@link Operation}). When {@link #apply(Batch)
+ * applied} to a batch, all operations in the list are {@link Operation#apply(Batch) applied} to that
+ * batch.
+ */
+public class ChangeLogImpl implements ChangeLog {
+
+ /**
+ * {@link Operation}s kept in this change log.
+ */
+ protected final List operations = new LinkedList();
+
+ public void addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid)
+ throws RepositoryException {
+
+ addOperation(Operations.addNode(parentId, nodeName, nodetypeName, uuid));
+ }
+
+ public void addProperty(NodeId parentId, Name propertyName, QValue value) throws RepositoryException {
+ addOperation(Operations.addProperty(parentId, propertyName, value));
+ }
+
+ public void addProperty(NodeId parentId, Name propertyName, QValue[] values)
+ throws RepositoryException {
+
+ addOperation(Operations.addProperty(parentId, propertyName, values));
+ }
+
+ public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException {
+ addOperation(Operations.move(srcNodeId, destParentNodeId, destName));
+ }
+
+ public void remove(ItemId itemId) throws RepositoryException {
+ addOperation(Operations.remove(itemId));
+ }
+
+ public void reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId)
+ throws RepositoryException {
+
+ addOperation(Operations.reorderNodes(parentId, srcNodeId, beforeNodeId));
+ }
+
+ public void setMixins(NodeId nodeId, Name[] mixinNodeTypeNames) throws RepositoryException {
+ addOperation(Operations.setMixins(nodeId, mixinNodeTypeNames));
+ }
+
+ public void setValue(PropertyId propertyId, QValue value) throws RepositoryException {
+ addOperation(Operations.setValue(propertyId, value));
+ }
+
+ public void setValue(PropertyId propertyId, QValue[] values) throws RepositoryException {
+ addOperation(Operations.setValue(propertyId, values));
+ }
+
+ public Batch apply(Batch batch) throws RepositoryException {
+ if (batch == null) {
+ throw new IllegalArgumentException("Batch must not be null");
+ }
+ for (Iterator it = operations.iterator(); it.hasNext(); ) {
+ Operation op = (Operation) it.next();
+ op.apply(batch);
+ }
+ return batch;
+ }
+
+ /**
+ * This method is called when an operation is added to the list of {@link #operations}
+ * kept by this change log.
+ * @param op {@link Operation} to add
+ * @throws RepositoryException
+ */
+ protected void addOperation(Operation op) throws RepositoryException {
+ operations.add(op);
+ }
+
+ // -----------------------------------------------------< Object >---
+
+ public String toString() {
+ StringBuffer b = new StringBuffer();
+ for (Iterator it = operations.iterator(); it.hasNext(); ) {
+ b.append(it.next());
+ if (it.hasNext()) {
+ b.append(", ");
+ }
+ }
+ return b.toString();
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof ChangeLogImpl) {
+ return equals((ChangeLogImpl) other);
+ }
+ return false;
+ }
+
+ public boolean equals(ChangeLogImpl other) {
+ return operations.equals(other.operations);
+ }
+
+ public int hashCode() {
+ throw new IllegalArgumentException("Not hashable");
+ }
+
+}
+
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ChangeLogImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,688 @@
+/*
+ * 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.batch;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Batch;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+
+/**
+ * A {@link ChangeLog} implementation which does basic consolidation on its
+ * {@link org.apache.jackrabbit.spi.commons.batch.Operation Operation}s. That is, cancelling
+ * operations are removed if possible. In general this is not possible across
+ * {@link org.apache.jackrabbit.spi.commons.batch.Operations.Move move} operations. The individual
+ * {@link CancelableOperation CancelableOperation} implementations document their behavior
+ * concerning cancellation.
+ */
+public class ConsolidatingChangeLog extends ChangeLogImpl {
+ private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+
+ /**
+ * Create a new instance of a consolidating change log.
+ */
+ public ConsolidatingChangeLog() {
+ super();
+ }
+
+ /**
+ * Create a {@link Path} from the {@link NodeId} of a parent and the {@link Name} of a
+ * child.
+ * @param parentId node id of the parent
+ * @param name name of the child
+ * @return the path of the item <code>name</code>
+ * @throws RepositoryException
+ */
+ protected static Path getPath(NodeId parentId, Name name) throws RepositoryException {
+ Path parent = parentId.getPath();
+ if (!parent.isAbsolute()) {
+ throw new IllegalArgumentException("Path not absoulte: " + parent);
+ }
+
+ return PATH_FACTORY.create(parent, name, true);
+ }
+
+ /**
+ * Determine the {@link Path} from an {@link ItemId}.
+ * @param itemId
+ * @return path of the item <code>itemId</code>
+ */
+ protected static Path getPath(ItemId itemId) {
+ Path path = itemId.getPath();
+ if (!path.isAbsolute()) {
+ throw new IllegalArgumentException("Path not absoulte: " + path);
+ }
+ return path;
+ }
+
+ // -----------------------------------------------------< ChangeLog >---
+
+ public void addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid)
+ throws RepositoryException {
+
+ addOperation(CancelableOperations.addNode(parentId, nodeName, nodetypeName, uuid));
+ }
+
+ public void addProperty(NodeId parentId, Name propertyName, QValue value) throws RepositoryException {
+ addOperation(CancelableOperations.addProperty(parentId, propertyName, value));
+ }
+
+ public void addProperty(NodeId parentId, Name propertyName, QValue[] values) throws RepositoryException {
+ addOperation(CancelableOperations.addProperty(parentId, propertyName, values));
+ }
+
+ public void move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) throws RepositoryException {
+ addOperation(CancelableOperations.move(srcNodeId, destParentNodeId, destName));
+ }
+
+ public void remove(ItemId itemId) throws RepositoryException {
+ addOperation(CancelableOperations.remove(itemId));
+ }
+
+ public void reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId)
+ throws RepositoryException {
+
+ addOperation(CancelableOperations.reorderNodes(parentId, srcNodeId, beforeNodeId));
+ }
+
+ public void setMixins(NodeId nodeId, Name[] mixinNodeTypeNames) throws RepositoryException {
+ addOperation(CancelableOperations.setMixins(nodeId, mixinNodeTypeNames));
+ }
+
+ public void setValue(PropertyId propertyId, QValue value) throws RepositoryException {
+ addOperation(CancelableOperations.setValue(propertyId, value));
+ }
+
+ public void setValue(PropertyId propertyId, QValue[] values) throws RepositoryException {
+ addOperation(CancelableOperations.setValue(propertyId, values));
+ }
+
+ /**
+ * Determines the cancellation behavior from the list of {@link ChangeLogImpl#operations operations}
+ * and the current operation <code>op</code>:
+ * <ul>
+ * <li>When the current operation is cancelled by the last operation, the list of operations
+ * is not modified.</li>
+ * <li>When the current operation and the last operation cancel each other, the last operation is
+ * removed from the list of operations.</li>
+ * <li>When the last operation is cancelled by this operation, the last operation is removed from
+ * the list of operations and determination of cancellation starts from scratch.</li>
+ * <li>Otherwise add the current operation to the list of operations.</li>
+ * </ul>
+ */
+ protected void addOperation(Operation op) throws RepositoryException {
+ if (!(op instanceof CancelableOperation)) {
+ throw new IllegalArgumentException("Operation not instance of "
+ + CancelableOperation.class.getName());
+ }
+
+ CancelableOperation otherOp = (CancelableOperation) op;
+ for (Iterator it = new OperationsBackwardWithSentinel(); it.hasNext(); ) {
+ CancelableOperation thisOp = (CancelableOperation) it.next();
+ switch (thisOp.cancel(otherOp)) {
+ case CancelableOperation.CANCEL_THIS:
+ it.remove();
+ continue;
+ case CancelableOperation.CANCEL_OTHER:
+ return;
+ case CancelableOperation.CANCEL_BOTH:
+ it.remove();
+ return;
+ case CancelableOperation.CANCEL_NONE:
+ super.addOperation(otherOp);
+ return;
+ default:
+ assert false : "Invalid case in switch";
+ }
+ }
+ }
+
+ // -----------------------------------------------------< private >---
+
+ private class OperationsBackwardWithSentinel implements Iterator {
+ private final ListIterator it = operations.listIterator(operations.size());
+ private boolean last = !it.hasPrevious();
+ private boolean done;
+
+ public boolean hasNext() {
+ return it.hasPrevious() || last;
+ }
+
+ public Object next() {
+ if (last) {
+ done = true;
+ return CancelableOperations.empty();
+ }
+ else {
+ Object o = it.previous();
+ last = !it.hasPrevious();
+ return o;
+ }
+ }
+
+ public void remove() {
+ if (done) {
+ throw new IllegalStateException("Cannot remove last element");
+ }
+ else {
+ it.remove();
+ }
+ }
+ }
+
+ // -----------------------------------------------------< CancelableOperations >---
+
+ /**
+ * This class represent an {@link Operation} which can be cancelled by another operation
+ * or which cancels another operation.
+ */
+ protected interface CancelableOperation extends Operation {
+
+ /**
+ * The other operation cancels this operations
+ */
+ public static final int CANCEL_THIS = 0;
+
+ /**
+ * This operation cancels the other operation
+ */
+ public static final int CANCEL_OTHER = 1;
+
+ /**
+ * This operation and the other operation cancel each other mutually
+ */
+ public static final int CANCEL_BOTH = 2;
+
+ /**
+ * No cancellation
+ */
+ public static final int CANCEL_NONE = 3;
+
+ /**
+ * Determines the cancellation behavior of the <code>other</code> operation
+ * on this operation.
+ * @param other
+ * @return Either {@link #CANCEL_THIS}, {@link #CANCEL_OTHER}, {@link #CANCEL_OTHER}
+ * or {@link #CANCEL_NONE}
+ * @throws RepositoryException
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException;
+ }
+
+ /**
+ * Factory for creating {@link ConsolidatingChangeLog.CancelableOperation CancelableOperation}s.
+ * The inner classes of this class all implement the <code>CancelableOperation</code> interface.
+ *
+ * @see Operation
+ */
+ protected static final class CancelableOperations {
+ private CancelableOperations() {
+ super();
+ }
+
+ // -----------------------------------------------------< Empty >---
+
+ /**
+ * An <code>Empty</code> operation never cancels another operation and is never
+ * cancelled by any other operation.
+ */
+ public static class Empty extends Operations.Empty implements CancelableOperation {
+
+ /**
+ * @return {@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE}
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating an {@link Empty Empty} operation.
+ * @return
+ */
+ public static CancelableOperation empty() {
+ return new Empty();
+ }
+
+ // -----------------------------------------------------< AddNode >---
+
+ /**
+ * An <code>AddNode</code> operation is is cancelled by a
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} operation higher up the tree.
+ * The remove operation is also cancelled if it is targeted at the same node than this add
+ * operation.
+ */
+ public static class AddNode extends Operations.AddNode implements CancelableOperation {
+
+ public AddNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) {
+ super(parentId, nodeName, nodetypeName, uuid);
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_BOTH CANCEL_BOTH} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has this node
+ * as target.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_THIS CANCEL_THIS} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has an node higher up
+ * the hierarchy as target.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE} otherwise.</li>
+ * </ul>
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ if (other instanceof Remove) {
+ Path thisPath = ConsolidatingChangeLog.getPath(parentId, nodeName);
+ Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+ if (thisPath.equals(otherPath)) {
+ return CANCEL_BOTH;
+ }
+ return (thisPath.isDescendantOf(otherPath))
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating an {@link AddNode AddNode} operation.
+ * @see Batch#addNode(NodeId, Name, Name, String)
+ *
+ * @param parentId
+ * @param nodeName
+ * @param nodetypeName
+ * @param uuid
+ * @return
+ */
+ public static CancelableOperation addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) {
+ return new AddNode(parentId, nodeName, nodetypeName, uuid);
+ }
+
+ // -----------------------------------------------------< AddProperty >---
+
+ /**
+ * <code>AddProperty</code> operations might cancel with
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetValue SetValue} operations.
+ */
+ public static class AddProperty extends Operations.AddProperty implements CancelableOperation {
+
+ public AddProperty(NodeId parentId, Name propertyName, QValue value) {
+ super(parentId, propertyName, value);
+ }
+
+ public AddProperty(NodeId parentId, Name propertyName, QValue[] values) {
+ super(parentId, propertyName, values);
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_BOTH CANCEL_BOTH} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has this property as
+ * target or if <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetValue SetValue} for a value of
+ * <code>null</code> and has this property as target.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_THIS CANCEL_THIS} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has a node higher up
+ * the hierarchy as target.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_OTHER CANCEL_OTHER} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetValue SetValue} and has this
+ * property as target.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE} otherwise.</li>
+ * <ul>
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ if (other instanceof Remove) {
+ Path thisPath = ConsolidatingChangeLog.getPath(parentId, propertyName);
+ Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+ if (thisPath.equals(otherPath)) {
+ return CANCEL_BOTH;
+ }
+ return (thisPath.isDescendantOf(otherPath))
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ if (other instanceof SetValue) {
+ SetValue setValue = (SetValue) other;
+ Path thisPath = ConsolidatingChangeLog.getPath(parentId, propertyName);
+ Path otherPath = ConsolidatingChangeLog.getPath(setValue.propertyId);
+ if (thisPath.equals(otherPath)) {
+ if (!isMultivalued && setValue.values[0] == null) {
+ return CANCEL_BOTH;
+ }
+ else if (values.length == setValue.values.length) {
+ for (int k = 0; k < values.length; k++) {
+ if (!values[k].equals(setValue.values[k])) {
+ return CANCEL_NONE;
+ }
+ }
+ return CANCEL_OTHER;
+ }
+ }
+ }
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating an {@link AddProperty AddProperty} operation.
+ *
+ * @see Batch#addProperty(NodeId, Name, QValue)
+ * @param parentId
+ * @param propertyName
+ * @param value
+ * @return
+ */
+ public static CancelableOperation addProperty(NodeId parentId, Name propertyName, QValue value) {
+ return new AddProperty(parentId, propertyName, value);
+ }
+
+ /**
+ * Factory method for creating an {@link AddProperty AddProperty} operation.
+ *
+ * @see Batch#addProperty(NodeId, Name, QValue[])
+ * @param parentId
+ * @param propertyName
+ * @param values
+ * @return
+ */
+ public static CancelableOperation addProperty(NodeId parentId, Name propertyName, QValue[] values) {
+ return new AddProperty(parentId, propertyName, values);
+ }
+
+ // -----------------------------------------------------< Move >---
+
+ /**
+ * An <code>Move</code> operation never cancels another operation and is never
+ * cancelled by any other operation.
+ */
+ public static class Move extends Operations.Move implements CancelableOperation {
+
+ public Move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) {
+ super(srcNodeId, destParentNodeId, destName);
+ }
+
+ /**
+ * @return {@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE}
+ */
+ public int cancel(CancelableOperation other) {
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link Move Move} operation.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param srcNodeId
+ * @param destParentNodeId
+ * @param destName
+ * @return
+ */
+ public static CancelableOperation move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) {
+ return new Move(srcNodeId, destParentNodeId, destName);
+ }
+
+ // -----------------------------------------------------< Remove >---
+
+ /**
+ * An <code>Remove</code> operation never cancels another operation and is never
+ * cancelled by any other operation.
+ */
+ public static class Remove extends Operations.Remove implements CancelableOperation {
+
+ public Remove(ItemId itemId) {
+ super(itemId);
+ }
+
+ /**
+ * @return {@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE}
+ */
+ public int cancel(CancelableOperation other) {
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link Remove Remove} operation.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param itemId
+ * @return
+ */
+ public static CancelableOperation remove(ItemId itemId) {
+ return new Remove(itemId);
+ }
+
+ // -----------------------------------------------------< Reorder Nodes >---
+
+ /**
+ * A <code>ReorderNodes</code> operation might cancel with
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and
+ * {@link ConsolidatingChangeLog.CancelableOperations.ReorderNodes ReorderNodes} operations.
+ */
+ public static class ReorderNodes extends Operations.ReorderNodes implements CancelableOperation {
+
+ public ReorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) {
+ super(parentId, srcNodeId, beforeNodeId);
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_THIS CANCEL_THIS} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has an node higher up
+ * the hierarchy or this node as target. Or if <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.ReorderNodes ReorderNodes} which
+ * has this node as target and neither <code>srcNodeId</code> nor <code>beforeNodeId</code>
+ * has same name siblings.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE} otherwise.</li>
+ * </ul>
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ if (other instanceof Remove) {
+ Path thisPath = ConsolidatingChangeLog.getPath(srcNodeId);
+ Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+ return thisPath.isDescendantOf(otherPath) || thisPath.equals(otherPath)
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ if (other instanceof ReorderNodes) {
+ Path thisPath = ConsolidatingChangeLog.getPath(parentId);
+ Path otherPath = ConsolidatingChangeLog.getPath(((ReorderNodes) other).parentId);
+ return thisPath.equals(otherPath) && !hasSNS(srcNodeId) && !hasSNS(beforeNodeId)
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ return CANCEL_NONE;
+ }
+
+ private boolean hasSNS(NodeId nodeId) {
+ Path p = ConsolidatingChangeLog.getPath(nodeId);
+ return p.getNameElement().getIndex() > 1;
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link ReorderNodes ReorderNodes} operation.
+ *
+ * @see Batch#reorderNodes(NodeId, NodeId, NodeId)
+ * @param parentId
+ * @param srcNodeId
+ * @param beforeNodeId
+ * @return
+ */
+ public static CancelableOperation reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) {
+ return new ReorderNodes(parentId, srcNodeId, beforeNodeId);
+ }
+
+ // -----------------------------------------------------< SetMixins >---
+
+ /**
+ * A <code>SetMixins</code> operation might cancel with
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetMixins SetMixins} operations.
+ */
+ public static class SetMixins extends Operations.SetMixins implements CancelableOperation {
+
+ public SetMixins(NodeId nodeId, Name[] mixinNodeTypeNames) {
+ super(nodeId, mixinNodeTypeNames);
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_THIS CANCEL_THIS} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has an node higher up
+ * the hierarchy or this node as target. Or if <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetMixins SetMixins} which has this node
+ * as target and has the same <code>mixinNodeTypeNames</code>.</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE} otherwise.</li>
+ * </ul>
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ if (other instanceof Remove) {
+ Path thisPath = ConsolidatingChangeLog.getPath(nodeId);
+ Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+ return thisPath.isDescendantOf(otherPath) || thisPath.equals(otherPath)
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ if (other instanceof SetMixins) {
+ SetMixins setMixin = (SetMixins) other;
+ if (mixinNodeTypeNames.length == setMixin.mixinNodeTypeNames.length) {
+ Path thisPath = ConsolidatingChangeLog.getPath(nodeId);
+ Path otherPath = ConsolidatingChangeLog.getPath(setMixin.nodeId);
+ if (thisPath.equals(otherPath)) {
+ for (int k = 0; k < mixinNodeTypeNames.length; k++) {
+ if (!mixinNodeTypeNames[k].equals(setMixin.mixinNodeTypeNames[k])) {
+ return CANCEL_NONE;
+ }
+ }
+ return CANCEL_THIS;
+ }
+ }
+ }
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link SetMixins SetMixins} operation.
+ *
+ * @see Batch#setMixins(NodeId, Name[])
+ * @param nodeId
+ * @param mixinNodeTypeNames
+ * @return
+ */
+ public static CancelableOperation setMixins(NodeId nodeId, Name[] mixinNodeTypeNames) {
+ return new SetMixins(nodeId, mixinNodeTypeNames);
+ }
+
+ // -----------------------------------------------------< SetValue >---
+
+ /**
+ * A <code>SetValue</code> operation might cancel with
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetValue SetValue} operations.
+ */
+ public static class SetValue extends Operations.SetValue implements CancelableOperation {
+ public SetValue(PropertyId propertyId, QValue value) {
+ super(propertyId, value);
+ }
+
+ public SetValue(PropertyId propertyId, QValue[] values) {
+ super(propertyId, values);
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_THIS CANCEL_THIS} if
+ * <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.Remove Remove} and has an node higher up
+ * the hierarchy or this node as target. Or if <code>other</code> is an instance of
+ * {@link ConsolidatingChangeLog.CancelableOperations.SetValue SetValue} which has this
+ * property as target</li>
+ * <li>{@link ConsolidatingChangeLog.CancelableOperation#CANCEL_NONE CANCEL_NONE} otherwise.</li>
+ * </ul>
+ */
+ public int cancel(CancelableOperation other) throws RepositoryException {
+ if (other instanceof Remove) {
+ Path thisPath = ConsolidatingChangeLog.getPath(propertyId);
+ Path otherPath = ConsolidatingChangeLog.getPath(((Remove) other).itemId);
+ return thisPath.isDescendantOf(otherPath) || thisPath.equals(otherPath)
+ ? CANCEL_THIS
+ : CANCEL_NONE;
+ }
+ if (other instanceof SetValue) {
+ Path thisPath = ConsolidatingChangeLog.getPath(propertyId);
+ Path otherPath = ConsolidatingChangeLog.getPath(((SetValue) other).propertyId);
+ if (thisPath.equals(otherPath)) {
+ return CANCEL_THIS;
+ }
+ }
+ return CANCEL_NONE;
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link SetValue SetValue} operation.
+ *
+ * @see Batch#setValue(PropertyId, QValue)
+ * @param propertyId
+ * @param value
+ * @return
+ */
+ public static CancelableOperation setValue(PropertyId propertyId, QValue value) {
+ return new SetValue(propertyId, value);
+ }
+
+ /**
+ * Factory method for creating a {@link SetValue SetValue} operation.
+ *
+ * @see Batch#setValue(PropertyId, QValue[])
+ * @param propertyId
+ * @param values
+ * @return
+ */
+ public static CancelableOperation setValue(PropertyId propertyId, QValue[] values) {
+ return new SetValue(propertyId, values);
+ }
+
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatingChangeLog.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,34 @@
+/*
+ * 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.batch;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Batch;
+
+/**
+ * An <code>Operation</code> represents a method call on a {@link Batch}.
+ */
+public interface Operation {
+
+ /**
+ * Apply this operation to the given {@link Batch}
+ * @param batch
+ * @throws RepositoryException
+ */
+ public void apply(Batch batch) throws RepositoryException;
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operation.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,698 @@
+/*
+ * 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.batch;
+
+import java.util.Arrays;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Batch;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.QValue;
+
+/**
+ * Factory for creating {@link Operation}s. The inner classes of this class
+ * all implement the <code>Operation</code> interface. They are representatives
+ * for the method calls on a {@link Batch}. In addition {@link Empty} represents
+ * the empty operation which does nothing.
+ */
+public final class Operations {
+ private Operations() {
+ super();
+ }
+
+ // -----------------------------------------------------< Empty >---
+
+ /**
+ * Representative of the empty {@link Operation} which does nothing when
+ * applied to a {@link Batch}.
+ */
+ public static class Empty implements Operation {
+ private static final Empty INSTANCE = new Empty();
+
+ protected Empty() {
+ super();
+ }
+
+ /**
+ * This method has no effect.
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException { /* nothing to do */ }
+
+ public String toString() {
+ return "Empty[]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ return other instanceof Empty;
+ }
+
+ public int hashCode() {
+ return Empty.class.hashCode();
+ }
+ }
+
+ /**
+ * Factory method for creating an {@link Empty} operation.
+ * @return
+ */
+ public static Operation empty() {
+ return Empty.INSTANCE;
+ }
+
+ // -----------------------------------------------------< AddNode >---
+
+ /**
+ * Representative of an add-node {@link Operation} which calls
+ * {@link Batch#addNode(NodeId, Name, Name, String)} when applied to a {@link Batch}.
+ */
+ public static class AddNode implements Operation {
+ protected final NodeId parentId;
+ protected final Name nodeName;
+ protected final Name nodetypeName;
+ protected final String uuid;
+
+ /**
+ * Create a new add-node {@link Operation} for the given arguments.
+ * @see Batch#addNode(NodeId, Name, Name, String)
+ *
+ * @param parentId
+ * @param nodeName
+ * @param nodetypeName
+ * @param uuid
+ */
+ public AddNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) {
+ super();
+ this.parentId = parentId;
+ this.nodeName = nodeName;
+ this.nodetypeName = nodetypeName;
+ this.uuid = uuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ batch.addNode(parentId, nodeName, nodetypeName, uuid);
+ }
+
+ public String toString() {
+ return "AddNode[" + parentId + ", " + nodeName + ", " + nodetypeName + ", " + uuid + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof AddNode) {
+ return equals((AddNode) other);
+ }
+ return false;
+ }
+
+ public boolean equals(AddNode other) {
+ return Operations.equals(parentId, other.parentId)
+ && Operations.equals(nodeName, other.nodeName)
+ && Operations.equals(nodetypeName, other.nodetypeName)
+ && Operations.equals(uuid, other.uuid);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 * (
+ 41 * (
+ 41 + Operations.hashCode(parentId))
+ + Operations.hashCode(nodeName))
+ + Operations.hashCode(nodetypeName))
+ + Operations.hashCode(uuid);
+ }
+
+ }
+
+ /**
+ * Factory method for creating an {@link AddNode} operation.
+ * @see Batch#addNode(NodeId, Name, Name, String)
+ *
+ * @param parentId
+ * @param nodeName
+ * @param nodetypeName
+ * @param uuid
+ * @return
+ */
+ public static Operation addNode(NodeId parentId, Name nodeName, Name nodetypeName, String uuid) {
+ return new AddNode(parentId, nodeName, nodetypeName, uuid);
+ }
+
+ // -----------------------------------------------------< AddProperty >---
+
+ /**
+ * Representative of an add-property {@link Operation} which calls
+ * {@link Batch#addProperty(NodeId, Name, QValue)} or {@link Batch#addProperty(NodeId, Name, QValue[])}
+ * depending on whether the property is multi valued or not when applied to a {@link Batch}.
+ */
+ public static class AddProperty implements Operation {
+ protected final NodeId parentId;
+ protected final Name propertyName;
+ protected final QValue[] values;
+ protected final boolean isMultivalued;
+
+ private AddProperty(NodeId parentId, Name propertyName, QValue[] values, boolean isMultivalued) {
+ super();
+ this.parentId = parentId;
+ this.propertyName = propertyName;
+ this.values = values;
+ this.isMultivalued = isMultivalued;
+ }
+
+ /**
+ * Create a new add-property {@link Operation} for the given arguments.
+ * @see Batch#addProperty(NodeId, Name, QValue)
+ *
+ * @param parentId
+ * @param propertyName
+ * @param value
+ */
+ public AddProperty(NodeId parentId, Name propertyName, QValue value) {
+ this(parentId, propertyName, new QValue[] { value }, false);
+ }
+
+ /**
+ * Create a new add-property {@link Operation} for the given arguments.
+ * @see Batch#addProperty(NodeId, Name, QValue[])
+ *
+ * @param parentId
+ * @param propertyName
+ * @param values
+ */
+ public AddProperty(NodeId parentId, Name propertyName, QValue[] values) {
+ this(parentId, propertyName, values, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ if (isMultivalued) {
+ batch.addProperty(parentId, propertyName, values);
+ }
+ else {
+ batch.addProperty(parentId, propertyName, values[0]);
+ }
+ }
+
+ public String toString() {
+ return "AddProperty[" + parentId + ", " + propertyName + ", " + values + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof AddProperty) {
+ return equals((AddProperty) other);
+ }
+ return false;
+ }
+
+ public boolean equals(AddProperty other) {
+ return Operations.equals(parentId, other.parentId)
+ && Operations.equals(propertyName, other.propertyName)
+ && isMultivalued == other.isMultivalued
+ && Arrays.equals(values, other.values);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 * (
+ 41 + Operations.hashCode(parentId))
+ + Operations.hashCode(propertyName))
+ + Operations.hashCode(values);
+ }
+ }
+
+ /**
+ * Factory method for creating an {@link AddProperty} operation.
+ *
+ * @see Batch#addProperty(NodeId, Name, QValue)
+ * @param parentId
+ * @param propertyName
+ * @param value
+ * @return
+ */
+ public static Operation addProperty(NodeId parentId, Name propertyName, QValue value) {
+ return new AddProperty(parentId, propertyName, value);
+ }
+
+ /**
+ * Factory method for creating an {@link AddProperty} operation.
+ *
+ * @see Batch#addProperty(NodeId, Name, QValue[])
+ * @param parentId
+ * @param propertyName
+ * @param values
+ * @return
+ */
+ public static Operation addProperty(NodeId parentId, Name propertyName, QValue[] values) {
+ return new AddProperty(parentId, propertyName, values);
+ }
+
+ // -----------------------------------------------------< Move >---
+
+ /**
+ * Representative of a move {@link Operation} which calls
+ * {@link Batch#move(NodeId, NodeId, Name)} when applied to a {@link Batch}.
+ */
+ public static class Move implements Operation {
+ protected final NodeId srcNodeId;
+ protected final NodeId destParentNodeId;
+ protected final Name destName;
+
+ /**
+ * Create a new move {@link Operation} for the given arguments.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param srcNodeId
+ * @param destParentNodeId
+ * @param destName
+ */
+ public Move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) {
+ super();
+ this.srcNodeId = srcNodeId;
+ this.destParentNodeId = destParentNodeId;
+ this.destName = destName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ batch.move(srcNodeId, destParentNodeId, destName);
+ }
+
+ public String toString() {
+ return "Move[" + srcNodeId + ", " + destParentNodeId + ", " + destName + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Move) {
+ return equals((Move) other);
+ }
+ return false;
+ }
+
+ public boolean equals(Move other) {
+ return Operations.equals(srcNodeId, other.srcNodeId)
+ && Operations.equals(destParentNodeId, other.destParentNodeId)
+ && Operations.equals(destName, other.destName);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 * (
+ 41 + Operations.hashCode(srcNodeId))
+ + Operations.hashCode(destParentNodeId))
+ + Operations.hashCode(destName);
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link Move} operation.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param srcNodeId
+ * @param destParentNodeId
+ * @param destName
+ * @return
+ */
+ public static Operation move(NodeId srcNodeId, NodeId destParentNodeId, Name destName) {
+ return new Move(srcNodeId, destParentNodeId, destName);
+ }
+
+ // -----------------------------------------------------< Remove >---
+
+ /**
+ * Representative of a remove {@link Operation} which calls {@link Batch#remove(ItemId)} when
+ * applied to a {@link Batch}.
+ */
+ public static class Remove implements Operation {
+ protected final ItemId itemId;
+
+ /**
+ * Create a new remove {@link Operation} for the given arguments.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param itemId
+ */
+ public Remove(ItemId itemId) {
+ super();
+ this.itemId = itemId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ batch.remove(itemId);
+ }
+
+ public String toString() {
+ return "Remove[" + itemId + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Remove) {
+ return equals((Remove) other);
+ }
+ return false;
+ }
+
+ public boolean equals(Remove other) {
+ return Operations.equals(itemId, other.itemId);
+ }
+
+ public int hashCode() {
+ return 41 + Operations.hashCode(itemId);
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link Remove} operation.
+ *
+ * @see Batch#move(NodeId, NodeId, Name)
+ * @param itemId
+ * @return
+ */
+ public static Operation remove(ItemId itemId) {
+ return new Remove(itemId);
+ }
+
+ // -----------------------------------------------------< ReorderNodes >---
+
+ /**
+ * Representative of a reorder-nodes {@link Operation} which calls
+ * {@link Batch#reorderNodes(NodeId, NodeId, NodeId)} when applied to a {@link Batch}.
+ */
+ public static class ReorderNodes implements Operation {
+ protected final NodeId parentId;
+ protected final NodeId srcNodeId;
+ protected final NodeId beforeNodeId;
+
+ /**
+ * Create a new reorder-nodes {@link Operation} for the given arguments.
+ *
+ * @see Batch#reorderNodes(NodeId, NodeId, NodeId)
+ * @param parentId
+ * @param srcNodeId
+ * @param beforeNodeId
+ */
+ public ReorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) {
+ super();
+ this.parentId = parentId;
+ this.srcNodeId = srcNodeId;
+ this.beforeNodeId = beforeNodeId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ batch.reorderNodes(parentId, srcNodeId, beforeNodeId);
+ }
+
+ public String toString() {
+ return "ReorderNodes[" + parentId + ", " + srcNodeId + ", " + beforeNodeId + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof ReorderNodes) {
+ return equals((ReorderNodes) other);
+ }
+ return false;
+ }
+
+ public boolean equals(ReorderNodes other) {
+ return Operations.equals(parentId, other.parentId)
+ && Operations.equals(srcNodeId, other.srcNodeId)
+ && Operations.equals(beforeNodeId, other.beforeNodeId);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 * (
+ 41 + Operations.hashCode(parentId))
+ + Operations.hashCode(srcNodeId))
+ + Operations.hashCode(beforeNodeId);
+ }
+ }
+
+ /**
+ * Factory method for creating a reorder-nodes {@link Operation} for the given arguments.
+ *
+ * @see Batch#reorderNodes(NodeId, NodeId, NodeId)
+ * @param parentId
+ * @param srcNodeId
+ * @param beforeNodeId
+ * @return
+ */
+ public static Operation reorderNodes(NodeId parentId, NodeId srcNodeId, NodeId beforeNodeId) {
+ return new ReorderNodes(parentId, srcNodeId, beforeNodeId);
+ }
+
+ // -----------------------------------------------------< SetMixins >---
+
+ /**
+ * Representative of a set-mixin {@link Operation} which calls
+ * {@link Batch#setMixins(NodeId, Name[])} when applied to a {@link Batch}.
+ */
+ public static class SetMixins implements Operation {
+ protected final NodeId nodeId;
+ protected final Name[] mixinNodeTypeNames;
+
+ /**
+ * Create a new set-mixin {@link Operation} for the given arguments.
+ *
+ * @see Batch#setMixins(NodeId, Name[])
+ * @param nodeId
+ * @param mixinNodeTypeNames
+ */
+ public SetMixins(NodeId nodeId, Name[] mixinNodeTypeNames) {
+ super();
+ this.nodeId = nodeId;
+ this.mixinNodeTypeNames = mixinNodeTypeNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ batch.setMixins(nodeId, mixinNodeTypeNames);
+ }
+
+ public String toString() {
+ return "SetMixins[" + nodeId + ", " + mixinNodeTypeNames + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof SetMixins) {
+ return equals((SetMixins) other);
+ }
+ return false;
+ }
+
+ public boolean equals(SetMixins other) {
+ return Operations.equals(nodeId, other.nodeId)
+ && Arrays.equals(mixinNodeTypeNames, other.mixinNodeTypeNames);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 + Operations.hashCode(nodeId))
+ + Operations.hashCode(mixinNodeTypeNames);
+ }
+ }
+
+ /**
+ * Factory method for creating a set-mixin {@link Operation} for the given arguments.
+ *
+ * @see Batch#setMixins(NodeId, Name[])
+ * @param nodeId
+ * @param mixinNodeTypeNames
+ * @return
+ */
+ public static Operation setMixins(NodeId nodeId, Name[] mixinNodeTypeNames) {
+ return new SetMixins(nodeId, mixinNodeTypeNames);
+ }
+
+ // -----------------------------------------------------< SetValue >---
+
+ /**
+ * Representative of a set-value {@link Operation} which calls
+ * {@link Batch#setValue(PropertyId, QValue)} or {@link Batch#setValue(PropertyId, QValue[])}
+ * depending on whether the property is multi valued or not when applied to a {@link Batch}.
+ */
+ public static class SetValue implements Operation {
+ protected final PropertyId propertyId;
+ protected final QValue[] values;
+ protected final boolean isMultivalued;
+
+ private SetValue(PropertyId propertyId, QValue[] values, boolean isMultivalued) {
+ super();
+ this.propertyId = propertyId;
+ this.values = values;
+ this.isMultivalued = isMultivalued;
+ }
+
+ /**
+ * Create a new set-value {@link Operation} for the given arguments.
+ *
+ * @see Batch#setValue(PropertyId, QValue)
+ * @param propertyId
+ * @param value
+ */
+ public SetValue(PropertyId propertyId, QValue value) {
+ this(propertyId, new QValue[]{ value }, false);
+ }
+
+ /**
+ * Create a new set-value {@link Operation} for the given arguments.
+ *
+ * @see Batch#setValue(PropertyId, QValue[])
+ * @param propertyId
+ * @param values
+ */
+ public SetValue(PropertyId propertyId, QValue[] values) {
+ this(propertyId, values, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void apply(Batch batch) throws RepositoryException {
+ if (isMultivalued) {
+ batch.setValue(propertyId, values);
+ }
+ else {
+ batch.setValue(propertyId, values[0]);
+ }
+ }
+
+ public String toString() {
+ return "SetValue[" + propertyId + ", " + values + "]";
+ }
+
+ public boolean equals(Object other) {
+ if (null == other) {
+ return false;
+ }
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof SetValue) {
+ return equals((SetValue) other);
+ }
+ return false;
+ }
+
+ public boolean equals(SetValue other) {
+ return Operations.equals(propertyId, other.propertyId)
+ && isMultivalued == other.isMultivalued
+ && Arrays.equals(values, other.values);
+ }
+
+ public int hashCode() {
+ return 41 * (
+ 41 + Operations.hashCode(propertyId))
+ + Operations.hashCode(values);
+ }
+ }
+
+ /**
+ * Factory method for creating set-value {@link Operation} for the given arguments.
+ *
+ * @see Batch#setValue(PropertyId, QValue)
+ * @param propertyId
+ * @param value
+ * @return
+ */
+ public static Operation setValue(PropertyId propertyId, QValue value) {
+ return new SetValue(propertyId, value);
+ }
+
+ /**
+ * Factory method for creating a set-value {@link Operation} for the given arguments.
+ *
+ * @see Batch#setValue(PropertyId, QValue[])
+ * @param propertyId
+ * @param values
+ * @return
+ */
+ public static Operation setValue(final PropertyId propertyId, final QValue[] values) {
+ return new SetValue(propertyId, values);
+ }
+
+ // -----------------------------------------------------< private >---
+
+ protected static boolean equals(Object o1, Object o2) {
+ return o1 == null
+ ? o2 == null
+ : o1.equals(o2);
+ }
+
+ protected static int hashCode(Object o) {
+ return o == null
+ ? 0
+ : o.hashCode();
+ }
+
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/batch/Operations.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java?rev=747347&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java Tue Feb 24 11:26:41 2009
@@ -0,0 +1,447 @@
+/*
+ * 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.batch;
+
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import junit.framework.TestCase;
+
+import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.spi.Path.Element;
+import org.apache.jackrabbit.spi.commons.identifier.IdFactoryImpl;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl;
+
+public class ConsolidatedBatchTest extends TestCase {
+ private final IdFactory idFactory = IdFactoryImpl.getInstance();
+ private final PathFactory pathFactory = PathFactoryImpl.getInstance();
+ private final NameFactory nameFactory = NameFactoryImpl.getInstance();
+ private final QValueFactory valueFactory = QValueFactoryImpl.getInstance();
+
+ private final ChangeLog[][] changeLogs;
+
+ public ConsolidatedBatchTest() throws RepositoryException {
+ changeLogs = new TestChangeLog[][] {
+ { new TestChangeLog() // 1
+ .addNode("/my/path/MyNode")
+ .addNode("/my/path/MyNode2")
+ ,new TestChangeLog()
+ .addNode("/my/path/MyNode")
+ .addNode("/my/path/MyNode2")
+ }
+ ,
+ { new TestChangeLog() // 2
+ .addNode("/my/path/MyNode")
+ .delItem("/my/path/MyNode")
+ ,new TestChangeLog()
+ }
+ ,
+ { new TestChangeLog() // 3
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path/MyNode")
+ .addNode("/my/path/MyNode2")
+ .addNode("/my/path2")
+ .addNode("/my/path2/MyNode")
+ .delItem("/my")
+ ,new TestChangeLog()
+ }
+ ,
+ { new TestChangeLog() // 4
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .delItem("/my/path2")
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ }
+ ,
+ { new TestChangeLog() // 5
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .delItem("/my/path")
+ .delItem("/my/path2")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .delItem("/my/path")
+ .delItem("/my/path2")
+ }
+ ,
+ { new TestChangeLog() // 6
+ .addNode("/my")
+ .addNode("/my/path")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ }
+ ,
+ { new TestChangeLog() // 7
+ .addNode("/my")
+ .addNode("/my/path")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .delItem("/my/path/Prop")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addNode("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 8
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .movItem("/my/path", "my/path2")
+ .delItem("/my")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .movItem("/my/path", "my/path2")
+ .delItem("/my")
+ }
+ ,
+ { new TestChangeLog() // 9
+ .addNode("/my")
+ .addNode("/my/path")
+ .addNode("/my/path2")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .delItem("/my")
+ .movItem("/my/path", "my/path2")
+ ,new TestChangeLog()
+ .movItem("/my/path", "my/path2")
+ }
+ ,
+ { new TestChangeLog() // 10
+ .ordNode("/my/path")
+ .ordNode("/my/path")
+ .ordNode("/my/path")
+ ,new TestChangeLog()
+ .ordNode("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 11
+ .ordNode("/my/path")
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .delItem("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 12
+ .addNode("/my")
+ .ordNode("/my/path")
+ .delItem("/my")
+ ,new TestChangeLog()
+ }
+ ,
+ { new TestChangeLog() // 13
+ .addNode("/my")
+ .mixNode("/my", "MyMixin")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .mixNode("/my", "MyMixin")
+ }
+ ,
+ { new TestChangeLog() // 14
+ .addNode("/my")
+ .mixNode("/my", "MyMixin")
+ .mixNode("/my", "MyMixin")
+ .mixNode("/my", "MyMixin")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .mixNode("/my", "MyMixin")
+ }
+ ,
+ { new TestChangeLog() // 15
+ .addNode("/my")
+ .mixNode("/my", "MyMixin")
+ .mixNode("/my", "MyMixin")
+ .mixNode("/my", "MyMixin")
+ .delItem("/my")
+ ,new TestChangeLog()
+ }
+ ,
+ { new TestChangeLog() // 16
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello2", PropertyType.STRING)
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello2", PropertyType.STRING)
+ }
+ ,
+ { new TestChangeLog() // 17
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello", PropertyType.STRING)
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ }
+ ,
+ { new TestChangeLog() // 18
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello2", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello3", PropertyType.STRING)
+ ,new TestChangeLog()
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello3", PropertyType.STRING)
+ }
+ ,
+ { new TestChangeLog() // 19
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello2", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello3", PropertyType.STRING)
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .delItem("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 20
+ .addNode("/my")
+ .addProp("/my/path/Prop", "hello", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello2", PropertyType.STRING)
+ .setValu("/my/path/Prop", "hello3", PropertyType.STRING)
+ .delItem("/my")
+ ,new TestChangeLog()
+ }
+ ,
+ { new TestChangeLog() // 21
+ .addNode("/my")
+ .ordNode("/my")
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .ordNode("/my")
+ .delItem("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 22
+ .addNode("/my")
+ .ordNode("/my")
+ .ordNode("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .ordNode("/my")
+ .ordNode("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 23
+ .addNode("/my")
+ .ordNode("/my")
+ .addNode("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .ordNode("/my")
+ .addNode("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 24
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .delItem("/my/path")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .delItem("/my/path")
+ }
+ ,
+ { new TestChangeLog() // 25
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .mixNode("/my", "mix2")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .mixNode("/my", "mix2")
+ }
+ ,
+ { new TestChangeLog() // 26
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .addNode("/my2")
+ ,new TestChangeLog()
+ .addNode("/my")
+ .mixNode("/my", "mix")
+ .addNode("/my2")
+ }
+ ,
+ { new TestChangeLog() // 27
+ .setValu("/my/Prop", "value", PropertyType.STRING)
+ .delItem("/my2")
+ ,new TestChangeLog()
+ .setValu("/my/Prop", "value", PropertyType.STRING)
+ .delItem("/my2")
+ }
+ ,
+ { new TestChangeLog() // 28
+ .setValu("/my/Prop", "value", PropertyType.STRING)
+ .addNode("/my2")
+ ,new TestChangeLog()
+ .setValu("/my/Prop", "value", PropertyType.STRING)
+ .addNode("/my2")
+ }
+ ,
+ { new TestChangeLog() // 29
+ .addProp("/my/Prop", "value", PropertyType.STRING)
+ .delItem("/my2")
+ ,new TestChangeLog()
+ .addProp("/my/Prop", "value", PropertyType.STRING)
+ .delItem("/my2")
+ }
+ ,
+ { new TestChangeLog() // 30
+ .addProp("/my/Prop", "value", PropertyType.STRING)
+ .setValu("/my/Prop", null, PropertyType.STRING)
+ ,new TestChangeLog()
+ }
+ ,
+ };
+ }
+
+ public void testChangeLogConsolidation() throws RepositoryException {
+ for (int k = 0; k < changeLogs.length; k++) {
+ ChangeLog changeLog = changeLogs[k][0];
+ ChangeLog expected = changeLogs[k][1];
+ assertEquals("Test no " + (k + 1), expected, changeLog.apply(new ConsolidatingChangeLog()));
+ }
+ }
+
+ // -----------------------------------------------------< private >---
+
+ private String nsPrefix(String name) {
+ return name.startsWith("{")
+ ? name
+ : "{}" + name;
+ }
+
+ private Name createName(String name) {
+ return nameFactory.create(nsPrefix(name));
+ }
+
+ private Path createPath(String path) {
+ String[] names = path.split("/");
+ Element[] elements = new Element[names.length];
+ for (int k = 0; k < names.length; k++) {
+ if ("".equals(names[k])) {
+ elements[k] = pathFactory.getRootElement();
+ }
+ else {
+ elements[k] = pathFactory.createElement(nameFactory.create(nsPrefix(names[k])));
+ }
+ }
+ if (elements.length == 0) {
+ return pathFactory.getRootPath();
+ }
+ return pathFactory.create(elements);
+ }
+
+ private NodeId createNodeId(Path path) {
+ return idFactory.createNodeId((String) null, path);
+ }
+
+ public NodeId createNodeId(String nodeId) {
+ return createNodeId(createPath(nodeId));
+ }
+
+ public PropertyId createPropertyId(String propertyId) throws PathNotFoundException {
+ Path path = createPath(propertyId);
+ return idFactory.createPropertyId(createNodeId(path.getAncestor(1)), path.getNameElement().getName());
+ }
+
+ private QValue createValue(String value, int type) throws RepositoryException {
+ return value == null
+ ? null
+ : valueFactory.create(value, type);
+ }
+
+ // -----------------------------------------------------< ChangeLog >---
+
+ private class TestChangeLog extends ChangeLogImpl {
+
+ public TestChangeLog addNode(String nodeId) throws RepositoryException {
+ Path path = createPath(nodeId);
+ addNode(createNodeId(path.getAncestor(1)), path.getNameElement().getName(),
+ createName("anyType"), null);
+
+ return this;
+ }
+
+ public TestChangeLog addProp(String propertyId, String value, int type) throws RepositoryException {
+ Path path = createPath(propertyId);
+ addProperty(createNodeId(path.getAncestor(1)), path.getNameElement().getName(), createValue(value, type));
+ return this;
+ }
+
+ public TestChangeLog movItem(String srcNodeId, String destNodeId) throws RepositoryException {
+ Path path = createPath(destNodeId);
+ move(createNodeId(srcNodeId), createNodeId(path.getAncestor(1)), path.getNameElement().getName());
+ return this;
+ }
+
+ public TestChangeLog delItem(String nodeId) throws RepositoryException {
+ remove(createNodeId(nodeId));
+ return this;
+ }
+
+ public ChangeLog removeProperty(String propertyId) throws RepositoryException {
+ remove(createPropertyId(propertyId));
+ return this;
+ }
+
+ public TestChangeLog ordNode(String nodeId) throws RepositoryException {
+ NodeId srcNodeId = createNodeId(nodeId);
+ NodeId parentId = createNodeId(srcNodeId.getPath());
+ reorderNodes(parentId, srcNodeId, createNodeId("/any/path"));
+ return this;
+ }
+
+ public TestChangeLog mixNode(String nodeId, String mixinName) throws RepositoryException {
+ setMixins(createNodeId(nodeId), new Name[] { createName(mixinName) });
+ return this;
+ }
+
+ public TestChangeLog setValu(String propertyId, String value, int type) throws RepositoryException {
+ setValue(createPropertyId(propertyId), createValue(value, type));
+ return this;
+ }
+
+ }
+
+}
+
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/batch/ConsolidatedBatchTest.java
------------------------------------------------------------------------------
svn:eol-style = native