Index: extensions/modeshape-connector-jdbc-metadata/pom.xml
===================================================================
--- extensions/modeshape-connector-jdbc-metadata/pom.xml (revision 2574)
+++ extensions/modeshape-connector-jdbc-metadata/pom.xml (working copy)
@@ -37,10 +37,6 @@
test-jar
test
-
- org.modeshape
- modeshape-graph
-
org.modeshape
modeshape-graph
Index: extensions/modeshape-sequencer-cnd/src/main/java/org/modeshape/sequencer/cnd/CndSequencer.java
===================================================================
--- extensions/modeshape-sequencer-cnd/src/main/java/org/modeshape/sequencer/cnd/CndSequencer.java (revision 2574)
+++ extensions/modeshape-sequencer-cnd/src/main/java/org/modeshape/sequencer/cnd/CndSequencer.java (working copy)
@@ -26,7 +26,6 @@ package org.modeshape.sequencer.cnd;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.modeshape.cnd.CndImporter;
@@ -114,10 +113,10 @@ public class CndSequencer implements StreamSequencer {
/**
* {@inheritDoc}
*
- * @see org.modeshape.graph.io.Destination#create(org.modeshape.graph.property.Path, java.util.List)
+ * @see org.modeshape.graph.io.Destination#create(Path, Iterable)
*/
public void create( Path path,
- List properties ) {
+ Iterable properties ) {
path = checkPath(path);
for (Property property : properties) {
output.setProperty(path, property.getName(), property.getValuesAsArray());
Index: modeshape-graph/src/main/java/org/modeshape/graph/io/Destination.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/io/Destination.java (revision 2574)
+++ modeshape-graph/src/main/java/org/modeshape/graph/io/Destination.java (working copy)
@@ -23,7 +23,6 @@
*/
package org.modeshape.graph.io;
-import java.util.List;
import net.jcip.annotations.NotThreadSafe;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.property.Path;
@@ -50,7 +49,7 @@ public interface Destination {
* @param properties the properties for the node; never null, but may be empty if there are no properties
*/
public void create( Path path,
- List properties );
+ Iterable properties );
/**
* Create a node at the supplied path and with the supplied attributes. The path will be absolute.
Index: modeshape-graph/src/main/java/org/modeshape/graph/io/GraphBatchDestination.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/io/GraphBatchDestination.java (revision 2574)
+++ modeshape-graph/src/main/java/org/modeshape/graph/io/GraphBatchDestination.java (working copy)
@@ -23,7 +23,6 @@
*/
package org.modeshape.graph.io;
-import java.util.List;
import net.jcip.annotations.NotThreadSafe;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.Graph;
@@ -89,17 +88,12 @@ public class GraphBatchDestination implements Destination {
/**
* {@inheritDoc}
*
- * @see org.modeshape.graph.io.Destination#create(org.modeshape.graph.property.Path, java.util.List)
+ * @see org.modeshape.graph.io.Destination#create(org.modeshape.graph.property.Path, Iterable)
*/
public void create( Path path,
- List properties ) {
+ Iterable properties ) {
assert properties != null;
- Create create = null;
- if (properties.isEmpty()) {
- create = batch.create(path);
- } else {
- create = batch.create(path, properties);
- }
+ Create create = batch.create(path, properties);
assert create != null;
NodeConflictBehavior behavior = createBehaviorFor(path);
if (behavior != null) {
Index: modeshape-graph/src/main/java/org/modeshape/graph/property/PropertyFactory.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/property/PropertyFactory.java (revision 2574)
+++ modeshape-graph/src/main/java/org/modeshape/graph/property/PropertyFactory.java (working copy)
@@ -97,4 +97,14 @@ public interface PropertyFactory {
PropertyType desiredType,
Iterator> values );
+ /**
+ * Create a property with the supplied name and {@link Path} value. This method is provided because Path implements
+ * Iterable<Segment>.
+ *
+ * @param name the property name; may not be null
+ * @param value the path value
+ * @return the resulting property
+ */
+ Property create( Name name,
+ Path value );
}
Index: modeshape-graph/src/main/java/org/modeshape/graph/property/basic/BasicPropertyFactory.java
===================================================================
--- modeshape-graph/src/main/java/org/modeshape/graph/property/basic/BasicPropertyFactory.java (revision 2574)
+++ modeshape-graph/src/main/java/org/modeshape/graph/property/basic/BasicPropertyFactory.java (working copy)
@@ -30,6 +30,7 @@ import java.util.List;
import net.jcip.annotations.Immutable;
import org.modeshape.common.util.CheckArg;
import org.modeshape.graph.property.Name;
+import org.modeshape.graph.property.Path;
import org.modeshape.graph.property.Property;
import org.modeshape.graph.property.PropertyFactory;
import org.modeshape.graph.property.PropertyType;
@@ -55,6 +56,17 @@ public class BasicPropertyFactory implements PropertyFactory {
/**
* {@inheritDoc}
+ *
+ * @see org.modeshape.graph.property.PropertyFactory#create(org.modeshape.graph.property.Name,
+ * org.modeshape.graph.property.Path)
+ */
+ public Property create( Name name,
+ Path value ) {
+ return new BasicSingleValueProperty(name, value);
+ }
+
+ /**
+ * {@inheritDoc}
*/
public Property create( Name name,
Iterable> values ) {
@@ -93,6 +105,10 @@ public class BasicPropertyFactory implements PropertyFactory {
if (values.length == 1) {
Object value = values[0];
// Check whether the sole value was a collection ...
+ if (value instanceof Path) {
+ value = factory.create(value);
+ return new BasicSingleValueProperty(name, value);
+ }
if (value instanceof Collection>) {
// The single value is a collection, so create property with the collection's contents ...
return create(name, desiredType, (Iterable>)value);
Index: modeshape-graph/src/test/java/org/modeshape/graph/xml/XmlHandlerTest.java
===================================================================
--- modeshape-graph/src/test/java/org/modeshape/graph/xml/XmlHandlerTest.java (revision 2574)
+++ modeshape-graph/src/test/java/org/modeshape/graph/xml/XmlHandlerTest.java (working copy)
@@ -33,7 +33,6 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
@@ -526,7 +525,7 @@ public class XmlHandlerTest {
private final String workspace = "Recording Workspace";
public void create( Path path,
- List properties ) {
+ Iterable properties ) {
assert path != null;
Path parent = path.getParent();
Name child = path.getLastSegment().getName();
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jdbc/JcrDriverIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jdbc/JcrDriverIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jdbc/JcrDriverIntegrationTest.java (working copy)
@@ -530,6 +530,7 @@ public class JcrDriverIntegrationTest extends AbstractMultiUseModeShapeTest {
"Repo NULL mmcore:model VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"Repo NULL mmcore:tags VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"Repo NULL mode:defined VIEW Is Mixin: true NULL NULL NULL null DERIVED",
+ "Repo NULL mode:derived VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"Repo NULL mode:hashed VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"Repo NULL mode:lock VIEW Is Mixin: false NULL NULL NULL null DERIVED",
"Repo NULL mode:locks VIEW Is Mixin: false NULL NULL NULL null DERIVED",
@@ -603,7 +604,7 @@ public class JcrDriverIntegrationTest extends AbstractMultiUseModeShapeTest {
while (rs.next()) {
tableNames.add(rs.getString("TABLE_NAME"));
}
- assertThat(tableNames.size(), is(179));
+ assertThat(tableNames.size(), is(180));
List tablesWithProblems = new ArrayList();
for (String table : tableNames) {
try {
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/CndSequencerIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/CndSequencerIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/CndSequencerIntegrationTest.java (working copy)
@@ -78,7 +78,7 @@ public class CndSequencerIntegrationTest extends AbstractSequencerTest {
Node cnd = assertNode(path, "nt:unstructured");
printSubgraph(cnd);
- Node file1 = assertNode(path + "/nt:activity", "nt:nodeType");
+ Node file1 = assertNode(path + "/nt:activity", "nt:nodeType", "mode:derived");
assertThat(file1, is(notNullValue()));
printQuery("SELECT * FROM [nt:nodeType]", 34);
@@ -99,7 +99,7 @@ public class CndSequencerIntegrationTest extends AbstractSequencerTest {
Node cnd = assertNode(path, "nt:unstructured");
printSubgraph(cnd);
- Node file1 = assertNode(path + "/nt:activity", "nt:nodeType");
+ Node file1 = assertNode(path + "/nt:activity", "nt:nodeType", "mode:derived");
assertThat(file1, is(notNullValue()));
printQuery("SELECT * FROM [nt:nodeType]", 34);
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/JavaSequencerIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/JavaSequencerIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/JavaSequencerIntegrationTest.java (working copy)
@@ -83,7 +83,7 @@ public class JavaSequencerIntegrationTest extends AbstractSequencerTest {
Node java = assertNode(path, "nt:unstructured");
printSubgraph(java);
- assertNode(path + "/ClusteringTest", "class:class");
+ assertNode(path + "/ClusteringTest", "class:class", "mode:derived");
assertNode(path + "/ClusteringTest/class:constructors", "class:constructors");
assertNode(path + "/ClusteringTest/class:methods", "class:methods");
assertNode(path + "/ClusteringTest/class:methods/beforeAll()", "class:method");
@@ -132,7 +132,7 @@ public class JavaSequencerIntegrationTest extends AbstractSequencerTest {
Node java = assertNode(path, "nt:unstructured");
printSubgraph(java);
- assertNode(path + "/" + typeName, "class:class");
+ assertNode(path + "/" + typeName, "class:class", "mode:derived");
assertNode(path + "/" + typeName + "/class:constructors", "class:constructors");
assertNode(path + "/" + typeName + "/class:methods", "class:methods");
// etc.
@@ -190,7 +190,7 @@ public class JavaSequencerIntegrationTest extends AbstractSequencerTest {
Node java = assertNode(path, "nt:unstructured");
printSubgraph(java);
- assertNode(path + "/SequencerTest", "class:class");
+ assertNode(path + "/SequencerTest", "class:class", "mode:derived");
assertNode(path + "/SequencerTest/class:constructors", "class:constructors");
assertNode(path + "/SequencerTest/class:methods", "class:methods");
// etc.
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/TeiidSequencerIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/TeiidSequencerIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/TeiidSequencerIntegrationTest.java (working copy)
@@ -98,7 +98,7 @@ public class TeiidSequencerIntegrationTest extends AbstractSequencerTest {
Thread.sleep(200); // wait a bit while the new content is indexed
// Find the sequenced node ...
- Node vdb = assertNode("/sequenced/teiid/vdbs/qe", "vdb:virtualDatabase", "mix:referenceable");
+ Node vdb = assertNode("/sequenced/teiid/vdbs/qe", "vdb:virtualDatabase", "mix:referenceable", "mode:derived");
printSubgraph(vdb);
printQuery("SELECT * FROM [vdb:virtualDatabase]", 1);
printQuery("SELECT * FROM [vdb:model]", 3);
@@ -119,7 +119,7 @@ public class TeiidSequencerIntegrationTest extends AbstractSequencerTest {
Thread.sleep(200); // wait a bit while the new content is indexed
// Find the sequenced node ...
- Node vdb = assertNode("/sequenced/teiid/vdbs/my/favorites/qe", "vdb:virtualDatabase", "mix:referenceable");
+ Node vdb = assertNode("/sequenced/teiid/vdbs/my/favorites/qe", "vdb:virtualDatabase", "mix:referenceable", "mode:derived");
printSubgraph(vdb);
printQuery("SELECT * FROM [vdb:virtualDatabase]", 1);
printQuery("SELECT * FROM [vdb:model]", 3);
@@ -140,7 +140,7 @@ public class TeiidSequencerIntegrationTest extends AbstractSequencerTest {
Thread.sleep(200); // wait a bit while the new content is indexed
// Find the sequenced node ...
- Node vdb = assertNode("/sequenced/teiid/vdbs/PartsFromXml", "vdb:virtualDatabase", "mix:referenceable");
+ Node vdb = assertNode("/sequenced/teiid/vdbs/PartsFromXml", "vdb:virtualDatabase", "mix:referenceable", "mode:derived");
printSubgraph(vdb);
printQuery("SELECT * FROM [vdb:virtualDatabase]", 1);
printQuery("SELECT * FROM [vdb:model]", 2);
@@ -163,7 +163,7 @@ public class TeiidSequencerIntegrationTest extends AbstractSequencerTest {
Thread.sleep(200); // wait a bit while the new content is indexed
// Find the sequenced node ...
- Node vdb = assertNode("/sequenced/teiid/vdbs/YahooUdfTest", "vdb:virtualDatabase", "mix:referenceable");
+ Node vdb = assertNode("/sequenced/teiid/vdbs/YahooUdfTest", "vdb:virtualDatabase", "mix:referenceable", "mode:derived");
printSubgraph(vdb);
printQuery("SELECT * FROM [vdb:virtualDatabase]", 1);
printQuery("SELECT * FROM [vdb:model]", 4);
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/XmlSequencerIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/XmlSequencerIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/XmlSequencerIntegrationTest.java (working copy)
@@ -76,7 +76,7 @@ public class XmlSequencerIntegrationTest extends AbstractSequencerTest {
// Find the sequenced node ...
printSubgraph(assertNode("/sequenced/xml", "nt:unstructured"));
String path = "/sequenced/xml/jcr-import-test.xml";
- Node xml = assertNode(path, "modexml:document");
+ Node xml = assertNode(path, "modexml:document", "mode:derived");
printSubgraph(xml);
// Node file1 = assertNode(path + "/nt:activity", "nt:nodeType");
@@ -101,7 +101,7 @@ public class XmlSequencerIntegrationTest extends AbstractSequencerTest {
// Find the sequenced node ...
String path = "/sequenced/xml/a/b/jcr-import-test.xml";
- Node xml = assertNode(path, "modexml:document");
+ Node xml = assertNode(path, "modexml:document", "mode:derived");
printSubgraph(xml);
// Node file1 = assertNode(path + "/nt:activity", "nt:nodeType");
Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/ZipSequencerIntegrationTest.java
===================================================================
--- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/ZipSequencerIntegrationTest.java (revision 2574)
+++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/sequencer/ZipSequencerIntegrationTest.java (working copy)
@@ -79,7 +79,7 @@ public class ZipSequencerIntegrationTest extends AbstractSequencerTest {
// Find the sequenced node ...
String path = "/sequenced/zip/test-files.zip";
- Node zipped = assertNode(path, "zip:file");
+ Node zipped = assertNode(path, "zip:file", "mode:derived");
Node file1 = assertNode(path + "/MODE-966-fix.patch", "nt:file");
Node data1 = assertNode(path + "/MODE-966-fix.patch/jcr:content", "nt:resource");
Node fold1 = assertNode(path + "/testFolder", "nt:folder");
@@ -116,7 +116,7 @@ public class ZipSequencerIntegrationTest extends AbstractSequencerTest {
// Find the sequenced node ...
String path = "/sequenced/zip/a/b/test-files.zip";
- Node zipped = assertNode(path, "zip:file");
+ Node zipped = assertNode(path, "zip:file", "mode:derived");
Node file1 = assertNode(path + "/MODE-966-fix.patch", "nt:file");
Node data1 = assertNode(path + "/MODE-966-fix.patch/jcr:content", "nt:resource");
Node fold1 = assertNode(path + "/testFolder", "nt:folder");
Index: modeshape-jcr/src/main/resources/org/modeshape/jcr/modeshape_builtins.cnd
===================================================================
--- modeshape-jcr/src/main/resources/org/modeshape/jcr/modeshape_builtins.cnd (revision 2574)
+++ modeshape-jcr/src/main/resources/org/modeshape/jcr/modeshape_builtins.cnd (working copy)
@@ -84,3 +84,6 @@
// A marker node type that can be used to denote areas into which files can be published.
// Published areas have optional titles and descriptions.
[mode:publishArea] > mix:title mixin
+
+[mode:derived] mixin
+- mode:derivedFrom (path)
Index: modeshape-repository/src/main/java/org/modeshape/repository/ModeShapeLexicon.java
===================================================================
--- modeshape-repository/src/main/java/org/modeshape/repository/ModeShapeLexicon.java (revision 2574)
+++ modeshape-repository/src/main/java/org/modeshape/repository/ModeShapeLexicon.java (working copy)
@@ -51,6 +51,8 @@ public class ModeShapeLexicon extends org.modeshape.graph.ModeShapeLexicon {
public static final Name CLUSTERING = new BasicName(Namespace.URI, "clustering");
public static final Name CONFIGURATION = new BasicName(Namespace.URI, "configuration");
public static final Name CLUSTER_NAME = new BasicName(Namespace.URI, "clusterName");
+ public static final Name DERIVED = new BasicName(Namespace.URI, "derived");
+ public static final Name DERIVED_FROM = new BasicName(Namespace.URI, "derivedFrom");
public static final Name INITIAL_CONTENT = new BasicName(Namespace.URI, "initialContent");
public static final Name CONTENT = new BasicName(Namespace.URI, "content");
Index: modeshape-repository/src/main/java/org/modeshape/repository/sequencer/StreamSequencerAdapter.java
===================================================================
--- modeshape-repository/src/main/java/org/modeshape/repository/sequencer/StreamSequencerAdapter.java (revision 2574)
+++ modeshape-repository/src/main/java/org/modeshape/repository/sequencer/StreamSequencerAdapter.java (working copy)
@@ -26,6 +26,7 @@ package org.modeshape.repository.sequencer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -41,6 +42,7 @@ import org.modeshape.graph.JcrLexicon;
import org.modeshape.graph.JcrNtLexicon;
import org.modeshape.graph.Location;
import org.modeshape.graph.Node;
+import org.modeshape.graph.io.Destination;
import org.modeshape.graph.observe.NetChangeObserver.NetChange;
import org.modeshape.graph.property.Binary;
import org.modeshape.graph.property.Name;
@@ -53,6 +55,7 @@ import org.modeshape.graph.property.ValueFactories;
import org.modeshape.graph.property.Path.Segment;
import org.modeshape.graph.sequencer.StreamSequencer;
import org.modeshape.graph.sequencer.StreamSequencerContext;
+import org.modeshape.repository.ModeShapeLexicon;
import org.modeshape.repository.RepositoryI18n;
import org.modeshape.repository.util.RepositoryNodePath;
@@ -61,13 +64,22 @@ import org.modeshape.repository.util.RepositoryNodePath;
*/
public class StreamSequencerAdapter implements Sequencer {
+ public static final boolean DEFAULT_ADD_DEFAULT_MIXIN = true;
+
private static final Logger LOGGER = Logger.getLogger(StreamSequencerAdapter.class);
private SequencerConfig configuration;
private final StreamSequencer streamSequencer;
+ private final boolean addDerivedMixin;
public StreamSequencerAdapter( StreamSequencer streamSequencer ) {
+ this(streamSequencer, DEFAULT_ADD_DEFAULT_MIXIN);
+ }
+
+ public StreamSequencerAdapter( StreamSequencer streamSequencer,
+ boolean addDerivedMixin ) {
this.streamSequencer = streamSequencer;
+ this.addDerivedMixin = addDerivedMixin;
}
/**
@@ -187,6 +199,7 @@ public class StreamSequencerAdapter implements Sequencer {
Set builtPaths = new HashSet();
// Find each output node and save the image metadata there ...
+ final Path inputPath = input.getLocation().getPath();
for (RepositoryNodePath outputPath : outputPaths) {
// Get the name of the repository source, workspace and the path to the output node
final String repositoryWorkspaceName = outputPath.getWorkspaceName();
@@ -199,7 +212,7 @@ public class StreamSequencerAdapter implements Sequencer {
// Node outputNode = context.graph().getNodeAt(nodePath);
// Now save the image metadata to the output node ...
- saveOutput(nodePath, output, context, builtPaths);
+ saveOutput(inputPath, nodePath, output, context, builtPaths);
}
context.getDestination().submit();
@@ -257,19 +270,21 @@ public class StreamSequencerAdapter implements Sequencer {
* Save the sequencing output to the supplied node. This method does not need to save the output, as that is done by the
* caller of this method.
*
- * @param nodePath the existing node onto (or below) which the output is to be written; never null
+ * @param inputPath the existing node that was sequenced; never null
+ * @param outputPath the existing node onto (or below) which the output is to be written; never null
* @param output the (immutable) sequencing output; never null
* @param context the execution context for this sequencing operation; never null
* @param builtPaths a set of the paths that have already been created but not submitted in this batch
*/
- protected void saveOutput( String nodePath,
+ protected void saveOutput( Path inputPath,
+ String outputPath,
SequencerOutputMap output,
SequencerContext context,
Set builtPaths ) {
if (output.isEmpty()) return;
final PathFactory pathFactory = context.getExecutionContext().getValueFactories().getPathFactory();
final PropertyFactory propertyFactory = context.getExecutionContext().getPropertyFactory();
- final Path outputNodePath = pathFactory.create(nodePath);
+ final Path outputNodePath = pathFactory.create(outputPath);
// Get the existing list of children under the output path ...
PathStrategy pathStrategy = null;
@@ -297,6 +312,8 @@ public class StreamSequencerAdapter implements Sequencer {
// Iterate over the entries in the output, in Path's natural order (shorter paths first and in lexicographical order by
// prefix and name)
+ final Set pathsOfTopLevelNodes = new HashSet();
+ final Destination destination = context.getDestination();
for (SequencerOutputMap.Entry entry : output) {
Path path = entry.getPath();
Path targetNodePath = pathStrategy.validate(path);
@@ -304,7 +321,7 @@ public class StreamSequencerAdapter implements Sequencer {
// Resolve this path relative to the output node path, handling any parent or self references ...
Path absolutePath = targetNodePath.isAbsolute() ? targetNodePath : outputNodePath.resolve(targetNodePath);
- List properties = new LinkedList();
+ Collection properties = new LinkedList();
// Set all of the properties on this
for (SequencerOutputMap.PropertyValue property : entry.getPropertyValues()) {
Object value = property.getValue();
@@ -313,14 +330,65 @@ public class StreamSequencerAdapter implements Sequencer {
// TODO: Handle reference properties - currently passed in as Paths
}
+ if (targetNodePath.size() <= 1 && addDerivedMixin && pathsOfTopLevelNodes.add(absolutePath)) {
+ properties = addDerivedProperties(properties, context, inputPath);
+ }
+
if (absolutePath.getParent() != null) {
buildPathTo(absolutePath.getParent(), context, builtPaths);
}
- context.getDestination().create(absolutePath, properties);
+ destination.create(absolutePath, properties);
builtPaths.add(absolutePath);
}
}
+ protected Collection addDerivedProperties( Collection properties,
+ SequencerContext context,
+ Path inputPath ) {
+ Map propertiesByName = new HashMap();
+ for (Property property : properties) {
+ propertiesByName.put(property.getName(), property);
+ }
+
+ // Find the mixinTypes property and figure out the new value(s) for this property ...
+ Object values = null;
+ Property mixinTypes = propertiesByName.get(JcrLexicon.MIXIN_TYPES);
+ if (mixinTypes == null || mixinTypes.isEmpty()) {
+ values = ModeShapeLexicon.DERIVED;
+ } else {
+ // Add the mixin to the value(s) ...
+ if (mixinTypes.isSingle()) {
+ Name name = context.getExecutionContext().getValueFactories().getNameFactory().create(mixinTypes.getFirstValue());
+ values = new Object[] {name, ModeShapeLexicon.DERIVED};
+ } else {
+ Object[] oldValues = mixinTypes.getValuesAsArray();
+ Object[] newValues = new Object[oldValues.length + 1];
+ newValues[0] = ModeShapeLexicon.DERIVED;
+ System.arraycopy(oldValues, 0, newValues, 1, oldValues.length);
+ values = newValues;
+ }
+ }
+ PropertyFactory propertyFactory = context.getExecutionContext().getPropertyFactory();
+ Property newMixinTypes = propertyFactory.create(JcrLexicon.MIXIN_TYPES, values);
+ propertiesByName.put(newMixinTypes.getName(), newMixinTypes);
+
+ // Add the other 'mode:derived' property/properties ...
+ Property derivedFrom = propertiesByName.get(ModeShapeLexicon.DERIVED_FROM);
+ if (derivedFrom == null) {
+ // Only do this if the sequencer didn't already do this ...
+ assert inputPath != null;
+ if (!inputPath.isRoot() && inputPath.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
+ // We want to point to the sequenced 'nt:file' node, not the 'jcr:content' node ...
+ inputPath = inputPath.getParent();
+ }
+ derivedFrom = propertyFactory.create(ModeShapeLexicon.DERIVED_FROM, inputPath);
+ propertiesByName.put(derivedFrom.getName(), derivedFrom);
+ }
+
+ // Return the properties ...
+ return propertiesByName.values();
+ }
+
protected String[] extractMixinTypes( Object value ) {
if (value instanceof String[]) return (String[])value;
if (value instanceof String) return new String[] {(String)value};
Index: modeshape-repository/src/test/java/org/modeshape/repository/sequencer/StreamSequencerAdapterTest.java
===================================================================
--- modeshape-repository/src/test/java/org/modeshape/repository/sequencer/StreamSequencerAdapterTest.java (revision 2574)
+++ modeshape-repository/src/test/java/org/modeshape/repository/sequencer/StreamSequencerAdapterTest.java (working copy)
@@ -46,6 +46,7 @@ import org.modeshape.common.collection.Problems;
import org.modeshape.common.collection.SimpleProblems;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.Graph;
+import org.modeshape.graph.JcrLexicon;
import org.modeshape.graph.Location;
import org.modeshape.graph.Node;
import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource;
@@ -58,6 +59,7 @@ import org.modeshape.graph.property.Property;
import org.modeshape.graph.sequencer.SequencerOutput;
import org.modeshape.graph.sequencer.StreamSequencer;
import org.modeshape.graph.sequencer.StreamSequencerContext;
+import org.modeshape.repository.ModeShapeLexicon;
import org.modeshape.repository.util.RepositoryNodePath;
/**
@@ -108,7 +110,7 @@ public class StreamSequencerAdapterTest {
}
}
};
- sequencer = new StreamSequencerAdapter(streamSequencer);
+ sequencer = new StreamSequencerAdapter(streamSequencer, false);
seqContext = new SequencerContext(context, graph, graph);
}
@@ -549,7 +551,7 @@ public class StreamSequencerAdapterTest {
@Test
public void shouldNotCreateExtraNodesWhenSavingOutput() throws Exception {
SequencerOutputMap output = new SequencerOutputMap(context.getValueFactories());
- Map props;
+ Map props = null;
/*
* Create several output properties and make sure the resulting graph
@@ -562,7 +564,7 @@ public class StreamSequencerAdapterTest {
output.setProperty(path("a/b[2]/c"), name("property1"), "value1");
Set builtPaths = new HashSet();
- sequencer.saveOutput("/", output, seqContext, builtPaths);
+ sequencer.saveOutput(path("/input/path"), "/", output, seqContext, builtPaths);
seqContext.getDestination().submit();
Node rootNode = graph.getNodeAt("/");
@@ -644,6 +646,60 @@ public class StreamSequencerAdapterTest {
}
+ @FixFor( "MODE-1033" )
+ @Test
+ public void shouldAddDerivedMixinAndDerivedFromPropertyToRootNodeOfSequencedOutput() {
+ sequencer = new StreamSequencerAdapter(streamSequencer, true);
+ SequencerOutputMap output = new SequencerOutputMap(context.getValueFactories());
+ Map props = null;
+
+ /*
+ * Create several output properties and make sure the resulting graph
+ * does not contain duplicate nodes
+ */
+ output.setProperty(path("a"), name("property1"), "value1");
+ output.setProperty(path("a/b"), name("property1"), "value1");
+ output.setProperty(path("a/b"), name("property2"), "value2");
+ output.setProperty(path("a/b[2]"), name("property1"), "value1");
+ output.setProperty(path("a/b[2]/c"), name("property1"), "value1");
+
+ Set builtPaths = new HashSet();
+ Path inputPath = path("/input/path");
+ sequencer.saveOutput(inputPath, "/", output, seqContext, builtPaths);
+ seqContext.getDestination().submit();
+
+ Node rootNode = graph.getNodeAt("/");
+ assertThat(rootNode.getChildren().size(), is(1));
+
+ Node nodeA = graph.getNodeAt("/a");
+ props = nodeA.getPropertiesByName();
+
+ assertThat(nodeA.getChildren().size(), is(2));
+ assertThat(props.size(), is(4)); // Need to add one to account for dna:uuid, jcr:mixinTypes and mode:derivedFrom
+ assertThat(props.get(nameFor("property1")).getFirstValue().toString(), is("value1"));
+ assertThat(props.get(JcrLexicon.MIXIN_TYPES).getFirstValue(), is((Object)ModeShapeLexicon.DERIVED));
+ assertThat(props.get(ModeShapeLexicon.DERIVED_FROM).getFirstValue(), is((Object)inputPath));
+
+ Node nodeB = graph.getNodeAt("/a/b[1]");
+ props = nodeB.getPropertiesByName();
+
+ assertThat(props.size(), is(3)); // Need to add one to account for dna:uuid
+ assertThat(props.get(nameFor("property1")).getFirstValue().toString(), is("value1"));
+ assertThat(props.get(nameFor("property2")).getFirstValue().toString(), is("value2"));
+
+ Node nodeB2 = graph.getNodeAt("/a/b[2]");
+ props = nodeB2.getPropertiesByName();
+
+ assertThat(props.size(), is(2)); // Need to add one to account for dna:uuid
+ assertThat(props.get(nameFor("property1")).getFirstValue().toString(), is("value1"));
+
+ Node nodeC = graph.getNodeAt("/a/b[2]/c");
+ props = nodeC.getPropertiesByName();
+
+ assertThat(props.size(), is(2)); // Need to add one to account for dna:uuid
+ assertThat(props.get(nameFor("property1")).getFirstValue().toString(), is("value1"));
+ }
+
private void verifyProperty( StreamSequencerContext context,
String name,
Object... values ) {
Index: utils/modeshape-jdbc/src/test/java/org/modeshape/jdbc/JcrDriverIntegrationTest.java
===================================================================
--- utils/modeshape-jdbc/src/test/java/org/modeshape/jdbc/JcrDriverIntegrationTest.java (revision 2574)
+++ utils/modeshape-jdbc/src/test/java/org/modeshape/jdbc/JcrDriverIntegrationTest.java (working copy)
@@ -484,6 +484,7 @@ public class JcrDriverIntegrationTest extends ConnectionResultsComparator {
"cars NULL mix:title VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"cars NULL mix:versionable VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"cars NULL mode:defined VIEW Is Mixin: true NULL NULL NULL null DERIVED",
+ "cars NULL mode:derived VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"cars NULL mode:hashed VIEW Is Mixin: true NULL NULL NULL null DERIVED",
"cars NULL mode:lock VIEW Is Mixin: false NULL NULL NULL null DERIVED",
"cars NULL mode:locks VIEW Is Mixin: false NULL NULL NULL null DERIVED",