How do I use DOTGenerator to convert a parse tree to DOT/graphviz format in ANTLR4?
I found this related question but the only answer uses TreeViewer to display the tree in a JPanel and that's not what I'm after. This other question is exacly what I need but it didn't get answered. Everything else I stumbled upon relates to DOTTreeGenerator from ANTLR3 and it's not helpful.
I'm using Java with the ANTLR4 plugin for IntelliJ.
I have a small project that has all kind of utility methods w.r.t. ANTLR4 grammar debugging/testing. I haven't found the time to provide it of some proper documentation so that I can put it on Github. But here's a part of it responsible for creating a DOT file from a grammar.
Stick it all in a single file called Main.java (and of course generate the lexer and parser for Expression.g4), and you will see a DOT string being printed to your console:
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import java.util.*;
public class Main {
public static void main(String[] args) {
/*
// Expression.g4
grammar Expression;
expression
: '-' expression
| expression ('*' | '/') expression
| expression ('+' | '-') expression
| '(' expression ')'
| NUMBER
| VARIABLE
;
NUMBER
: [0-9]+ ( '.' [0-9]+ )?
;
VARIABLE
: [a-zA-Z] [a-zA-Z0-9]+
;
SPACE
: [ \t\r\n] -> skip
;
*/
String source = "3 + 42 * (PI - 3.14159)";
ExpressionLexer lexer = new ExpressionLexer(CharStreams.fromString(source));
ExpressionParser parser = new ExpressionParser(new CommonTokenStream(lexer));
SimpleTree tree = new SimpleTree.Builder()
.withParser(parser)
.withParseTree(parser.expression())
.withDisplaySymbolicName(false)
.build();
DotOptions options = new DotOptions.Builder()
.withParameters(" labelloc=\"t\";\n label=\"Expression Tree\";\n\n")
.withLexerRuleShape("circle")
.build();
System.out.println(new DotTreeRepresentation().display(tree, options));
}
}
class DotTreeRepresentation {
public String display(SimpleTree tree) {
return display(tree, DotOptions.DEFAULT);
}
public String display(SimpleTree tree, DotOptions options) {
return display(new InOrderTraversal().traverse(tree), options);
}
public String display(List<SimpleTree.Node> nodes, DotOptions options) {
StringBuilder builder = new StringBuilder("graph tree {\n\n");
Map<SimpleTree.Node, String> nodeNameMap = new HashMap<>();
int nodeCount = 0;
if (options.parameters != null) {
builder.append(options.parameters);
}
for (SimpleTree.Node node : nodes) {
nodeCount++;
String nodeName = String.format("node_%s", nodeCount);
nodeNameMap.put(node, nodeName);
builder.append(String.format(" %s [label=\"%s\", shape=%s];\n",
nodeName,
node.getLabel().replace("\"", "\\\""),
node.isTokenNode() ? options.lexerRuleShape : options.parserRuleShape));
}
builder.append("\n");
for (SimpleTree.Node node : nodes) {
String name = nodeNameMap.get(node);
for (SimpleTree.Node child : node.getChildren()) {
String childName = nodeNameMap.get(child);
builder.append(" ").append(name).append(" -- ").append(childName).append("\n");
}
}
return builder.append("}\n").toString();
}
}
class InOrderTraversal {
public List<SimpleTree.Node> traverse(SimpleTree tree) {
if (tree == null)
throw new IllegalArgumentException("tree == null");
List<SimpleTree.Node> nodes = new ArrayList<>();
traverse(tree.root, nodes);
return nodes;
}
private void traverse(SimpleTree.Node node, List<SimpleTree.Node> nodes) {
if (node.hasChildren()) {
traverse(node.getChildren().get(0), nodes);
}
nodes.add(node);
for (int i = 1; i < node.getChildCount(); i++) {
traverse(node.getChild(i), nodes);
}
}
}
class DotOptions {
public static final DotOptions DEFAULT = new DotOptions.Builder().build();
public static final String DEFAULT_PARAMETERS = null;
public static final String DEFAULT_LEXER_RULE_SHAPE = "box";
public static final String DEFAULT_PARSER_RULE_SHAPE = "ellipse";
public static class Builder {
private String parameters = DEFAULT_PARAMETERS;
private String lexerRuleShape = DEFAULT_LEXER_RULE_SHAPE;
private String parserRuleShape = DEFAULT_PARSER_RULE_SHAPE;
public DotOptions.Builder withParameters(String parameters) {
this.parameters = parameters;
return this;
}
public DotOptions.Builder withLexerRuleShape(String lexerRuleShape) {
this.lexerRuleShape = lexerRuleShape;
return this;
}
public DotOptions.Builder withParserRuleShape(String parserRuleShape) {
this.parserRuleShape = parserRuleShape;
return this;
}
public DotOptions build() {
if (lexerRuleShape == null)
throw new IllegalStateException("lexerRuleShape == null");
if (parserRuleShape == null)
throw new IllegalStateException("parserRuleShape == null");
return new DotOptions(parameters, lexerRuleShape, parserRuleShape);
}
}
public final String parameters;
public final String lexerRuleShape;
public final String parserRuleShape;
private DotOptions(String parameters, String lexerRuleShape, String parserRuleShape) {
this.parameters = parameters;
this.lexerRuleShape = lexerRuleShape;
this.parserRuleShape = parserRuleShape;
}
}
class SimpleTree {
public static class Builder {
private Parser parser = null;
private ParseTree parseTree = null;
private Set<Integer> ignoredTokenTypes = new HashSet<>();
private boolean displaySymbolicName = true;
public SimpleTree build() {
if (parser == null) {
throw new IllegalStateException("parser == null");
}
if (parseTree == null) {
throw new IllegalStateException("parseTree == null");
}
return new SimpleTree(parser, parseTree, ignoredTokenTypes, displaySymbolicName);
}
public SimpleTree.Builder withParser(Parser parser) {
this.parser = parser;
return this;
}
public SimpleTree.Builder withParseTree(ParseTree parseTree) {
this.parseTree = parseTree;
return this;
}
public SimpleTree.Builder withIgnoredTokenTypes(Integer... ignoredTokenTypes) {
this.ignoredTokenTypes = new HashSet<>(Arrays.asList(ignoredTokenTypes));
return this;
}
public SimpleTree.Builder withDisplaySymbolicName(boolean displaySymbolicName) {
this.displaySymbolicName = displaySymbolicName;
return this;
}
}
public final SimpleTree.Node root;
private SimpleTree(Parser parser, ParseTree parseTree, Set<Integer> ignoredTokenTypes, boolean displaySymbolicName) {
this.root = new SimpleTree.Node(parser, parseTree, ignoredTokenTypes, displaySymbolicName);
}
public SimpleTree(SimpleTree.Node root) {
if (root == null)
throw new IllegalArgumentException("root == null");
this.root = root;
}
public SimpleTree copy() {
return new SimpleTree(root.copy());
}
public String toLispTree() {
StringBuilder builder = new StringBuilder();
toLispTree(this.root, builder);
return builder.toString().trim();
}
private void toLispTree(SimpleTree.Node node, StringBuilder builder) {
if (node.isLeaf()) {
builder.append(node.getLabel()).append(" ");
}
else {
builder.append("(").append(node.label).append(" ");
for (SimpleTree.Node child : node.children) {
toLispTree(child, builder);
}
builder.append(") ");
}
}
#Override
public String toString() {
return String.format("%s", this.root);
}
public static class Node {
protected String label;
protected int level;
protected boolean isTokenNode;
protected List<SimpleTree.Node> children;
Node(Parser parser, ParseTree parseTree, Set<Integer> ignoredTokenTypes, boolean displaySymbolicName) {
this(parser.getRuleNames()[((RuleContext)parseTree).getRuleIndex()], 0, false);
traverse(parseTree, this, parser, ignoredTokenTypes, displaySymbolicName);
}
public Node(String label, int level, boolean isTokenNode) {
this.label = label;
this.level = level;
this.isTokenNode = isTokenNode;
this.children = new ArrayList<>();
}
public void replaceWith(SimpleTree.Node node) {
this.label = node.label;
this.level = node.level;
this.isTokenNode = node.isTokenNode;
this.children.remove(node);
this.children.addAll(node.children);
}
public SimpleTree.Node copy() {
SimpleTree.Node copy = new SimpleTree.Node(this.label, this.level, this.isTokenNode);
for (SimpleTree.Node child : this.children) {
copy.children.add(child.copy());
}
return copy;
}
public void normalizeLevels(int level) {
this.level = level;
for (SimpleTree.Node child : children) {
child.normalizeLevels(level + 1);
}
}
public boolean hasChildren() {
return !children.isEmpty();
}
public boolean isLeaf() {
return !hasChildren();
}
public int getChildCount() {
return children.size();
}
public SimpleTree.Node getChild(int index) {
return children.get(index);
}
public int getLevel() {
return level;
}
public String getLabel() {
return label;
}
public boolean isTokenNode() {
return isTokenNode;
}
public List<SimpleTree.Node> getChildren() {
return new ArrayList<>(children);
}
private void traverse(ParseTree parseTree, SimpleTree.Node parent, Parser parser, Set<Integer> ignoredTokenTypes, boolean displaySymbolicName) {
List<SimpleTree.ParseTreeParent> todo = new ArrayList<>();
for (int i = 0; i < parseTree.getChildCount(); i++) {
ParseTree child = parseTree.getChild(i);
if (child.getPayload() instanceof CommonToken) {
CommonToken token = (CommonToken) child.getPayload();
if (!ignoredTokenTypes.contains(token.getType())) {
String tempText = displaySymbolicName ?
String.format("%s: '%s'",
parser.getVocabulary().getSymbolicName(token.getType()),
token.getText()
.replace("\r", "\\r")
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("'", "\\'")) :
String.format("%s",
token.getText()
.replace("\r", "\\r")
.replace("\n", "\\n")
.replace("\t", "\\t"));
if (parent.label == null) {
parent.label = tempText;
}
else {
parent.children.add(new SimpleTree.Node(tempText, parent.level + 1, true));
}
}
}
else {
SimpleTree.Node node = new SimpleTree.Node(parser.getRuleNames()[((RuleContext)child).getRuleIndex()], parent.level + 1, false);
parent.children.add(node);
todo.add(new SimpleTree.ParseTreeParent(child, node));
}
}
for (SimpleTree.ParseTreeParent wrapper : todo) {
traverse(wrapper.parseTree, wrapper.parent, parser, ignoredTokenTypes, displaySymbolicName);
}
}
#Override
public String toString() {
return String.format("{label=%s, level=%s, isTokenNode=%s, children=%s}", label, level, isTokenNode, children);
}
}
private static class ParseTreeParent {
final ParseTree parseTree;
final SimpleTree.Node parent;
private ParseTreeParent(ParseTree parseTree, SimpleTree.Node parent) {
this.parseTree = parseTree;
this.parent = parent;
}
}
}
And if you paste the output in a DOT viewer, you will get this:
You may also want to look at alternatives. DOT graphs aren't the pretties among possible graph representations. Maybe you like an svg graph instead? If so have a look at the ANTLR4 grammar extension for Visual Studio Code, which generates and exports an svg graphic with the click of a mouse button (and you can style that with own CSS code).
Related
I have a class that do a tree from directory, subdirectory and files. And need to satisfy two conditions. Sort the contents in following way:
-Directories should go first.
-Directories and files are sorted in lexicographic order (case-insensitive).
I work on windows system and code below work fine, all is sorted how I want. Directories go first and are sorted in lexicographic order. But I read that on windows system is automatically sorted in lexicographic order. But this code doesn't work on Linux, the line children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1)); do not work and files\directory aren't sorted in lexicographic order (case-insensitive). How to solve problem with sort conditions on linux system? How can change sort conditions in code?
import static java.util.Comparator.comparing;
public class TreeNode<T> implements Iterable<TreeNode<T>> {
public T data;
public TreeNode<T> parent;
public List<TreeNode<T>> children;
public boolean isRoot() {
return parent == null;
}
private List<TreeNode<T>> elementsIndex;
public TreeNode(T data) {
this.data = data;
this.children = new LinkedList<TreeNode<T>>();
this.elementsIndex = new LinkedList<TreeNode<T>>();
this.elementsIndex.add(this);
}
public TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
this.registerChildForSearch(childNode);
return childNode;
}
private void registerChildForSearch(TreeNode<T> node) {
elementsIndex.add(node);
if (parent != null)
parent.registerChildForSearch(node);
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<TreeNode<T>> iterator() {
TreeNode<T> iter = new TreeNode<T>((T) this);
return (Iterator<TreeNode<T>>) iter;
}
public static TreeNode<File> createDirTree(File folder) {
if (!folder.isDirectory()) {
throw new IllegalArgumentException("folder is not a Directory");
}
List<File> children = Arrays.asList(folder.listFiles());
children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1));
TreeNode<File> DirRoot = new TreeNode<File>(folder);
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, DirRoot);
} else {
appendFile(file, DirRoot);
}
}
return DirRoot;
}
public static void appendDirTree(File folder, TreeNode<File> dirRoot) {
dirRoot.addChild(folder);
List<File> children = Arrays.asList(folder.listFiles());
children.sort(comparing(file -> file.isDirectory() ? -1 : 1));
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, dirRoot.children.get(dirRoot.children.size() - 1));
} else {
appendFile(file, dirRoot.children.get(dirRoot.children.size() - 1));
}
}
}
public static void appendFile(File file, TreeNode<File> filenode) {
filenode.addChild(file);
}
public static String renderDirectoryTree(TreeNode<File> tree) {
List<StringBuilder> lines = renderDirectoryTreeLines(tree);
String newline = "\n";
StringBuilder sb = new StringBuilder(lines.size() * 20);
for (StringBuilder line : lines) {
sb.append(line);
sb.append(newline);
}
//System.out.println(sb);
return sb.toString();
}
public static List<StringBuilder> renderDirectoryTreeLines(TreeNode<File> tree) {
List<StringBuilder> result = new LinkedList<>();
result.add(new StringBuilder().append(tree.data.getName() + " " + calculateFileSize(tree) + " bytes"));
Iterator<TreeNode<File>> iterator = tree.children.iterator();
while (iterator.hasNext()) {
List<StringBuilder> subtree = renderDirectoryTreeLines(iterator.next());
if (iterator.hasNext()) {
addSubtree(result, subtree);
} else {
addLastSubtree(result, subtree);
}
}
return result;
}
private static void addSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "├─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, "│ "));
}
}
private static void addLastSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "└─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, " "));
}
}
public static long calculateFileSize(TreeNode<File> tree) {
long fileSize = 0;
if (tree.data.isDirectory()) {
List<TreeNode<File>> children = tree.children;
for (TreeNode<File> child : children) {
fileSize += calculateFileSize(child);
}
} else {
fileSize = tree.data.length();
}
return fileSize;
}
}
A short version could look something like this.
Comparator<File> lexicographicFileComparator = Comparator.comparing(File::isFile)
.thenComparing(Comparator.naturalOrder());
You've wrote that File.compareTo doesn't even check whether the file exists or not. I don't think it's the job of compareTo or Comparator to check if a file exists. Files that don't exist should be filtered before.
I think your example is overusing static methods. TreeNode should be an abstract class, while you have an implementation called FileTreeNode that replaces your static methods.
Here is an example
TreeNode
public abstract class TreeNode<N extends TreeNode<N, T>, T> implements Iterable<N> {
private static <N extends TreeNode<N, ?>> Stream<CharSequence> renderBranch(N node) {
Stream.Builder<CharSequence> result = Stream.builder();
result.add(node.printNode());
Iterator<N> iterator = node.getChildren().iterator();
while (iterator.hasNext()) {
Stream<CharSequence> subtree = renderBranch(iterator.next());
Iterator<CharSequence> subtreeIterator = subtree.iterator();
String branchSplit = "├─ ";
String branchSpacer = "│ ";
if (!iterator.hasNext()) {
branchSplit = "└─ ";
branchSpacer = " ";
}
result.add(branchSplit + subtreeIterator.next());
while (subtreeIterator.hasNext()) {
result.add(branchSpacer + subtreeIterator.next());
}
}
return result.build();
}
private final T data;
private final N parent;
private final List<N> children;
public TreeNode(T data) {
this(data, null);
}
protected TreeNode(T data, N parent) {
this.data = data;
this.parent = parent;
children = initChildren();
}
/**
* Called in constructor to initialize the children list.
*/
protected abstract List<N> initChildren();
/**
* Used to avoid unsafe casting.
*
* #return This
*/
protected abstract N instance();
/**
* TreeNode knows how to print the tree, but not how to print the current element. This way the child class can decide how it wants to be printed in the tree;
*
* #return readable text representation of node.
* #see TreeNode#renderBranch()
*/
protected abstract CharSequence printNode();
/**
* #return Returns a string representation of the entire branch starting at this node.
*/
public String renderBranch() {
Stream<CharSequence> lines = renderBranch(instance());
return lines.collect(Collectors.joining("\n"));
}
/**
* #return Returns a stream of the entire branch starting at this node
*/
public Stream<N> streamBranch() {
return Stream.concat(Stream.of(instance()), getChildren().stream().flatMap(TreeNode::streamBranch));
}
public T getData() {
return data;
}
public N getParent() {
return parent;
}
public List<N> getChildren() {
return children;
}
public boolean isRoot() {
return parent == null;
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<N> iterator() {
// No clue what you want to do here, but your method will not work.
return children.iterator();
}
}
FileTreeNode
public class FileTreeNode extends TreeNode<FileTreeNode, File> {
public FileTreeNode(File root) {
super(root);
}
protected FileTreeNode(File data, FileTreeNode parent) {
super(data, parent);
}
public long calculateFileSize() {
return streamBranch().mapToLong(value -> value.getData().length()).sum();
}
#Override
protected CharSequence printNode() {
return getData().getName() + " " + calculateFileSize() + " bytes";
}
#Override
protected List<FileTreeNode> initChildren() {
File file = getData();
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
return Arrays.stream(files)
.sorted(Comparator.comparing(File::isFile).thenComparing(Comparator.naturalOrder()))
.map(path -> new FileTreeNode(path, this))
.toList();
}
}
return Collections.emptyList();
}
#Override
protected FileTreeNode instance() {
return this;
}
}
Usage
File file = new File("C:\\dir");
FileTreeNode rootNode = new FileTreeNode(file);
System.out.println(rootNode.renderBranch());
This way you can easily implement a TreeNode structure for other classes too. For example File is outdated and replaced by Path so maybe you will want a PathTreeNode in the future. Who knows.
I'm trying to create a 'skeletal' like system using class Bone and having the "primary" bone's children consist of all other bones that are connected.
public class Main {
public static void main(String[] args) {
Bone body = new Bone("primary", null);
Bone leftArm1 = new Bone("left_arm_1", body);
Bone leftArm2 = new Bone("left_arm_2", leftArm1);
Bone rightArm1 = new Bone("right_arm_1", body);
Bone rightArm2 = new Bone("right_arm_2", rightArm1);
List<Bone> bones = new ArrayList<Bone>();
for(Bone child : body.getChildren()) {
System.out.println(child.getName());
}
}
}
public class Bone {
private Bone parent = null;
private String name = null;
private List<Bone> children = new ArrayList<Bone>();
public Bone(String name, Bone parent) {
this.name = name;
this.parent = parent;
if(parent != null) {
parent.addChild(this);
}
}
public String getName() {
return this.name;
}
public Bone getParent() {
return this.parent;
}
public boolean hasParent() {
if(this.parent != null) {
return true;
} else {
return false;
}
}
public List<Bone> getChildren() {
return this.children;
}
public boolean hasChildren() {
if(this.children.isEmpty()) {
return false;
} else {
return true;
}
}
public void addChild(Bone child) {
this.children.add(child);
}
}
The current program is outputting...
left_arm_1
right_arm_1
when it should be outputting...
left_arm_1
left_arm_2
right_arm_1
right_arm_2
how would I make the program output the correct strings?
I would use recursion
public void printBones(Bone bone) {
if(bone == null) {
return;
}
List<Bone> children = bone.getChildren();
if(children != null && children.size() > 0) {
for(Bone bone : children) {
printBones(bone);
}
}
System.out.println(bone.getName());
}
Because body has only 2 children, the output is only left_arm_1 right_arm_1
If you want to print all children, you need to do it in recursion for all children of the children and so on.
I have found few similar threads about infinity loop while trying save object as Json, but most of them are about java + jpa, and these solutions don't work for me. I would like to create tree structure of some data. There is class ProjectNode which got
private ArrayList<ProjectNode> children;
field. I'm pretty sure this one course the problem but its the main field in this structure, I can't ignore it. I have tested and I can convert object which has list with node which doesn't have children, but cant if any child got another children in the below code:
public class ProjectNode implements Comparable<ProjectNode>{
private String parent;
private String nodeName;
private String nodeHref;
private boolean hasChild;
private int td;
private ArrayList<ProjectNode> children;
public ProjectNode() {
super();
// TODO Auto-generated constructor stub
}
+ getters and setters
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(parser.getProjectFactory().get(12));
parser.getProjectFactory() returns list of ParentNodes (parent=null), I would like to convert 12 elements because there is a case where node has child and this child has child. Cause this error:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"]->java.util.ArrayList[31]->ProjectNode["children"])
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:734)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
EDIT:
Parser - parse project/hosts list from cacti:
public List<ProjectNode> getProjectFactory() throws FailingHttpStatusCodeException, MalformedURLException, IOException {
FileWriter fstream = new FileWriter("graphMap.txt");
BufferedWriter out = new BufferedWriter(fstream);
String [] hostInfo = null;
HtmlTableCell cel= (HtmlTableCell) page.getHtmlPage().getByXPath("/html/body/table/tbody/tr[5]/td[1]").get(0);
System.out.println(cel.getChildElementCount());
List<HtmlDivision> divs = cel.getByXPath(".//div");
System.out.println(divs.size());
//checks if list of host has been added to the project
ProjectTree projectTree = new ProjectTree();
Map<Integer,String> suppMap = new HashMap<Integer,String>();
for(HtmlDivision div : divs) {
List<DomNode> td = div.getByXPath(".//table/tbody/tr/td");
if(td.size()>2) {
ProjectNode pn = new ProjectNode(getNameSrc(td).split("#")[0],getNameSrc(td).split("#")[1],td.size());
hostInfo = getNameSrc(td).split("#");
if(pn.getTd()>3) {
if(!suppMap.get(pn.getTd()-1).isEmpty()) {
pn.setParent(suppMap.get(pn.getTd()-1));
}
if(suppMap.get(pn.getTd())!=null){
suppMap.remove(pn.getTd());
}
suppMap.put(pn.getTd(), pn.getNodeName());
projectTree.addNodeToTree(pn);
} else {
projectTree.addNodeToTree(pn);
suppMap.put(pn.getTd(), pn.getNodeName());
//out.write(pn.toString()+"\n");
}
}
}
ArrayList<ProjectNode> pns = projectTree.getNodeList();
Collections.sort(pns, Comparator.comparing(ProjectNode::getTd));
Collections.reverse(pns);
projectTree.setNodeList(pns);
List<ProjectNode> finalList = new ArrayList<ProjectNode>();
for(ProjectNode pn :pns ) {
if(pn.getTd()==3) {
finalList.add(pn);
}else {
projectTree.addToParent(pn);
}
}
Collections.reverse(finalList);
for(ProjectNode pn : finalList) {
Collections.sort(pn.getChildren());
}
out.write(finalList.get(12).getChildren().get(4).getChildren().toString());
out.close();
System.out.println(finalList.size());
return finalList;
}
ProjectTree class
public class ProjectTree {
private ProjectNode rootNode;
private ArrayList<ProjectNode> NodeList;
public ProjectTree() {
super();
this.NodeList = new ArrayList<ProjectNode>();
}
public ProjectTree(ProjectNode rootNode, ArrayList<ProjectNode> nodeList) {
super();
this.rootNode = rootNode;
NodeList = nodeList;
}
public ArrayList<ProjectNode> groupHosts(){
return this.NodeList;
}
public void addToParent(ProjectNode pn) {
for(ProjectNode pnA :this.NodeList) {
if(pnA.getNodeName().equals(pn.getParent())) {
if(pnA.getChildren()!=null && pnA.getChildren().size()!=0) {
pnA.sortChildren();
}
pnA.addChlild(pn);
}
}
}
public ProjectNode getRootNode() {
return rootNode;
}
public void setRootNode(ProjectNode rootNode) {
this.rootNode = rootNode;
}
public ArrayList<ProjectNode> getNodeList() {
return NodeList;
}
public void setNodeList(ArrayList<ProjectNode> nodeList) {
NodeList = nodeList;
}
}
ProjectNode.class
public class ProjectNode implements Comparable<ProjectNode>{
private String parent;
private String nodeName;
private String nodeHref;
private boolean hasChild;
private int td;
private ArrayList<ProjectNode> children;
public ProjectNode() {
super();
// TODO Auto-generated constructor stub
}
public ProjectNode( String nodeName, String nodeHref, int td) {
super();
this.nodeName = nodeName;
this.nodeHref = nodeHref;
this.hasChild =false;
this.td=td;
}
public void addChlild(ProjectNode pn) {
if(children!=null) {
this.children.add(pn);
}else {
this.children = new ArrayList<ProjectNode>();
this.children.add(pn);
}
}
public List<ProjectNode> getChildren() {
return children;
}
public void sortChildren() {
Collections.sort(this.getChildren());;
//Collections.sort(this.getChildren(), Comparator.comparing(ProjectNode::getNodeName));
//System.out.println(this.children.toString());
}
public void setChildren(ArrayList<ProjectNode> children) {
this.children = children;
}
public int getTd() {
return td;
}
public void setTd(int td) {
this.td = td;
}
public boolean isHasChild() {
return hasChild;
}
public void setHasChild(boolean hasChild) {
this.hasChild = hasChild;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public String getNodeHref() {
return nodeHref;
}
public void setNodeHref(String nodeHref) {
this.nodeHref = nodeHref;
}
#Override
public String toString() {
return "ProjectNode [parent=" + parent + ", nodeName=" + nodeName + ", nodeHref=" + nodeHref + ", hasChild="
+ hasChild + ", td=" + td + ", children=" +"]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((children == null) ? 0 : children.hashCode());
result = prime * result + (hasChild ? 1231 : 1237);
result = prime * result + ((nodeHref == null) ? 0 : nodeHref.hashCode());
result = prime * result + ((nodeName == null) ? 0 : nodeName.hashCode());
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
result = prime * result + td;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProjectNode other = (ProjectNode) obj;
if (children == null) {
if (other.children != null)
return false;
} else if (!children.equals(other.children))
return false;
if (hasChild != other.hasChild)
return false;
if (nodeHref == null) {
if (other.nodeHref != null)
return false;
} else if (!nodeHref.equals(other.nodeHref))
return false;
if (nodeName == null) {
if (other.nodeName != null)
return false;
} else if (!nodeName.equals(other.nodeName))
return false;
if (parent == null) {
if (other.parent != null)
return false;
} else if (!parent.equals(other.parent))
return false;
if (td != other.td)
return false;
return true;
}
#Override
public int compareTo(ProjectNode o) {
// TODO Auto-generated method stub
return nodeName.compareTo(o.getNodeName());
}
}
I would use recursive to groupd all nodes in corect place but i had same error (with stackoverflow) so i had to use work around which is addToParent() function.
I have managed it by breaking loop when parent of my node have been found. I made it just for performance because before even if parent has been found loop was checking all elements anyway so i added break, and it works.. Not sure why..
Code below:
public void group() {
int td = this.highestTd;
while (td > 2) {
List<ProjectNode> toRemove = new ArrayList<ProjectNode>();
for (ProjectNode pnA : this.NodeList) {
if(pnA.getTd()==3) {
pnA.setParent("root");
}
if (pnA.getTd() == td) {
for (ProjectNode pnB : this.NodeList) {
System.out.println(pnB.toString());
if (pnA.getParent()!=null && pnA.getParent().equals(pnB.getNodeName())) {
System.out.println("Dodaje "+pnA.getNodeName() + " Do "+pnB.getNodeName());
pnB.addChlild(pnA);
toRemove.add(pnA);
break;
}
}
}
}
td--;
this.NodeList.removeAll(toRemove);
}
}
I have been trying to translate this tutorial into Java code, as I want to make a simple game with level/achievements in android (and haven't found as thorough/basic examples in java online, if you have one please share)
Please help me understand:
How can I link different file of classes together? in the example they don't seem to refer to each other? Basically how can I pass on the properties and settings from the tasks/games to these functions which are elsewhere in the code? do I just refer to the class several times throughout the code?
For example I am stuck in this part, could use help in understanding how this works in java code? (examples are most appreciated)
> private var mProps :Object; // dictionary of properties
private var mAchievements :Object; // dictionary of achievements
public function Achieve() {
mProps = { };
mAchievements = { };
}
public function defineProperty(theName :String, theInitialValue :int, theaActivationMode :String, theValue :int) :void {
mProps[theName] = new Property(theName, theInitialValue, theaActivationMode, theValue);
}
public function defineAchievement(theName :String, theRelatedProps :Array) :void {
mAchievements[theName] = new Achievement(theName, theRelatedProps);
}
Remember that each class must go to its own file.
public class Property {
private String mName;
private int mValue;
private String mActivation;
private int mActivationValue;
private int mInitialValue;
public Property(String theName, int theInitialValue, String theActivation, int theActivationValue) {
mName = theName;
mActivation = theActivation;
mActivationValue = theActivationValue;
mInitialValue = theInitialValue;
}
public int getValue() {
return mValue;
}
public void setValue(int n) {
mValue = n;
}
public boolean isActive() {
boolean aRet = false;
switch(mActivation) {
case Achieve.ACTIVE_IF_GREATER_THAN: aRet = mValue > mActivationValue; break;
case Achieve.ACTIVE_IF_LESS_THAN: aRet = mValue < mActivationValue; break;
case Achieve.ACTIVE_IF_EQUALS_TO: aRet = mValue == mActivationValue; break;
}
return aRet;
}
public String getActivation() {
return mActivation;
}
}
import java.util.ArrayList;
public class Achievement {
private String mName; // achievement name
private ArrayList<String> mProps; // array of related properties
private boolean mUnlocked; // achievement is unlocked or not
public Achievement(String theId, ArrayList<String> theRelatedProps) {
mName = theId;
mProps = theRelatedProps;
mUnlocked = false;
}
public boolean isUnlocked() {
return mUnlocked;
}
public void setUnlocked(boolean b) {
mUnlocked = b;
}
public ArrayList<String> getProps() {
return mProps;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Achieve {
// activation rules
public static final String ACTIVE_IF_GREATER_THAN = ">";
public static final String ACTIVE_IF_LESS_THAN = "<";
public static final String ACTIVE_IF_EQUALS_TO = "==";
private HashMap<String,Property> mProps; // dictionary of properties
private HashMap<String,Achievement> mAchievements; // dictionary of achievements
public Achieve() {
mProps = new HashMap<String,Property>();
mAchievements = new HashMap<String,Achievement>();
}
public void defineProperty(String theName, int theInitialValue, String theaActivationMode, int theValue) {
mProps.put(theName, new Property(theName, theInitialValue, theaActivationMode, theValue));
}
public void defineAchievement(String theName, ArrayList<String> theRelatedProps) {
mAchievements.put(theName, new Achievement(theName, theRelatedProps));
}
public int getValue(String theProp) {
Property p = mProps.get(theProp);
if (p != null) return p.getValue();
return 0;
}
public void setValue(String theProp, int theValue) {
Property p = mProps.get(theProp);
if (p == null) return;
switch(p.getActivation()) {
case Achieve.ACTIVE_IF_GREATER_THAN:
theValue = theValue > p.getValue() ? theValue : p.getValue();
break;
case Achieve.ACTIVE_IF_LESS_THAN:
theValue = theValue < p.getValue() ? theValue : p.getValue();
break;
}
p.setValue(theValue);
}
public void addValue(ArrayList<String> theProps, int theValue) {
for (int i = 0; i < theProps.size(); i++) {
String aPropName = theProps.get(i);
setValue(aPropName, getValue(aPropName) + theValue);
}
}
public ArrayList<Achievement> checkAchievements() {
ArrayList<Achievement> aRet = new ArrayList<Achievement>();
Iterator<Map.Entry<String,Achievement>> it = mAchievements.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Achievement> pair = it.next();
Achievement aAchievement = pair.getValue();
if (!aAchievement.isUnlocked()) {
int aActiveProps = 0;
ArrayList<String> props = aAchievement.getProps();
for (int p = 0; p < props.size(); p++) {
Property aProp= mProps.get(props.get(p));
if (aProp.isActive()) {
aActiveProps++;
}
}
if (aActiveProps == props.size()) {
aAchievement.setUnlocked(true);
aRet.add(aAchievement);
}
}
}
return aRet;
}
}
I am trying to write a class that implements the TreeModel class. I was hoping someone could guide me in the right direction. Below is my class. The problem is when I bind it to a jTree Component, the second level keeps being added over and over. So I suspect my reference to the parent object is wrong:
public class PMEntry implements TreeModel{
private String title;
private List<PMEntry> pmEntryCollection;
private String pmId;
private String href;
private PMEntry root;
private ModuleType type;
public PMEntry (PMEntry root){
this.root = root;
}
#Override
public Object getRoot() {
return ((PMEntry)this.root);
}
#Override
public Object getChild(Object o, int i) {
if(getPmEntryCollection().isEmpty()){
return null;
}else {
return (PMEntry) getPmEntryCollection().get(i);
}
}
#Override
public int getChildCount(Object o) {
if(getPmEntryCollection().isEmpty()){
return 0;
}else {
return getPmEntryCollection().size();
}
}
#Override
public boolean isLeaf(Object o) {
PMEntry pmentry = (PMEntry)o;
return (pmentry.getType() == ModuleType.DM) ? true : false;
}
#Override
public void valueForPathChanged(TreePath tp, Object o) {
//todo
}
#Override
public int getIndexOfChild(Object parent, Object child) {
if (!(parent instanceof PMEntry)){
System.out.println("Returning -1");
return -1;
}
PMEntry pParent = (PMEntry) parent;
List<PMEntry> children = pParent.getPmEntryCollection();
if (children == null) {
System.out.println("children = null, Returning -1");
return -1;
}
for (int i = 0; i < children.size(); i++) {
System.out.println("Child:" + child);
if (children.get(i) == child) {
return i;
}
}
return -1;
}
#Override
public void addTreeModelListener(TreeModelListener tl) {
//todo
}
#Override
public void removeTreeModelListener(TreeModelListener tl) {
//todo
}
#Override
public String toString(){
return this.getTitle();
}
public enum ModuleType {
PM,
DM
}
// getters and setters here....
And here is a snippet of how I am binding the data
PMEntry tm = new PMEntry(null);
tm.setTitle("Root");
PMEntry pmRoot = new PMEntry((PMEntry)(tm));
pmRoot.setTitle("Project");
PMEntry pm1 = new PMEntry(pmRoot);
pm1.setType(PMEntry.ModuleType.DM);
pm1.setTitle("Publication Module");
PMEntry pm2 = new PMEntry(pmRoot);
pm2.setType(PMEntry.ModuleType.PM);
pm2.setTitle("Chapter");
List<PMEntry> pmCollection = new ArrayList<PMEntry>();
List<PMEntry> pmCollection1 = new ArrayList<PMEntry>();
PMEntry pm3 = new PMEntry(null);
pm3.setType(PMEntry.ModuleType.DM);
pm3.setTitle("Data Module");
PMEntry pm4 = new PMEntry(null);
pm4.setType(PMEntry.ModuleType.DM);
pm4.setTitle("Data Module");
pmCollection1.add(pm3);
pmCollection1.add(pm4);
pm2.setPmEntryCollection(pmCollection1);
pmCollection.add(pm1);
pmCollection.add(pm2);
pmRoot.setPmEntryCollection(pmCollection);
this.jTree1.setModel(pmRoot);
I'd wonder why you think you need to implement TreeModel. Have you looked into DefaultTreeModel? What new behavior over and above that class do you plan to implement?
I have to agree with #duffymo & #HFOE: don't reject DefaultTreeModel prematurely. There's an example here that illustrates a TreeCellEditor for editing the name of a userObject.
If you really do need to implement TreeModel, FileTreeModel, discussed here, is a fairly accessible example.