Creating an Hierarchy-Object with an undefined number of childs - java

I am currently working on a "code parser" parsing Valve Map Format (.vmf files) into a java readable Object.
In vmf files,
there are 2 types of objects: Classes and Properties.
classes have a name and can contain other classes and properties.
properties have a name and an unlimited number of values.
Therefore I created a VMFClass Object Class and a VMFProperty Object Class.
I created a List with self-created HierarchyObjects, containing the VMFClass/VMFProperty Object, an UUID and the parentUUID.
The VMFClass Object Contains 2 Lists one with sub-VMFClasses, one with properties.
My Problem is that I have no clue on how to achieve that a Class contains all of its subclasses, since I can't tell how much subclasses the subclasses have and so on...
Here is my Code (Github):
HierachyObject:
package net.minecraft.sourcecraftreloaded.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HierarchyObject {
private static Map<Long, Long> usedUUIDs = new HashMap<>();
private long parentUUID;
private long UUID;
private Object object;
/**
*
* #param Object
* #param parent -1 is maximum level
*/
public HierarchyObject(Object object, long parent) {
this.object = object;
this.parentUUID = parent;
while (true) {
long random = (long) (Math.random() * Long.MAX_VALUE);
if (usedUUIDs.containsKey(random)) {
this.UUID = random;
usedUUIDs.put(random, parent);
break;
}
}
}
public long getUUID() {
return UUID;
}
public long getParentUUID() {
return parentUUID;
}
public static long getParentUUIDbyUUID(long UUID) {
if (usedUUIDs.containsKey(UUID)) {
return usedUUIDs.get(UUID);
}
return -1;
}
public Object getObject() {
return object;
}
public static boolean hasChild(long UUID){
if(usedUUIDs.containsValue(UUID)){
return true;
}
if(UUID == -1){
return true;
}
return false;
}
public boolean hasChild(){
return hasChild(this.UUID);
}
public static long[] getChildUUIDs(long UUID){
if(hasChild(UUID)){
List<Long> cUUIDs = new ArrayList<>();
for(int i = 0; i < usedUUIDs.size(); i++){
for (Map.Entry<Long, Long> e : usedUUIDs.entrySet()) {
if(e.getValue().longValue() == UUID){
cUUIDs.add(e.getKey());
}
}
}
return ListUtils.toPrimitivebyList(cUUIDs);
}
return null;
}
}
VMFProperty:
package net.minecraft.sourcecraftreloaded.source;
public class VMFProperty{
private String name;
private String[] values;
public VMFProperty(String name, String... values) {
this.name = name;
this.values = values;
}
public String getName() {
return name;
}
public String[] getValues() {
return values;
}
#Override
public boolean equals(Object paramObject){
if(paramObject instanceof VMFProperty){
return ((VMFProperty)paramObject).name.equals(this.name) && ((VMFProperty)paramObject).values.equals(this.values);
}
return false;
}
}
VMFClass:
package net.minecraft.sourcecraftreloaded.source;
import java.util.List;
public class VMFClass{
private List<VMFClass> classes;
private List<VMFProperty> properties;
private String name;
public VMFClass(String name, List<VMFClass> classes, List<VMFProperty> properties) {
this.name = name;
this.classes = classes;
this.properties = properties;
}
public String getName() {
return name;
}
public List<VMFClass> getClasses() {
return classes;
}
public List<VMFProperty> getProperties() {
return properties;
}
public void add(VMFClass vmfclass) {
classes.add(vmfclass);
}
public void add(VMFProperty vmfproperty) {
properties.add(vmfproperty);
}
public void remove(VMFClass vmfclass) {
classes.remove(vmfclass);
}
public void remove(VMFProperty vmfproperty) {
properties.remove(vmfproperty);
}
#Override
public boolean equals(Object paramObject){
if(paramObject instanceof VMFClass){
return ((VMFClass)paramObject).properties.equals(this.properties) && ((VMFClass)paramObject).classes.equals(this.classes) && ((VMFClass)paramObject).name.equals(this.name);
}
return false;
}
}
VMFObject (the class executing all the code):
package net.minecraft.sourcecraftreloaded.source;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.sourcecraftreloaded.utils.HierarchyObject;
public class VMFObject {
private String rawfile = "";
private List<VMFClass> toplevelclasses;
private static final String INVALID_CHARS = "\\*,;<>|?=`´#'+~^°!§$%&()[].:-_";
public VMFObject(List<VMFClass> toplevelclasses) {
this.toplevelclasses = toplevelclasses;
}
public VMFObject() {
this(new ArrayList<VMFClass>());
}
public void write(File file) {
VMFWriter.write(file, rawfile);
}
public VMFObject read(File file) throws VMFParsingException {
this.rawfile = VMFReader.read(file);
parse();
return this;
}
public List<VMFClass> getClasses() {
return toplevelclasses;
}
private void parse() throws VMFParsingException {
evaluate();
get();
}
private void evaluate() throws VMFParsingException {
char[] textchars = rawfile.toCharArray();
int[] c = new int[]{0, 0, 0};
int line = 0;
int linepos = 0;
for (int i : textchars) {
linepos++;
if (textchars[i] == '\n') {
line++;
linepos = 0;
c[3] = 0;
if (c[3] % 2 != 0) {
throw new VMFParsingException("Invalid quotes on line" + line + ":" + linepos);
}
}
if (textchars[i] == '{') {
c[1]++;
}
if (textchars[i] == '}') {
c[2]++;
}
if (textchars[i] == '"') {
c[3]++;
if (c[1] - c[2] == 0) {
}
}
if (textchars[i] == '/' && textchars[i + 1] == '/') {
while (true) {
i++;
if (textchars[i] == '\n') {
break;
}
}
}
if (textchars[i] == '/' && textchars[i + 1] == ' ') {
throw new VMFParsingException("Invalid Character '/' on line" + line + ":" + linepos);
}
if (INVALID_CHARS.indexOf(textchars[i]) != -1) {
throw new VMFParsingException("Invalid Character '" + textchars[i] + "' on line" + line + ":" + linepos);
}
}
if (c[1] != c[2]) {
throw new VMFParsingException("Unbalanced brackets in vmf File");
}
}
public void add(VMFClass vmfclass) {
toplevelclasses.add(vmfclass);
}
private void get() throws VMFParsingException {
List<HierarchyObject> content = new ArrayList<>();
long curparent = -1;
String[] text = rawfile.split("\n");
for (int i = 0; i < text.length; i++) {
String line = text[i].trim();
if (line.startsWith("//")) {
continue;
} else {
byte quotec = 0;
char[] linechar = line.toCharArray();
boolean readp = false;
List<String> reads = new ArrayList<>();
byte creads = 0;
for (int y = 0; y < linechar.length; y++) {
if (linechar[y] == '/' && linechar[y + 1] == '/') {
break;
}
if (linechar[y] == '"') {
quotec++;
if (quotec % 2 == 0) {
readp = false;
creads++;
} else {
readp = true;
}
}
if (readp) {
reads.set(creads, reads.get(creads) + linechar[y]);
}
if (linechar[y] == '{') {
HierarchyObject object = new HierarchyObject(new VMFClass(line.substring(line.substring(0, y).lastIndexOf(' '), y).trim(), null, null), curparent);
content.add(object);
curparent = object.getUUID();
}
if (linechar[y] == '}') {
curparent = HierarchyObject.getParentUUIDbyUUID(curparent);
}
}
content.add(new HierarchyObject(new VMFProperty(reads.remove(0), reads.toArray(new String[reads.size()])), curparent));
}
}
buildObject(content);
}
private void buildObject(List<HierarchyObject> content) {
long curUUID = -1;
for(int i = 0; i < HierarchyObject.getChildUUIDs(curUUID).length; i++){
HierarchyObject.getChildUUIDs(curUUID);
}
//TODO implement
}
}
the //TODO part is where the Hierachy Object should get "converted" to the actual object.

