* 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;
}
/**
* 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
*/

View File

@ -21,33 +21,58 @@ import java.util.Set;
import java.util.HashSet;
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 {
private static org.apache.log4j.Logger log = Logger.getLogger(PathTree.class);
private long id = 0;
private List<NodePair> children = new ArrayList<NodePair>();
private List<PathNode> parents = new ArrayList<PathNode>();
private NodeContext ctx = null;
/**
* New node, with 0 id.
*/
public PathNode() {
this(new NodeContext());
}
/**
* New node, with id determined by provided ctx
*
* @param ctx NodeContext, for id increments
*/
public PathNode(NodeContext ctx) {
this.ctx = ctx;
this.id = this.ctx.nextId();
}
/**
* This node's id
*/
public long getId() {
return this.id;
}
/**
* The NodeContext used by this node
*/
public NodeContext getContext() {
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() {
return getAllNodes(this);
}
@ -67,13 +92,6 @@ public class PathNode {
return nodes;
}
public void addParent(PathNode cp) {
if (!parents.contains(cp)) {
this.parents.add(cp);
}
}
public List<NodePair> getChildren() {
Collections.sort(this.children);
return this.children;
@ -83,16 +101,45 @@ public class PathNode {
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) {
this.parents = parents;
}
/**
* add entire List of parents
*/
public void addParents(List<PathNode> parents) {
for (PathNode pn : parents) {
addParent(pn);
}
}
/**
* get the inferred name of this node, through the referring NodePair.
*/
public String getName() {
String name = "";
for (NodePair child : this.getParents().get(0).getChildren()) {
@ -103,10 +150,16 @@ public class PathNode {
return name;
}
/**
* Traverse up the tree, and get the highest ancestor PathNode.
*/
public PathNode getStartNode() {
return getStartNode(this);
}
/**
* Traverse up the tree, and get the highest ancestor PathNode, for node.
*/
public PathNode getStartNode(PathNode node) {
if (node.getParents().size() == 0) {
return node; // this is the end!
@ -118,10 +171,16 @@ public class PathNode {
return node; // when in doubt, return yourself
}
/**
* Traverse down the tree, and get the "endMarker" child.
*/
public PathNode getEndNode() {
return getEndNode(this);
}
/**
* Traverse down the tree, and get the "endMarker" child, for node.
*/
public PathNode getEndNode(PathNode node) {
if (node.getChildren().size() == 0) {
return node; // this is the end!
@ -132,7 +191,7 @@ public class PathNode {
return node; // when in doubt, return yourself
}
/*
/**
* same number of children with the same names for child nodes
*/
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.
*
* TODO - this is a stub
* FIXME This is not working correctly yet
*
* @param that PathNode to check for
* @return boolean of truth!
@ -181,17 +240,14 @@ public class PathNode {
if (thisnp.getName().startsWith("$") || thisnp.getName().equals(thatnp.getName())) {
result = thisnp.getConnection().includes(thatnp.getConnection());
found.add(new Boolean(result).booleanValue());
log.debug("includes: " + thisnp + " == " + thatnp);
break;
}
found.add(Boolean.FALSE);
}
}
if (found.contains(Boolean.FALSE)) {
return false;
} else {
return true;
}
return (!found.contains(Boolean.FALSE));
}
/**

View File

@ -236,7 +236,7 @@ public class PathTree {
while (value != -1) {
String someBits = Integer.toString(value, 2);
for (int pad = 0; pad < 8 - someBits.length(); pad++) {
this.nodeBits.append("0");
this.getNodeBits().append("0");
}
this.getNodeBits().append(someBits);
value = bais.read();
@ -334,7 +334,6 @@ public class PathTree {
break;
}
}
// XXX do hot stuff
}
return false;
@ -349,7 +348,7 @@ public class PathTree {
*/
public void setContentSets(List<String> contentSets) throws PayloadException {
this.modified = true;
this.nodeBits = null;
this.setNodeBits(null);
this.setNodeCount(0);
this.pathNodeContext = new NodeContext();
@ -381,7 +380,6 @@ public class PathTree {
}
this.payload = data.toByteArray();
this.modified = false;
}
@ -462,9 +460,9 @@ public class PathTree {
* @return the Set of weighted PathNode
*/
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>();
StringBuffer myNodeBits = new StringBuffer(this.getNodeBits().toString());
StringBuffer myNodeBits = new StringBuffer(theseNodeBits.toString());
for (HuffNode node : nodeDictionary) {
pathNodes.add((PathNode) node.getValue());
boolean stillNode = true;
@ -476,8 +474,7 @@ public class PathTree {
while (nameValue == null && stillNode) {
nameBits.append(myNodeBits.charAt(0));
myNodeBits.deleteCharAt(0);
Object lookupValue = findHuffNodeValueByBits(pathTrie,
nameBits.toString());
Object lookupValue = pathTrie.findByBits(nameBits.toString()).getValue();
if (lookupValue != null) {
if (lookupValue.equals(HuffNode.END_NODE)) {
stillNode = false;
@ -495,8 +492,7 @@ public class PathTree {
while (nodeValue == null && stillNode) {
pathBits.append(myNodeBits.charAt(0));
myNodeBits.deleteCharAt(0);
PathNode lookupValue = (PathNode) findHuffNodeValueByBits(nodeTrie,
pathBits.toString());
PathNode lookupValue = (PathNode) nodeTrie.findByBits(pathBits.toString()).getValue();
if (lookupValue != null) {
nodeValue = lookupValue;
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) {
int smallest = -1;
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;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
@ -38,16 +42,46 @@ public class TestPathNode {
assertTrue(pn0.isEquivalentTo(pn1));
PathNode pn2 = new PathNode(pn0.getContext());
PathNode pn3 = new PathNode(pn1.getContext());
NodePair np0b = new NodePair("bar",endMarker);
pn0.addChild(np0b);
NodePair np1b = new NodePair("baz",endMarker);
pn1.addChild(np1b);
// XXX finish this test !!
//assertTrue(pn0.isEquivalentTo(pn1));
assertFalse(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.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
@ -31,7 +31,7 @@ public class TestPathTree {
@Test
public void testNew1() {
PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list");
List<String> contents = TestHelpers.loadContents(this, "contents.list");
try {
pt = new PathTree(contents);
} catch (PayloadException ex) {
@ -43,7 +43,7 @@ public class TestPathTree {
@Test
public void testNew2() {
PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list");
List<String> contents = TestHelpers.loadContents(this, "contents.list");
try {
pt.setContentSets(contents);
} catch (PayloadException ex) {
@ -55,7 +55,7 @@ public class TestPathTree {
@Test
public void testValidation() {
PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list");
List<String> contents = TestHelpers.loadContents(this, "contents.list");
// matches a path
String shouldPass = "/content/beta/rhel/server/5/5server/x86_64/sap/os/repomd.xml";
// is not a match
@ -68,7 +68,7 @@ public class TestPathTree {
fail(ex.toString());
}
// for good measure ...
assertTrue(cmpStrings(contents, pt.toList()));
assertTrue(TestHelpers.cmpStrings(contents, pt.toList()));
assertTrue(pt.validate(shouldPass));
assertFalse(pt.validate(shouldFail));
@ -81,7 +81,7 @@ public class TestPathTree {
@Test
public void testRootNode() {
PathTree pt = new PathTree();
List<String> contents = loadContents("contents.list");
List<String> contents = TestHelpers.loadContents(this, "contents.list");
try {
pt.setContentSets(contents);
} catch (PayloadException ex) {
@ -111,7 +111,7 @@ public class TestPathTree {
List<String> contents0;
List<String> contents1;
bytes = loadBytes("test.bin");
bytes = TestHelpers.loadBytes(this, "test.bin");
pt0 = new PathTree(bytes);
contents0 = pt0.toList();
for (String str : contents0) {
@ -126,7 +126,7 @@ public class TestPathTree {
assertNotNull(pt1);
//printByteArray(pt1.getPayload());
contents1 = pt1.toList();
assertTrue(cmpStrings(contents0, contents1));
assertTrue(TestHelpers.cmpStrings(contents0, contents1));
for (String str : contents1) {
System.out.println(str);
}
@ -142,7 +142,7 @@ public class TestPathTree {
PathTree pt1;
PathTree pt2 = new PathTree();
PathTree pt3;
List<String> contents0 = loadContents("contents.list");
List<String> contents0 = TestHelpers.loadContents(this, "contents.list");
List<String> contents1;
List<String> contents2;
List<String> contents3;
@ -162,7 +162,7 @@ public class TestPathTree {
contents1 = pt1.toList();
// FIXME These next two fail
assertTrue(cmpStrings(contents0, contents1));
assertTrue(TestHelpers.cmpStrings(contents0, contents1));
assertEquals(contents0.size(), contents1.size());
@ -174,89 +174,38 @@ public class TestPathTree {
}
contents2 = pt2.toList();
assertTrue(cmpStrings(contents1, contents2));
assertTrue(TestHelpers.cmpStrings(contents1, contents2));
assertEquals(contents1.size(), contents2.size());
pt3 = new PathTree(pt2.getPayload());
contents3 = pt3.toList();
assertTrue(cmpStrings(contents2, contents3));
assertTrue(TestHelpers.cmpStrings(contents2, contents3));
assertEquals(contents2.size(), contents3.size());
}
// Helpers
//
private 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);
}
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];
/*
@Test
public void testSettingContentsTwice() {
PathNode pn0 = new PathNode();
PathNode pn1 = new PathNode();
PathTree pt = new PathTree();
List<String> contents0 = TestHelpers.loadContents(this, "contents.list");
List<String> contents1 = TestHelpers.loadContents(this, "contents_small.list");
try {
while ((nRead = in.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} catch (IOException ex) {
pt.setContentSets(contents0);
pn0 = pt.getRootPathNode(); // setup the larger PathNode
System.out.println(contents1);
pt.setContentSets(contents1);
pn1 = pt.getRootPathNode(); // setup the small PathNode
} catch (PayloadException ex) {
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