XML Data Binding in Java MicroEdition - java

I have a J2ME Application in which i need to bind my XML response in J2ME.Will you please help me in this case?How to bind XML Data Binding in J2ME?

JiBX seems to support J2ME. See the following related JIRA issue: [#JIBX-110] Having a J2ME compatible official jibx release.
Once downloaded, you'll have to ant-build the j2me jars using the j2me target (ant j2me from the build directory where build.xml is sitting). You can just build it with a standard javac, no need for a specialized compiler (see this discussion in the JiBX users list).

It seems that what you want is to unmarshall an XML file to a Java class. If so, I have shared a generic way at my blog. It uses two classes to implement it. The code of the first class is:
public class XMLTag {
// if you do not have enough memory, use lazy
// instantiation on these attributes
private Hashtable attributes = new Hashtable();
private Vector childs = new Vector();
public void setAttributeValue(String attribute, String value) {
if (attribute != null && value != null) {
attributes.put(attribute, value);
}
}
public String getAttributeValue (String attribute) {
return (String) attributes.get(attribute);
}
public void addChild (XMLTag child) {
childs.addElement(child);
}
public Enumeration getChilds () {
return childs.elements();
}
public XMLTag getChildAt (int index) {
return (XMLTag) childs.elementAt(index);
}
}
Below is the source code of the second class:
class XMLBinder extends org.xml.sax.helpers.DefaultHandler {
private Hashtable map = new Hashtable();
private Stack stack = new Stack();
private XMLTag rootElement;
private String attribute;
private StringBuffer value = new StringBuffer();
/**
* #param map with String keys and XMLTag values
*/
public XMLBinder(Hashtable map) {
Enumeration e = map.keys();
while (e.hasMoreElements()) {
Object key = e.nextElement();
Object tag = map.get(key);
if (validateMapping(key, tag)) {
this.map.put(key, tag);
} else {
throw new IllegalArgumentException("key " + key);
}
}
}
private boolean validateMapping (Object key, Object tag) {
return key instanceof String
&& tag instanceof Class
&& XMLTag.class.isAssignableFrom((Class) tag);
}
public XMLTag unmarshall (InputStream in) throws IOException {
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(in, this);
return rootElement;
} catch (Exception ex) {
throw new IOException("caused by " + ex);
}
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
Class tag = (Class) map.get(qName);
if (tag != null) {
try {
XMLTag newTag = (XMLTag) tag.newInstance();
addAttributesToXMLTag(attributes, newTag);
stack.push(newTag);
} catch (Exception e) {
throw new SAXException("caused by " + e);
}
} else {
attribute = qName;
}
}
private void addAttributesToXMLTag (Attributes attributes, XMLTag newTag) {
if (attributes != null) {
for (int i = attributes.getLength() - 1; i >= 0; i--) {
String attrName = attributes.getQName(i);
String attrValue = attributes.getValue(i);
newTag.setAttributeValue(attrName, attrValue);
}
}
}
public void characters(char[] ch, int start, int length) {
if (attribute != null) {
value.append(ch, start, length);
}
}
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (stack.isEmpty()) {
throw new SAXException("no mapping for " + qName);
}
if (attribute != null && attribute.equals(qName)) {
XMLTag parent = (XMLTag) stack.peek();
parent.setAttributeValue(attribute, value.toString());
attribute = null;
value.setLength(0);
} else {
XMLTag child = (XMLTag) stack.pop();
if (stack.isEmpty() == false) {
XMLTag parent = (XMLTag) stack.peek();
parent.addChild(child);
} else {
rootElement = (XMLTag) child;
}
}
}
}
To prevent the use of Class.forName we use a map with tags and classes. The key is a String with the tag name and the value is a Class that extends XMLTag. For example, reading an RSS feed would use below classes:
class RSS extends XMLTag {
Channel channel;
public void addChild(XMLTag child) {
if (child instanceof Channel) {
channel = (Channel) child;
}
}
}
class Channel extends XMLTag {
public void addChild(XMLTag child) {
if (child instanceof Item) {
super.addChild(child);
}
}
}
class Item extends XMLTag {
}
And the following map:
Hashtable map = new Hashtable();
map.put("rss", RSS.class);
map.put("channel", Channel.class);
map.put("item", Item.class);
The binder can then be used:
XMLBinder binder = new XMLBinder(map);
rss = (RSS) binder.unmarshall(in);
Update after comments
For your xml sample you need to create the following classes:
class DataTable extends XMLTag {
XsSchema xsSchema;
DiffgrDiffgram diffgrDiffgram;
public void addChild(XMLTag child) {
if (child instanceof XsSchema) {
xsSchema = (XsSchema) child;
}
else if (child instanceof DiffgrDiffgram) {
diffgrDiffgram = (DiffgrDiffgram) child;
}
}
}
class XsSchema extends XMLTag {
}
class DiffgrDiffgram extends XMLTag {
}
and use the following map
Hashtable map = new Hashtable();
map.put("DataTable", DataTable.class);
map.put("xs:schema", XsSchema.class);
map.put("diffgr:diffgram", DiffgrDiffgram.class);

