package classes; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.UUID; import classes.holonControlUnit.HolonControlUnit; import ui.controller.Control; import ui.model.DecoratedNetwork; import ui.model.IntermediateCableWithState; import ui.model.MinimumModel; import ui.model.Model; public class Holon { public String name = new String(); private Holon parent = null; public ArrayList childHolons = new ArrayList(); private List elements = new ArrayList(); //HolonObject is the lowest representation of a holon. private final HolonObject holonObject; public final boolean isPhysical; public HolonControlUnit holonControlUnit; private String uniqueID; public Model model; private MinimumModel minModel; /** stores whether the part between this holon and the child is occupied by the superholon */ private boolean canRunIndependentFromParent; public Holon(String name, Model model) { this.name = name; this.parent = parent; this.holonObject = null; isPhysical = false; this.uniqueID = UUID.randomUUID().toString(); this.model = model; this.holonControlUnit = new HolonControlUnit(this); this.minModel = new MinimumModel(new ArrayList(), new ArrayList()); this.canRunIndependentFromParent = true; } public Holon(HolonObject object, Model model) { holonObject = object; object.holon = this; this.parent = parent; name = object.getName(); elements.addAll(object.getElements()); for(HolonElement ele : elements) { ele.holon = this; } isPhysical = true; this.uniqueID = UUID.randomUUID().toString(); this.model = model; this.holonControlUnit = new HolonControlUnit(this); ArrayList list = new ArrayList(); list.add(object); this.minModel = new MinimumModel(list, new ArrayList()); this.canRunIndependentFromParent = true; } public void addElement(HolonElement element) { element.holon = this; elements.add(element); } public void removeElement(HolonElement element) { element.holon = null; elements.remove(element); } public int elementsCount() { return elements.size(); } public void addChild(Holon child) { if(!this.childHolons.contains(child)) { child.parent = this; this.childHolons.add(child); } this.holonControlUnit.addSubHolon(child); } public void addChildHolon(Holon child, int index) { if(!this.childHolons.contains(child)) { child.parent = this; childHolons.add(index, child); } this.holonControlUnit.addSubHolon(child); } public void removeChildHolon(Holon child) { child.parent = null; childHolons.remove(child); this.holonControlUnit.getHierarchyController().removeSubHolon(child.getUniqueID()); revalidateMinModel(); } public void removeFromParent() { if(parent != null) { parent.removeChildHolon(this); } parent = model.getStateHolon(); } public int getChildCount() { return childHolons.size(); } public Holon getParent() { return parent; } public void setParent(Holon parent) { this.parent = parent; this.holonControlUnit.getHierarchyController().setSuperHolon(parent.getUniqueID()); } public HolonObject getHolonObject() { return holonObject; } public List getChildView(){ return Collections.unmodifiableList(childHolons); } public List getElementView(){ return Collections.unmodifiableList(elements); } @Override public String toString() { return name; } public void reassignAllChildren(Holon other) { for(Holon child: this.childHolons) { other.merge(child); } childHolons.clear(); } public void removeAllRefrences() { parent = null; this.childHolons.clear(); this.elements.clear(); } public int getLayer() { return parent != null ? parent.getLayer() + 1 : 0; } public Holon cloneWithoutParent() { Holon cloned = new Holon(this.name, this.model); model.getHolonsByID().put(cloned.getUniqueID(), cloned); cloned.childHolons = this.childHolons; cloned.elements = this.elements; return cloned; } public boolean checkHolonArePhysicalConnected(Holon other, Control control) { HashMap table = control.getSimManager().getActualDecorState().getHolonObjectNetworkTable(); HolonObject a = tryGetAPhysicalHolon(); HolonObject b = other.tryGetAPhysicalHolon(); boolean aHolonIsPureAbstract = a == null || b == null; return aHolonIsPureAbstract || table.get(a) == table.get(b); } private HolonObject tryGetAPhysicalHolon() { if(holonObject != null) { return holonObject; } for(Holon holon : childHolons) { HolonObject object = holon.tryGetAPhysicalHolon(); if(object != null) { return object; } } return null; } public void checkRepairHolarchy(HashMap table, Holon stateHolon) { if(childHolons.isEmpty()) { return; } //To establish the invariant that all child holons are repaired for(Holon other : childHolons) { other.checkRepairHolarchy(table, stateHolon); } //Repair this Holon HolonObject first = tryGetAPhysicalHolon(); if(first == null) { return; } List removeList = new ArrayList(); for(Holon other : childHolons) { HolonObject otherHolonObject = other.tryGetAPhysicalHolon(); boolean isPureAbstract = otherHolonObject == null; if(isPureAbstract) { continue; } boolean isPhysicalConnected = model.checkHolonObjectsAreConnected(this, other); if(!isPhysicalConnected) { removeList.add(other); } } //Remove holons for(Holon holon : removeList) { holon.split(stateHolon); } } public void addNewVirtualNeighbor(String virtualNeighbor) { if(!this.uniqueID.equals(virtualNeighbor)) { this.holonControlUnit.addNewVirtualNeighbor(virtualNeighbor); } } public String getUniqueID() { return uniqueID; } public ArrayList getAllHolonObjects() { ArrayList list = new ArrayList(); for(Holon h : this.childHolons) { list.addAll(h.getAllHolonObjects()); } if(this.holonObject != null) { list.add(this.holonObject); } return list; } public MinimumModel getMinimumModel() { return this.minModel; } public void merge(Holon child) { if(!this.model.getHolonsByID().containsKey(child.getUniqueID())) { System.err.println("could not find: "+child.getHolonObject()); return; } if(this.childHolons.contains(child)) { return; } if(this.getLayer() == 0) { this.addChild(child); return; } //check if path between this holon and child holon is already occupied ArrayList holarchy = getAllHolonObjects(); holarchy.addAll(child.getAllHolonObjects()); HashMap> paths = this.model.getShortestPathToHolarchy(this.minModel, child.getMinimumModel(), holarchy); HashMap> free_paths = new HashMap>(); HashMap> occ_paths = new HashMap>(); HashMap, Holon> occ_by = new HashMap, Holon>(); for(Float f : paths.keySet()) { ArrayList path = paths.get(f); Holon holder = isPathOccupied(path); if(holder == null || holder.getLayer() == 0 || holder.equals(this.parent)) { free_paths.put(f, path); } else { occ_paths.put(f, path); occ_by.put(path, holder); } } if(free_paths.size() > 0) { //there is a free path, take it and add child directly this.addChild(child); this.addChildToMinModel(child, free_paths.get(getShortestPath(free_paths))); if(this.getLayer() > 1) { this.parent.recalculateMinModel(); } return; } if(occ_paths.size() < 1) { System.err.println("something went wrong when looking for shortest path while merging "+this.uniqueID+" and "+child.getUniqueID()); } ArrayList> shortestPaths = sortPaths(occ_paths); for(int i=0; i< shortestPaths.size(); i++) { //check if holon should merge with holder of this path ArrayList path = shortestPaths.get(i); Holon holder = occ_by.get(path); //if true try to merge with holon which is using the path if(holder != null && holder.getLayer() > 0) { this.split(holder); this.addChild(child); this.addChildToMinModel(child, path); if(this.parent != null && this.getLayer() > 1) { this.parent.recalculateMinModel(); } this.canRunIndependentFromParent = false; } break; } } /** * checks whether a path is occupied * if true returns the holon in which the path is located * @param path * @return */ private Holon isPathOccupied(ArrayList path) { Holon h = this.parent; Holon r = this; Holon holder = null; while(h != null) { holder = h.containsPath(path, r); if(holder != null) { //found a holon which contains the path return holder; } r = h; h = h.parent; } return null; } private ArrayList> sortPaths(HashMap> paths) { ArrayList> shortestPaths = new ArrayList>(); while(paths.size() > 0) { shortestPaths.add(paths.remove(getShortestPath(paths))); } return shortestPaths; } private Float getShortestPath(HashMap> paths){ float min = Float.MAX_VALUE; for(Float f : paths.keySet()) { if(f < min) { min = f; } } return min; } /** * removes this holon from current superholon and merge with new superholon */ public void split(Holon newParent) { if(newParent == null) { System.err.println("can not split without new super holon"); return; } //ensure that this holarchy can run independent(path is not occupied by superholon) from the superholon if(!this.canRunIndependentFromParent) { return; } if(this.parent != null) { this.parent.removeChildHolon(this); } newParent.merge(this); } /** * recalculates the min model after a holon was added to a subholon * does NOT recalculate the shortest paths between its subholons */ public void recalculateMinModel() { ArrayList objects = new ArrayList(); ArrayList edges = new ArrayList(); for(Holon child : this.childHolons) { MinimumModel cmm = child.getMinimumModel(); objects.addAll(cmm.getHolonObjectList()); objects.addAll(cmm.getNodeList()); objects.addAll(cmm.getSwitchList()); for(IntermediateCableWithState icws : cmm.getEdgeList()) { edges.add(icws.getModel()); } } //go through the edge list and add all edges connecting the subholons for(IntermediateCableWithState icws : this.minModel.getEdgeList()) { if(!edges.contains(icws.getModel())) { edges.add(icws.getModel()); } } this.minModel = new MinimumModel(objects, edges); //notify superholon to recalculate its minModel if(this.parent != null && this.getLayer() > 0) { this.parent.recalculateMinModel(); } } /** * adds a child and its min model (incl. all its subholons) to this min model * @param child */ private void addChildToMinModel(Holon child, ArrayList path) { if(this.minModel.getHolonObjectList().contains(child.getHolonObject()) || getLayer() == 0) return; ArrayList edgeList = new ArrayList(); //add all holon objects that are part of this holarchy ArrayList holarchy = getAllHolonObjects(); holarchy.addAll(child.getAllHolonObjects()); edgeList.addAll(path); for(IntermediateCableWithState icws : this.minModel.getEdgeList()) { edgeList.add(icws.getModel()); } for(IntermediateCableWithState icws : child.minModel.getEdgeList()) { edgeList.add(icws.getModel()); } //aggregate all objects ArrayList objects = new ArrayList(); for(Edge e : edgeList) { AbstractCanvasObject a = e.getA(); AbstractCanvasObject b = e.getB(); if(!objects.contains(a)) { objects.add(a); } if(!objects.contains(b)) { objects.add(b); } } this.minModel = new MinimumModel(objects, edgeList); } /** * revalidate this min model after a subholon was removed */ public void revalidateMinModel() { if(getLayer() == 0) return; this.canRunIndependentFromParent = true; ArrayList list = new ArrayList(); list.add(this.holonObject); this.minModel = new MinimumModel(list, new ArrayList()); for(int i=0; i< this.childHolons.size(); i++) { Holon child = this.childHolons.remove(0); this.merge(child); } if(this.parent != null) { this.parent.revalidateMinModel(); } } /** * returns the holon which uses the specified path if it is included in min model, otherwise null */ public Holon containsPath(ArrayList path, Holon requester) { if(this.minModel.containsPath(path) || this.getLayer() == 0) { for(Holon child : this.childHolons) { if(child.equals(requester)) continue; Holon h = child.containsPath(path, this); if(h != null) { return h; } } return this; } return null; } }