From 0f923ee9f8d07c604be1f804736bae6c96da50b2 Mon Sep 17 00:00:00 2001 From: Jim Moore Date: Tue, 9 Jun 2015 16:21:20 -0600 Subject: [PATCH] Implements mixins and namespace support Implemented basic mixin support in NodeImpl Implemented basic NamePathResolver and NamespaceRegistry support in SessionImpl Fixes #6 --- README.md | 2 +- RELEASE_NOTES.md | 5 + build.gradle | 2 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../twcable/jackalope/impl/jcr/NodeImpl.java | 103 +++++++++- .../jackalope/impl/jcr/NodeTypeImpl.java | 11 +- .../impl/jcr/NodeTypeManagerImpl.java | 190 ++++++++++++++++++ .../jackalope/impl/jcr/SessionImpl.java | 94 ++++++++- .../jackalope/impl/jcr/WorkspaceImpl.java | 21 +- .../jackalope/impl/jcr/NodeImplSpec.groovy | 58 ++++-- .../impl/jcr/NodeTypeImplSpec.groovy | 13 ++ .../impl/jcr/NodeTypeManagerImplSpec.groovy | 36 ++++ 13 files changed, 495 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeManagerImpl.java create mode 100644 src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeManagerImplSpec.groovy diff --git a/README.md b/README.md index ce44041..36f2cec 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ repositories { } } -testCompile 'com.twcable.jackalope:jackalope:3.0.0' +testCompile 'com.twcable.jackalope:jackalope:3.1.0' ``` # LICENSE diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e075655..29d3071 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,10 @@ # RELEASE NOTES +## 3.1.0 + +* Implemented basic mixin support in NodeImpl +* Implemented basic NamePathResolver and NamespaceRegistry support in SessionImpl + ## 3.0.0 Updated to support AEM 6.0, from AEM 5.6.1 diff --git a/build.gradle b/build.gradle index 6547bab..ffdf1b1 100644 --- a/build.gradle +++ b/build.gradle @@ -50,7 +50,7 @@ dependencies { runtime "ch.qos.logback:logback-classic:1.0.4" testCompile "org.codehaus.groovy:groovy:2.3.6" - testCompile "org.spockframework:spock-core:0.7-groovy-2.0", { + testCompile "org.spockframework:spock-core:1.0-groovy-2.3", { exclude group: 'org.codehaus.groovy', module: 'groovy-all' } } diff --git a/gradle.properties b/gradle.properties index 2c207f4..086f698 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ # suppress inspection "UnusedProperty" for whole file description = Provides a convenient way of stubbing out Sling and the JCR group = com.twcable.jackalope -version = 3.0.0 +version = 3.1.0-SNAPSHOT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c93c687..ad9d71b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,2 +1,2 @@ #Tue Feb 24 15:51:41 MST 2015 -distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/NodeImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/NodeImpl.java index 049ca22..eb74493 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/NodeImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/NodeImpl.java @@ -18,6 +18,8 @@ import com.twcable.jackalope.impl.common.Paths; import com.twcable.jackalope.impl.common.Values; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import javax.annotation.Nonnull; import javax.jcr.AccessDeniedException; @@ -45,6 +47,8 @@ import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeManager; +import javax.jcr.nodetype.PropertyDefinition; import javax.jcr.version.ActivityViolationException; import javax.jcr.version.Version; import javax.jcr.version.VersionException; @@ -52,11 +56,12 @@ import java.io.InputStream; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.UUID; +import static java.util.Collections.singletonList; + /** * Implementation of jcr Node interface. @@ -336,43 +341,122 @@ public boolean hasProperties() throws RepositoryException { @Override public NodeType getPrimaryNodeType() throws RepositoryException { - return new NodeTypeImpl(getProperty("jcr:primaryType").getString()); + return new NodeTypeImpl(primaryTypeProperty().getString()); + } + + + private PropertyImpl primaryTypeProperty() throws RepositoryException { + return getOrCreateProperty(getJcrNameForQName(Property.JCR_PRIMARY_TYPE)); } + private PropertyImpl mixinProperty() throws RepositoryException { + return getOrCreateProperty(getJcrNameForQName(Property.JCR_MIXIN_TYPES)); + } + + + /** + * Returns the declared mixin node types of this node. + *