Overview
It seems to me that your class layout is overcomplicated.
Let's try to simplify it...
What you have described with the VMF model is essentially a linked-list Tree.
Here's what the model looks like:
[.vmf file] (root)
/ \
_____/ \ _____
/ \
/ \
(VMFClass) (VMFClass)
/ \ / \
/ \ / \
/ \ / \
(VMFClass) (VMFProperties) (VMFClass) (VMFProperties)
/ \
/ \
/ \
(VMFClass) (VMFProperties)
What you need:
A Parser class (in your case, you have VMFObject, but lets call this class VMFParser).
The VMFClass and VMFProperty classes which you have are fine.
What you don't need:
The HierarchyObject class. The VMFParser can be the main controller and container for the hierarchy (e.g. the linked-list Tree model).
All the UUIDs (parent, child, etc.) These are just complicated things, but I see why you have them. You don't need them to track the hierarchy - Java will do this for us!!
VMFClass
public class VMFClass
{
// name of the class
private String name;
// reference back up to the parent
private VMFClass parentClass = null;
// all direct children go here
private List<VMFClass> children = new ArrayList<VMFClass>();
// I don't think you need a list of properties here since your VMFProperty class holds onto an array of properties
private VMFProperty properties;
// set the parent of this class
public void setParent (VMFClass parent)
{
this.parentClass = parent;
}
// get the direct children
public List<VMFClass> getChildren()
{
return this.children;
}
// rest of methods...
}
VMFParser
class VMFParser
{
private String rawfile = "";
// this is really the container for everything - think of it as the file shell
private VMFClass root = new VMFClass("root", null, null);
// construct yourself with the file
public VMFParser (String fileName)
{
this.rawfile = fileName;
}
public void parse ()
{
// all the parsing code goes here
read();
evaluate();
get();
// now at this point your hierarchy is built and stored in the
// root object in this class.
// Use the traverse method to go through it
}
private void get() throws VMFParsingException
{
// keep a reference to the current VMFClass parent
// starts out as root
VMFClass currParentClass = root;
// main parse loop
for (...)
{
// if you find a class
VMFClass currClass = new VMFClass(/* params here */);
// add this class to the parent
currParentClass.add(currClass);
// set the parent of this class
currClass.setParent(currParentClass);
// if you find a property
// parse and add all the properties to the property
VMFProperty property = new VMFProperty (/* value params here */);
// attach this property to the last VMF class that got parsed
currClass.setPoperties(property);
// If you nest deeper into classes, then the parent becomes the current class
currParentClass = currClass;
// If you go back out of a class
currParentClass = currClass.getParent();
}
}
// Traverse the hierarchy
public void traverse ()
{
traverseTree(root);
}
private void traverseTree (VMFClass root)
{
System.out.println("Class Name: " + root.getName());
// print out any properties
VMFProperty prop = root.getProperty();
if (prop != null)
{
System.out.println("Property Name: " + prop.getName());
String [] props = prop.getValues();
for (String s: props)
{
System.out.println("Value: " + s);
}
}
// get all child classes
List<VMFClass> children = root.getChildren();
for (VMFClass c: children)
{
traverseTree(c);
}
}
}
Client Code
Example
public static void main(String[] args)
{
VMFParser vmfParser = null;
try
{
vmfParser = new VMFParser("myFile.vmf");
vmfParser.parse();
// access the vmfParser for the hierarchy
vmfParser.traverse();
}
catch (VMFParsingException vpe)
{
// do something here
vpe.printStackTrace();
}
finally
{
// clean up...
}
}

