Skip to content

Instantly share code, notes, and snippets.

@noizu
Last active November 24, 2015 10:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save noizu/bebddcd44819ab373988 to your computer and use it in GitHub Desktop.
Save noizu/bebddcd44819ab373988 to your computer and use it in GitHub Desktop.
All better. (Generic handling for serialization and encoding of nodes to Google Endpoints and Objectify). Allowing arbitrary tree structures to be encoded/decoded and presented via api.
/*
There we go, much cleaner than https://gist.github.com/noizu/579fe422e3d600b0eee4
encoding/decoding is handled in base class. We still require a recursive generic but we avoid the need to use a seperat helper class by forcing derived class to explicitly state the concrete class used for json encoding children.
getChildren/setChildren
Some boiler plate has to be added whenever including a Tree structure but its much more minimal than it was. No conditional logic, etc. Load to and from objectify appears to work. Load to and from memcache appears to work.
Important, any entity that embeds a tree should call OnSave, OnLoad on the nested entity as these may not called under certain conditions such as when using custom memcached encoding/decoding logic to work around poor built in cache granularity controls. ( https://github.com/noizu/fragmented-keys-4java )
*/
public class MyNode extends ObjectifyNodeBase<MyNode> {
// MyNode specific fields and methods
// Abstract Interface (must be explicitly included in top most class
// so that Google Endpoints associates concrete class with the field children.
public List<MyNode> getChildren() {
return children;
}
public void setChildren(List<MyNode> value) {
children = value;
}
}
/* This layer allows us to serialize and deseralize using a flat list of MyNodes with our recursive (children) references. */
/* This introducing a sychroniziation risk between tree & hierarchy, but in practice is should not become a problem as internally everthing is pointing at the same objects.*/
public class MyNodeTree {
@JsonIgnore /* we don't encode the flat format when returning json */
public List<MyNode> tree;
@Ignore /* we don't save this field as it is included in tree. the system would only save the first node as is since it skips children nodes due to @Ignore flag in base class */
public MyNode hierarchy;
public MyNodeTree() {
this.tree = new ArrayList<>();
this.hierarchy = new ConstraintNode();
}
//==========================================================================
// Objectify
//==========================================================================
@OnSave public void onSave() {
this.tree = this.hierarchy.flatten();
}
@OnLoad public void onLoad() {
this.hierarchy = new ConstraintNode().expand(tree);
}
/* other tree specific stuff - normally nothing */
}
/* Generic handling of serialization/deserilization in a google endpoints and google objectify friendly manner */
public abstract class ObjectifyNodeBase<NODE_CLASS extends ObjectifyNodeBase> {
public Integer nodeType;
public String materializedPath;
@Ignore
public List<NODE_CLASS> children;
/* Required to force deriving class to explicitly add method with out referencing generic type */
public abstract List<NODE_CLASS> getChildren();
public abstract void setChildren(List<NODE_CLASS> children);
//======================================================================
// Serialization/Deserialization
//======================================================================
public List<NODE_CLASS> flatten() {
List<NODE_CLASS> serializable = new ArrayList<>();
updateNodeMaterializedPathAndPopulateTreeArray("1", (NODE_CLASS) this, /* out */ serializable);
return serializable;
}
protected void updateNodeMaterializedPathAndPopulateTreeArray(String path, NODE_CLASS node, /* out */ List<NODE_CLASS> serializable) {
// 2. Calculate Materialized Path & Populate tree array.
// - Walk through tree
// - Build path string (root is node 1, it's first child is 1.1, it's second child is 1.2, etc.)
// - Update node's materializedPath property with current location.
// - Add hierarchy node to tree array
// - Recursively Process children
serializable.add(node);
node.materializedPath = path;
List<NODE_CLASS> _children = node.children;
if (_children != null) {
for (int i = 0; i < _children.size(); i++) {
updateNodeMaterializedPathAndPopulateTreeArray(path + "." + (i + 1), _children.get(i), /* out */ serializable);
}
}
}
public NODE_CLASS expand(List<NODE_CLASS> serialized) {
if (serialized != null) {
// 1. Walk through the array list, and build matrix lookup table "Path" -> Node O(n)
HashMap<String, NODE_CLASS> lookup = new HashMap<>();
for (NODE_CLASS node : serialized) {
lookup.put(node.materializedPath, node);
}
// 2. Build tree based on matrix encoding.
// - start with node "1",
// - progress through 1's children until list is exhausted, "1.1", "1.2" . . .
// - then walk through each child's children until exhausted.
if (lookup.containsKey("1")) {
NODE_CLASS root = lookup.get("1");
updateNodeChildren("1", root, lookup);
return root;
} else {
// todo - log malformed data.
return null;
}
}
// todo - log null serialized.
return null;
}
protected void updateNodeChildren(String path, NODE_CLASS node, HashMap<String, NODE_CLASS> lookup) {
// Prepare data structures.
List<NODE_CLASS> _children = new ArrayList<>();
// 1. Check hashmap for node's children
// 2. If child found recursively process to find it's children.
// 3. Once child has been recursively processed append to node's children list.
// 4. Proceed to check for remaining children. Return once no remaining children are found.
int i = 1;
boolean sentinal = true;
while (sentinal) {
String childPath = path + "." + i;
if (lookup.containsKey(childPath)) {
// found child, process child and then append to children.
NODE_CLASS childNode = lookup.get(childPath);
updateNodeChildren(childPath, childNode, lookup);
_children.add(childNode);
i++;
} else {
sentinal = false;
}
}
node.children = _children;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment