* moving HuffNode search, into the HuffNode class

* documenting the PathNode class
* trying to track down the empty stringbuffer on twice called PathTree.setContentSets
* cleaned up the unit test helper (TestHelpers)
This commit is contained in:
Vincent Batts 2012-11-08 14:06:20 -05:00
parent 2deb763c1c
commit cf6fa76d5e
7 changed files with 289 additions and 134 deletions

View file

@ -110,6 +110,34 @@ public class HuffNode {
return this.right; return this.right;
} }
/**
* search down the tree, for the node,
* at address of String bits
*/
public HuffNode findByBits(String bits) {
return this.findByBits(this, bits);
}
/**
* search down the tree, for the node,
* at address of String bits, on HuffNode trie
*/
public HuffNode findByBits(HuffNode trie, String bits) {
if (bits.length() == 0) {
return trie;
}
char bit = bits.charAt(0);
if (bit == '0') {
if (getLeft() == null) { throw new RuntimeException("Encoded path not in trie"); }
return getLeft().findByBits(bits.substring(1));
}
else if (bit == '1') {
if (getRight() == null) { throw new RuntimeException("Encoded path not in trie"); }
return getRight().findByBits(bits.substring(1));
}
return null;
}
/** /**
* pretty information * pretty information
*/ */

View file

@ -21,33 +21,58 @@ import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.util.Collections; import java.util.Collections;
import org.apache.log4j.Logger;
/**
* PathNode is the relationship to an item in the path tree.
*
* It holds the relationships to children, as well as all parents that regard it as a child.
*
* The Name of a given PathNode, is inferred by the NodePair that regards this PathNode as it's "connection"
*/
public class PathNode { public class PathNode {
private static org.apache.log4j.Logger log = Logger.getLogger(PathTree.class);
private long id = 0; private long id = 0;
private List<NodePair> children = new ArrayList<NodePair>(); private List<NodePair> children = new ArrayList<NodePair>();
private List<PathNode> parents = new ArrayList<PathNode>(); private List<PathNode> parents = new ArrayList<PathNode>();
private NodeContext ctx = null; private NodeContext ctx = null;
/**
* New node, with 0 id.
*/
public PathNode() { public PathNode() {
this(new NodeContext()); this(new NodeContext());
} }
/**
* New node, with id determined by provided ctx
*
* @param ctx NodeContext, for id increments
*/
public PathNode(NodeContext ctx) { public PathNode(NodeContext ctx) {
this.ctx = ctx; this.ctx = ctx;
this.id = this.ctx.nextId(); this.id = this.ctx.nextId();
} }
/**
* This node's id
*/
public long getId() { public long getId() {
return this.id; return this.id;
} }
/**
* The NodeContext used by this node
*/
public NodeContext getContext() { public NodeContext getContext() {
return this.ctx; return this.ctx;
} }
public void addChild(NodePair cp) { /**
this.children.add(cp); * Get the nodes, from here down.
} *
* @return A unique list of all PathNode nodes, from this node down
*/
public Set<PathNode> getAllNodes() { public Set<PathNode> getAllNodes() {
return getAllNodes(this); return getAllNodes(this);
} }
@ -67,13 +92,6 @@ public class PathNode {
return nodes; return nodes;
} }
public void addParent(PathNode cp) {
if (!parents.contains(cp)) {
this.parents.add(cp);
}
}
public List<NodePair> getChildren() { public List<NodePair> getChildren() {
Collections.sort(this.children); Collections.sort(this.children);
return this.children; return this.children;
@ -83,16 +101,45 @@ public class PathNode {
return this.parents; return this.parents;
} }
/**
* A NodePair cp, as a child.
*
* TODO - determine uniqueness?
*/
public void addChild(NodePair cp) {
this.children.add(cp);
}
/**
* A PathNode cp, as a parent.
*
* Checks whether this parent is already a parent.
*/
public void addParent(PathNode cp) {
if (!parents.contains(cp)) {
this.parents.add(cp);
}
}
/**
* Set parents as the new List collection.
*/
public void setParents(List<PathNode> parents) { public void setParents(List<PathNode> parents) {
this.parents = parents; this.parents = parents;
} }
/**
* add entire List of parents
*/
public void addParents(List<PathNode> parents) { public void addParents(List<PathNode> parents) {
for (PathNode pn : parents) { for (PathNode pn : parents) {
addParent(pn); addParent(pn);
} }
} }
/**
* get the inferred name of this node, through the referring NodePair.
*/
public String getName() { public String getName() {
String name = ""; String name = "";
for (NodePair child : this.getParents().get(0).getChildren()) { for (NodePair child : this.getParents().get(0).getChildren()) {
@ -103,10 +150,16 @@ public class PathNode {
return name; return name;
} }
/**
* Traverse up the tree, and get the highest ancestor PathNode.
*/
public PathNode getStartNode() { public PathNode getStartNode() {
return getStartNode(this); return getStartNode(this);
} }
/**
* Traverse up the tree, and get the highest ancestor PathNode, for node.
*/
public PathNode getStartNode(PathNode node) { public PathNode getStartNode(PathNode node) {
if (node.getParents().size() == 0) { if (node.getParents().size() == 0) {
return node; // this is the end! return node; // this is the end!
@ -118,10 +171,16 @@ public class PathNode {
return node; // when in doubt, return yourself return node; // when in doubt, return yourself
} }
/**
* Traverse down the tree, and get the "endMarker" child.
*/
public PathNode getEndNode() { public PathNode getEndNode() {
return getEndNode(this); return getEndNode(this);
} }
/**
* Traverse down the tree, and get the "endMarker" child, for node.
*/
public PathNode getEndNode(PathNode node) { public PathNode getEndNode(PathNode node) {
if (node.getChildren().size() == 0) { if (node.getChildren().size() == 0) {
return node; // this is the end! return node; // this is the end!
@ -132,7 +191,7 @@ public class PathNode {
return node; // when in doubt, return yourself return node; // when in doubt, return yourself
} }
/* /**
* same number of children with the same names for child nodes * same number of children with the same names for child nodes
*/ */
public boolean isEquivalentTo(PathNode that) { public boolean isEquivalentTo(PathNode that) {
@ -159,7 +218,7 @@ public class PathNode {
/** /**
* check whether current PathNode, includes the paths in PathNode that, like a mask. * check whether current PathNode, includes the paths in PathNode that, like a mask.
* *
* TODO - this is a stub * FIXME This is not working correctly yet
* *
* @param that PathNode to check for * @param that PathNode to check for
* @return boolean of truth! * @return boolean of truth!
@ -181,17 +240,14 @@ public class PathNode {
if (thisnp.getName().startsWith("$") || thisnp.getName().equals(thatnp.getName())) { if (thisnp.getName().startsWith("$") || thisnp.getName().equals(thatnp.getName())) {
result = thisnp.getConnection().includes(thatnp.getConnection()); result = thisnp.getConnection().includes(thatnp.getConnection());
found.add(new Boolean(result).booleanValue()); found.add(new Boolean(result).booleanValue());
log.debug("includes: " + thisnp + " == " + thatnp);
break; break;
} }
found.add(Boolean.FALSE); found.add(Boolean.FALSE);
} }
} }
if (found.contains(Boolean.FALSE)) { return (!found.contains(Boolean.FALSE));
return false;
} else {
return true;
}
} }
/** /**

View file

@ -236,7 +236,7 @@ public class PathTree {
while (value != -1) { while (value != -1) {
String someBits = Integer.toString(value, 2); String someBits = Integer.toString(value, 2);
for (int pad = 0; pad < 8 - someBits.length(); pad++) { for (int pad = 0; pad < 8 - someBits.length(); pad++) {
this.nodeBits.append("0"); this.getNodeBits().append("0");
} }
this.getNodeBits().append(someBits); this.getNodeBits().append(someBits);
value = bais.read(); value = bais.read();
@ -334,7 +334,6 @@ public class PathTree {
break; break;
} }
} }
// XXX do hot stuff
} }
return false; return false;
@ -349,7 +348,7 @@ public class PathTree {
*/ */
public void setContentSets(List<String> contentSets) throws PayloadException { public void setContentSets(List<String> contentSets) throws PayloadException {
this.modified = true; this.modified = true;
this.nodeBits = null; this.setNodeBits(null);
this.setNodeCount(0); this.setNodeCount(0);
this.pathNodeContext = new NodeContext(); this.pathNodeContext = new NodeContext();
@ -381,7 +380,6 @@ public class PathTree {
} }
this.payload = data.toByteArray(); this.payload = data.toByteArray();
this.modified = false; this.modified = false;
} }
@ -462,9 +460,9 @@ public class PathTree {
* @return the Set of weighted PathNode * @return the Set of weighted PathNode
*/ */
private Set<PathNode> populatePathNodes(List<HuffNode> nodeDictionary, private Set<PathNode> populatePathNodes(List<HuffNode> nodeDictionary,
HuffNode pathTrie, HuffNode nodeTrie, StringBuffer nodeBits) { HuffNode pathTrie, HuffNode nodeTrie, StringBuffer theseNodeBits) {
Set<PathNode> pathNodes = new HashSet<PathNode>(); Set<PathNode> pathNodes = new HashSet<PathNode>();
StringBuffer myNodeBits = new StringBuffer(this.getNodeBits().toString()); StringBuffer myNodeBits = new StringBuffer(theseNodeBits.toString());
for (HuffNode node : nodeDictionary) { for (HuffNode node : nodeDictionary) {
pathNodes.add((PathNode) node.getValue()); pathNodes.add((PathNode) node.getValue());
boolean stillNode = true; boolean stillNode = true;
@ -476,8 +474,7 @@ public class PathTree {
while (nameValue == null && stillNode) { while (nameValue == null && stillNode) {
nameBits.append(myNodeBits.charAt(0)); nameBits.append(myNodeBits.charAt(0));
myNodeBits.deleteCharAt(0); myNodeBits.deleteCharAt(0);
Object lookupValue = findHuffNodeValueByBits(pathTrie, Object lookupValue = pathTrie.findByBits(nameBits.toString()).getValue();
nameBits.toString());
if (lookupValue != null) { if (lookupValue != null) {
if (lookupValue.equals(HuffNode.END_NODE)) { if (lookupValue.equals(HuffNode.END_NODE)) {
stillNode = false; stillNode = false;
@ -495,8 +492,7 @@ public class PathTree {
while (nodeValue == null && stillNode) { while (nodeValue == null && stillNode) {
pathBits.append(myNodeBits.charAt(0)); pathBits.append(myNodeBits.charAt(0));
myNodeBits.deleteCharAt(0); myNodeBits.deleteCharAt(0);
PathNode lookupValue = (PathNode) findHuffNodeValueByBits(nodeTrie, PathNode lookupValue = (PathNode) nodeTrie.findByBits(pathBits.toString()).getValue();
pathBits.toString());
if (lookupValue != null) { if (lookupValue != null) {
nodeValue = lookupValue; nodeValue = lookupValue;
nodeValue.addParent((PathNode) node.getValue()); nodeValue.addParent((PathNode) node.getValue());
@ -540,26 +536,6 @@ public class PathTree {
} }
} }
private Object findHuffNodeValueByBits(HuffNode trie, String bits) {
HuffNode left = trie.getLeft();
HuffNode right = trie.getRight();
if (bits.length() == 0) {
return trie.getValue();
}
char bit = bits.charAt(0);
if (bit == '0') {
if (left == null) { throw new RuntimeException("Encoded path not in trie"); }
return findHuffNodeValueByBits(left, bits.substring(1));
}
else if (bit == '1') {
if (right == null) { throw new RuntimeException("Encoded path not in trie"); }
return findHuffNodeValueByBits(right, bits.substring(1));
}
return null;
}
private int findSmallest(int exclude, List<HuffNode> nodes) { private int findSmallest(int exclude, List<HuffNode> nodes) {
int smallest = -1; int smallest = -1;
for (int index = 0; index < nodes.size(); index++) { for (int index = 0; index < nodes.size(); index++) {

View file

@ -0,0 +1,107 @@
package com.redhat.trie;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
/**
* This class is just to provide helpers for the other tests
*/
public class TestHelpers {
/**
* junit requires at least one runnable test
*/
@Test
public void testDeadVicker() {
assertNotNull(new String("What's its diocese?"));
}
// Helpers
//
public static boolean cmpStrings(List<String> thisList, List<String> thatList) {
Collection<String> thisColl = new ArrayList(thisList);
Collection<String> thatColl = new ArrayList(thatList);
Collection<String> similar = new HashSet<String>( thisColl );
Collection<String> different = new HashSet<String>();
different.addAll( thisColl );
different.addAll( thatColl );
similar.retainAll( thatColl );
different.removeAll( similar );
if (different.size() > 0) {
System.out.printf("Different:%s%n", different);
}
return (different.size() == 0);
}
public static void printByteArray(byte[] bytes) {
int width = 30;
int counter = 0;
for (byte b : bytes) {
System.out.format("%02X ", b);
counter++;
if (counter > width) {
counter = 0;
System.out.println();
}
}
System.out.println();
}
public static InputStream resStream(Object klass, String filename) {
return klass.getClass().getClassLoader().getResourceAsStream(filename);
}
public static byte[] loadBytes(Object klass, String filename) {
InputStream in = resStream(klass, filename);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16834];
try {
while ((nRead = in.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} catch (IOException ex) {
fail(ex.toString());
}
return buffer.toByteArray();
}
public static List<String> loadContents(Object klass, String filename) {
String content;
List<String> contentList = new ArrayList<String>();
InputStream in = resStream(klass, filename);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
try {
while ((content = br.readLine()) != null) {
contentList.add(content);
}
} catch (IOException ex) {
fail();
}
return contentList;
}
}

View file

@ -1,8 +1,12 @@
package com.redhat.trie; package com.redhat.trie;
import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test; import org.junit.Test;
@ -38,16 +42,46 @@ public class TestPathNode {
assertTrue(pn0.isEquivalentTo(pn1)); assertTrue(pn0.isEquivalentTo(pn1));
PathNode pn2 = new PathNode(pn0.getContext());
PathNode pn3 = new PathNode(pn1.getContext());
NodePair np0b = new NodePair("bar",endMarker); NodePair np0b = new NodePair("bar",endMarker);
pn0.addChild(np0b); pn0.addChild(np0b);
NodePair np1b = new NodePair("baz",endMarker);
pn1.addChild(np1b);
// XXX finish this test !! assertFalse(pn0.isEquivalentTo(pn1));
//assertTrue(pn0.isEquivalentTo(pn1)); }
@Test
public void testIncludes() {
PathNode pn0 = new PathNode();
PathNode pn1 = new PathNode();
PathNode pn2 = new PathNode();
PathTree pt0 = new PathTree();
PathTree pt1 = new PathTree();
PathTree pt2 = new PathTree();
List<String> contents0 = TestHelpers.loadContents(this, "contents.list");
List<String> contents1 = TestHelpers.loadContents(this, "contents_small.list");
try {
pt0.setContentSets(contents0);
pn0 = pt0.getRootPathNode(); // setup the larger PathNode
pt1.setContentSets(contents1);
pn1 = pt1.getRootPathNode(); // setup the small PathNode
contents1.add(new String("/this/is/not/in/the/list"));
pt2.setContentSets(contents1);
pn2 = pt2.getRootPathNode(); // setup the small PathNode
} catch (PayloadException ex) {
fail(ex.toString());
}
assertNotNull(pn0);
assertNotNull(pn1);
assertNotNull(pn2);
// FIXME - Finish the test first
//assertTrue(pn0.includes(pn1));
//assertFalse(pn0.includes(pn2));
} }
} }

View file

@ -7,9 +7,9 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.InputStream; import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -31,7 +31,7 @@ public class TestPathTree {
@Test @Test
public void testNew1() { public void testNew1() {
PathTree pt = new PathTree(); PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list"); List<String> contents = TestHelpers.loadContents(this, "contents.list");
try { try {
pt = new PathTree(contents); pt = new PathTree(contents);
} catch (PayloadException ex) { } catch (PayloadException ex) {
@ -43,7 +43,7 @@ public class TestPathTree {
@Test @Test
public void testNew2() { public void testNew2() {
PathTree pt = new PathTree(); PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list"); List<String> contents = TestHelpers.loadContents(this, "contents.list");
try { try {
pt.setContentSets(contents); pt.setContentSets(contents);
} catch (PayloadException ex) { } catch (PayloadException ex) {
@ -55,7 +55,7 @@ public class TestPathTree {
@Test @Test
public void testValidation() { public void testValidation() {
PathTree pt = new PathTree(); PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list"); List<String> contents = TestHelpers.loadContents(this, "contents.list");
// matches a path // matches a path
String shouldPass = "/content/beta/rhel/server/5/5server/x86_64/sap/os/repomd.xml"; String shouldPass = "/content/beta/rhel/server/5/5server/x86_64/sap/os/repomd.xml";
// is not a match // is not a match
@ -68,7 +68,7 @@ public class TestPathTree {
fail(ex.toString()); fail(ex.toString());
} }
// for good measure ... // for good measure ...
assertTrue(cmpStrings(contents, pt.toList())); assertTrue(TestHelpers.cmpStrings(contents, pt.toList()));
assertTrue(pt.validate(shouldPass)); assertTrue(pt.validate(shouldPass));
assertFalse(pt.validate(shouldFail)); assertFalse(pt.validate(shouldFail));
@ -81,7 +81,7 @@ public class TestPathTree {
@Test @Test
public void testRootNode() { public void testRootNode() {
PathTree pt = new PathTree(); PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list"); List<String> contents = TestHelpers.loadContents(this, "contents.list");
try { try {
pt.setContentSets(contents); pt.setContentSets(contents);
} catch (PayloadException ex) { } catch (PayloadException ex) {
@ -111,7 +111,7 @@ public class TestPathTree {
List<String> contents0; List<String> contents0;
List<String> contents1; List<String> contents1;
bytes = loadBytes("test.bin"); bytes = TestHelpers.loadBytes(this, "test.bin");
pt0 = new PathTree(bytes); pt0 = new PathTree(bytes);
contents0 = pt0.toList(); contents0 = pt0.toList();
for (String str : contents0) { for (String str : contents0) {
@ -126,7 +126,7 @@ public class TestPathTree {
assertNotNull(pt1); assertNotNull(pt1);
//printByteArray(pt1.getPayload()); //printByteArray(pt1.getPayload());
contents1 = pt1.toList(); contents1 = pt1.toList();
assertTrue(cmpStrings(contents0, contents1)); assertTrue(TestHelpers.cmpStrings(contents0, contents1));
for (String str : contents1) { for (String str : contents1) {
System.out.println(str); System.out.println(str);
} }
@ -142,7 +142,7 @@ public class TestPathTree {
PathTree pt1; PathTree pt1;
PathTree pt2 = new PathTree(); PathTree pt2 = new PathTree();
PathTree pt3; PathTree pt3;
List<String> contents0 = loadContents("contents.list"); List<String> contents0 = TestHelpers.loadContents(this, "contents.list");
List<String> contents1; List<String> contents1;
List<String> contents2; List<String> contents2;
List<String> contents3; List<String> contents3;
@ -162,7 +162,7 @@ public class TestPathTree {
contents1 = pt1.toList(); contents1 = pt1.toList();
// FIXME These next two fail // FIXME These next two fail
assertTrue(cmpStrings(contents0, contents1)); assertTrue(TestHelpers.cmpStrings(contents0, contents1));
assertEquals(contents0.size(), contents1.size()); assertEquals(contents0.size(), contents1.size());
@ -174,89 +174,38 @@ public class TestPathTree {
} }
contents2 = pt2.toList(); contents2 = pt2.toList();
assertTrue(cmpStrings(contents1, contents2)); assertTrue(TestHelpers.cmpStrings(contents1, contents2));
assertEquals(contents1.size(), contents2.size()); assertEquals(contents1.size(), contents2.size());
pt3 = new PathTree(pt2.getPayload()); pt3 = new PathTree(pt2.getPayload());
contents3 = pt3.toList(); contents3 = pt3.toList();
assertTrue(cmpStrings(contents2, contents3)); assertTrue(TestHelpers.cmpStrings(contents2, contents3));
assertEquals(contents2.size(), contents3.size()); assertEquals(contents2.size(), contents3.size());
} }
/*
// Helpers @Test
// public void testSettingContentsTwice() {
private boolean cmpStrings(List<String> thisList, List<String> thatList) { PathNode pn0 = new PathNode();
Collection<String> thisColl = new ArrayList(thisList); PathNode pn1 = new PathNode();
Collection<String> thatColl = new ArrayList(thatList); PathTree pt = new PathTree();
List<String> contents0 = TestHelpers.loadContents(this, "contents.list");
Collection<String> similar = new HashSet<String>( thisColl ); List<String> contents1 = TestHelpers.loadContents(this, "contents_small.list");
Collection<String> different = new HashSet<String>();
different.addAll( thisColl );
different.addAll( thatColl );
similar.retainAll( thatColl );
different.removeAll( similar );
if (different.size() > 0) {
System.out.printf("Different:%s%n", different);
}
return (different.size() == 0);
}
private void printByteArray(byte[] bytes) {
int width = 30;
int counter = 0;
for (byte b : bytes) {
System.out.format("%02X ", b);
counter++;
if (counter > width) {
counter = 0;
System.out.println();
}
}
System.out.println();
}
private InputStream resStream(String filename) {
return getClass().getClassLoader().getResourceAsStream(filename);
}
private byte[] loadBytes(String filename) {
InputStream in = resStream(filename);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16834];
try { try {
while ((nRead = in.read(data, 0, data.length)) != -1) { pt.setContentSets(contents0);
buffer.write(data, 0, nRead); pn0 = pt.getRootPathNode(); // setup the larger PathNode
} System.out.println(contents1);
buffer.flush(); pt.setContentSets(contents1);
} catch (IOException ex) { pn1 = pt.getRootPathNode(); // setup the small PathNode
} catch (PayloadException ex) {
fail(ex.toString()); fail(ex.toString());
} }
return buffer.toByteArray(); assertNotNull(pn0);
} }
*/
private List<String> loadContents(String filename) {
String content;
List<String> contentList = new ArrayList<String>();
InputStream in = resStream(filename);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
try {
while ((content = br.readLine()) != null) {
contentList.add(content);
}
} catch (IOException ex) {
fail();
}
return contentList;
}
} }

View file

@ -0,0 +1,5 @@
/content/beta/rhel/server/5/$releasever/$basearch/vt/os
/content/dist/rhel/server/5/$releasever/$basearch/vt/os
/content/dist/rhel/server/6/$releasever/$basearch/sap/os
/content/dist/rhel/server/6/$releasever/$basearch/debug
/content/rhb/rhel/client/6/$releasever/$basearch/devtoolset/os