If you are just looking to find all sub classes of particular class or interface , this might help you,
How can I get a list of all the implementations of an interface programmatically in Java?

Related

split strings with backtracking

I'm trying to write a code that split a spaceless string into meaningful words but when I give sentence like "arealways" it returns ['a', 'real', 'ways'] and what I want is ['are', 'always'] and my dictionary contains all this words. How can I can write a code that keep backtracking till find the best matching?
the code that returns 'a', 'real', 'ways':
splitter.java:
public class splitter {
HashMap<String, String> map = new HashMap<>();
Trie dict;
public splitter(Trie t) {
dict = t;
}
public String split(String test) {
if (dict.contains(test)) {
return (test);
} else if (map.containsKey(test)) {
return (map.get(test));
} else {
for (int i = 0; i < test.length(); i++) {
String pre = test.substring(0, i);
if (dict.contains(pre)) {
String end = test.substring(i);
String fixedEnd = split(end);
if(fixedEnd != null){
map.put(test, pre + " " + fixedEnd);
return pre + " " + fixedEnd;
}else {
}
}
}
}
map.put(test,null);
return null;
}
}
Trie.java:
public class Trie {
public static class TrieNode {
private HashMap<Character, TrieNode> charMap = new HashMap<>();
public char c;
public boolean endOWord;
public void insert(String s){
}
public boolean contains(String s){
return true;
}
}
public TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String s){
TrieNode p = root;
for(char c : s.toCharArray()) {
if(! p.charMap.containsKey(c)) {
TrieNode node = new TrieNode();
node.c = c;
p.charMap.put(c, node);
}
p = p.charMap.get(c);
}
p.endOWord = true;
}
public boolean contains(String s){
TrieNode p = root;
for(char c : s.toCharArray()) {
if(!p.charMap.containsKey(c)) {
return false;
}
p = p.charMap.get(c);
}
return p.endOWord;
}
public void insertDictionary(String filename) throws FileNotFoundException{
File file = new File(filename);
Scanner sc = new Scanner(file);
while(sc.hasNextLine())
insert(sc.nextLine());
}
public void insertDictionary(File file) throws FileNotFoundException{
Scanner sc = new Scanner(file);
while(sc.hasNextLine())
insert(sc.nextLine());
}
}
WordSplitter class:
public class WordSplitter {
public static void main(String[] args) throws FileNotFoundException {
String test = "arealways";
String myFile = "/Users/abc/Desktop/dictionary.txt";
Trie dict = new Trie();
dict.insertDictionary(myFile);
splitter sp = new splitter(dict);
test = sp.split(test);
if(test != null)
System.out.println(test);
else
System.out.println("No Splitting Found.");
}
}
Using the OP's split method and the implementation of Trie found in The Trie Data Structure in Java Baeldung's article, I was able to get the following results:
realways=real ways
arealways=a real ways
However, if I remove the word "real" or "a" from the dictionary, I get the following results:
realways=null
arealways=are always
Here's the entire code I used to get these results:
public class Splitter {
private static Map<String, String> map = new HashMap<>();
private Trie dict;
public Splitter(Trie t) {
dict = t;
}
/**
* #param args
*/
public static void main(String[] args) {
List<String> words = List.of("a", "always", "are", "area", "r", "way", "ways"); // The order of these words does not seem to impact the final result
String test = "arealways";
Trie t = new Trie();
for (String word : words) {
t.insert(word);
}
System.out.println(t);
Splitter splitter = new Splitter(t);
splitter.split(test);
map.entrySet().forEach(System.out::println);
}
public String split(String test) {
if (dict.find(test)) {
return (test);
} else if (map.containsKey(test)) {
return (map.get(test));
} else {
for (int i = 0; i < test.length(); i++) {
String pre = test.substring(0, i);
if (dict.find(pre)) {
String end = test.substring(i);
String fixedEnd = split(end);
if (fixedEnd != null) {
map.put(test, pre + " " + fixedEnd);
return pre + " " + fixedEnd;
} else {
}
}
}
}
map.put(test, null);
return null;
}
public static class Trie {
private TrieNode root = new TrieNode();
public boolean find(String word) {
TrieNode current = root;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
TrieNode node = current.getChildren().get(ch);
if (node == null) {
return false;
}
current = node;
}
return current.isEndOfWord();
}
public void insert(String word) {
TrieNode current = root;
for (char l : word.toCharArray()) {
current = current.getChildren().computeIfAbsent(l, c -> new TrieNode());
}
current.setEndOfWord(true);
}
#Override
public String toString() {
return toString(root);
}
/**
* #param root2
* #return
*/
private String toString(TrieNode node) {
return node.toString();
}
public static class TrieNode {
private Map<Character, TrieNode> children = new HashMap<>() ;
private String contents;
private boolean endOfWord;
public Map<Character, TrieNode> getChildren() {
return children;
}
public void setEndOfWord(boolean endOfWord) {
this.endOfWord = endOfWord;
}
public boolean isEndOfWord() {
return endOfWord;
}
#Override
public String toString() {
StringBuilder sbuff = new StringBuilder();
if (isLeaf()) {
return sbuff.toString();
}
children.entrySet().forEach(entry -> {
sbuff.append(entry.getKey() + "\n");
});
sbuff.append(" ");
return children.toString();
}
private boolean isLeaf() {
return children.isEmpty();
}
}
public void delete(String word) {
delete(root, word, 0);
}
private boolean delete(TrieNode current, String word, int index) {
if (index == word.length()) {
if (!current.isEndOfWord()) {
return false;
}
current.setEndOfWord(false);
return current.getChildren().isEmpty();
}
char ch = word.charAt(index);
TrieNode node = current.getChildren().get(ch);
if (node == null) {
return false;
}
boolean shouldDeleteCurrentNode = delete(node, word, index + 1) && !node.isEndOfWord();
if (shouldDeleteCurrentNode) {
current.getChildren().remove(ch);
return current.getChildren().isEmpty();
}
return false;
}
}
}
I improved the original code by adding a toString() method to the Trie and TrieNode. Now, when I print out the Trie object "t", I get the following result:
{a={r={e={a=}}, l={w={a={y={s=}}}}}, w={a={y={s=}}}}
My conclusion is that the OP's TrieNode implementation is incorrect. The way the Trie is built, given the inputted string value, the behavior described by the OP seems to be correct.

