Skip to content

Commit

Permalink
finalized first version of node type api and added a simple test. issue
Browse files Browse the repository at this point in the history
  • Loading branch information
hpoul authored and bigbear3001 committed Feb 2, 2014
1 parent 4c670f9 commit 06c56cf
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@


public interface WorkingTree {
public static final @Nonnull String NAME_NODETYPE = ":nodetype";

@Nonnull WorkingTreeNode getRootNode();

@Nonnull Commit commit(String message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public interface WorkingTreeNode {
Map<String, Property> getProperties();

Iterable<WorkingTreeNode> getChildrenByProperty(@Nonnull String propertyName, Object value);

/**
* TODO: does it make sense to pass in a String here, instead of a NodeTypeDefinition object?
* @param nodeType fully qualified node type name
* @return all direct children with the given node type.
*/
Iterable<WorkingTreeNode> getChildrenByNodeType(@Nonnull String nodeTypeName);

/**
* @return the storage id of the node. might return null, if it was not yet committed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import javax.annotation.Nullable;

import com.dc2f.dstore.hierachynodestore.ChildQueryAdapter;
import com.dc2f.dstore.hierachynodestore.WorkingTree;
import com.dc2f.dstore.hierachynodestore.WorkingTreeNode;
import com.dc2f.dstore.storage.Property;
import com.dc2f.dstore.storage.StorageId;
Expand Down Expand Up @@ -123,6 +124,12 @@ public Iterable<WorkingTreeNode> getChildrenByProperty(@Nonnull String propertyN
// return ret;
// }

@Override
public Iterable<WorkingTreeNode> getChildrenByNodeType(
@Nonnull String nodeTypeName) {
return getChildrenByProperty(WorkingTree.NAME_NODETYPE, nodeTypeName);
}

@Override @Nonnull
public WorkingTreeNode addChild(@Nonnull String childName) {
WorkingTreeNode child = addChild();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,96 @@
package com.dc2f.dstore.hierachynodestore.impl.nodetype;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.dc2f.dstore.hierachynodestore.WorkingTree;
import com.dc2f.dstore.hierachynodestore.WorkingTreeNode;
import com.dc2f.dstore.hierachynodestore.exception.NodeStoreException;
import com.dc2f.dstore.hierachynodestore.impl.WorkingTreeImpl;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeAccessor;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeDefinition;
import com.dc2f.dstore.storage.Property;
import com.google.common.collect.Iterables;

import static com.dc2f.utils.NullUtils.assertNotNull;

// FIXME: We currently create a new NodeTypeDefinitionImpl instance for each accessor call, maybe we should cache it internally?
public class NodeTypeAccessorImpl implements NodeTypeAccessor {
@SuppressWarnings("unused")
private @Nonnull WorkingTreeImpl workingTreeImpl;
private @Nullable WorkingTreeNode rootNode;

public NodeTypeAccessorImpl(@Nonnull WorkingTreeImpl workingTreeImpl) {
this.workingTreeImpl = workingTreeImpl;
}

@Override
public @Nonnull Collection<NodeTypeDefinition> listNodeTypeDefinitions() {
throw new UnsupportedOperationException();
WorkingTreeNode child = getNodeTypeRootNode();

List<NodeTypeDefinition> nodeTypeDefs = new ArrayList<>();
for (WorkingTreeNode nodeTypeChild : child.getChildrenByNodeType(NodeTypeDefinition.NODETYPE_NAME_NODETYPEDEFINTION)) {
nodeTypeDefs.add(new NodeTypeDefinitionImpl(assertNotNull(nodeTypeChild)));
}
return nodeTypeDefs;
}

private @Nonnull WorkingTreeNode getNodeTypeRootNode() {
WorkingTreeNode nodeTypeRoot = rootNode;
if (nodeTypeRoot == null) {
WorkingTreeNode rootNode = workingTreeImpl.getRootNode();
Iterable<WorkingTreeNode> children = rootNode.getChildrenByProperty(Property.PROPERTY_NAME, WorkingTree.NAME_NODETYPE);
try {
nodeTypeRoot = Iterables.getOnlyElement(children);
} catch (IllegalArgumentException e) {
// iterable contained too many items?
throw new NodeStoreException("Too many root nodes with node type name.", e);
} catch (NoSuchElementException e) {
// returned iterable was empty
// we have to create a new root node.
}
if (nodeTypeRoot == null) {
nodeTypeRoot = rootNode.addChild(WorkingTree.NAME_NODETYPE);
}
rootNode = nodeTypeRoot;
}
return nodeTypeRoot;
}

@Override
@Nonnull
public NodeTypeDefinition addNodeTypeDefinition(@Nonnull String nodeTypeName) {
if (getNodeTypeDefinitionByName(nodeTypeName) != null) {
throw new IllegalArgumentException("Nodetype already exists.");
}
WorkingTreeNode root = getNodeTypeRootNode();
WorkingTreeNode child = root.addChild();
child.setProperty(Property.PROPERTY_NAME, new Property(nodeTypeName));
child.setProperty(WorkingTree.NAME_NODETYPE, new Property(NodeTypeDefinition.NODETYPE_NAME_NODETYPEDEFINTION));
NodeTypeDefinitionImpl nodeTypeDefinition = new NodeTypeDefinitionImpl(child);
return nodeTypeDefinition;
}

@Override
@Nullable
public NodeTypeDefinition getNodeTypeDefinitionByName(
@Nonnull String nodeTypeName) {
WorkingTreeNode root = getNodeTypeRootNode();
Iterable<WorkingTreeNode> children = root.getChildrenByProperty(Property.PROPERTY_NAME, new Property(nodeTypeName));
try {
WorkingTreeNode node = Iterables.getOnlyElement(children);
return new NodeTypeDefinitionImpl(assertNotNull(node));
} catch (IllegalArgumentException e) {
throw new NodeStoreException("There are two node types of this name.", e);
} catch (NoSuchElementException e) {
// empty iterable, no such child.
return null;
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import javax.annotation.Nonnull;

import com.dc2f.dstore.hierachynodestore.WorkingTree;
import com.dc2f.dstore.hierachynodestore.WorkingTreeNode;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeDefinition;
import com.dc2f.dstore.hierachynodestore.nodetype.PropertyDefinition;
Expand Down Expand Up @@ -39,6 +40,7 @@ public Collection<PropertyDefinition> listPropertyDefinitions() {
public PropertyDefinition addPropertyDefinition(@Nonnull String name) {
WorkingTreeNode propertyNode = node.addChild();
propertyNode.setProperty(PROPERTY_NAME, new Property(name));
propertyNode.setProperty(WorkingTree.NAME_NODETYPE, new Property(NodeTypeDefinition.NODETYPE_NAME_PROPERTYDEFINITION));
return new PropertyDefinitionImpl(propertyNode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public String getName() {
// a property definition must always have a name..
return assertNotNull(node.getProperty(PROPERTY_NAME)).getString();
}

@Override
public @Nonnull PropertyDefinition setType(@Nonnull PropertyType type) {
node.setProperty(PROPERTY_TYPE, new Property(assertNotNull(type.name())));
return this;
}

@Override
@Nonnull
Expand All @@ -42,8 +48,9 @@ public PropertyType getType() {
}

@Override
public void setRequired(boolean isRequired) {
public @Nonnull PropertyDefinition setRequired(boolean isRequired) {
node.setProperty(PROPERTY_REQUIRED, new Property(new Boolean(isRequired)));
return this;
}

@Override
Expand All @@ -56,8 +63,9 @@ public boolean isRequired() {
}

@Override
public void setIndexed(boolean isIndexed) {
public @Nonnull PropertyDefinition setIndexed(boolean isIndexed) {
node.setProperty(PROPERTY_INDEXED, new Property(new Boolean(isIndexed)));
return this;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collection;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.dc2f.dstore.hierachynodestore.WorkingTree;

Expand All @@ -17,9 +18,22 @@ public interface NodeTypeAccessor {
/**
* find all node type definitions in the current working tree and return them.
* TODO: really all, or just those within /:nodetypes/ ??
* @return list of all node type definnitions.
* @return list of all node type definitions.
*/
@Nonnull Collection<NodeTypeDefinition> listNodeTypeDefinitions();

/**
* Creates a new empty node type definition and returns it.
* @param nodeTypeName name of the new node type - must be unique.
* @return a new node type defintion.
* @throws IllegalArgumentException if a node with that type already exists.
*/
@Nonnull NodeTypeDefinition addNodeTypeDefinition(@Nonnull String nodeTypeName) throws IllegalArgumentException;

/**
* finds the node type definition by the given name and returns it.
* @param nodeTypeName name of the node type
* @return node type definition, or null if it does not exist.
*/
@Nullable NodeTypeDefinition getNodeTypeDefinitionByName(@Nonnull String nodeTypeName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
* TODO: maybe we should also extend {@link WorkingTreeNode}?
*/
public interface NodeTypeDefinition {
/**
* the 'nodetype' name for node type definition
*/
public static final @Nonnull String NODETYPE_NAME_NODETYPEDEFINTION = "com.dc2f.nodetype.NodeTypeDefinition";
public static final @Nonnull String NODETYPE_NAME_PROPERTYDEFINITION = "com.dc2f.nodetype.PropertyDefinition";

@Nonnull Collection<PropertyDefinition> listPropertyDefinitions();
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
*/
public interface PropertyDefinition {
@Nonnull String getName();

@Nonnull PropertyDefinition setType(@Nonnull PropertyType type);
@Nonnull PropertyType getType();

void setRequired(boolean isRequired);
@Nonnull PropertyDefinition setRequired(boolean isRequired);
boolean isRequired();

void setIndexed(boolean isIndexed);
@Nonnull PropertyDefinition setIndexed(boolean isIndexed);
boolean isIndexed();
}
8 changes: 4 additions & 4 deletions src/main/java/com/dc2f/dstore/storage/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ public class Property {
*/
@Nonnull public static final String PROPERTY_NAME = "name";
public static enum PropertyType {
LONG(Long.class),
DOUBLE(Double.class),
STRING(String.class),
BOOLEAN(Boolean.class);
@Nonnull LONG(Long.class),
@Nonnull DOUBLE(Double.class),
@Nonnull STRING(String.class),
@Nonnull BOOLEAN(Boolean.class);


private Class<?> valueClass;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.dc2f.dstore.test.hierachynodestore.nodetype;

import static com.dc2f.dstore.test.TreeAssertions.assertTree;
import static com.dc2f.dstore.test.TreeAssertions.node;
import static com.dc2f.dstore.test.TreeAssertions.properties;

import java.io.IOException;

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dc2f.dstore.hierachynodestore.HierarchicalNodeStore;
import com.dc2f.dstore.hierachynodestore.WorkingTree;
import com.dc2f.dstore.hierachynodestore.WorkingTreeUtils;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeAccessor;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeDefinition;
import com.dc2f.dstore.storage.Property.PropertyType;
import com.dc2f.dstore.storage.StorageBackend;
import com.dc2f.dstore.storage.map.HashMapStorage;
import com.dc2f.dstore.test.TreeAssertions.ExpectedNode;

public class NodeTypeApiTest {
private Logger logger = LoggerFactory.getLogger(NodeTypeApiTest.class);
private HierarchicalNodeStore nodeStore;

@Before
public void setupDstore() throws IOException {
StorageBackend storageBackend = initStorageBackend();
nodeStore = new HierarchicalNodeStore(storageBackend);
}

protected StorageBackend initStorageBackend() {
return new HashMapStorage();
}


@Test
public void testSimpleNodeTypeSetup() {
WorkingTree wt = nodeStore.checkoutBranch("master");
NodeTypeAccessor nodeTypeAccessor = wt.getNodeTypeAccessor();

NodeTypeDefinition entryNodeType = nodeTypeAccessor.addNodeTypeDefinition("com.dc2f.blog.BlogEntry");

// we need a 'nodetype' property (no inheritance, grml)
entryNodeType.addPropertyDefinition(WorkingTree.NAME_NODETYPE)
// for now we have no special property type for this..
.setType(PropertyType.STRING)
.setRequired(true);

entryNodeType.addPropertyDefinition("title")
.setRequired(true)
.setType(PropertyType.STRING);

entryNodeType.addPropertyDefinition("body")
.setRequired(true)
.setType(PropertyType.STRING);

entryNodeType.addPropertyDefinition("slug")
.setRequired(true)
.setType(PropertyType.STRING);

wt.commit(null);

logger.info("Got tree: " + WorkingTreeUtils.debugRecursiveTree(wt.getRootNode()));

ExpectedNode expectedNode = node(properties("name", ""),
node(properties("name", ":nodetype"),
node(properties("name", "com.dc2f.blog.BlogEntry",
":nodetype",
"com.dc2f.nodetype.NodeTypeDefinition"),

node(properties(":name", ":nodetype",
":nodetype", "com.dc2f.nodetype.PropertyDefinition",
":required", true,
":type", "STRING")),

node(properties(":name", "title",
":nodetype", "com.dc2f.nodetype.PropertyDefinition",
":required", true,
":type", "STRING")),

node(properties(":name", "body",
":nodetype", "com.dc2f.nodetype.PropertyDefinition",
":required", true,
":type", "STRING")),

node(properties(":name", "slug",
":nodetype", "com.dc2f.nodetype.PropertyDefinition",
":required", true,
":type", "STRING"))

)));

assertTree(expectedNode, wt.getRootNode());
}

}

0 comments on commit 06c56cf

Please sign in to comment.