I need your help because I don't find the solution in Java for my problem.
I stored in object LinkedHashMap<String, String> this content:
TAG1.TAG2.TAG11 : value1
TAG1.TAG2.TAG12 : value2
TAG1.TAG2.TAG3.TAG131 : value3
TAG1.TAG2.TAG3.TAG132 : value4
TAG1.TAG2.TAG3.TAG133 : value5
TAG1.TAG2.TAG3.TAG134 : value6
TAG1.TAG4.TAG5.TAG21 : value7
TAG1.TAG4.TAG5.TAG22 : value8
TAG1.TAG4.TAG5.TAG23 : value9
TAG6 : value10
I need to display if a tag has 2 or more children, the list of child.
Here is the expected result:
TAG1.TAG2
TAG11 : value1
TAG12 : value2
TAG1.TAG2.TAG3
TAG131 : value3
TAG132 : value4
TAG133 : value5
TAG134 : value6
TAG1.TAG4.TAG5
TAG21 : value7
TAG22 : value8
TAG23 : value9
TAG6 : value10
EDIT 14/06/2022 :
In fact, my original analyse is bad because initialy I have a XML file :
<TAG1>
<TAG2>
<TAG11>value1</TAG11>
<TAG12>value2</TAG12>
<TAG3>
<TAG131>value3</TAG131>
<TAG132>value4</TAG132>
<TAG133>value5</TAG133>
<TAG134>value6</TAG134>
</TAG3>
</TAG2>
<TAG4>
<TAG5>
<TAG21>value7</TAG21>
<TAG22>value8</TAG22>
<TAG23>value9</TAG23>
</TAG5>
</TAG4>
</TAG1>
<TAG6>value10</TAG6>
And I created a map to store it :
TAG1.TAG2.TAG11 : value1
TAG1.TAG2.TAG12 : value2
TAG1.TAG2.TAG3.TAG131 : value3
TAG1.TAG2.TAG3.TAG132 : value4
TAG1.TAG2.TAG3.TAG133 : value5
TAG1.TAG2.TAG3.TAG134 : value6
TAG1.TAG4.TAG5.TAG21 : value7
TAG1.TAG4.TAG5.TAG22 : value8
TAG1.TAG4.TAG5.TAG23 : value9
TAG6 : value10
But, today I have a this case :
<TAG1>
<TAG2>
<TAG11>value1</TAG11>
<TAG12>value2</TAG12>
<TAG3>
<TAG131>value3</TAG131>
<TAG132>value4</TAG132>
<TAG133>value5</TAG133>
<TAG134>value6</TAG134>
</TAG3>
<TAG3>
<TAG131>value11</TAG131>
<TAG132>value12</TAG132>
<TAG133>value13</TAG133>
<TAG134>value14</TAG134>
</TAG3>
</TAG2>
<TAG4>
<TAG5>
<TAG21>value7</TAG21>
<TAG22>value8</TAG22>
<TAG23>value9</TAG23>
</TAG5>
</TAG4>
</TAG1>
<TAG6>value10</TAG6>
But the Map object does not allow to store many keys (in the example many TAG3). Have you got an idea how I can resolve this problem ?
EDIT 15/06/2022 :
In fact the expected result needs to keep the original XML structure.
Here the result of last sample :
TAG1.TAG2
TAG11 : value1
TAG12 : value2
TAG1.TAG2.TAG3
TAG131 : value3
TAG132 : value4
TAG133 : value5
TAG134 : value6
TAG1.TAG2.TAG3
TAG131 : value11
TAG132 : value12
TAG133 : value13
TAG134 : value14
TAG1.TAG4.TAG5
TAG21 : value7
TAG22 : value8
TAG23 : value9
TAG6 : value10
It's to display xml more human reader.
EDIT 04/07/2022 :
I detect a problem of inconsistent with "new TreeMap<>(Comparator.comparingInt(MyTag::getAppearanceOrder)". Indeed, some MyTag object are the same AppearanceOrder, so there is a problem of inconsistent ordering. Some value in Map are so removed.
To resolve I used :
map.entrySet().stream().sorted(Map.Entry.comparingByKey(. . .))
And I store the result in Map with collect().
Below the working code :
public class Main {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
//Accessing the xml file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("data.xml"));
document.getDocumentElement().normalize();
Element root = document.getDocumentElement();
//Retrieving a List of records where each record contains: the original chain of tags, the numbered chain of tags and the value
String tagSep = ".";
List<Record> listRecords = new ArrayList<>();
visitXMLFile(listRecords, root.getChildNodes(), tagSep, "", "", new HashMap<>());
//Queue sorted by the numbered tag's length in descending order (from the longest to the shortest)
PriorityQueue<Record> queue = new PriorityQueue<>(Comparator.comparing(Record::getTagNumberedLen).reversed());
queue.addAll(listRecords);
//Using a set to have unique numbered tags (no duplicates) to group by in the resulting map
Set<MyTag> setMyTags = new HashSet<>();
//Checking for each numbered tag if its largest substring is equal to any other numbered tag's beginning:
// - if it does, then the substring is collected as a key to group by within the final map
//
// - if it doesn't, then another substring is generated from the previous substring until a matching value is found.
// If no value is found, then the numbered tag is collected entirely as a key for the resulting map.
while (!queue.isEmpty()) {
Record rec = queue.poll();
//This loop keeps creating substrings of the current numbered tag until:
// - the substring matches another numbered tag's beginning
// - or no more substrings can be generated
int lastIndexTagNum = rec.getTagNumbered().lastIndexOf(tagSep);
int lastIndexTag = rec.getTag().lastIndexOf(tagSep);
while (lastIndexTagNum > 0) {
//Checking if the substring matches the beginning of any numbered tag except the current one
String subStrTagNum = rec.getTagNumbered().substring(0, lastIndexTagNum);
if (listRecords.stream().anyMatch(r -> !r.getTagNumbered().equals(rec.getTagNumbered()) && r.getTagNumbered().startsWith(subStrTagNum + tagSep))) {
String subStrTag = rec.getTag().substring(0, lastIndexTag);
int appearanceOrder = listRecords.stream().filter(r -> r.getTagNumbered().startsWith(subStrTagNum + tagSep)).map(r -> r.getAppearanceOrder()).min(Comparator.naturalOrder()).orElse(0);
//If a match is found then the current substring is added to the set and the substring iteration is interrupted
setMyTags.add(new MyTag(subStrTag, subStrTagNum + tagSep, appearanceOrder));
break;
}
//Creating a new substring from the previous substring if no match has been found
lastIndexTagNum = rec.getTagNumbered().substring(0, lastIndexTagNum).lastIndexOf(tagSep);
lastIndexTag = rec.getTag().substring(0, lastIndexTag).lastIndexOf(tagSep);
}
//If no substrings of the current numbered tag matches the beginning of any other numbered tag,
//then the current numbered tag is collected as a key for the resulting map
if (lastIndexTagNum < 0) {
int appearanceOrder = listRecords.stream().filter(r -> r.getTagNumbered().startsWith(rec.getTagNumbered())).map(r -> r.getAppearanceOrder()).min(Comparator.naturalOrder()).orElse(0);
setMyTags.add(new MyTag(rec.getTag(), rec.getTagNumbered(), appearanceOrder));
}
}
//Creating a temporary resulting map (not sorted as the input)
Map<MyTag, List<String>> mapTemp = listRecords.stream()
.collect(Collectors.toMap(
rec -> {
//Looking for the longest numbered tag which matches the beginning of the current record's numbered tag.
//The reason why we need the longest match (i.e. the most accurate) is because some elements
//may share the same parents but be on different levels, for example the values 3, 4, 5 and 6
//have a key whose beginning matches both "TAG1.TAG2" and "TAG1.TAG2.TAG3", but only the longest
//match is actually the right one.
return setMyTags.stream().filter(mt -> rec.getTagNumbered().startsWith(mt.getTagNumbered())).max(Comparator.comparingInt(MyTag::getTagNumberedLen)).orElseThrow(() -> new RuntimeException("No key found"));
},
rec -> {
//Retrieving, like above, the numbered tag that will be used to map the current value
MyTag myTag = setMyTags.stream().filter(mt -> rec.getTagNumbered().startsWith(mt.getTagNumbered())).max(Comparator.comparingInt(MyTag::getTagNumberedLen)).orElseThrow(() -> new RuntimeException("No key found"));
//If the new numbered tag and the record's numbered tag are equal then a List with the current value is returned
if (myTag.getTagNumbered().equals(rec.getTagNumbered())) {
return new ArrayList<>(List.of(rec.getValue()));
} else { //If the new numbered tag is a substring of the record's numbered tag then the rest of the current (non-numbered) tag is added to the value
return new ArrayList<>(List.of(rec.getTag().substring(myTag.getTag().length() + 1) + " : " + rec.getValue()));
}
},
//Handling colliding cases by merging the lists together
(list1, list2) -> {
list1.addAll(list2);
return list1;
}
)
);
//Creating a TreeMap whose ordering is based on the insertion order of the input
Map<MyTag, List<String>> mapRes =
mapTemp.entrySet().stream()
.sorted(Map.Entry.comparingByKey(Comparator.comparingInt(MyTag::getAppearanceOrder)))
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
//Printing the resulting map
for (Map.Entry<MyTag, List<String>> entry : mapRes.entrySet()) {
System.out.println(entry.getKey());
for (String value : entry.getValue()) {
System.out.println("\t" + value);
}
}
}
private static void visitXMLFile(List<Record> listInput, NodeList nodeList, String tagSep, String tag, String tagNumbered, Map<String, Integer> mapTagOccurrence) {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.hasChildNodes()) {
String newTag = tag.isEmpty() ? node.getNodeName() : tag + tagSep + node.getNodeName();
//Setting or incrementing the number of appearances of a tag chain
//(sometimes a same chain of tags can be repeated, ex: TAG1.TAG2.TAG3)
if (!mapTagOccurrence.containsKey(newTag)) {
mapTagOccurrence.put(newTag, 1);
} else {
mapTagOccurrence.computeIfPresent(newTag, (key, val) -> val + 1);
}
//Creating a numbered version of the tag where its number of appearances is added at the end.
//This is done to uniquely identify different groups of tag chain when these are repeated (ex: TAG1.TAG2.TAG3)
String newTagNum = tagNumbered.isEmpty() ? node.getNodeName() + mapTagOccurrence.get(newTag) : tagNumbered + tagSep + node.getNodeName() + mapTagOccurrence.get(newTag);
visitXMLFile(listInput, node.getChildNodes(), tagSep, newTag, newTagNum, mapTagOccurrence);
} else {
if (!node.getTextContent().trim().equals("")) {
int appearanceOrder = listInput.size() + 1;
listInput.add(new Record(tag, tagNumbered, node.getTextContent().trim(), appearanceOrder));
}
}
}
}
}
class MyTag {
//Tag chain for the user
private String tag;
//Unique tag chain for identification
private String tagNumbered;
private int appearanceOrder;
public MyTag(String tag, String tagNumbered, int appearanceOrder) {
this.tag = tag;
this.tagNumbered = tagNumbered;
this.appearanceOrder = appearanceOrder;
}
public String getTag() {
return tag;
}
public String getTagNumbered() {
return tagNumbered;
}
public int getTagNumberedLen() {
return tagNumbered == null ? 0 : tagNumbered.length();
}
public int getAppearanceOrder() {
return appearanceOrder;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyTag tagPair = (MyTag) o;
return Objects.equals(tagNumbered, tagPair.tagNumbered);
}
#Override
public int hashCode() {
return Objects.hash(tagNumbered);
}
#Override
public String toString() {
return tag;
}
}
class Record {
//Tag chain for the user
private String tag;
//Unique tag chain for identification
private String tagNumbered;
private String value;
private int appearanceOrder;
public Record(String tag, String tagNumbered, String value, int appearanceOrder) {
this.tag = tag;
this.tagNumbered = tagNumbered;
this.value = value;
this.appearanceOrder = appearanceOrder;
}
public String getTag() {
return tag;
}
public String getTagNumbered() {
return tagNumbered;
}
public int getTagNumberedLen() {
return tagNumbered == null ? 0 : tagNumbered.length();
}
public String getValue() {
return value;
}
public int getAppearanceOrder() {
return appearanceOrder;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Record record = (Record) o;
return Objects.equals(tagNumbered, record.tagNumbered);
}
#Override
public int hashCode() {
return Objects.hash(tagNumbered);
}
#Override
public String toString() {
return tag + " - " + tagNumbered + " - " + value;
}
}
Edit
At this point to answer your edited question, I had to use a List instead of a Map to store the input since multiple values share the same key and a Map<String, List<String>> wouldn't maintain the insertion order. In fact, the values from 3 to 6 would be alternated with the values from 11 to 14.
Besides, since the same chain of tags can appear several times (ex: TAG1.TAG2.TAG3), I had to implement two custom classes: MyTag and Record.
The first class represents a custom tag made of two fields: tag and tagNumbered. The first field holds the tag chain that must be shown to the user, while the second is used as the actual identifier to group by in the stream operation. tagNumbered is basically a copy of tag where at the end of each nested tag is added its number of appearances.
Instead, the class Record is used to represent a value accompanied by its tag chain and numbered tag chain.
So, the following XML is represented as follows by the respective classes:
<x>
<y>
<z>value1</z>
</y>
<y>
<z>value2</z>
</y>
</x>
Record:
Record1:
- tag: x.y.z
- tagNumbered: x1.y1.z1
- value: value1
Record2:
- tag: x.y.z
- tagNumbered: x1.y2.z1 //because y appears twice within x
- value: value2
MyTag (MyTag is created from Record):
MyTag1:
- tag: x.y.z
- tagNumbered: x1.y1.z1
MyTag2:
- tag: x.y.z
- tagNumbered: x1.y2.z1 //because y appears twice within x
Here is an XML sample based on your question's input, that I've used for the code below.
<root>
<TAG1>
<TAG2>
<TAG11>value1</TAG11>
<TAG12>value2</TAG12>
<TAG3>
<TAG131>value3</TAG131>
<TAG132>value4</TAG132>
<TAG133>value5</TAG133>
<TAG134>value6</TAG134>
</TAG3>
<TAG3>
<TAG131>value11</TAG131>
<TAG132>value12</TAG132>
<TAG133>value13</TAG133>
<TAG134>value14</TAG134>
</TAG3>
</TAG2>
<TAG4>
<TAG5>
<TAG21>value7</TAG21>
<TAG22>value8</TAG22>
<TAG23>value9</TAG23>
</TAG5>
</TAG4>
</TAG1>
<TAG6>value10</TAG6>
</root>
Original Answer Updated
The first part of the problem consists in creating a List<Record> while reading from the XML file which is achieved with the visitXMLFile method.
After reading the records from the file, we need to create a Set of unique numbered tag chains to identify each group of values. This is actually done with a Set<MyTag>; however MyTag's equals() and hashCode() are based exclusively on tagNumbered.
After creating the Set of unique numbered tags, we need to stream the input list of entries with a single operation: collect(Collectors.toMap()). In this operation, each record is mapped to a MyTag (i.e., a numbered tag) of the Set previously created.
Finally, to maintain the original insertion order, the resulting Map has been implemented as a TreeMap initialized with a Comparator defined on the order of the input list's records.
Here is an implementation with detailed comments explaining the whole logic step by step:
public class Main {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
//Accessing the xml file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("data.xml"));
document.getDocumentElement().normalize();
Element root = document.getDocumentElement();
//Retrieving a List of records where each record contains: the original chain of tags, the numbered chain of tags and the value
String tagSep = ".";
List<Record> listRecords = new ArrayList<>();
visitXMLFile(listRecords, root.getChildNodes(), tagSep, "", "", new HashMap<>());
//Queue sorted by the numbered tag's length in descending order (from the longest to the shortest)
PriorityQueue<Record> queue = new PriorityQueue<>(Comparator.comparing(Record::getTagNumberedLen).reversed());
queue.addAll(listRecords);
//Using a set to have unique numbered tags (no duplicates) to group by in the resulting map
Set<MyTag> setMyTags = new HashSet<>();
//Checking for each numbered tag if its largest substring is equal to any other numbered tag's beginning:
// - if it does, then the substring is collected as a key to group by within the final map
//
// - if it doesn't, then another substring is generated from the previous substring until a matching value is found.
// If no value is found, then the numbered tag is collected entirely as a key for the resulting map.
while (!queue.isEmpty()) {
Record rec = queue.poll();
//This loop keeps creating substrings of the current numbered tag until:
// - the substring matches another numbered tag's beginning
// - or no more substrings can be generated
int lastIndexTagNum = rec.getTagNumbered().lastIndexOf(tagSep);
int lastIndexTag = rec.getTag().lastIndexOf(tagSep);
while (lastIndexTagNum > 0) {
//Checking if the substring matches the beginning of any numbered tag except the current one
String subStrTagNum = rec.getTagNumbered().substring(0, lastIndexTagNum);
if (listRecords.stream().anyMatch(r -> !r.getTagNumbered().equals(rec.getTagNumbered()) && r.getTagNumbered().startsWith(subStrTagNum + tagSep))) {
String subStrTag = rec.getTag().substring(0, lastIndexTag);
int appearanceOrder = listRecords.stream().filter(r -> r.getTagNumbered().startsWith(subStrTagNum + tagSep)).map(r -> r.getAppearanceOrder()).min(Comparator.naturalOrder()).orElse(0);
//If a match is found then the current substring is added to the set and the substring iteration is interrupted
setMyTags.add(new MyTag(subStrTag, subStrTagNum + tagSep, appearanceOrder));
break;
}
//Creating a new substring from the previous substring if no match has been found
lastIndexTagNum = rec.getTagNumbered().substring(0, lastIndexTagNum).lastIndexOf(tagSep);
lastIndexTag = rec.getTag().substring(0, lastIndexTag).lastIndexOf(tagSep);
}
//If no substrings of the current numbered tag matches the beginning of any other numbered tag,
//then the current numbered tag is collected as a key for the resulting map
if (lastIndexTagNum < 0) {
int appearanceOrder = listRecords.stream().filter(r -> r.getTagNumbered().startsWith(rec.getTagNumbered())).map(r -> r.getAppearanceOrder()).min(Comparator.naturalOrder()).orElse(0);
setMyTags.add(new MyTag(rec.getTag(), rec.getTagNumbered(), appearanceOrder));
}
}
//Creating a temporary resulting map (not sorted as the input)
Map<MyTag, List<String>> mapTemp = listRecords.stream()
.collect(Collectors.toMap(
rec -> {
//Looking for the longest numbered tag which matches the beginning of the current record's numbered tag.
//The reason why we need the longest match (i.e. the most accurate) is because some elements
//may share the same parents but be on different levels, for example the values 3, 4, 5 and 6
//have a key whose beginning matches both "TAG1.TAG2" and "TAG1.TAG2.TAG3", but only the longest
//match is actually the right one.
return setMyTags.stream().filter(mt -> rec.getTagNumbered().startsWith(mt.getTagNumbered())).max(Comparator.comparingInt(MyTag::getTagNumberedLen)).orElseThrow(() -> new RuntimeException("No key found"));
},
rec -> {
//Retrieving, like above, the numbered tag that will be used to map the current value
MyTag myTag = setMyTags.stream().filter(mt -> rec.getTagNumbered().startsWith(mt.getTagNumbered())).max(Comparator.comparingInt(MyTag::getTagNumberedLen)).orElseThrow(() -> new RuntimeException("No key found"));
//If the new numbered tag and the record's numbered tag are equal then a List with the current value is returned
if (myTag.getTagNumbered().equals(rec.getTagNumbered())) {
return new ArrayList<>(List.of(rec.getValue()));
} else { //If the new numbered tag is a substring of the record's numbered tag then the rest of the current (non-numbered) tag is added to the value
return new ArrayList<>(List.of(rec.getTag().substring(myTag.getTag().length() + 1) + " : " + rec.getValue()));
}
},
//Handling colliding cases by merging the lists together
(list1, list2) -> {
list1.addAll(list2);
return list1;
}
)
);
//Creating a TreeMap whose ordering is based on the insertion order of the input
Map<MyTag, List<String>> mapRes = new TreeMap<>(Comparator.comparingInt(MyTag::getAppearanceOrder));
mapRes.putAll(mapTemp);
//Printing the resulting map
for (Map.Entry<MyTag, List<String>> entry : mapRes.entrySet()) {
System.out.println(entry.getKey());
for (String value : entry.getValue()) {
System.out.println("\t" + value);
}
}
}
private static void visitXMLFile(List<Record> listInput, NodeList nodeList, String tagSep, String tag, String tagNumbered, Map<String, Integer> mapTagOccurrence) {
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.hasChildNodes()) {
String newTag = tag.isEmpty() ? node.getNodeName() : tag + tagSep + node.getNodeName();
//Setting or incrementing the number of appearances of a tag chain
//(sometimes a same chain of tags can be repeated, ex: TAG1.TAG2.TAG3)
if (!mapTagOccurrence.containsKey(newTag)) {
mapTagOccurrence.put(newTag, 1);
} else {
mapTagOccurrence.computeIfPresent(newTag, (key, val) -> val + 1);
}
//Creating a numbered version of the tag where its number of appearances is added at the end.
//This is done to uniquely identify different groups of tag chain when these are repeated (ex: TAG1.TAG2.TAG3)
String newTagNum = tagNumbered.isEmpty() ? node.getNodeName() + mapTagOccurrence.get(newTag) : tagNumbered + tagSep + node.getNodeName() + mapTagOccurrence.get(newTag);
visitXMLFile(listInput, node.getChildNodes(), tagSep, newTag, newTagNum, mapTagOccurrence);
} else {
if (!node.getTextContent().trim().equals("")) {
int appearanceOrder = listInput.size() + 1;
listInput.add(new Record(tag, tagNumbered, node.getTextContent().trim(), appearanceOrder));
}
}
}
}
}
class MyTag {
//Tag chain for the user
private String tag;
//Unique tag chain for identification
private String tagNumbered;
private int appearanceOrder;
public MyTag(String tag, String tagNumbered, int appearanceOrder) {
this.tag = tag;
this.tagNumbered = tagNumbered;
this.appearanceOrder = appearanceOrder;
}
public String getTag() {
return tag;
}
public String getTagNumbered() {
return tagNumbered;
}
public int getTagNumberedLen() {
return tagNumbered == null ? 0 : tagNumbered.length();
}
public int getAppearanceOrder() {
return appearanceOrder;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyTag tagPair = (MyTag) o;
return Objects.equals(tagNumbered, tagPair.tagNumbered);
}
#Override
public int hashCode() {
return Objects.hash(tagNumbered);
}
#Override
public String toString() {
return tag;
}
}
class Record {
//Tag chain for the user
private String tag;
//Unique tag chain for identification
private String tagNumbered;
private String value;
private int appearanceOrder;
public Record(String tag, String tagNumbered, String value, int appearanceOrder) {
this.tag = tag;
this.tagNumbered = tagNumbered;
this.value = value;
this.appearanceOrder = appearanceOrder;
}
public String getTag() {
return tag;
}
public String getTagNumbered() {
return tagNumbered;
}
public int getTagNumberedLen() {
return tagNumbered == null ? 0 : tagNumbered.length();
}
public String getValue() {
return value;
}
public int getAppearanceOrder() {
return appearanceOrder;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Record record = (Record) o;
return Objects.equals(tagNumbered, record.tagNumbered);
}
#Override
public int hashCode() {
return Objects.hash(tagNumbered);
}
#Override
public String toString() {
return tag + " - " + tagNumbered + " - " + value;
}
}
Output
TAG1.TAG2
TAG11 : value1
TAG12 : value2
TAG1.TAG2.TAG3
TAG131 : value3
TAG132 : value4
TAG133 : value5
TAG134 : value6
TAG1.TAG2.TAG3
TAG131 : value11
TAG132 : value12
TAG133 : value13
TAG134 : value14
TAG1.TAG4.TAG5
TAG21 : value7
TAG22 : value8
TAG23 : value9
TAG6
value10
Could somebody explain to me why this simple piece of code will not compile?
Node.groovy
class Node{
Integer key
String value
Node leftNode
Node rightNode
Node(){}
Node(Integer k, String v){
this.key = k
this.value = v
}
}
BinaryTree.groovy
class BinaryTree{
Node root;
def addNode(k, v){
def newNode = new Node(k,v)
if(!root){
root = newNode
}else{
Node currentNode = root
Node parent
while(true){
parent = currentNode
if(k < currentNode.key) {
currentNode = currentNode.leftNode
if(!currentNode){
parent.leftNode = newNode
return
}
} else {
currentNode = currentNode.rightNode
if(!currentNode){
parent.rightNode = newNode
return
}
}
}
}
}
def inOrderTraversal(def node, def silent){
if(node){
inOrderTraversal(node.leftNode)
!silent ?: println("Node ${node.dump()}")
inOrderTraversal(node.rightNode)
}
}
}
Main.groovy
//Test the binaryTree Project
binaryTree = new BinaryTree();
binaryTree.addNode(45, "v1")
binaryTree.addNode(60, "v4")
binaryTree.addNode(12, "v3")
binaryTree.addNode(32, "v9")
binaryTree.addNode(415, "v7")
binaryTree.inOrderTraversal(binaryTree.root, false)
3 simple files. This is that I get when I press play in intellij, or when I try to run this: groovy -cp ./src src/Main.groovy
Caught: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: groovy.util.Node(java.lang.Integer, java.lang.String)
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: groovy.util.Node(java.lang.Integer, java.lang.String)
at BinaryTree.addNode(BinaryTree.groovy:7)
at BinaryTree$addNode.call(Unknown Source)
at Main.run(Main.groovy:4)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
The constructor in Node looks fine to me.
I am using Java 8 and groovy 2.4
Thanks
How do I traverse a SortedTree list with a public String method?
I'm trying to write a method that determined the next item in the list, sets cursor to null, an returns the item. I then set the cursor to null and return null. Ideally, I'd like to append to a string (like toString), and then select an entry that follows the one matching cursor. (If there is an entry).
Or would I be better off using something like an ArrayList?
My tree driver:
SortedTree tree = new SortedTree();
// Populate the tree
tree.add("Jim");
tree.add("John");
tree.add("Henry");
tree.add("Paul");
tree.add("Ringo");
tree.add("Mick");
tree.add("Keith");
tree.add("Charlie");
tree.add("Eric");
tree.add("David");
tree.add("George");
// traverse it looking for eeeeerie names
System.out.println("Eeeeerie items in our tree:");
String item = tree.first();
while (item != null) {
if (item.contains("e") || item.contains("E"))
System.out.println(item);
item = tree.next();
}
Here is the method I'm trying to make to return the appropriate names.
As it stands, my method is only returning the name "Charlie", but the output should be:
"Eeeeerie items in our tree:
Charlie
Eric
George
Henry
Keith"
public String next() {
cursor = root;
String nodeList = null;
while (cursor.right != null) {
cursor = cursor.right;
nodeList += cursor.item;
}
if (cursor == null) {
return null;
}
return null;
}
I should also include my code for the cursor:
TreeNode cursor; // node to remember the current traversal position
public void setCursor(TreeNode node) {
this.cursor = node;
}
Here is my first() method which is called in the driver
public String first() {
TreeNode beginning = root;
do {
beginning = beginning.left;
} while (beginning.left != null);
setCursor(beginning);
return beginning.item;
}
Like this:
List<String> names = new ArrayList<String>();
names.add("Jim");
// ... add rest here ...
List<String> eerie = new ArrayList<String>(); // a new empty list
// Collect the ones you want
for(String name : names) {
// I'm not sure what 'contains()' is (in your code), but this will work.
if(item.indexOf("e") != -1 || item.indexOf("E") != -1) {
eerie.add(name);
}
}
// Sort 'em
Collections.sort(eerie);
// Print 'em
for(String name : eerie) {
System.out.println(name);
}
I have a Neo4j graph database which maps a file system structure consisting of nodes representing folders and files.
Each node has a FATHER_OF and a CHILD_OF relationship.
Now I need to create a TreeNode structure in Java starting from the Neo4j graph: currently I implemented a breadth first print of the NEO4J structure as follows:
public Traverser getTraverser()
{
Transaction tx = graphDb.beginTx();
Traverser traverser = traverseStorage(rootNode);
return traverser;
}
private static Traverser traverseStorage(final Node startNode) {
TraversalDescription td = Traversal.description()
.breadthFirst()
.relationships(GraphStorage.RelTypes.FATHER_OF, Direction.OUTGOING);
return td.traverse(startNode);
}
Now I'm trying to create a Tree using the above breadth-first traverser but can't figure out how to properly assign the correct parent to each node.
TreeNode root = new DefaultTreeNode("root", null);
Traverser traverser = graphStorage.getTraverser();
TreeNode parent = root;
for (Path directoryPath : traverser) {
DefaultTreeNode tmp1 = new DefaultTreeNode((String)directoryPath.endNode().getProperty("name"), parent);
}
I hoped there was something like directoryPath.endNode().getParent() but apparently there isn't.
I'm searching for a solution which doesn't require me to use Cypher query language, any help?
Ok found out, just need a HashMap to map Neo4j node id's to TreeNode objects:
HashMap<Long, TreeNode> treeNodeMap = new HashMap();
then the rest becomes:
TreeNode root = new DefaultTreeNode("root", null);
Traverser traverser = graphStorage.getTraverser();
TreeNode parent = root;
Relationship parentRelationship = directoryPath.endNode().getSingleRelationship(
GraphStorage.RelTypes.CHILD_OF, Direction.OUTGOING);
if (parentRelationship != null) {
Node parentFileNode = parentRelationship.getEndNode();
if (parentFileNode != null) {
long parentId = parentFileNode.getId();
parent = treeNodeMap.get(new Long(parentId));
}
DefaultTreeNode tmp1 = new DefaultTreeNode((String)directoryPath.endNode().getProperty("name"), parent);
treeNodeMap.put(new Long(directoryPath.endNode().getId()), tmp1);
}
The above correctly works.
i have list of departments and each department might have a parent or not,department domain object is as follows:
- departmentId
- parentDepartmentId (null if current department has no parent i,e should be under root directly, and have value if current department have parent).
.
.
.
looking at icefaces tutorial code for creating basic tree:
// create root node with its children expanded
DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode();
IceUserObject rootObject = new IceUserObject(rootTreeNode);
rootObject.setText("Root Node");
rootObject.setExpanded(true);
rootTreeNode.setUserObject(rootObject);
// model is accessed by by the ice:tree component via a getter method, this object is what's needed in the view to display the tree
model = new DefaultTreeModel(rootTreeNode);
// add some child nodes
for (int i = 0; i <3; i++) {
DefaultMutableTreeNode branchNode = new DefaultMutableTreeNode();
IceUserObject branchObject = new IceUserObject(branchNode);
branchObject.setText("node-" + i);
branchNode.setUserObject(branchObject);
rootTreeNode.add(branchNode);
}
it's all about constructing basic node, and adding childs.
my case is complex that child A which is under root may have child nodes B,C,D and D have for example child nodes and so on so on.
so i am thinking of a best practice about how to accomplish something like that, i need a sample code or hints if anyone can help.
You would need a recursive method to construct the tree from your model.
public void buildRecursiveTreeNode(DefaultMutableTreeNode parentTreeNode,
String treeId, String treeName, GenericTreeVo modelVo)
{
// if the database model contains more children.
// add the current nodes first and pass in this nodes tree id and name to construct the children for this parent nodes.
}
Updated answer to include recursion example.
http://www.danzig.us/java_class/recursion.html
just added a recursion link, all I am saying is when you iterate the data from the database, you would see if you have any child records, if you have child records you would call the same method by passing the DefaultMutableTreeNode and that would become the parent.
finally i was able to do it as follows:
List<Department> departmentList = getAllDepartments();
// create root node with its children expanded
DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode();
IceUserObject rootObject = new IceUserObject(rootTreeNode);
rootObject.setText("Root");
rootObject.setExpanded(true);
rootTreeNode.setUserObject(rootObject);
HashMap<Department, DefaultMutableTreeNode> createdNodesMap = new HashMap<Department, DefaultMutableTreeNode>(
0);
for (Department department : departmentList) {
DefaultMutableTreeNode currentNode = null;
if (createdNodesMap.get(department) == null) {
log.debug("############ CREATING NODE "
+ department.getName());
currentNode = new DefaultMutableTreeNode();
IceUserObject currentObject = new IceUserObject(currentNode);
currentObject.setText(department.getName());
currentObject.setExpanded(true);
currentNode.setUserObject(currentObject);
if (department.getParentDepartment() == null) {
rootTreeNode.add(currentNode);
log.debug("######### NODE " + department.getName()
+ " ADDED UNDER ROOT");
}
createdNodesMap.put(department, currentNode);
} else {
log.debug("############ GETTING CREATED NODE "
+ department.getName());
currentNode = createdNodesMap.get(department);
}
if (department.getChildren().size() > 0)
log.debug("############ NODE " + department.getName()
+ " HAVE " + department.getChildren().size()
+ " CHILDREN");
else
log.debug("############ NODE " + department.getName()
+ " DOES NOT HAVE CHILDREN");
for (Department department2 : department.getChildren()) {
log.debug("############ CREATING CHILD "
+ department2.getName() + " FOR PARENT "
+ department.getName());
DefaultMutableTreeNode branchNode;
if (createdNodesMap.get(department2) == null) {
branchNode = new DefaultMutableTreeNode();
IceUserObject branchObject = new IceUserObject(
branchNode);
branchObject.setText(department2.getName());
branchObject.setExpanded(true);
branchNode.setUserObject(branchObject);
} else
branchNode = createdNodesMap.get(department2);
createdNodesMap.put(department2, branchNode);
currentNode.add(branchNode);
}
}
model = new DefaultTreeModel(rootTreeNode);
Check http://click.avoka.com/click-examples/tree/checkbox-tree-page.htm
The latter is done via the Apache Click framework. Right now I'm developing a project where this data structure (hierarchy tree) is heavily used. You can set the root node or if you need to have several starting points, you can create a wildcard root node that won't affect the functionality, the subclasses, like others have commented, need to be created recursively.