Find methods with modified code between two versions of a Java class

I'd like to find the methods which changed between two arbitrary Java files.
What I've Tried
I've tried using diff (GNU diffutils 3.3) to find changes to lines in the file and diff --show-c-function connect those lines to the changed method. Unfortunately, in Java this lists the class, not the function.
I also tried git diff which seems to properly be able to find the changed function (at least as displayed on GitHub), but it doesn't always list the full signature and my files are not in the same Git repository (https://github.com/apache/commons-math/commit/34adc606601cb578486d4a019b4655c5aff607b5).
Desired Results
Input:
~/repos/d4jBugs/Math_54_buggy/src/main/java/org/apache/commons/math/dfp/Dfp.java
~/repos/d4jBugs/Math_54_fixed/src/main/java/org/apache/commons/math/dfp/Dfp.java
State of Files:
The changed methods between those two files are public double toDouble() and protected Dfp(final DfpField field, double x)
Output: (fully qualified names)
org.apache.commons.math.dfp.Dfp.toDouble()
org.apache.commons.math.dfp.Dfp(final DfpField field, double x)
Summary
Can I find the modified methods with the GNU diffutils tool or git diff and if yes, how would I do that? (Note: I'm not bound to those tools and am happy to install something else if needed.)
I used JavaParser 3.4.4, but it probably works but has not been tested with other versions.
It can be imported in Gradle with:
compile group: 'com.github.javaparser', name: 'javaparser-core', version: '3.4.4'
You can use my class like:
HashSet<String> changedMethods = MethodDiff.methodDiffInClass(
oldFileNameWithPath,
newFileNameWithPath
);
MethodDiff Source:
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.printer.PrettyPrinterConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Created by Loren Klingman on 10/19/17.
* Finds Changes Between Methods of Two Java Source Files
*/
public class MethodDiff {
private static PrettyPrinterConfiguration ppc = null;
class ClassPair {
final ClassOrInterfaceDeclaration clazz;
final String name;
ClassPair(ClassOrInterfaceDeclaration c, String n) {
clazz = c;
name = n;
}
}
public static PrettyPrinterConfiguration getPPC() {
if (ppc != null) {
return ppc;
}
PrettyPrinterConfiguration localPpc = new PrettyPrinterConfiguration();
localPpc.setColumnAlignFirstMethodChain(false);
localPpc.setColumnAlignParameters(false);
localPpc.setEndOfLineCharacter("");
localPpc.setIndent("");
localPpc.setPrintComments(false);
localPpc.setPrintJavadoc(false);
ppc = localPpc;
return ppc;
}
public static <N extends Node> List<N> getChildNodesNotInClass(Node n, Class<N> clazz) {
List<N> nodes = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
// Don't go into a nested class
continue;
}
if (clazz.isInstance(child)) {
nodes.add(clazz.cast(child));
}
nodes.addAll(getChildNodesNotInClass(child, clazz));
}
return nodes;
}
private List<ClassPair> getClasses(Node n, String parents, boolean inMethod) {
List<ClassPair> pairList = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration c = (ClassOrInterfaceDeclaration)child;
String cName = parents+c.getNameAsString();
if (inMethod) {
System.out.println(
"WARNING: Class "+cName+" is located inside a method. We cannot predict its name at"
+ " compile time so it will not be diffed."
);
} else {
pairList.add(new ClassPair(c, cName));
pairList.addAll(getClasses(c, cName + "$", inMethod));
}
} else if (child instanceof MethodDeclaration || child instanceof ConstructorDeclaration) {
pairList.addAll(getClasses(child, parents, true));
} else {
pairList.addAll(getClasses(child, parents, inMethod));
}
}
return pairList;
}
private List<ClassPair> getClasses(String file) {
try {
CompilationUnit cu = JavaParser.parse(new File(file));
return getClasses(cu, "", false);
} catch (FileNotFoundException f) {
throw new RuntimeException("EXCEPTION: Could not find file: "+file);
}
}
public static String getSignature(String className, CallableDeclaration m) {
return className+"."+m.getSignature().asString();
}
public static HashSet<String> methodDiffInClass(String file1, String file2) {
HashSet<String> changedMethods = new HashSet<>();
HashMap<String, String> methods = new HashMap<>();
MethodDiff md = new MethodDiff();
// Load all the method and constructor values into a Hashmap from File1
List<ClassPair> cList = md.getClasses(file1);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
methods.put(methodSignature, m.getBody().get().toString(getPPC()));
} else {
System.out.println("Warning: No Body for "+file1+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
methods.put(methodSignature, con.getBody().toString(getPPC()));
}
}
// Compare everything in file2 to what is in file1 and log any differences
cList = md.getClasses(file2);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
String body1 = methods.remove(methodSignature);
String body2 = m.getBody().get().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
} else {
System.out.println("Warning: No Body for "+file2+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
String body1 = methods.remove(methodSignature);
String body2 = con.getBody().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
}
// Anything left in methods was only in the first set and so is "changed"
for (String method : methods.keySet()) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(method.replace(" ", ""));
}
}
return changedMethods;
}
private static void removeComments(Node node) {
for (Comment child : node.getAllContainedComments()) {
child.remove();
}
}
}