Related

How to sort file\directory tree in lexicographic order (case-insensitive) on Linux system

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.

BeanPropertyRowMapper cache problem on Tomcat

I am using my custom row mapper extended from BeanPropertyRowMapper to override underscoreName method. It was working well. I added new fields to DTO class and deployed to Tomcat again. But it couldn't map new fields. In BeanPropertyRowMapper's initialize method it is using BeanUtils.getPropertyDescriptors(mappedClass). I think the problem is due to not updated cache on deploy.
I tried to restart tomcat but it didn't work.
#Slf4j
public class ColumnRowMapper<T> extends BeanPropertyRowMapper<T> {
private ColumnRowMapper(final Class<T> mappedClass) {
super(mappedClass);
}
#Override
protected String underscoreName(final String name) {
Field declaredField = getField(getMappedClass(), name);
String columnName = getColumnName(declaredField);
return columnName == null ? super.underscoreName(name) : columnName.toLowerCase();
}
private Field getField(Class<?> clazz, String name) {
if (clazz == null) {
return null;
}
Field field = null;
try {
field = clazz.getDeclaredField(name);
} catch (Exception e) {
field = getField(clazz.getSuperclass(), name);
}
if (field == null) {
log.warn("Ups, field «{}» not found in «{}».", name, clazz);
}
return field;
}
private String getColumnName(Field declaredField) {
Column annotation;
String columnName;
if (declaredField == null || (annotation = declaredField.getAnnotation(Column.class)) == null
|| StringUtils.isEmpty(columnName = annotation.name())) {
return null;
}
return columnName;
}
public static <T> BeanPropertyRowMapper<T> newInstance(final Class<T> mappedClass) {
return new ColumnRowMapper<>(mappedClass);
}
}

ANTLR4 parse tree to DOT using DOTGenerator

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).

Can a custom class loader be created for Java to conditionally load a class from JAR or from CLASS_PATH?

