package group04; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import group04.myBatis.Nodeinfo; /** * basic node class of node, it can explane a single node and the level of node */ public class BasicNode implements Iterable { /** * <0 : invalidity node * =0 : undifineded node * >0 : created node */ private long id = 0l; private BasicNode nextNode = null; private BasicNode pervNode = null; private BasicNode childLevel = null; private BasicNode parentNode = null; private static ArrayList NeedUpload = new ArrayList<>(); /** * create new node * @throws IOException no mapper */ public BasicNode(){} /** * create node by confirm id * @param id node id * @throws IOException no mapper */ public BasicNode(long id) throws IOException { setId(Math.abs(id)); } @Override public NodeIterator iterator() { BasicNode node = startOfNodeLevel(); return new NodeIterator(node); } /** * get node id * @return id of this node */ public long getId() { return id; } /** * get next node of this node * @return next node */ public BasicNode getNextNode() { return nextNode; } /** * get previous node of this node * @return previous node */ public BasicNode getPervNode() { return pervNode; } /** * get parrent node of this node * @return parrent node */ public BasicNode getParentNode() { return parentNode; } /** * get child level * @return first node of child nodes */ public BasicNode getChildLevel() { if(childLevel != null) { while(childLevel.pervNode != null) { childLevel = childLevel.pervNode; } } return childLevel; } public void setNeedUpload() { synchronized(NeedUpload) { if(!NeedUpload.contains(this)) { NeedUpload.add(this); } } } /** * set Node id,also it can destroy this node by seting id to minus value * @param id new id * @throws IOException no mapper */ public void setId(long id) throws IOException { synchronized(this) { //defineded id if(id == 0 || !vaildity()) return; //not null if(id < 0) { //delete NodeDao.getCurrentMapper().deleteByPrimaryKey(this.id); //if database vaildity, it will going on. dropFromLevel(); //delete all child nodes BasicNode node = this.getChildLevel(); while(node != null) { BasicNode next = node.getNextNode(); node.deleteNode(); node = next; } } else if(NodeDao.state() != NodeDao.DaoState.Reading) { Nodeinfo data = getNodeInfo(); data.setId(id); try { //id is primary key of node info, we can't modify the primary key by primary key, so it need delete first NodeDao.getCurrentMapper().insert(data); //delete if(defineded()) NodeDao.getCurrentMapper().deleteByPrimaryKey(this.id); } catch (IOException IOEx) { //no mapper throw IOEx; } catch (Exception e) { //sql errery return; } } else { InitAfterLoad(id); } this.id = id; } } /** * for override, invoke when load feom database * @param id currectt id information */ private void InitAfterLoad(long id) { } /** * convert to NodeInfo data * @return Node info for sql upload */ public Nodeinfo getNodeInfo() { //gen data for update database Nodeinfo data = new Nodeinfo(); BasicNode node = null; //node id data.setId(getId()); //next element node = getNextNode(); if(node != null && node.vaildity())data.setNextId(node.getId()); else data.setNextId(null); //perv element node = getPervNode(); if(node != null && node.vaildity())data.setPrevId(node.getId()); else data.setPrevId(null); //parent element node = getParentNode(); if(node != null && node.vaildity())data.setParentId(node.getId()); else data.setParentId(null); //chile level node = getChildLevel(); if(node != null && node.vaildity()) { node = node.startOfNodeLevel(); data.setChildId(node.getId()); } else data.setChildId(null); //node class data.setObjType(this.getClass().getName()); return data; } /** * upload to database * @throws IOException no mapper */ public void uploadNode() throws IOException { synchronized(this) { Nodeinfo data = getNodeInfo(); NodeDao.getCurrentMapper().updateByPrimaryKey(data); } } /** * check node vaild * @return if is false it should not exists in database */ public boolean vaildity() { if(getId() >= 0) { for (BasicNode basicNode : NeedUpload) { if(this.equals(basicNode) && basicNode != this) return false; } return true; } return false; } /** * check id defined statue * @return if is true, node id should defineded */ public boolean defineded() { return vaildity() && getId() > 0; } /** * InnerHTML of tab * @return DOM Context.InnerHTML */ public String tabContextInnerHTML() { return "id : " + getId(); } /** * remove itselfs from its level */ public void dropFromLevel() { synchronized(this) { if(this.nextNode != null) { this.nextNode.pervNode = this.pervNode; this.nextNode.setNeedUpload(); } if(this.pervNode != null) { this.pervNode.nextNode = this.nextNode; this.pervNode.setNeedUpload(); } else if(this.parentNode != null) { this.parentNode.childLevel = this.nextNode; this.parentNode.setNeedUpload(); } this.parentNode = null; this.nextNode = null; this.pervNode = null; this.setNeedUpload(); } } /** * instert a {@link #node} after this node * @param node node to instert/ */ public boolean insertNext(BasicNode node) { synchronized(this) { if(node != null && node.vaildity() && this.vaildity()) { if(!node.subTreeContains(this)) { //remove from its level node.dropFromLevel(); //cache BasicNode nextNode = this.nextNode; //replace next this.nextNode = node; node.nextNode = nextNode; //replace perv node.pervNode = this; //set to same parent node node.parentNode = this.parentNode; //check if exiet if(nextNode != null) { nextNode.pervNode = node; nextNode.setNeedUpload(); } this.setNeedUpload(); return true; } } return false; } } /** * instert a {@link #node} before this node * @param node node to instert */ public boolean insertPerv(BasicNode node) { synchronized(this) { if(node != null && node.vaildity() && this.vaildity()) { if(!node.subTreeContains(this)) { //remove from its level node.dropFromLevel(); //cache BasicNode pervNode = this.pervNode; //replace next this.pervNode = node; node.pervNode = pervNode; //replace last node.nextNode = this; //set to same parent node node.parentNode = this.parentNode; //check if exiet if(pervNode != null) { pervNode.nextNode = node; pervNode.setNeedUpload(); } else //remove if parrentNode exiet if(this.parentNode != null) { this.parentNode.childLevel = node; this.parentNode.setNeedUpload(); } this.setNeedUpload(); return true; } } return false; } } /** * replace current pos of its level * @param node node for replace */ public boolean replaceBy(BasicNode node) { synchronized(this) { if(!vaildity()) return false; if(node != null && node.vaildity()) { if(!node.subTreeContains(this)) { node.dropFromLevel(); node.parentNode = this.parentNode; node.nextNode = this.nextNode; node.pervNode = this.pervNode; this.dropFromLevel(); if(node.nextNode != null) { node.nextNode.pervNode = node; } if(node.pervNode != null) { node.pervNode.nextNode = node; } else if(node.parentNode != null) { node.parentNode.childLevel = node; node.parentNode.setNeedUpload(); } return true; } } else { this.dropFromLevel(); return true; } return false; } } public boolean exchangeWith(BasicNode node) { synchronized(this) { if(!vaildity()) return false; if(node != null && node.vaildity()) { if(!node.subTreeContains(this) && !this.subTreeContains(node)) { //cache BasicNode next = node.nextNode; BasicNode perv = node.pervNode; BasicNode parent = node.parentNode; if(next == this) { next = this.pervNode; this.pervNode = this; } if(perv == this) { perv = this.nextNode; this.nextNode = this; } //set node to this node.nextNode = this.nextNode; node.pervNode = this.pervNode; node.parentNode = this.parentNode; node.setNeedUpload(); //check near by node update if(node.nextNode != null) { node.nextNode.pervNode = node; node.nextNode.setNeedUpload(); } if(node.pervNode != null) { node.pervNode.nextNode = node; node.pervNode.setNeedUpload(); } else if(node.parentNode != null) { node.parentNode.childLevel = node; node.parentNode.setNeedUpload(); } //set this to next this.nextNode = next; this.pervNode = perv; this.parentNode = parent; this.setNeedUpload(); //check near by node update if(this.nextNode != null) { this.nextNode.pervNode = this; this.nextNode.setNeedUpload(); } if(this.pervNode != null) { this.pervNode.nextNode = this; this.pervNode.setNeedUpload(); } else if(this.parentNode != null) { this.parentNode.childLevel = this; this.parentNode.setNeedUpload(); } return true; } } else { this.dropFromLevel(); return true; } return false; } } /** * Level's start */ public BasicNode startOfNodeLevel() { BasicNode node = this; while(node.pervNode != null) { node = node.pervNode; } return node; } /** * Level's end */ public BasicNode endOfNodeLevel() { BasicNode node = this; while(node.nextNode != null) { node = node.nextNode; } return node; } /** * get root of node tree * @return node of the minimum level */ public BasicNode rootNode() { BasicNode node = this; while(node.parentNode != null) { node = node.parentNode; } return node; } /** * replace child level to {@link #childLevel} * @param childLevel target * @return if success , return droped node else null */ public BasicNode setChildLevel(BasicNode childLevel) { //is use as level, so we need to if(childLevel != null && vaildity() && childLevel.vaildity()) { //save usage childLevel = childLevel.startOfNodeLevel(); if(childLevel.subTreeContains(this.startOfNodeLevel())) return null; BasicNode result = this.childLevel; if(childLevel != null) { for(BasicNode node : childLevel) { node.parentNode = this; } } if(this.childLevel != null) { for(BasicNode node : this.childLevel) { node.parentNode = null; } } this.childLevel = childLevel; return result; } return null; } /** * check if level contains {@ling #node} * @param node node for check * @return {@value true} mean contains {@value false} mean not contains */ public boolean levelContains(BasicNode node) { if(node != null) { if(node.parentNode != null && this.parentNode != null) { return node.parentNode == this.parentNode; } else { BasicNode cache1 = node; BasicNode cache2 = this; do { //check 2 node's perv node at same time if(cache1.equals(this) || cache2.equals(node)) return true; if(cache1.pervNode != null) cache1 = cache1.pervNode; if(cache2.pervNode != null) cache2 = cache2.pervNode; } while(cache2.pervNode != null || cache1.pervNode != null); return cache1 == cache2; } } return false; } /** * check if node tree has node * @param node node for check * @return {@value true} mean contains {@value false} mean not contains */ public boolean treeContains(BasicNode node) { if(node != null) { return node.rootNode().levelContains(this.rootNode()); } return false; } /** * check if this node is a sub node of the node * @param node node for check * @return if this node is a sub node of the node, it will return true */ public boolean subTreeContains(BasicNode node) { if(node != null) { do { if(node.equals(this)) { return true; } node = node.parentNode; } while(node != null); } return false; } public long getLayout() { long result = 0; BasicNode node =this.getParentNode(); while(node != null) { node = node.getParentNode(); ++result; } return result; } @Override public String toString() { String result = this.getId() + "{ "; for(BasicNode node : this) { result += node.getId(); if(node.getChildLevel() != null) result += "+"; if(node.nextNode != null) result += " , "; } return result + " }"; } public void deleteNode() throws IOException { this.setId(-1l); } /** * check level * @param levelA value a * @param levelB value b * @return if is same level,it will return true */ public static boolean levelEqul(BasicNode levelA,BasicNode levelB) { return levelA.startOfNodeLevel().id == levelB.startOfNodeLevel().id; } @Override public boolean equals(Object o) { // TODO Auto-generated method stub BasicNode other = (BasicNode)o; if(other != null) return other.id == this.id; else return super.equals(o); } /** * check all loaded node and upload data * @throws IOException no sql mapper */ public static void Commit() throws IOException { synchronized(NeedUpload) { for (BasicNode basicNode : NeedUpload) { basicNode.uploadNode(); } NodeDao.getCurrentMapper().commit(); NeedUpload.clear(); } } public static BasicNode TryGetNotCommitNodeById(long id) { synchronized(NeedUpload) { for (BasicNode basicNode : NeedUpload) { if(basicNode.getId() == id) { return basicNode; } } return null; } } /** * Iterator of {@link #BasicNode} */ class NodeIterator implements Iterator { BasicNode next; private NodeIterator(BasicNode node) { next = node; } @Override public boolean hasNext() { return next != null; } @Override public BasicNode next() { BasicNode next = this.next; this.next = next.nextNode; return next; } } }