Hibernate gives a strange ClassCast exception (using Transformers)

This code:
#Override
public List<FactCodeDto> getAllFactsWithoutParentsAsFactDto() {
String completeQuery = FactCodeQueries.SELECT_DTO_FROM_FACT_WITH_NO_PARENTS;
Query query = createHibernateQueryForUnmappedTypeFactDto(completeQuery);
List<FactCodeDto> factDtoList = query.list(); //line 133
return factDtoList;
}
calling this method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
gives me a ClassCastException -> part of the trace:
Caused by: java.lang.ClassCastException: org.bamboomy.cjr.dto.FactCodeDto cannot be cast to java.util.Map
at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:78)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:75)
at org.hibernate.loader.custom.CustomLoader.getResultList(CustomLoader.java:435)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
at org.hibernate.loader.Loader.list(Loader.java:2418)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:336)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1898)
at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:318)
at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125)
at org.bamboomy.cjr.dao.factcode.FactCodeDAOImpl.getAllFactsWithoutParentsAsFactDto(FactCodeDAOImpl.java:133)
Which is pretty strange because, indeed, if you look up the source code of Hibernate it tries to do this:
#Override
#SuppressWarnings("unchecked")
public void set(Object target, Object value, SessionFactoryImplementor factory) {
( (Map) target ).put( propertyName, value ); //line 102
}
Which doesn't make any sense...
target is of type Class and this code tries to cast it to Map,
why does it try to do that???
any pointers are more than welcome...
I'm using Hibernate 5 (and am upgrading from 3)...
edit: I also use Spring (4.2.1.RELEASE; also upgrading) which calls these methods upon deploy, any debugging pointers are most welcome as well...
edit 2: (the whole FactCodeDto class, as requested)
package org.bamboomy.cjr.dto;
import org.bamboomy.cjr.model.FactCode;
import org.bamboomy.cjr.model.FactCodeType;
import org.bamboomy.cjr.utility.FullDateUtil;
import org.bamboomy.cjr.utility.Locales;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.Assert;
import java.util.*;
#Getter
#Setter
#ToString
public class FactCodeDto extends TreeNodeValue {
private String cdFact;
private String cdFactSuffix;
private Boolean isSupplementCode;
private Boolean isTitleCode;
private Boolean mustBeFollowed;
private Date activeFrom;
private Date activeTo;
private Boolean isCode;
private Long idFact;
private Long idParent;
private String type;
Map<Locale, String> description = new HashMap<Locale, String>(3);
public FactCodeDto() {
}
public FactCodeDto(String prefix, String suffix) {
super();
this.cdFact = prefix;
this.cdFactSuffix = suffix;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed, Long idFact, Long idParent, Boolean isCode, Boolean isTitleCode, Date from, Date to, Map<Locale, String> descriptions,String type) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
this.idFact = idFact;
this.idParent = idParent;
this.isCode = isCode;
this.isTitleCode = isTitleCode;
this.activeFrom = from;
this.activeTo = to;
if (descriptions != null) {
this.description = descriptions;
}
this.type = type;
}
public FactCodeDto(FactCode fc) {
this(fc.getPrefix(), fc.getSuffix(), fc.isSupplementCode(), fc.isHasMandatorySupplCodes(), fc.getId(), fc.getParent(), fc.isActualCode(), fc.isTitleCode(), fc.getActiveFrom(), fc.getActiveTo(), fc.getAllDesc(),fc.getType().getCode());
}
public String formatCode() {
return FactCode.formatCode(cdFact, cdFactSuffix);
}
public boolean isActive() {
Date now = new Date(System.currentTimeMillis());
return FullDateUtil.isBetweenDates(now, this.activeFrom, this.activeTo);
}
public void setDescFr(String s) {
description.put(Locales.FRENCH, s);
}
public void setDescNl(String s) {
description.put(Locales.DUTCH, s);
}
public void setDescDe(String s) {
description.put(Locales.GERMAN, s);
}
/**
* public String toString() {
* StringBuilder sb = new StringBuilder();
* sb.append(getIdFact() + ": ")
* .append(getIdParent() + ": ")
* .append(" " + cdFact + cdFactSuffix + ": " + (isSupplementCode ? "NO Principal " : " Principal "))
* .append((mustBeFollowed ? " Must Be Followed " : "NOT Must Be Followed "));
* return sb.toString();
* }
*/
public Map<Locale, String> getDescription() {
return description;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
String fullCode = formatCode();
result = prime * result + ((fullCode == null) ? 0 : fullCode.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;
}
FactCodeDto other = (FactCodeDto) obj;
return formatCode().equals(other.formatCode());
}
#Override
public boolean isChildOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isChild = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isChild = this.getIdParent().equals(((FactCodeDto) value).getIdFact());
}
}
return isChild;
}
#Override
public boolean isBrotherOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isBrother = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isBrother = this.getIdParent().equals(((FactCodeDto) value).getIdParent());
}
}
return isBrother;
}
#Override
public boolean isParentOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isParent = false;
if (value instanceof FactCodeDto) {
isParent = this.getIdFact().equals(((FactCodeDto) value).getIdParent());
}
return isParent;
}
#Override
public int compareTo(TreeNodeValue to) {
if (to instanceof FactCodeDto) {
return formatCode().compareTo(((FactCodeDto) to).formatCode());
} else return 1;
}
public String getCode() {
return formatCode();
}
}
I found that AliasToBean has changed in Hibernate 5. For me adding getter for my field fixed the problem.
This exception occurs when the setters and getters are not mapped correctly to the column names.
Make sure you have the correct getters and setters for the query(Correct names and correct datatypes).
Read more about it here:
http://javahonk.com/java-lang-classcastexception-com-wfs-otc-datamodels-imagineexpirymodel-cannot-cast-java-util-map/
I do some investigation on this question. The problem is that Hibernate converts aliases for column names to upper case — cdFact becomesCDFACT.
Read for a more deeply explanation and workaround here:
mapping Hibernate query results to custom class?
In the end it wasn't so hard to find a solution,
I just created my own (custom) ResultTransformer and specified that in the setResultTransformer method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(new FactCodeDtoResultTransformer());
//return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
the code of the custom result transformer:
package org.bamboomy.cjr.dao.factcode;
import org.bamboomy.cjr.dto.FactCodeDto;
import java.util.Date;
import java.util.List;
/**
* Created by a162299 on 3-11-2015.
*/
public class FactCodeDtoResultTransformer implements org.hibernate.transform.ResultTransformer {
#Override
public Object transformTuple(Object[] objects, String[] strings) {
FactCodeDto result = new FactCodeDto();
for (int i = 0; i < objects.length; i++) {
setField(result, strings[i], objects[i]);
}
return result;
}
private void setField(FactCodeDto result, String string, Object object) {
if (string.equalsIgnoreCase("cdFact")) {
result.setCdFact((String) object);
} else if (string.equalsIgnoreCase("cdFactSuffix")) {
result.setCdFactSuffix((String) object);
} else if (string.equalsIgnoreCase("isSupplementCode")) {
result.setIsSupplementCode((Boolean) object);
} else if (string.equalsIgnoreCase("isTitleCode")) {
result.setIsTitleCode((Boolean) object);
} else if (string.equalsIgnoreCase("mustBeFollowed")) {
result.setMustBeFollowed((Boolean) object);
} else if (string.equalsIgnoreCase("activeFrom")) {
result.setActiveFrom((Date) object);
} else if (string.equalsIgnoreCase("activeTo")) {
result.setActiveTo((Date) object);
} else if (string.equalsIgnoreCase("descFr")) {
result.setDescFr((String) object);
} else if (string.equalsIgnoreCase("descNl")) {
result.setDescNl((String) object);
} else if (string.equalsIgnoreCase("descDe")) {
result.setDescDe((String) object);
} else if (string.equalsIgnoreCase("type")) {
result.setType((String) object);
} else if (string.equalsIgnoreCase("idFact")) {
result.setIdFact((Long) object);
} else if (string.equalsIgnoreCase("idParent")) {
result.setIdParent((Long) object);
} else if (string.equalsIgnoreCase("isCode")) {
result.setIsCode((Boolean) object);
} else {
throw new RuntimeException("unknown field");
}
}
#Override
public List transformList(List list) {
return list;
}
}
in hibernate 3 you could set Aliasses to queries but you can't do that anymore in hibernate 5 (correct me if I'm wrong) hence the aliasToBean is something you only can use when actually using aliasses; which I didn't, hence the exception.
Im my case :
=> write sql query and try to map result to Class List
=> Use "Transformers.aliasToBean"
=> get Error "cannot be cast to java.util.Map"
Solution :
=> just put \" before and after query aliases
ex:
"select first_name as \"firstName\" from test"
The problem is that Hibernate converts aliases for column names to upper case or lower case
I solved it by defining my own custom transformer as given below -
import org.hibernate.transform.BasicTransformerAdapter;
public class FluentHibernateResultTransformer extends BasicTransformerAdapter {
private static final long serialVersionUID = 6825154815776629666L;
private final Class<?> resultClass;
private NestedSetter[] setters;
public FluentHibernateResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
createCachedSetters(resultClass, aliases);
Object result = ClassUtils.newInstance(resultClass);
for (int i = 0; i < aliases.length; i++) {
setters[i].set(result, tuple[i]);
}
return result;
}
private void createCachedSetters(Class<?> resultClass, String[] aliases) {
if (setters == null) {
setters = createSetters(resultClass, aliases);
}
}
private static NestedSetter[] createSetters(Class<?> resultClass, String[] aliases) {
NestedSetter[] result = new NestedSetter[aliases.length];
for (int i = 0; i < aliases.length; i++) {
result[i] = NestedSetter.create(resultClass, aliases[i]);
}
return result;
}
}
And used this way inside the repository method -
#Override
public List<WalletVO> getWalletRelatedData(WalletRequest walletRequest,
Set<String> requiredVariablesSet) throws GenericBusinessException {
String query = getWalletQuery(requiredVariablesSet);
try {
if (query != null && !query.isEmpty()) {
SQLQuery sqlQuery = mEntityManager.unwrap(Session.class).createSQLQuery(query);
return sqlQuery.setResultTransformer(new FluentHibernateResultTransformer(WalletVO.class))
.list();
}
} catch (Exception ex) {
exceptionThrower.throwDatabaseException(null, false);
}
return Collections.emptyList();
}
It worked perfectly !!!
Try putting Column names and field names both in capital letters.
This exception occurs when the class that you specified in the AliasToBeanResultTransformer does not have getter for the corresponding columns. Although the exception details from the hibernate are misleading.

