Skip to content

Commit

Permalink
Merge pull request #7 from dc2f/nodetypeapi
Browse files Browse the repository at this point in the history
Userfriendly API for Node Types solves #1
  • Loading branch information
hpoul committed Jan 15, 2014
2 parents e930249 + 792b470 commit 1608a2b
Show file tree
Hide file tree
Showing 15 changed files with 536 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.Collections;

import javax.annotation.Nonnull;

import com.dc2f.dstore.hierachynodestore.impl.WorkingTreeImpl;
import com.dc2f.dstore.storage.Property;
import com.dc2f.dstore.storage.StorageBackend;
Expand All @@ -15,9 +17,9 @@
public class HierarchicalNodeStore {
private StorageBackend storageBackend;

private final static String ROOT_COMMIT_ID = "rootCommitId";
private final static String ROOT_NODE_NAME = "";
private final static String MASTER_BRANCH_NAME = "master";
private final static @Nonnull String ROOT_COMMIT_ID = "rootCommitId";
private final static @Nonnull String ROOT_NODE_NAME = "";
private final static @Nonnull String MASTER_BRANCH_NAME = "master";


public HierarchicalNodeStore(StorageBackend storageBackend) {
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/com/dc2f/dstore/hierachynodestore/WorkingTree.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package com.dc2f.dstore.hierachynodestore;

import javax.annotation.Nonnull;

import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeAccessor;


public interface WorkingTree {
WorkingTreeNode getRootNode();
public static final @Nonnull String NAME_NODETYPE = ":nodetype";

@Nonnull WorkingTreeNode getRootNode();

Commit commit(String message);
@Nonnull Commit commit(String message);

@Nonnull NodeTypeAccessor getNodeTypeAccessor();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,32 @@ public interface WorkingTreeNode {
* @return the created child for this node
*/
@Nonnull
WorkingTreeNode addChild(String childName);
WorkingTreeNode addChild(@Nonnull String childName);

@Nonnull
WorkingTreeNode addChild();


@Nonnull
Iterable<WorkingTreeNode> getChildren();
int getChildrenCount();

@Nullable
Property getProperty(String name);
Property getProperty(@Nonnull String name);

void setProperty(@Nonnull String name, @Nonnull Property value);

@Nonnull
Map<String, Property> getProperties();

Iterable<WorkingTreeNode> getChildrenByProperty(String propertyName, Object value);
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
@@ -0,0 +1,16 @@
package com.dc2f.dstore.hierachynodestore.exception;

/**
* generic exception from your node store.
*/
public class NodeStoreException extends RuntimeException {
private static final long serialVersionUID = 1L;


public NodeStoreException(String message) {
this(message, null);
}
public NodeStoreException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
import java.util.Map;
import java.util.Set;

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

import com.dc2f.dstore.hierachynodestore.Commit;
import com.dc2f.dstore.hierachynodestore.HierarchicalNodeStore;
import com.dc2f.dstore.hierachynodestore.WorkingTree;
import com.dc2f.dstore.hierachynodestore.WorkingTreeNode;
import com.dc2f.dstore.hierachynodestore.impl.nodetype.NodeTypeAccessorImpl;
import com.dc2f.dstore.hierachynodestore.nodetype.NodeTypeAccessor;
import com.dc2f.dstore.storage.MutableStoredFlatNode;
import com.dc2f.dstore.storage.Property;
import com.dc2f.dstore.storage.StorageBackend;
Expand All @@ -20,14 +25,19 @@

public class WorkingTreeImpl implements WorkingTree {

private HierarchicalNodeStore hierarchicalNodeStore;
/**
* Reference back to the hierarchical node store in case we need it(?)
*/
@SuppressWarnings("unused")
private @Nonnull HierarchicalNodeStore hierarchicalNodeStore;
private StoredCommit headCommit;
private String branchName;
private @Nullable NodeTypeAccessor nodeTypeAccessor;
StorageBackend storageBackend;
Map<StorageId, WorkingTreeNodeImpl> loadedNodes = new HashMap<>();
private List<WorkingTreeNodeImpl> changedNodes = new ArrayList<>();

public WorkingTreeImpl(HierarchicalNodeStore hierarchicalNodeStore,
public WorkingTreeImpl(@Nonnull HierarchicalNodeStore hierarchicalNodeStore,
StorageBackend storageBackend, StoredCommit headCommit, String branchName) {
this.hierarchicalNodeStore = hierarchicalNodeStore;
this.storageBackend = storageBackend;
Expand All @@ -36,13 +46,13 @@ public WorkingTreeImpl(HierarchicalNodeStore hierarchicalNodeStore,
}

@Override
public WorkingTreeNode getRootNode() {
public @Nonnull WorkingTreeNode getRootNode() {
StorageId rootNodeId = headCommit.getRootNode();
return getNodeByStorageId(rootNodeId, null);
// return new WorkingTreeNodeImpl(this, storageBackend.readNode(rootNodeId));
}

public WorkingTreeNode getNodeByStorageId(StorageId nodeStorageId, WorkingTreeNodeImpl parentNode) {
public @Nonnull WorkingTreeNode getNodeByStorageId(StorageId nodeStorageId, WorkingTreeNodeImpl parentNode) {
// TODO shouldn't we add caching right here?
WorkingTreeNodeImpl ret = loadedNodes.get(nodeStorageId);
if (ret == null) {
Expand All @@ -61,7 +71,7 @@ public void notifyNodeChanged(WorkingTreeNodeImpl workingTreeNodeImpl) {
}

@Override
public Commit commit(String message) {
public @Nonnull Commit commit(String message) {
if (message == null) {
message = "";
}
Expand Down Expand Up @@ -191,5 +201,15 @@ private Set<WorkingTreeNodeImpl> findNodesToUpdate() {
return toUpdate;
}

@Override
@Nonnull
public NodeTypeAccessor getNodeTypeAccessor() {
NodeTypeAccessor ret = nodeTypeAccessor;
if (ret == null) {
nodeTypeAccessor = ret = new NodeTypeAccessorImpl(this);
}
return ret;
}


}
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 @@ -58,7 +59,7 @@ public WorkingTreeNodeImpl(@Nonnull WorkingTreeImpl workingTreeImpl,
}

@Override
public Iterable<WorkingTreeNode> getChildrenByProperty(String propertyName, Object value) {
public Iterable<WorkingTreeNode> getChildrenByProperty(@Nonnull String propertyName, Object value) {
ChildQueryAdapter queryAdapter = workingTreeImpl.storageBackend.getAdapter(ChildQueryAdapter.class);

StorageId storageId = this.getStorageId();
Expand Down Expand Up @@ -123,10 +124,24 @@ public Iterable<WorkingTreeNode> getChildrenByProperty(String propertyName, Obje
// return ret;
// }

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

@Override @Nonnull
public WorkingTreeNode addChild(String childName) {
WorkingTreeNodeImpl child = new WorkingTreeNodeImpl(workingTreeImpl, null, this);
public WorkingTreeNode addChild(@Nonnull String childName) {
WorkingTreeNode child = addChild();
child.setProperty(Property.PROPERTY_NAME, new Property(childName));

return child;
}

@Override
@Nonnull
public WorkingTreeNode addChild() {
WorkingTreeNodeImpl child = new WorkingTreeNodeImpl(workingTreeImpl, null, this);
child.isNew = true;
workingTreeImpl.loadedNodes.put(child.getStorageId(), child);
if (createdChildren == null) {
Expand Down Expand Up @@ -228,7 +243,7 @@ public int getChildrenCount() {
}

@Nullable
public Property getProperty(String propertyName) {
public Property getProperty(@Nonnull String propertyName) {
return loadProperties().get(propertyName);
// Map<String, Property> properties = workingTreeImpl.storageBackend.readProperties(storedNode.getProperties());
// return properties.get(propertyName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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 {
private @Nonnull WorkingTreeImpl workingTreeImpl;
private @Nullable WorkingTreeNode rootNode;

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

@Override
public @Nonnull Collection<NodeTypeDefinition> listNodeTypeDefinitions() {
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
@@ -0,0 +1,55 @@
package com.dc2f.dstore.hierachynodestore.impl.nodetype;

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

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;
import com.dc2f.dstore.storage.Property;

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

public class NodeTypeDefinitionImpl implements NodeTypeDefinition {
public static final @Nonnull String PROPERTY_NAME = ":name";

private @Nonnull WorkingTreeNode node;

public NodeTypeDefinitionImpl(@Nonnull WorkingTreeNode node) {
// TODO assert node type?
this.node = node;
}

@Override
@Nonnull
public Collection<PropertyDefinition> listPropertyDefinitions() {
List<PropertyDefinition> ret = new ArrayList<>();
for (WorkingTreeNode child : node.getChildren()) {
ret.add(new PropertyDefinitionImpl(assertNotNull(child)));
}
return assertNotNull(Collections.unmodifiableCollection(ret));
}

@Override
@Nonnull
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);
}

@Override
@Nonnull
public Collection<NodeTypeDefinition> listAllowedChildNodeTypes() {
// FIXME implement me :)
return new ArrayList<>();
}


}
Loading

0 comments on commit 1608a2b

Please sign in to comment.