Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an Hierarchy-Object with an undefined number of childs

Tags:

java

hierarchy

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.

like image 943
RoiEX Avatar asked Apr 23 '16 11:04

RoiEX


People also ask

How to create new GameObject as child in hierarchy?

How to create new GameObject as child in hierarchy 1 Spawn as duplicate (i.e same parent) as selected. 2 Spawn as parent of selected. 3 Will always spawn at the exact same location of the selected (like if you were going to duplicate). More ...

How to create a child in a game object list?

Just hit SHIFT+ALT+N to create a child in the selected game object. Hi guys, I know this is an old thread, but you can easely just drug and drop one object to other in object list.

How do I create multiple hierarchies in planning analytics?

With a multiple hierarchy model, queries can have any number of hierarchies equal to or greater than the number of dimensions that make up the cube in the query. The new Turbo Integrator functions account for hierarchy specification in Planning Analytics. Use the HierarchyCreate function in a TI process for creating a new hierarchy in a dimension.

What is the difference between single hierarchy model and multiple hierarchy model?

A single hierarchy model requires all queries to have exactly the same number of hierarchies as the number of dimensions that make up the cube in the query. With a multiple hierarchy model, queries can have any number of hierarchies equal to or greater than the number of dimensions that make up the cube in the query.


2 Answers

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...
    }
}
like image 110
Michael Markidis Avatar answered Oct 05 '22 23:10

Michael Markidis


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?

like image 37
Sagar Avatar answered Oct 05 '22 23:10

Sagar