Is it possible to box user-defined Datatypes in more than 1 Level

I am working on a project with a datatructure that is deeper than 1 level and I try to box different user-defined datatypes to hold information of all levels.
First Level: A class that has only an ArrayList of level 2 datatype and some functions to access the data of all Levels.
Second Level: A class that has some standard variables and an ArrayList of Level 3 datatype and also some functions .
Third Level: A class / record of some standard variables
The instance of the first-level datatype should be used by more than one activity of my application (almost all).
Here is my problem: I can initialize the first-level and add some objects to the list, but I could not do so with the second level. If I add some object in the list of the second level all seems to be good, but when I access this data a NullPointerException occurs.
Here is my question: Is it possible to box datatypes in more than 1 level? And where I should search for the error.
For better understanding the sources:
modulData.java (First Level)
package de.myApp.dataholder;
import java.util.ArrayList;
import android.util.Log;
public class modulData {
// Constructor
private static final modulData mainModulDataHolder = new modulData();
public static modulData getInstance() {return mainModulDataHolder;}
// Data
private ArrayList<isModulData> mIsModulData;
// Functions
public void init() {
this.mIsModulData = new ArrayList<isModulData>();
}
public isModulData getModul(int mID) {
isModulData result = null;
int i;
for (i = 0; i < mIsModulData.size(); i++) {
if (mIsModulData.get(i).ID() == mID) {
result = mIsModulData.get(i);
break;
}
}
return result;
}
public isCatData getCategory(int mID, int cID) {
isCatData result = new isCatData();
isCatData buffer = new isCatData();
int i,j ;
for (i = 0; i < mIsModulData.size(); i++) {
if (mIsModulData.get(i).ID() == mID) {
for(j = 0; j < mIsModulData.get(i).CountCategorys(); j++) {
buffer = mIsModulData.get(i).getCategory(j);
if (buffer.ID() == cID) {
result = buffer;
break;
}
}
}
}
return result;
}
public int ModulCount() {
int result = 0;
if (mIsModulData != null) { result = mIsModulData.size(); }
return result;
}
public int CategoryCount(int mID) {
int result = -1;
int i;
for (i = 0; i < mIsModulData.size(); i++) {
if (mIsModulData.get(i).ID() == mID) {
result = mIsModulData.get(i).CountCategorys();
break;
}
}
return result;
}
public int addModulWithJSON(String jsonString) {
int result = -1;
isModulData newModul = new isModulData();
newModul.initCategorys();
if (mIsModulData == null) { this.init(); }
result = newModul.decodeFromJSON(jsonString);
mIsModulData.add(newModul);
return result;
}
public int addCategoryWithJSON(int mID, String jsonString) {
int result = -1;
int i;
for (i = 0; i < mIsModulData.size(); i++){
if (mIsModulData.get(i).ID() == mID) {
result = mIsModulData.get(i).addCategoryWithJSON(jsonString);
Log.i("AddCategoryWithJSON","Add Category "+ String.valueOf(result));
// Here is the Problem: Added but no access to Data. Null Object reference !!!!
Log.i("AddCategoryWithJSON", mIsModulData.get(mID).getCategory(result).Title());
break;
}
}
return result;
}
}
isModulData.java (second level)
package de.myApp.dataholder;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import android.util.Log;
public class isModulData {
// Constructor
private static final isModulData modulDataHolder = new isModulData();
public static isModulData getInstance() {return modulDataHolder;}
// Data
private ArrayList<isCatData> mIsCatData;
private int mID;
private String mTitle;
private boolean misActive;
private int mBackgroundID;
// Functions
public int ID() { return this.mID; }
public String Title() { return this.mTitle; }
public boolean ActiveState() { return this.misActive; }
public int BackgroundID() { return this.mBackgroundID; }
public int decodeFromJSON(String jsonString) {
int result;
try {
JSONArray jArray = new JSONArray(jsonString);
this.mID = Integer.parseInt(jArray.getString(0).toString());
this.mTitle = jArray.getString(1).toString();
this.misActive = Boolean.parseBoolean(jArray.getString(2).toString());
this.mBackgroundID = Integer.parseInt(jArray.getString(3).toString());
result = this.mID;
} catch (JSONException e) {
e.printStackTrace();
Log.w("JSONDecode CMD801","Decode raised in JSONException ["+e.toString()+"]");
result = -1;
}
return result;
}
public int addCategoryWithJSON(String jsonString) {
int result = -1;
if (this.mIsCatData == null) { this.initCategorys(); }
isCatData cNewCategory = new isCatData();
result = cNewCategory.decodeFromJSON(jsonString);
this.mIsCatData.add(cNewCategory);
return result;
}
public isCatData getCategory(int cID) {
isCatData result = new isCatData();
if (cID < mIsCatData.size() -1) {
result = mIsCatData.get(cID);
} else {
result = null;
}
return result;
}
public void initCategorys() {
this.mIsCatData = new ArrayList<isCatData>();
}
public int CountCategorys() {
return this.mIsCatData.size();
}
}
isCatData.java (third level)
package de.myApp.dataholder;
import org.json.JSONArray;
import org.json.JSONException;
import android.util.Log;
public class isCatData {
// Constructor
private static final isCatData catDataHolder = new isCatData();
public static isCatData getInstance() {return catDataHolder;}
// Data
private int cID;
private String cTitle;
private boolean cisActive;
private boolean cisWriteEnable;
private int cBackgroundID;
// Functions
public int ID() { return this.cID; }
public String Title() { return this.cTitle; }
public boolean ActiveState() { return this.cisActive; }
public boolean WriteEnableState() { return this.cisWriteEnable; }
public int BackgroundID() { return this.cBackgroundID; }
public int decodeFromJSON(String jsonString) {
int result;
try {
JSONArray jArray = new JSONArray(jsonString);
this.cID = Integer.parseInt(jArray.getString(0).toString());
this.cTitle = jArray.getString(1).toString();
this.cisActive = Boolean.parseBoolean(jArray.getString(2).toString());
this.cisWriteEnable = Boolean.parseBoolean(jArray.getString(3).toString());
this.cBackgroundID = Integer.parseInt(jArray.getString(4).toString());
result = this.cID;
} catch (JSONException e) {
e.printStackTrace();
Log.w("JSONDecode CMD801","Decode raised in JSONException ["+e.toString()+"]");
result = -1;
}
return result;
}
}