We had a discussion at work about pros and cons of static (from a built JAR) vs dynamic (from separate location in CLASS_PATH) loading of Java libraries.
In the middle of that discussion, it occurred to me: regardless of which side is right, maybe there's a way to have your cake and eat it too:
Have a default custom class loader for your organization
Class loader - when loading a specific library - checks a configuration that, for every library (or app+library combo for more granularity), contains a flag that determines if the library is to be loaded statically from the JAR or dynamically from CLASS_PATH
The apps all get built with the library classes in their JARs (default "backup" version of the library to use if you don't want to dynamically load new library version)
If you wish your library to be loaded statically (e.g. because new version is incompatible with older one, or just to eliminate change risk if warranted), you set the configuration flag for that library to true.
If instead your change risk is zero/low, you set the flag to false; and allow the library to be loaded dynamically, thus allowing you to release new versions of the library that get picked up by ALL apps without re-compilation and re-release (obviously, the apps would need to all be tested with new library for this to be anything but a minefield of bugs approach).
Whether my idea is good or bad, what I would like to know is, whether my second bullet point is even technically feasible with Java (say 1.8+), and if so, what would be involved in implementing it?
Yes, absolutely. I will assume Java 8 for this answer , as I have yet to familiarize myself with the capabilities of Java 9's Module System.
The main idea is straightforward--a ClassLoader whose loadClass(String, boolean) implementation adheres to the following "protocol":
If the class name argument refers to a system class, delegate loading to your parent loader and return. Otherwise proceed.
Does the name, according to your configuration, refer to an application class? If so proceed to step 2.1, otherwise to 3.
Get the application "search path" (the filesystem path of the JAR file itself according to the requirements in the question).
Attempt to find, in an implementation-specific manner (by means of e.g. path resolution against the search path's entries), a class resource below the search path that matches the name argument. If such a resource exists, proceed to step 5, otherwise to 3.
For each known library not yet examined, sorted by descending priority:
Does the name refer to a class of that library? If so continue; otherwise return to 3.
Get the search path for the particular library.
Attempt to find a class resource under the search path that matches the name argument. If successful, proceed to step 5, otherwise return to step 3.
If no matching class resource was established in step 2 or 3, raise an exception. Otherwise continue.
Retrieve the content of the resource, delegate to defineClass and resolveClass as appropriate, and return the new class.
Below is a (pretty ugly) sample implementation of such a ClassLoader.
Assumptions:
A module is an application or a library.
A module comprises a known set of classes; in other words, a module "is aware of its contents".
An application uses zero or more libraries.
Multiple logical applications may be running on the same JVM.
A Configuration communicates to its ClassLoader:
The "current" application requesting class loading.
Mappings between applications and search paths.
Mappings between library-application pairs and search paths.
Multiple loaders may use (the persisted representation of) the same configuration.
package com.example.q45313762;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
public final class ConfigurableClasspathClassLoader extends URLClassLoader {
public interface Configuration {
interface Module {
String getName();
String getVersion();
boolean includes(String resourceName);
}
interface Library extends Module {}
interface Application extends Module {}
enum LoadingMode {
STATIC, DYNAMIC;
}
Application getCurrentApplication();
Iterable<URL> getLibrarySearchPath(Library lib, LoadingMode mode, Application app);
Iterable<URL> getApplicationSearchPath(Application app);
Iterable<Library> getApplicationLibraries(Application app);
}
public static final class SimpleStaticConfiguration implements Configuration {
private static abstract class SimpleModule implements Module {
private final String name, version;
private final Predicate<String> resourceNameMatcher;
private SimpleModule(String name, String version, Predicate<String> resourceNameMatcher) {
requireNoneNull(name, version, resourceNameMatcher);
name = name.trim();
version = version.trim();
if (name.isEmpty() || version.isEmpty()) {
throw new IllegalArgumentException("arguments must not be empty.");
}
this.name = name;
this.version = version;
this.resourceNameMatcher = resourceNameMatcher;
}
#Override
public String getName() {
return name;
}
#Override
public String getVersion() {
return version;
}
#Override
public boolean includes(String resourceName) {
if (resourceName == null) {
return false;
}
return resourceNameMatcher.test(resourceName);
}
#Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((resourceNameMatcher == null) ? 0 : resourceNameMatcher.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
#Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof SimpleModule)) {
return false;
}
SimpleModule other = (SimpleModule) obj;
if (name == null) {
if (other.name != null) {
return false;
}
}
else if (!name.equals(other.name)) {
return false;
}
if (resourceNameMatcher == null) {
if (other.resourceNameMatcher != null) {
return false;
}
}
else if (!resourceNameMatcher.equals(other.resourceNameMatcher)) {
return false;
}
if (version == null) {
if (other.version != null) {
return false;
}
}
else if (!version.equals(other.version)) {
return false;
}
return true;
}
}
public static final class SimpleLibrary extends SimpleModule implements Library {
public SimpleLibrary(String name, String version, Predicate<String> resourceNameMatcher) {
super(name, version, resourceNameMatcher);
}
}
public static final class SimpleApplication extends SimpleModule implements Application {
public SimpleApplication(String name, String version, Predicate<String> resourceNameMatcher) {
super(name, version, resourceNameMatcher);
}
}
private static final class ModuleRegistry {
private static abstract class Key {
private final Module module;
private Key(Module module) {
requireNoneNull(module);
requireNoneNull(module.getName(), module.getVersion());
this.module = module;
}
private Module getModule() {
return module;
}
}
private static final class LibraryKey extends Key {
private final LoadingMode mode;
private final Application app;
private LibraryKey(Library lib, LoadingMode mode, Application app) {
super(lib);
requireNoneNull(mode);
requireNoneNull(app);
this.mode = mode;
this.app = app;
}
private Library getLibrary() {
return (Library) super.getModule();
}
private LoadingMode getLoadingMode() {
return mode;
}
private Application getApplication() {
return app;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
Library lib = getLibrary();
result = prime * result + ((lib == null) ? 0 : lib.hashCode());
result = prime * result + ((mode == null) ? 0 : mode.hashCode());
result = prime * result + ((app == null) ? 0 : app.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof LibraryKey)) {
return false;
}
LibraryKey other = (LibraryKey) obj;
Library thisLib = getLibrary(), othersLib = other.getLibrary();
if (thisLib == null) {
if (othersLib != null) {
return false;
}
}
else if (!thisLib.equals(othersLib)) {
return false;
}
if (mode != other.mode) {
return false;
}
if (app == null) {
if (other.app != null) {
return false;
}
}
else if (!app.equals(other.app)) {
return false;
}
return true;
}
}
private static final class ApplicationKey extends Key {
private ApplicationKey(Application app) {
super(app);
}
private Application getApplication() {
return (Application) super.getModule();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
Application app = getApplication();
result = prime * result + ((app == null) ? 0 : app.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ApplicationKey)) {
return false;
}
ApplicationKey other = (ApplicationKey) obj;
Application thisApp = getApplication(), othersApp = other.getApplication();
if (thisApp == null) {
if (othersApp != null) {
return false;
}
}
else if (!thisApp.equals(othersApp)) {
return false;
}
return true;
}
}
private static final class Value {
private final Set<URL> searchPath;
private Value(URL... searchPath) {
requireNoneNull((Object) searchPath);
if (searchPath == null || searchPath.length == 0) {
this.searchPath = EMPTY_SEARCH_PATH;
}
else {
this.searchPath = new LinkedHashSet<>(Arrays.asList(searchPath));
Iterator<URL> itr = this.searchPath.iterator();
while (itr.hasNext()) {
URL searchPathEntry = itr.next();
String proto = searchPathEntry.getProtocol();
if ("file".equals(proto) || "jar".equals(proto)) {
continue;
}
itr.remove();
}
verify();
}
}
private Set<URL> getSearchPath() {
verify();
return (searchPath == EMPTY_SEARCH_PATH) ? searchPath : Collections.unmodifiableSet(searchPath);
}
private void verify() {
Iterator<URL> itr = searchPath.iterator();
while (itr.hasNext()) {
try {
if (!Files.exists(Paths.get(itr.next().toURI()))) {
itr.remove();
}
}
catch (IllegalArgumentException | URISyntaxException | SecurityException e) {
itr.remove();
}
}
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((searchPath == null) ? 0 : searchPath.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Value)) {
return false;
}
Value other = (Value) obj;
if (searchPath == null) {
if (other.searchPath != null) {
return false;
}
}
else if (!searchPath.equals(other.searchPath)) {
return false;
}
return true;
}
}
private final Map<Key, Value> m = new LinkedHashMap<>();
private Supplier<Application> appProvider;
private ModuleRegistry() {
}
private ModuleRegistry(ModuleRegistry mr) {
m.putAll(mr.m);
appProvider = mr.appProvider;
}
private void putLibraryEntry(Library lib, LoadingMode mode, Application app, URL... searchPath) {
m.put(new LibraryKey(lib, mode, app), new Value(searchPath));
}
private void putApplicationEntry(Application app, URL... searchPath) {
m.put(new ApplicationKey(app), new Value(searchPath));
}
private Set<Library> getLibraries(Application app) {
Set<Library> ret = null;
for (Key k : m.keySet()) {
if (!(k instanceof LibraryKey)) {
continue;
}
LibraryKey lk = (LibraryKey) k;
if (lk.getApplication().equals(app)) {
if (ret == null) {
ret = new LinkedHashSet<>();
}
ret.add(lk.getLibrary());
}
}
if (ret == null) {
ret = NO_LIBS;
}
return ret;
}
private Set<URL> getLibrarySearchPath(Library lib, LoadingMode mode, Application app) {
Set<URL> ret = EMPTY_SEARCH_PATH;
Value v = m.get(new LibraryKey(lib, mode, app));
if (mode == LoadingMode.DYNAMIC && (v == null || v.getSearchPath().isEmpty())) {
v = m.get(new LibraryKey(lib, LoadingMode.STATIC, app));
}
if (v != null) {
ret = v.getSearchPath();
}
return ret;
}
private Set<URL> getApplicationSearchPath(Application app) {
Set<URL> ret = EMPTY_SEARCH_PATH;
Value v = m.get(new ApplicationKey(app));
if (v != null) {
ret = v.getSearchPath();
}
return ret;
}
private Supplier<Application> getApplicationProvider() {
return appProvider;
}
private void setApplicationProvider(Supplier<Application> appProvider) {
requireNoneNull(appProvider);
requireNoneNull(appProvider.get());
this.appProvider = appProvider;
}
private void clear() {
m.clear();
}
}
public static final class Builder {
private final ModuleRegistry registry = new ModuleRegistry();
private Builder() {
}
public synchronized Builder withLibrary(Library lib, LoadingMode mode, Application app, URL... searchPath) {
registry.putLibraryEntry(lib, mode, app, searchPath);
return this;
}
public synchronized Builder withApplication(Application app, URL... searchPath) {
registry.putApplicationEntry(app, searchPath);
return this;
}
public synchronized Builder withApplicationProvider(Supplier<Application> appProvider) {
registry.setApplicationProvider(appProvider);
return this;
}
public synchronized SimpleStaticConfiguration build() {
SimpleStaticConfiguration ret = new SimpleStaticConfiguration(this);
registry.clear();
return ret;
}
public synchronized Builder reset() {
registry.clear();
return this;
}
}
public static final Set<URL> EMPTY_SEARCH_PATH = Collections.emptySet();
private static final Set<Library> NO_LIBS = Collections.emptySet();
public static Builder newBuilder() {
return new Builder();
}
private final ModuleRegistry registry;
private SimpleStaticConfiguration(Builder b) {
registry = new ModuleRegistry(b.registry);
}
#Override
public Application getCurrentApplication() {
return registry.getApplicationProvider().get();
}
#Override
public Iterable<URL> getLibrarySearchPath(Library lib, LoadingMode mode, Application app) {
return registry.getLibrarySearchPath(lib, mode, app);
}
#Override
public Iterable<URL> getApplicationSearchPath(Application app) {
return registry.getApplicationSearchPath(app);
}
#Override
public Iterable<Library> getApplicationLibraries(Application app) {
return registry.getLibraries(app);
}
}
private static final String JAVA_HOME_PROP = System.getProperty("java.home");
private static void requireNoneNull(Object... args) {
if (args != null) {
for (Object o : args) {
Objects.requireNonNull(o);
}
}
}
private final Lock readLock, writeLock;
private Configuration cfg;
{
ReadWriteLock rwl = new ReentrantReadWriteLock(false);
readLock = rwl.readLock();
writeLock = rwl.writeLock();
}
public ConfigurableClasspathClassLoader(Configuration cfg, ClassLoader parent) {
super(new URL[0], parent);
setConfiguration(cfg);
}
public void setConfiguration(Configuration cfg) {
requireNoneNull(cfg);
try {
writeLock.lock();
this.cfg = cfg;
}
finally {
writeLock.unlock();
}
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name == null) {
throw new ClassNotFoundException(name);
}
synchronized (getClassLoadingLock(name)) {
Class<?> ret;
Class<?> self = getClass();
if (self.getName().equals(name)) {
// no need to "reload" our own class
return self;
}
ret = findLoadedClass(name);
if (ret != null) {
// already loaded
return ret;
}
// unknown
ret = findClass(name);
if (resolve) {
resolveClass(ret);
}
return ret;
}
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// perform a search on the global classpath (obviously far from ideal)
Enumeration<URL> allMatches;
String modifiedName = name.replace(".", "/").concat(".class");
try {
allMatches = getResources(modifiedName);
}
catch (IOException ioe) {
throw new ClassNotFoundException(name);
}
Set<URL> filteredMatches = new LinkedHashSet<>();
while (allMatches.hasMoreElements()) {
URL match = allMatches.nextElement();
if (match.getPath().replaceFirst("file:", "").startsWith(JAVA_HOME_PROP)) {
// probably a bootstrap classpath class - these are off limits to us
return getParent().loadClass(name);
}
// candidate match
filteredMatches.add(match);
}
if (!filteredMatches.isEmpty()) {
try {
readLock.lock();
BiFunction<Configuration.Module, Iterable<URL>, URL[]> matcher = (module, searchPath) -> {
URL[] ret = null;
if (module.includes(name)) {
outer: for (URL searchPathEntry : searchPath) {
for (URL filteredMatch : filteredMatches) {
if (filteredMatch != null && filteredMatch.getPath().replaceFirst("file:", "")
.startsWith(searchPathEntry.getPath())) {
ret = new URL[] { filteredMatch, searchPathEntry };
break outer;
}
}
}
}
return ret;
};
Configuration.Application app = cfg.getCurrentApplication();
URL matchedClassResource = null, matchingSearchPath = null;
if (app != null) {
// try an application search path match
URL[] tmp = matcher.apply(app, cfg.getApplicationSearchPath(app));
if (tmp != null) {
matchedClassResource = tmp[0];
matchingSearchPath = tmp[1];
}
else {
// try matching against the search path of any library "known to" app
for (Configuration.Library lib : cfg.getApplicationLibraries(app)) {
tmp = matcher.apply(lib,
cfg.getLibrarySearchPath(lib, Configuration.LoadingMode.DYNAMIC, app));
if (tmp != null) {
matchedClassResource = tmp[0];
matchingSearchPath = tmp[1];
break;
}
}
}
if (matchedClassResource != null) {
// matched - load
byte[] classData = readClassData(matchedClassResource);
return defineClass(name, classData, 0, classData.length,
constructClassDomain(matchingSearchPath));
}
}
}
finally {
readLock.unlock();
}
}
throw new ClassNotFoundException(name);
}
private byte[] readClassData(URL classResource) {
try (InputStream in = new BufferedInputStream(classResource.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
while (in.available() > 0) {
out.write(in.read());
}
return out.toByteArray();
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private ProtectionDomain constructClassDomain(URL codeSourceLocation) {
CodeSource cs = new CodeSource(codeSourceLocation, (Certificate[]) null);
return new ProtectionDomain(cs, getPermissions(cs), this, null);
}
}
Notes:
Search paths registered with the loader must be subsets (subtrees) of the effective classpath ("java.class.path" property). Furthermore, "fat JARs" are not supported.
I could not include a usage example due to post length constraints. I will provide one via gist if requested.

Append generated json tree

I would like to ask you for help. I have a Device object
public class Device {
public String name;
public String deviceId;
#JsonSerialize(using = CustomResourceSerializer.class)
public Map<String, Map<String, Object>> customResources;
}
My goal is to "extract" this map directly to Device Object. Firstly I used #JsonAnyGetter which worked well and Map was nested under field String of first map directly under Device object.
But I need more complex logic and I have two problems which I don't know how to solve.
Key of first map is for example "configuration/inputOne". With #JsonAnyGetter the example output is { "configuration/inputOne": { "rate":23 } }
What I need is nested structure based on delimiter, so
{ "configuration": { "inputOne": { "rate":23 } } }
This I was almost able to do easily with custom JsonSerializer
jsonGenerator.writeStartObject();
foreach(splited key)
jsonGenerator.writeObjectFieldStart(resourceUriItem);
foreach(value)
jsonGenerator.writeObjectField(k, v);
foreach(splitted key)
jsonGenerator.writeEndObject();
jsonGenerator.writeEndObject();
But final object looks like
{ "customResource": {"configuration": { "inputOne": { "rate":23 } } } }
CustomResource field is from Device object and I don't know how to get rid of it. As with JsonAnyGetter. That's the first problem.
As you see, I am splitting the key of the map to have more nested strucutre, so from the "configuration/inputOne" to { configuration { inputOne { .. } }. But the map customResources can have of course multiple items, so for example:
"configuration/inputOne"
"configuration/inputTwo"
"configuration"
Now you probably see where is the problem. As I am iterating over keys and I am creating nested structure, I will override it. So for example, firstly I will create object configuration, then inputOne and fill it with fields. Closing objects. Then second item in map, creating configuration object and inputTwo object. But with creation of configuration, I will delete the one previously created with inputOne.
Do you have any proposal how to solve this? Thanks.
You can turn your map into a type of a tree by splitting on the / and creating a parent child relationship on the split items.
Using the following class as a tree element / node.
class TreeElement {
private String key;
private Object value;
private List<TreeElement> children;
public TreeElement(String key) {
this.key = key;
}
// getters and setters here
public void addChild(TreeElement child) {
if (this.children == null) {
this.children = new ArrayList<TreeElement>();
}
this.children.add(child);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TreeElement other = (TreeElement) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equalsIgnoreCase(other.key))
return false;
return true;
}
#Override
public String toString() {
return "TreeElement [key=" + key + ", value=" + value + ", children=" + children + "]";
}
}
And the following test code.
public static void main(String[] args) {
try {
// create the config1, config2, etc.. here
Device device1 = new Device();
device1.customResources = new HashMap<String, Map<String, Object>>();
device1.customResources.put("configuration/inputOne", config1);
device1.customResources.put("configuration/inputTwo", config2);
device1.customResources.put("configuration", config3);
device1.customResources.put("configuration", duplicateConfig3);
device1.customResources.put("otherConfig", otherConfig);
device1.customResources.put("thirdConfig1", thirdConfig1);
device1.customResources.put("thirdConfig1/inputOne", thirdConfig2);
device1.customResources.put("thirdConfig1/inputOne", duplicateThirdConfig2);
List<TreeElement> elements = new ArrayList<TreeElement>();
for (Map.Entry<String, Map<String, Object>> entry : device1.customResources.entrySet()) {
TreeElement element = generateElement(null, entry.getKey(), entry.getValue());
elements.add(element);
}
List<TreeElement> joinedElements = joinElements(elements);
for (TreeElement e : joinedElements) {
System.out.println(e.getKey() + " - " + e.getValue());
if (e.getChildren() != null) {
for (TreeElement c : e.getChildren()) {
System.out.println("\t" + c.getKey() + " - " + c.getValue());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
This method generates a TreeElement from a Map> variable.
private static TreeElement generateElement(TreeElement parent, String item, Map<String, Object> value) {
try {
List<String> tokens = new ArrayList<String>(Arrays.asList(item.split("/")));
TreeElement child = new TreeElement(tokens.get(0));
boolean parentWasNull = false;
if (parent == null) {
parent = child;
parentWasNull = true;
}
if (tokens.size() > 1) {
if (parentWasNull == false) {
parent.addChild(child);
}
tokens.remove(0);
generateElement(child, StringUtils.join(tokens, "/"), value);
} else {
child.setValue(value);
if (parentWasNull == false) {
parent.addChild(child);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return parent;
}
This method joins common TreeElement objects into one parent and multiple children.
private static List<TreeElement> joinElements(List<TreeElement> elements) {
List<TreeElement> joinedElements = new ArrayList<TreeElement>();
for (TreeElement element : elements) {
if (joinedElements.contains(element) == true) {
// joined elment does not have children
if (joinedElements.get(joinedElements.indexOf(element)).getChildren() == null) {
joinedElements.get(joinedElements.indexOf(element)).setChildren(element.getChildren());
} else {
//joined element has children and the current element also has children
if (element.getChildren() != null) {
joinedElements.get(joinedElements.indexOf(element)).getChildren().addAll(element.getChildren());
}
}
/*
* set the value of joined element to the value of the current element; will overwrite
* any existing value if duplicates exist
*/
if (element.getValue() != null) {
joinedElements.get(joinedElements.indexOf(element)).setValue(element.getValue());
}
} else {
joinedElements.add(element);
}
}
return joinedElements;
}
I'm not sure how efficient this code is, but you get the below output which you can traverse in your custom serializer to print to JSON.
thirdConfig1 - {rate=30}
inputOne - {rate=3020}
configuration - {rate=1200}
inputOne - {rate=23}
inputTwo - {rate=50}
otherConfig - {rate=10}

Categories

Resources