Java tutorial
/* * Copyright (C) 2017 Till Uhlig <till.uhlig@student.uni-halle.de> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package treeNormalizer.structure.internal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.ArrayUtils; /** * Diese Klasse stellt die real existierenden Knoten einer treeBucket dar. Dabei * knnen diese Kinder besitzen und mehrere Eltern haben, sodass diese Knoten * selbst keine Bume darstellen sondern erst durch Interpretation der * nodeReferences zu Bumen werden. * * Jeder Knoten kann mehrere Kinder und mehrere Eltern besitzen. * * @author Till Uhlig {@literal <till.uhlig@student.uni-halle.de>} */ public class treeBucketNode { /** * die Attributliste (falls zustzliche Eigenschaften an den Knoten gebunden * werden sollen) Damit kann auch der Kontext gespeichert werden. */ private Map<String, String> attributes = new HashMap<>(); /** * die Kindknoten */ private ArrayList<treeBucketNode> childs = new ArrayList<>(); /** * der Hashwert (wird durch rehash berechnet) */ private int rawHash = 0; // Default to 0 /** * Unter dieser Id (quasi der Hash) wird dieser Knoten derzeit verwaltet. * Die wird bentigt, falls der Knoten verndert wird und wir ihn neu * speichern mssen. */ private int storeId = 0; /** * eine ID des realen Knotens, diese ID wird durch eine bergeordnete * Verwaltung (in einer HashMap) behandelt und soll zu der ID einen * entsprechenden Knoten liefern (diese id ist also global eindeutig) * * 0 = keine korrekte ID */ private long id = 0; /** * Diese Liste enthlt die Knotenreferenzen, welche auf diesen Knoten * zeigen. denn ein Knoten kann von mehreren Pfaden genutzt werden */ private final ArrayList<nodeReference> nodeReferences = new ArrayList<>(); /** * die Elternknoten */ private ArrayList<treeBucketNode> parents = new ArrayList<>(); /** * die uniqueID wird genutzt, um unterschiedliche hash-Werte fr eigentlich * gleich Knoten zu erzeugen, indem beide Knoten verschiedene uniqueIDs * bekommen */ private int uniqueId = 0; /* * dieses Flag gibt an, ob der Knoten verndert wurde und daher der Hash neu * berechnet werden muss */ private boolean changedNode = true; /** * setzt den Knoten als verndert (also changedNode = true) */ public void nodeChanged() { setChangedNode(true); } /** * erzeugt einen neuen Knoten */ public treeBucketNode() { // Leer } /** * erzeugt einen neuen Knoten * * @param id die ID des Knotens * @param label der Name */ public treeBucketNode(long id, String label) { addAttribute("label", label); this.id = id; } /** * erzeugt einen neuen Knoten * * @param label der Name */ public treeBucketNode(String label) { addAttribute("label", label); } /** * erzeugt einen neuen Knoten * * @param id die ID des Knotens * @param label das Label * @param type der Typ (Klasse) */ public treeBucketNode(long id, String label, String type) { addAttribute("label", label); addAttribute("type", type); this.id = id; } /** * erzeugt einen neuen Knoten * * @param label das Label * @param type der Typ (Klasse) */ public treeBucketNode(String label, String type) { addAttribute("label", label); addAttribute("type", type); } /** * erzeugt einen neuen Knoten * * @param id die ID des Knotens * @param label das Label * @param type der Typ (Klasse) * @param newattributes die Attribute */ public treeBucketNode(long id, String label, String type, Map<String, String> newattributes) { newattributes.entrySet().forEach((attribute) -> { addAttribute(attribute.getKey(), attribute.getValue()); }); this.id = id; addAttribute("label", label); addAttribute("type", type); } /** * erzeugt einen neuen Knoten * * @param id die ID des Knotens * @param newattributes die Attribute */ public treeBucketNode(long id, Map<String, String> newattributes) { newattributes.entrySet().forEach((attribute) -> { addAttribute(attribute.getKey(), attribute.getValue()); }); this.id = id; } /** * erzeugt einen neuen Knoten * * @param label das Label * @param type der Typ (Klasse) * @param newattributes die Attribute */ public treeBucketNode(String label, String type, Map<String, String> newattributes) { newattributes.entrySet().forEach((attribute) -> { addAttribute(attribute.getKey(), attribute.getValue()); }); addAttribute("label", label); addAttribute("type", type); } /** * fgt eine Attribut ein * * @param name der Name * @param value der Wert */ public final void addAttribute(String name, String value) { setAttribute(name, value); } /** * fgt ein Knoten als Kind hinzu (also eine Kante) * * @param child das neue Kind */ public void addChild(treeBucketNode child) { this.getChilds().add(child); nodeChanged(); } /** * fgt eine Menge von Kindern hinzu * * @param childs die neuen Kinder */ public void addChilds(List<treeBucketNode> childs) { childs.forEach((child) -> { this.getChilds().add(child); }); nodeChanged(); } /** * lscht alle Kindeintrge dieses Knotens */ public void resetChilds() { childs.clear(); } /** * fgt einen Knoten als Kind ein und eine Kante, von einem Quellknoten zu * diesem Knoten, ein * * @param sourceNode der Quellknoten */ public void addEdgeFrom(treeBucketNode sourceNode) { sourceNode.addEdgeTo(this); } /** * fgt einen Knoten als Kind ein und eine Kante zu dem Zielknoten ein * * @param targetNode der Zielknoten */ public void addEdgeTo(treeBucketNode targetNode) { if (this == targetNode) { return; // kein Kante zu sich selbst } targetNode.addParent(this); this.addChild(targetNode); nodeChanged(); } /** * fgt eine Knotenreferenz hinzu (darf noch nicht existieren) * * @param nodeReference die neue Referenz */ public void addNodeReference(nodeReference nodeReference) { if (!nodeReferences.contains(nodeReference)) { getNodeReferences().add(nodeReference); } } /** * fgt eine Menge von Referenzen ein * * @param nodeReferences die Referenzen */ public void addNodeReferences(ArrayList<nodeReference> nodeReferences) { nodeReferences.forEach((ref) -> { addNodeReference(ref); }); } /** * fgt einen Elternknoten hinzu (ein Elternknoten kann auch mehrfach * existieren) * * @param parent der neue Elternteil */ public void addParent(treeBucketNode parent) { getParents().add(parent); cleanParents(); } /** * fgt neue Eltern ein * * @param parents die neuen Eltern */ public void addParents(ArrayList<treeBucketNode> parents) { parents.forEach((parent) -> { addParent(parent); }); } /** * nimmt ein Kind auf (ein Kante von diesem Knoten zum Kind) * * @param child das neue Kind */ public void adoptChild(treeBucketNode child) { addChild(child); } /** * entfernt doppelte Elternknoten */ public void cleanParents() { ArrayList<treeBucketNode> newParents = new ArrayList<>(); getParents().stream().filter((parent) -> (!newParents.contains(parent))).forEachOrdered((parent) -> { newParents.add(parent); }); setParents(newParents); } /** * Erzeugt eine Kopie des Knotens (nur die Grunddaten) also: label, type, * attributes * * @param newId die KnotenId des dabei neu entstehenden Knoten * @return der neue Knoten */ public treeBucketNode cloneNodeBase(long newId) { treeBucketNode tmp = new treeBucketNode(newId, getAttributes()); return tmp; } /** * Erzeugt eine Kopie des Knotens (nur die Grunddaten) also: label, type, * attributes * * @return der neue Knoten */ public treeBucketNode cloneNodeBase() { // 0 wird dabei als ungltige ID angesehen return cloneNodeBase(0); } /** * prft, ob der Baum in Referenzenliste auftaucht * * @param tree der Baum * @return ob der Baum in der Liste auftaucht */ public boolean containsReferencedTree(internalTree tree) { for (nodeReference tmp : getNodeReferences()) { if (tmp == null) { continue; } if (!tmp.getTree().equals(tree)) { // es ist ein anderer Baum } else { // der Baum wurde gefunden return true; } } return false; } /** * verringert die uniqueId um 1 * * @return die neue uniqueId (maximal auf 0) */ public int decreaseUniqueId() { if (getUniqueId() == 0) { nodeChanged(); return getUniqueId(); } setUniqueId(getUniqueId() - 1); nodeChanged(); return getUniqueId(); } /** * entfernt alle Verbindungen dieses Knotens zu anderen Knoten */ public void disconnect() { removeChildEdges(); removeParentEdges(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final treeBucketNode other = (treeBucketNode) obj; return this.hashCode() == other.hashCode(); } /** * liefert den Wert eines Attributs * * @param name der Name * @return der Wert (wenn es nicht existiert kommt null) */ public String getAttribute(String name) { if (attributeExists(name)) { return attributes.get(name); } return null; } /** * prft, ob ein Attribut existiert * * @param name der Name * @return true = existiert, false = existiert nicht */ public boolean attributeExists(String name) { return getAttributes().containsKey(name); } /** * liefert alle Attribute * * @return die Attribute */ public final Map<String, String> getAttributes() { return attributes; } /** * setzt die Attributeliste * * @param attributes die Attribute */ public void setAttributes(Map<String, String> attributes) { this.attributes = attributes; nodeChanged(); } /** * liefert alle Kinder * * @return die Kinder */ public ArrayList<treeBucketNode> getChilds() { return childs; } /** * liefert das Kind an der Position pos * * @param pos die Position * @return das Kind oder null */ public treeBucketNode getChild(int pos) { if (pos >= 0 && pos < childs.size()) { return childs.get(pos); } return null; } /** * sucht ein Kind anhand des Knotens * * @param node der zu suchende Knoten * @return die Position oder -1 im Fehlerfall */ public int findChild(treeBucketNode node) { if (node == null) { return -1; } for (int i = 0; i < childs.size(); i++) { if (childs.get(i) == null) { continue; } if (childs.get(i).getId() == node.getId()) { return i; } } return -1; } /** * ermittelt alle Kinder des Knotens, welche node sind * * @param node der zu suchende Knoten * @return eine Liste der Kinderpositionen */ public int[] findChilds(treeBucketNode node) { List<Integer> list = new ArrayList<>(); if (node == null) { return new int[] {}; } ArrayList<treeBucketNode> list2 = getChilds(); for (int i = 0; i < list2.size(); i++) { if (list2.get(i) == null) { continue; } if (node.getId() == list2.get(i).getId()) { list.add(i); } } return ArrayUtils.toPrimitive(list.toArray(new Integer[list.size()])); } /** * setzt die Kinderliste(sollte nicht zum Verschieben oder Kopieren zwischen * zwei Knoten verwendet werden, dazu besser addChilds()) * * @param childs die neuen Kinder */ public void setChilds(ArrayList<treeBucketNode> childs) { if (childs == null) { childs = new ArrayList<>(); } this.childs = childs; nodeChanged(); } /** * setzt ein Kind an der Position i * * @param i die existierende Kind-Position * @param newChild das neue Kind * @return */ public boolean setChild(int i, treeBucketNode newChild) { if (i < 0 || i >= this.childs.size()) { return false; } if (newChild == null) { newChild = new treeBucketNode(); } this.childs.set(i, newChild); nodeChanged(); return true; } /** * liefert den ersten Elternknoten * * @return der erste Elternknoten */ public treeBucketNode getFirstParent() { if (hasParents()) { return getParents().get(0); } return null; } /** * liefert den Vater an der Position pos * * @param pos die Position * @return der Vater oder null */ public treeBucketNode getParent(int pos) { if (pos >= 0 && pos < getParents().size()) { return getParents().get(pos); } return null; } /** * liefert den Namen des Knotens * * @return der Name */ public String getLabel() { return getAttribute("label"); } /** * setzt den Namen des Knotens * * @param label der neue Name */ public void setLabel(String label) { setAttribute("label", label); nodeChanged(); } /** * liefert die Knotenreferenzen * * @return die Referenzen */ public ArrayList<nodeReference> getNodeReferences() { return nodeReferences; } /** * liefert alle ausgehenden Kanten (also Kinder) * * @return die ausgehenden Kanten */ public ArrayList<treeBucketNode> getOutgoingEdges() { return getChilds(); } /** * liefert alle Eltern * * @return die Eltern */ public ArrayList<treeBucketNode> getParents() { return parents; } /** * setzt die Elternliste * * @param parents die neuen Eltern */ public void setParents(ArrayList<treeBucketNode> parents) { if (parents == null) { parents = new ArrayList<>(); } this.parents = parents; } /** * entfernt alle Eltern aus der Elternliste */ public void removeAllParents() { setParents(null); } /** * liefert alle verknpften Bume * * @return die zugehrigen Bume */ public ArrayList<internalTree> getReferencedTrees() { ArrayList<internalTree> tmp = new ArrayList<>(); for (nodeReference ref : getNodeReferences()) { if (!tmp.contains(ref.getTree())) { tmp.add(ref.getTree()); } } return tmp; } /** * liefert den Typ des Knotens * * @return der Typ */ public String getType() { return getAttribute("type"); } /** * setzt den Knotentyp (Klasse) * * @param type der Typ */ public void setType(String type) { addAttribute("type", type); nodeChanged(); } /** * liefert die uniqueId * * @return die uniqueId */ public int getUniqueId() { return uniqueId; } /** * prft, ob der Knoten Kinder hat * * @return true = hat Kinder, false = hat keine Kinder */ public boolean hasChilds() { return !isLeaf(); } /** * prft, ob der Knoten Eltern hat * * @return true = hat Eltern, false = hat keine Eltern */ public boolean hasParents() { return !parents.isEmpty(); } /** * Returns a hash code for this treeBucketNode. * * @return a hash code value for this object. */ @Override public int hashCode() { if (isChangedNode()) { rehash(); } return getRawHash(); } /** * gibt die Anzahl der eingehenden Kanten des Knotens * * @return die Anzahl der eingehenden Kanten */ public int inDegree() { // obwohl es hier irgendwie um Bume geht, kann der Eingangsgrad > 1 // sein, weil treeBucketNode sich selbst nicht als Baum betrachtet (ein Knoten // kann hier mehrere Eltern haben) return getParents().size(); } /** * erhht den Wert der uniqueID um 1 * * @return die neue uniqueId */ public int increaseUniqueId() { setUniqueId(getUniqueId() + 1); if (getUniqueId() < 0) { nodeChanged(); return 0; } nodeChanged(); return getUniqueId(); } /** * prft, ob der Knoten ein Kind ist (also Eltern hat) * * @return true = ist ein Kind, false = ist kein Kind */ public boolean isChild() { return hasParents(); } /** * prft, ob der Knoten Kinderlos ist * * @return true = hat keine Kinder, false = hat Kinder */ public boolean isChildless() { return !hasChilds(); } /** * prft, ob der Knoten ein Blatt ist (keine weiteren Kinder) * * @return true = ist ein Blatt, false = kein Blatt */ public boolean isLeaf() { return getChilds().isEmpty(); } /** * prft, ob der Knoten keine Eltern hat * * @return true = keine Eltern, false = hat Eltern */ public boolean isOrphan() { return !hasParents(); } /** * prft, ob der Knoten eine Wurzel ist (keine Eltern hat) * * @return true = ist eine Wurzel, false = ist nicht die Wurzel */ public boolean isRoot() { return !hasParents(); } /** * prft, ob der Knoten mit dem Baum verbunden ist * * @param tree der Baum * @return ob der Knoten zum Baum gehrt */ public boolean isTreeReferenced(internalTree tree) { return containsReferencedTree(tree); } /** * liefert die Anzahl der Knotenreferenzen * * @return die Anzahl der Knotenreferenzen */ public int numberOfNodeReferences() { return getNodeReferences().size(); } /** * liefert die Anzahl der Elternknoten * * @return die Anzahl der Eltern */ public int numberOfParents() { return getParents().size(); } /** * liefert die Anzahl der verknpften Bume * * @return die Anzahl der Bume */ public int numberOfReferencedTrees() { return getReferencedTrees().size(); } /** * liefert die Anzahl der ausgehenden Kanten * * @return die ausgehenden Kanten */ public int outDegree() { return getChilds().size(); } /** * erzeugt eine druckbare Darstellung des Knotens * * @return die Textdarstellung */ public String print() { String tmp = getLabel() + "[" + getType() + "] #" + hashCode() + "\n"; if (hasParents()) { tmp += "parents: "; ArrayList<String> par = new ArrayList<>(); getParents().forEach((parent) -> { par.add(parent.getLabel() + " #" + parent.hashCode()); }); tmp += String.join(", ", par); tmp += "\n"; } if (hasChilds()) { tmp += "childs: "; ArrayList<String> par = new ArrayList<>(); getChilds().forEach((child) -> { par.add(child.getLabel() + " #" + child.hashCode()); }); tmp += String.join(", ", par); tmp += "\n"; } if (numberOfNodeReferences() > 0) { tmp += "refs: "; ArrayList<String> par = new ArrayList<>(); getNodeReferences().forEach((nodeId) -> { par.add(nodeId.getId() + "=>" + nodeId.getTree().getName()); }); tmp += String.join(", ", par); tmp += "\n"; } return tmp; } /** * aktualisiert den Hashwert des Objekts */ public void rehash() { // danach bentigen wir kein rehash mehr setChangedNode(false); // label, type, children, attributes String tmpHash = getLabel() + "_" + getUniqueId() + "_" + getType(); for (treeBucketNode child : getChilds()) { if (child == null) { tmpHash += "_null"; } else { tmpHash += "_" + child.hashCode(); } } for (Map.Entry<String, String> attribute : getAttributes().entrySet()) { tmpHash += "." + attribute.getKey() + "=" + attribute.getValue(); } setRawHash(tmpHash.hashCode()); } /** * entfernt alle Attribute */ public void removeAllAttributes() { getAttributes().clear(); nodeChanged(); } /** * entfernt alle Knotenreferenzen */ public void removeAllNodeReferences() { getNodeReferences().clear(); } /** * entfernt einen Attributeintrag * * @param name der Name */ public void removeAttribute(String name) { getAttributes().remove(name); nodeChanged(); } /** * entfernt ein Kindverweis * * @param id die Position im Kind-Array */ public void removeChild(int id) { getChilds().remove(id); nodeChanged(); } /** * entfernt ein Kindverweis * * @param object das Kind */ public void removeChild(treeBucketNode object) { getChilds().remove(object); nodeChanged(); } /** * entfernt alle Verbindungen zu den Kindern dieses Knotens */ public void removeChildEdges() { ArrayList<treeBucketNode> list = getChilds(); for (int i = 0; i < list.size();) { treeBucketNode child = list.get(i); removeEdgeTo(child); } nodeChanged(); } /** * entfernt eine Kante von einem anderen Knoten zu diesem * * @param sourceNode der Quellknoten */ public void removeEdgeFrom(treeBucketNode sourceNode) { sourceNode.removeEdgeTo(this); } /** * entfernt eine Kante zu einem anderen Knoten * * @param targetNode der Zielknoten */ public void removeEdgeTo(treeBucketNode targetNode) { targetNode.removeParent(this); removeChild(targetNode); nodeChanged(); } /** * setzt eine Kante zu einem Kind auf null und entfernt diesen Knoten dort * als Vater * * @param targetNode */ public void unsetEdgeTo(treeBucketNode targetNode) { targetNode.removeParent(this); unsetChild(targetNode); } /** * setzt ein Kind auf null * * @param targetNode das betroffene Kind */ public void unsetChild(treeBucketNode targetNode) { if (childs.indexOf(targetNode) >= 0) { int a = childs.indexOf(targetNode); unsetChild(childs.indexOf(targetNode)); } nodeChanged(); } /** * setzt ein Kind auf null * * @param targetNode das betroffene Kind */ public void unsetChild(int targetNode) { childs.set(targetNode, null); nodeChanged(); } /** * enfernt eine Referenz * * @param id die Referenz-ID der Referenz */ public void removeNodeReference(int id) { if (id < 0 || id >= getNodeReferences().size()) { return; } getNodeReferences().remove(id); } /** * enfernt eine Referenz * * @param nodeReference die Referenz */ public void removeNodeReference(nodeReference nodeReference) { int myid = getNodeReferences().indexOf(nodeReference); if (myid >= 0) { getNodeReferences().remove(myid); } } /** * enfernt einen Vater * * @param parent der Vater */ public void removeParent(treeBucketNode parent) { getParents().remove(parent); } /** * enfernt einen Vater * * @param id die Position im Parent-Array */ public void removeParent(int id) { getParents().remove(id); } /** * entfernt alle Verbindungen zu den Eltern dieses Knotens */ public void removeParentEdges() { ArrayList<treeBucketNode> list = getParents(); for (int i = 0; i < list.size();) { treeBucketNode parent = list.get(i); removeEdgeFrom(parent); } } /** * setzt die eindeutige ID zurck auf 0 (der Standardwert) */ public void resetUniqueId() { setUniqueId(0); } /** * setzt das Attribut label auf value * * @param name der Name des Attributs * @param value der neue Wert */ public void setAttribute(String name, String value) { if (attributeExists(name)) { attributes.replace(name, value); } else { attributes.put(name, value); } nodeChanged(); } /** * hashCode() verwenden! (diese Methode fhrt kein initiales rehash aus) * * @return the hash (sollte nicht verwendet werden) */ public int getRawHash() { return rawHash; } /** * @param hash the hash to set (sollte nicht verwendet werden) */ public void setRawHash(int hash) { this.rawHash = hash; } /** * @param uniqueId the uniqueId to set */ public void setUniqueId(int uniqueId) { if (uniqueId < 0) { uniqueId = 0; } this.uniqueId = uniqueId; nodeChanged(); } /** * prft, ob der Knoten als verndert markiert wurde (er bentigt dann ein * rehash) * * @return the changedNode, true = wurde verndert, false = nicht verndert */ public boolean isChangedNode() { return changedNode; } /** * setzt, ob der Knoten verndert wurde oder nicht manuell * * @param changedNode the changedNode to set (true = vernderter Knoten, * false = unvernder) */ public void setChangedNode(boolean changedNode) { this.changedNode = changedNode; } /** * liefert die storeId (den Speicherort) * * @return the storeId die derzeitige Id */ public int getStoreId() { return storeId; } /** * setzt die storeId (der derzeitige Speicherort) * * @param storeId setzt den neuen Speicherort */ public void setStoreId(int storeId) { this.storeId = storeId; } /** * setzt die storeId auf den aktuellen Hash */ public void updateStoreId() { this.storeId = getRawHash(); } /** * liefert die ID dieses Knotens zurck * * @return the id */ public long getId() { return id; } /** * setzt die ID des Knotens * * @param id the id to set */ public void setId(long id) { this.id = id; } /** * prft, ob die ID gltig ist (also ungleich 0) * * @return true = hat eine gtige ID, false = nicht */ public boolean hasValidId() { return this.id != 0; } }