Building a tree from string input

I am stuck at the logic as for how to generate a tree when a string input is provided . Such as when i have a input of following form -
(1 (2 (3) (4)) (5 (6) ())
Representing tree will be like so -
1
/ \
2 5
/ \ /\
3 4 6 ()
I can build tree from usual like tree.add(data) and then looking for the new node to be self added by judging if its greater or smaller than parent node . But i am not able to understand how to implement how to store above the above mention string in binary data structure form.
Here's what i have tried so far -
public class BinaryTree {
static Node root;
public static void levelorder(Node<?> n) {
Queue<Node<?>> nodequeue = new LinkedList<Node<?>>();
if (n != null)
nodequeue.add(n);
while (!nodequeue.isEmpty()) {
Node<?> next = nodequeue.remove();
System.out.print(next.data + " ");
if (next.getLeft() != null) {
nodequeue.add(next.getLeft());
}
if (next.getRight() != null) {
nodequeue.add(next.getRight());
}
}
}
private static String[] breakString(String elements) {
int indexOfOpenBracket = elements.indexOf("(");
int indexOfLastBracket = elements.lastIndexOf(")");
String removedPString = elements.substring(indexOfOpenBracket + 1,
indexOfLastBracket);
String[] breakRemovedPString = removedPString.split(" ");
if (breakRemovedPString[1].contains("(")) {
add(breakRemovedPString[0], breakRemovedPString[1], breakRemovedPString[2]);
}
return breakRemovedPString;
}
static void add(String parent, String leftString, String rightString) {
Node<String> nodeToAdd = new Node<String>(parent);
if (root == null) {
root = nodeToAdd;
root.left = new Node<String>(leftString);
root.right = new Node<String>(rightString);
} else {
}
}
public static void main(final String[] args) {
String treeString = "(1 (2) (3))";
breakString(treeString);
levelorder(root);
System.out.println();
}
}
Please suggest some implementation for this problem.
This is a classical parsing problem. The simplest approach is probably recursive descent. Here is a grammar for the tree language:
T -> ( number T T )
| ( number )
| ()
To turn this into a parser, we can go through a formal transformation to LL(1) form and then code. I'll let you read up on that and show what results.
package treereader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
enum Token { LPAREN, RPAREN, NUMBER, EOF, ERROR };
class Scanner {
final Reader in;
final char [] buf = new char[1];
final StringBuilder token = new StringBuilder();
private static final char EOF_MARK = Character.MIN_VALUE;
Scanner(Reader in) {
this.in = in;
read();
}
final void read() {
try {
if (in.read(buf) < 1) {
buf[0] = EOF_MARK;
}
} catch (IOException ex) {
System.err.println("i/o error");
buf[0] = EOF_MARK;
}
}
Token getNext() {
while (Character.isWhitespace(buf[0])) {
read();
}
if (Character.isDigit(buf[0])) {
token.setLength(0);
do {
token.append(buf[0]);
read();
} while (Character.isDigit(buf[0]));
return Token.NUMBER;
}
if (buf[0] == '(') {
read();
return Token.LPAREN;
}
if (buf[0] == ')') {
read();
return Token.RPAREN;
}
if (buf[0] == EOF_MARK) {
return Token.EOF;
}
return Token.ERROR;
}
String getString() {
return token.toString();
}
}
class Node {
public void print(PrintStream out) {
out.print("()");
}
}
class UnaryNode extends Node {
int val;
public UnaryNode(int val) {
this.val = val;
}
#Override
public void print(PrintStream out) {
out.print("(" + val + ")");
}
}
class BinaryNode extends Node {
int val;
Node left, right;
public BinaryNode(int val, Node left, Node right) {
this.val = val;
this.left = left;
this.right = right;
}
#Override
public void print(PrintStream out) {
out.print("(" + val + " ");
left.print(out);
out.print(' ');
right.print(out);
out.print(')');
}
}
class Parser {
final Scanner scanner;
Token lookAhead;
Parser(Reader in) {
scanner = new Scanner(in);
lookAhead = scanner.getNext();
}
void advance() {
lookAhead = scanner.getNext();
}
void match(Token token) throws IOException {
if (lookAhead == token) {
advance();
} else {
throw new IOException("Expected " + token + ", found " + lookAhead);
}
}
Node parse() throws IOException {
Node tree;
match(Token.LPAREN);
if (lookAhead == Token.NUMBER) {
int val = Integer.valueOf(scanner.getString());
advance();
if (lookAhead == Token.LPAREN) {
Node left = parse();
Node right = parse();
tree = new BinaryNode(val, left, right);
} else {
tree = new UnaryNode(val);
}
} else {
tree = new Node();
}
match(Token.RPAREN);
return tree;
}
}
public class TreeReader {
public static void main(String[] args) {
try {
Parser parser = new Parser(new BufferedReader(new FileReader(new File(args[0]))));
Node tree = parser.parse();
tree.print(System.out);
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
}

Categories