+ * The default implementation uses the values of the + * jcr:mixinTypes property to look up the mixin node types + * from the {@link NodeTypeManager} of the current workspace. + * + * @return mixin node types + * @throws RepositoryException if an error occurs + */ @Override public NodeType[] getMixinNodeTypes() throws RepositoryException { - return new NodeType[0]; + NodeTypeManager manager = getSession().getWorkspace().getNodeTypeManager(); + Property property = mixinProperty(); + Value[] values = property.getValues(); + if (values == null) return new NodeType[0]; + NodeType[] types = new NodeType[values.length]; + for (int i = 0; i < values.length; i++) { + types[i] = manager.getNodeType(values[i].getString()); + } + return types; + } + + + private String getJcrNameForQName(String name) throws RepositoryException { + return getJcrName(NameFactoryImpl.getInstance().create(name)); + } + + + private String getJcrName(Name name) throws RepositoryException { + return session.getNamePathResolver().getJCRName(name); } @Override public boolean isNodeType(String nodeTypeName) throws RepositoryException { - return getProperty("jcr:primaryType").getString().equals(nodeTypeName); + return primaryTypeProperty().getString().equals(nodeTypeName); } @Override public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { - setProperty("jcr:primaryType", nodeTypeName); + setProperty(getJcrNameForQName(Property.JCR_PRIMARY_TYPE), nodeTypeName); } + /** + * Very simple implementation of Mixin support: Does not check permissions, check for conflicts, etc. + */ @Override public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { - //Not Implemented + Property mixinProperty = mixinProperty(); + Value[] values = mixinProperty.getValues(); + final Value[] newValues; + if (values == null) { + newValues = new Value[1]; + newValues[0] = new ValueImpl(mixinName); + } + else { + newValues = new Value[values.length + 1]; + System.arraycopy(values, 0, newValues, 0, values.length); + newValues[values.length] = new ValueImpl(mixinName); + } + mixinProperty.setValue(newValues); + session.changeItem(this); } @Override public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { - //Not Implemented + Property mixinProperty = mixinProperty(); + Value[] values = mixinProperty.getValues(); + if (values == null) { + throw new NoSuchNodeTypeException(mixinName); + } + + boolean found = false; + Value[] newValues = new Value[values.length - 1]; + for (int idx = 0, newIdx = 0; idx < values.length; ) { + Value value = values[idx]; + if (value.getString().equals(mixinName)) { + idx++; + found = true; + } + else { + if (newIdx < newValues.length) + newValues[newIdx] = values[idx]; + idx++; + newIdx++; + } + } + + if (found) { + mixinProperty.setValue(newValues); + session.changeItem(this); + } } @Override public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException { - return false; + return true; } @@ -426,7 +510,7 @@ public String getCorrespondingNodePath(String workspaceName) throws ItemNotFound @Override public NodeIterator getSharedSet() throws RepositoryException { - return new NodeIteratorImpl(Arrays.asList((Node)this)); //To change body of implemented methods use File | Settings | File Templates. + return new NodeIteratorImpl(singletonList((Node)this)); } @@ -541,4 +625,5 @@ public void accept(ItemVisitor visitor) throws RepositoryException { private PropertyImpl getOrCreateProperty(String name) throws RepositoryException { return hasProperty(name) ? (PropertyImpl)getProperty(name) : new PropertyImpl(session, Paths.resolve(getPath(), name)); } + } diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeImpl.java index 5309656..bdaa13d 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeImpl.java @@ -25,8 +25,10 @@ /** * Simple implementation of an {@link NodeType} */ +@SuppressWarnings("unused") public class NodeTypeImpl implements NodeType { private final String nodeTypeName; + private boolean isMixin; public NodeTypeImpl(String nodeTypeName) { @@ -60,7 +62,7 @@ public NodeTypeIterator getDeclaredSubtypes() { @Override public boolean isNodeType(String nodeTypeName) { - return this.nodeTypeName.equals(nodeTypeName); //To change body of implemented methods use File | Settings | File Templates. + return this.nodeTypeName.equals(nodeTypeName); } @@ -136,9 +138,14 @@ public boolean isAbstract() { } + public void setIsMixin(boolean isMixin) { + this.isMixin = isMixin; + } + + @Override public boolean isMixin() { - return false; + return this.isMixin; } diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeManagerImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeManagerImpl.java new file mode 100644 index 0000000..a9e25f4 --- /dev/null +++ b/src/main/java/com/twcable/jackalope/impl/jcr/NodeTypeManagerImpl.java @@ -0,0 +1,190 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed 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 com.twcable.jackalope.impl.jcr; + +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.nodetype.InvalidNodeTypeDefinitionException; +import javax.jcr.nodetype.NoSuchNodeTypeException; +import javax.jcr.nodetype.NodeDefinitionTemplate; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeDefinition; +import javax.jcr.nodetype.NodeTypeExistsException; +import javax.jcr.nodetype.NodeTypeIterator; +import javax.jcr.nodetype.NodeTypeManager; +import javax.jcr.nodetype.NodeTypeTemplate; +import javax.jcr.nodetype.PropertyDefinitionTemplate; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Simple implementation of {@link NodeTypeManager}. Use {@link #registerNodeType(NodeType)} to add NodeTypes. + */ +@SuppressWarnings("DuplicateThrows") +public class NodeTypeManagerImpl implements NodeTypeManager { + private Map nameToNodeType = new LinkedHashMap<>(); + + + @Override + public NodeType getNodeType(String nodeTypeName) throws NoSuchNodeTypeException, RepositoryException { + NodeType nodeType = nameToNodeType.get(nodeTypeName); + if (nodeType == null) throw new NoSuchNodeTypeException(nodeTypeName); + return nodeType; + } + + + @Override + public boolean hasNodeType(String name) throws RepositoryException { + return nameToNodeType.containsKey(name); + } + + + @Override + public NodeTypeIterator getAllNodeTypes() throws RepositoryException { + return new NodeTypeIteratorImpl(nameToNodeType.values().iterator()); + } + + + @Override + public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException { + ArrayList nodeTypes = new ArrayList<>(); + for (NodeType nodeType : nameToNodeType.values()) { + if (!nodeType.isMixin()) nodeTypes.add(nodeType); + } + return new NodeTypeIteratorImpl(nodeTypes.iterator()); + } + + + @Override + public NodeTypeIterator getMixinNodeTypes() throws RepositoryException { + ArrayList nodeTypes = new ArrayList<>(); + for (NodeType nodeType : nameToNodeType.values()) { + if (nodeType.isMixin()) nodeTypes.add(nodeType); + } + return new NodeTypeIteratorImpl(nodeTypes.iterator()); + } + + + @Override + public NodeTypeTemplate createNodeTypeTemplate() throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @Override + public NodeTypeTemplate createNodeTypeTemplate(NodeTypeDefinition ntd) throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @Override + public NodeDefinitionTemplate createNodeDefinitionTemplate() throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @Override + public PropertyDefinitionTemplate createPropertyDefinitionTemplate() throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @Override + public NodeType registerNodeType(NodeTypeDefinition ntd, boolean allowUpdate) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @SuppressWarnings("unused") + public void registerNodeType(NodeType nt) { + nameToNodeType.put(nt.getName(), nt); + } + + + @Override + public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedOperationException(); + } + + + @Override + public void unregisterNodeType(String name) throws UnsupportedRepositoryOperationException, NoSuchNodeTypeException, RepositoryException { + nameToNodeType.remove(name); + } + + + @Override + public void unregisterNodeTypes(String[] names) throws UnsupportedRepositoryOperationException, NoSuchNodeTypeException, RepositoryException { + for (String name : names) { + unregisterNodeType(name); + } + } + + + private static class NodeTypeIteratorImpl implements NodeTypeIterator { + private final Iterator iterator; + + + public NodeTypeIteratorImpl(Iterator iterator) { + this.iterator = iterator; + } + + + @Override + public NodeType nextNodeType() { + return (NodeType)next(); + } + + + @Override + public void skip(long skipNum) { + throw new UnsupportedOperationException(); + } + + + @Override + public long getSize() { + throw new UnsupportedOperationException(); + } + + + @Override + public long getPosition() { + throw new UnsupportedOperationException(); + } + + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + + @Override + public Object next() { + return iterator.next(); + } + + + @Override + public void remove() { + iterator.remove(); + } + } +} diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java index 6d76cda..b5b4a4c 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java @@ -17,10 +17,14 @@ package com.twcable.jackalope.impl.jcr; import com.twcable.jackalope.impl.common.Paths; +import org.apache.jackrabbit.spi.Name; +import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; +import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import javax.annotation.Nonnull; +import javax.jcr.AccessDeniedException; import javax.jcr.Credentials; import javax.jcr.InvalidSerializedDataException; import javax.jcr.Item; @@ -28,6 +32,7 @@ import javax.jcr.ItemNotFoundException; import javax.jcr.LoginException; import javax.jcr.NamespaceException; +import javax.jcr.NamespaceRegistry; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.Property; @@ -47,6 +52,7 @@ import java.io.OutputStream; import java.security.AccessControlException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -67,6 +73,8 @@ public class SessionImpl implements Session { private Set changedItems = new HashSet<>(); private Workspace workspace = null; + private NamespaceRegistry namespaceRegistry = null; + private DefaultNamePathResolver namePathResolver; @Override @@ -96,14 +104,28 @@ public Object getAttribute(String name) { @Override public Workspace getWorkspace() { if (workspace == null) { - WorkspaceImpl workspaceImpl = new WorkspaceImpl(); - workspaceImpl.setSession(this); - workspace = workspaceImpl; + workspace = new WorkspaceImpl(this); } return workspace; } + public NamespaceRegistry getNamespaceRegistry() { + if (namespaceRegistry == null) { + namespaceRegistry = new MyNamespaceRegistry(); + } + return namespaceRegistry; + } + + + public NamePathResolver getNamePathResolver() { + if (namePathResolver == null) { + namePathResolver = new DefaultNamePathResolver(getNamespaceRegistry()); + } + return namePathResolver; + } + + @Override public Node getRootNode() { return (Node)itemStore.get("/"); // Added in ctor @@ -265,24 +287,25 @@ public void exportDocumentView(String absPath, OutputStream out, boolean skipBin @Override public void setNamespacePrefix(String prefix, String uri) throws NamespaceException, RepositoryException { + namespaceRegistry.registerNamespace(prefix, uri); } @Override public String[] getNamespacePrefixes() throws RepositoryException { - return new String[0]; + return namespaceRegistry.getPrefixes(); } @Override public String getNamespaceURI(String prefix) throws NamespaceException, RepositoryException { - return null; + return namespaceRegistry.getURI(prefix); } @Override public String getNamespacePrefix(String uri) throws NamespaceException, RepositoryException { - return null; + return namespaceRegistry.getPrefix(uri); } @@ -423,4 +446,63 @@ boolean isNew(ItemImpl item) { boolean isModified(ItemImpl item) { return changedItems.contains(item.getPath()); } + + + private static class MyNamespaceRegistry implements NamespaceRegistry { + private final Map prefixToUri = new HashMap<>(); + + + public MyNamespaceRegistry() { + prefixToUri.put(Name.NS_JCR_PREFIX, Name.NS_JCR_URI); + prefixToUri.put(Name.NS_MIX_PREFIX, Name.NS_MIX_URI); + prefixToUri.put(Name.NS_NT_PREFIX, Name.NS_NT_URI); + prefixToUri.put(Name.NS_REP_PREFIX, Name.NS_REP_URI); + } + + + @Override + public void registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException { + prefixToUri.put(prefix, uri); + } + + + @Override + public void unregisterNamespace(String prefix) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException { + prefixToUri.remove(prefix); + } + + + @Override + public String[] getPrefixes() throws RepositoryException { + Set keySet = prefixToUri.keySet(); + return keySet.toArray(new String[keySet.size()]); + } + + + @Override + public String[] getURIs() throws RepositoryException { + Collection values = prefixToUri.values(); + return values.toArray(new String[values.size()]); + } + + + @Override + public String getURI(String prefix) throws NamespaceException, RepositoryException { + String uri = prefixToUri.get(prefix); + if (uri == null) { + throw new NamespaceException(prefix); + } + return uri; + } + + + @Override + public String getPrefix(String uri) throws NamespaceException, RepositoryException { + for (Map.Entry entry : prefixToUri.entrySet()) { + if (entry.getValue().equalsIgnoreCase(uri)) return entry.getKey(); + } + throw new NamespaceException(uri); + } + } + } diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/WorkspaceImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/WorkspaceImpl.java index 89f3ac1..a108418 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/WorkspaceImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/WorkspaceImpl.java @@ -46,12 +46,18 @@ */ @SuppressWarnings("DuplicateThrows") public class WorkspaceImpl implements Workspace { - private Session session = null; + private SessionImpl session = null; private ObservationManager observationManager = null; + private NodeTypeManager nodeTypeManager = null; protected QueryManager queryManager = null; + public WorkspaceImpl(SessionImpl session) { + this.session = session; + } + + @Override public Session getSession() { return session; @@ -103,13 +109,16 @@ public QueryManager getQueryManager() throws RepositoryException { @Override public NamespaceRegistry getNamespaceRegistry() throws RepositoryException { - return null; + return session.getNamespaceRegistry(); } @Override public NodeTypeManager getNodeTypeManager() throws RepositoryException { - return null; + if (nodeTypeManager == null) { + nodeTypeManager = new NodeTypeManagerImpl(); + } + return nodeTypeManager; } @@ -160,12 +169,8 @@ public void deleteWorkspace(String name) throws AccessDeniedException, Unsupport } - public void setSession(Session session) { - this.session = session; - } - - public void setQueryManager(QueryManager queryManager) { this.queryManager = queryManager; } + } diff --git a/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeImplSpec.groovy b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeImplSpec.groovy index 5d0257a..7442219 100644 --- a/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeImplSpec.groovy +++ b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeImplSpec.groovy @@ -20,14 +20,18 @@ import com.google.common.collect.Lists import spock.lang.Specification import spock.lang.Subject +import javax.jcr.Node import javax.jcr.Value import javax.jcr.nodetype.NodeType @Subject(NodeImpl) class NodeImplSpec extends Specification { + SessionImpl session = new SessionImpl() + + def "A new node can be created"() { when: - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") then: node.isNode() @@ -36,7 +40,7 @@ class NodeImplSpec extends Specification { def "Properties can be set on nodes"() { - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") node.getSession().save() when: @@ -83,7 +87,7 @@ class NodeImplSpec extends Specification { def "Nodes have a set of properties"() { - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") node.setProperty("first", "a") node.setProperty("second", "b") node.setProperty("third", "c") @@ -100,7 +104,7 @@ class NodeImplSpec extends Specification { def "Child nodes can be created"() { - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") node.getSession().save() when: @@ -118,7 +122,7 @@ class NodeImplSpec extends Specification { def "Child nodes can be created with a specific node type"() { - def node = new NodeImpl(new RepositoryImpl().login(), "test") + def node = new NodeImpl(new RepositoryImpl().login() as SessionImpl, "test") node.getSession().save() when: @@ -130,14 +134,14 @@ class NodeImplSpec extends Specification { def "A node can have multiple child nodes"() { - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") node.addNode("first") node.addNode("second") node.addNode("third") node.getSession().save() when: - def nodes = Lists.newArrayList(node.getNodes()) + def nodes = Lists. newArrayList(node.getNodes()) then: nodes.find { it.getName() == "first" } @@ -149,7 +153,7 @@ class NodeImplSpec extends Specification { def "A node can be saved"() { when: - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") then: node.isNew() node.getSession().hasPendingChanges() @@ -176,7 +180,7 @@ class NodeImplSpec extends Specification { def "Saving a node saves its children"() { when: - def node = new NodeImpl(new SessionImpl(), "test") + def node = new NodeImpl(session, "test") def child = node.addNode("child") then: node.isNew() @@ -205,8 +209,6 @@ class NodeImplSpec extends Specification { def "Saving a node does not save any other node"() { - def session = new SessionImpl() - when: def node1 = new NodeImpl(session, "node1") def node2 = new NodeImpl(session, "node2") @@ -246,7 +248,6 @@ class NodeImplSpec extends Specification { def "remove() deletes the node from the session"() { - def session = new SessionImpl() def parent = new NodeImpl(session, "parent") def child = parent.addNode("child") session.nodeExists("parent/child") @@ -260,11 +261,10 @@ class NodeImplSpec extends Specification { def "remove deletes descendent nodes from the session"() { - def session = new SessionImpl() def parent = new NodeImpl(session, "parent") def child = parent.addNode("child") def grandchild = child.addNode("grandchild") - def greatgrandchild = grandchild.addNode("greatgrandchild") + grandchild.addNode("greatgrandchild") session.nodeExists("parent/child") session.nodeExists("parent/child/grandchild") session.nodeExists("parent/child/grandchild/greatgrandchild") @@ -280,7 +280,6 @@ class NodeImplSpec extends Specification { def "getParent for a standalone node returns the virtual root"() { - def session = new SessionImpl() def node = new NodeImpl(session, "node") when: @@ -289,4 +288,33 @@ class NodeImplSpec extends Specification { then: parent == session.getRootNode() } + + + def "can add and remove mixins"() { + def nodeTypeManager = session.workspace.nodeTypeManager as NodeTypeManagerImpl + nodeTypeManager.registerNodeType(new NodeTypeImpl("fooble")) + nodeTypeManager.registerNodeType(new NodeTypeImpl("bobble")) + + def node = new NodeImpl(session, "/node") + + when: + node.addMixin("bobble") + node.addMixin("fooble") + + then: + node.getMixinNodeTypes().collect { it.name } as Set == ["fooble", "bobble"] as Set + + when: + node.removeMixin("bobble") + + then: + node.getMixinNodeTypes().collect { it.name } as Set == ["fooble"] as Set + + when: + node.removeMixin("bzzzt") + + then: + node.getMixinNodeTypes().collect { it.name } as Set == ["fooble"] as Set + } + } diff --git a/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeImplSpec.groovy b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeImplSpec.groovy index 56e8094..c2cd65e 100644 --- a/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeImplSpec.groovy +++ b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeImplSpec.groovy @@ -20,6 +20,7 @@ import spock.lang.Specification import spock.lang.Subject @Subject(NodeTypeImpl) +@SuppressWarnings("GroovyPointlessBoolean") class NodeTypeImplSpec extends Specification { def "NodeType has a name"() { @@ -34,4 +35,16 @@ class NodeTypeImplSpec extends Specification { !new NodeTypeImpl("nodetype").isNodeType("nodetypex") } + + def "NodeType can set isMixin"() { + given: + NodeTypeImpl nodeType = new NodeTypeImpl("aNodeType") + + when: + nodeType.setIsMixin(true) + + then: + nodeType.isMixin() == true + } + } diff --git a/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeManagerImplSpec.groovy b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeManagerImplSpec.groovy new file mode 100644 index 0000000..e4e912f --- /dev/null +++ b/src/test/groovy/com/twcable/jackalope/impl/jcr/NodeTypeManagerImplSpec.groovy @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Time Warner Cable, Inc. + * + * Licensed 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 com.twcable.jackalope.impl.jcr + +import spock.lang.Specification +import spock.lang.Subject + +class NodeTypeManagerImplSpec extends Specification { + + @Subject + NodeTypeManagerImpl nodeTypeManager = new NodeTypeManagerImpl() + + + def "can register and get node types"() { + when: + nodeTypeManager.registerNodeType(new NodeTypeImpl("jcr:mixinTypes")) + + then: + nodeTypeManager.hasNodeType("jcr:mixinTypes") + } + +}