package com.cari.voip.keyboard.stack.events;
import java.util.HashMap;
import java.util.Map;
public class XMLNode {
//tag name
private String name = null;
// tag attributes
private Map<String,String> attr = null;
// tag character contect,empty string if none
private String txt = null;
// tag offset from start of parent tag character content
private int off = -1;
//next tag with same name in same section and depth
private XMLNode next = null;
private XMLNode lastNext = null;
//next tag with different name in same section and depth
private XMLNode sibling = null;
//next tag
private XMLNode ordered = null;
private XMLNode lastOrdered = null;
// head of sub tag list, null if none
private XMLNode child = null;
// parent tag ,null if current tag is root tag
private XMLNode parent = null;
//flags
private int flags = 0;
protected static int HAVE_SORTED_BY_NAME = 1<<0;
private boolean hasChildNode;
public XMLNode(){
this(null,null);
}
public XMLNode(XMLNode parent){
this(parent,null);
}
public XMLNode(XMLNode parent,String name){
this.attr = new HashMap<String,String>();
this.name = name;
this.parent = parent;
if(parent != null){
if(parent.child == null){
parent.child = this;
this.off = 0;
}
else{
parent.child.addBrotherOrdered(this);
if(this.name != null){
parent.child.addBrotherSiblingOrNext(this);
this.flags |= XMLNode.HAVE_SORTED_BY_NAME;
}
}
}
}
public XMLNode getParent(){
return this.parent;
}
public String getName(){
return this.name;
}
public void setName(String name){
if(name == null ||
name.equals(this.name)){
return;
}
if(this.parent != null &&
this.parent.child != null){
if(this.parent.child == this){
XMLNode downNode = this.next;
XMLNode prevNode = this;
XMLNode nextNode = this.sibling;
while(nextNode != null && name.equals(nextNode.name) == false){
prevNode = nextNode;
nextNode = nextNode.sibling;
}
if(nextNode != null){
if(downNode != null){
prevNode.sibling = downNode;
downNode.sibling = nextNode.sibling;
}
else{
prevNode.sibling = nextNode.sibling;
nextNode.sibling = null;
}
this.next = nextNode;
}
else{
if(downNode != null){
downNode.sibling = this.sibling;
this.sibling = downNode;
this.next = null;
}
}
this.name = name;
}
else{
if((this.flags & XMLNode.HAVE_SORTED_BY_NAME) != 0){
this.parent.child.removeBrotherSiblingOrNext(this);
}
this.name = name;
this.parent.child.addBrotherSiblingOrNext(this);
this.flags |= XMLNode.HAVE_SORTED_BY_NAME;
}
}
else{
this.name = name;
}
}
public int getOff(){
return this.off;
}
public XMLNode getNext(){
return this.next;
}
public void setNext(XMLNode tag){
this.next = tag;
}
public XMLNode getSiblingNext(){
return this.sibling;
}
public XMLNode getChild(String name){
if(name == null){
return this.child;
}
XMLNode node = null;
if(this.child != null){
if(name.equals(this.child.name)){
node = this.child;
}
else{
XMLNode nextNode = this.child.sibling;
while(nextNode != null && name.equals(nextNode.name) == false){
nextNode = nextNode.sibling;
}
if(nextNode != null){
node = nextNode;
}
}
}
return node;
}
public void setSibling(XMLNode tag){
this.sibling = tag;
}
public XMLNode getOrderedNext(){
return this.ordered;
}
public void setOrderedNext(XMLNode tag){
this.ordered = tag;
}
protected void removeBrotherNext(XMLNode tag){
if(this.next == null){
return;
}
if(this.next == tag){
this.next = tag.next;
}
else{
XMLNode prevNode = this.next;
XMLNode nextNode = prevNode.next;
while(nextNode != null && nextNode != tag){
prevNode = nextNode;
nextNode = prevNode.next;
}
if(nextNode != null){
prevNode.next = nextNode.next;
}
}
if(this.lastNext == tag){
this.lastNext = null;
}
}
protected void addBrotherNext(XMLNode tag){
if(this.next == null){
this.next = tag;
}
else{
if(this.lastNext != null){
this.lastNext.next = tag;
}
else {
XMLNode prevNode = this.next;
XMLNode nextNode = prevNode.next;
while(nextNode != null){
prevNode = nextNode;
nextNode = prevNode.next;
}
prevNode.next = tag;
}
}
this.lastNext = tag;
}
protected void removeBrotherSibling(XMLNode tag){
if(this.sibling == null){
return;
}
if(this.sibling == tag){
if(tag.next != null){
this.sibling = tag.next;
this.sibling.sibling = tag.sibling;
}
else{
this.sibling = tag.sibling;
}
}
else{
this.sibling.removeBrotherSiblingOrNext(tag);
}
}
protected void addBrotherSibling(XMLNode tag){
if(this.sibling == null){
this.sibling = tag;
}
else{
this.sibling.addBrotherSiblingOrNext(tag);
}
}
protected void addBrotherOrdered(XMLNode tag){
if(this.ordered == null){
this.ordered = tag;
tag.off = this.off+1;
}
else{
if(this.lastOrdered != null){
this.lastOrdered.ordered = tag;
tag.off = this.lastOrdered.off+1;
}
else{
XMLNode prevNode = this.ordered;
XMLNode nextNode = prevNode.ordered;
while(nextNode != null){
prevNode = nextNode;
nextNode = prevNode.ordered;
}
prevNode.ordered = tag;
tag.off = prevNode.off + 1;
}
}
this.lastOrdered = tag;
}
public void removeBrotherSiblingOrNext(XMLNode brother){
if(brother != null && brother.name != null && brother != this){
if(this.name.equals(brother.getName())){
this.removeBrotherNext(brother);
}
else{
this.removeBrotherSibling(brother);
}
}
}
public void addBrotherSiblingOrNext(XMLNode brother){
if(brother != null && brother.name != null && brother != this){
if(this.name.equals(brother.getName())){
this.addBrotherNext(brother);
}
else{
this.addBrotherSibling(brother);
}
}
}
public void setAttr(String AttrName,String value){
this.attr.put(AttrName, value);
}
public String getAttr(String AttrName){
return this.attr.get(AttrName);
}
protected boolean setNameAndAttrFromString(String xmlString,int firstK,int endK){
if(endK < firstK){
return false;
}
boolean ok = true;
try{
String headString = xmlString.substring(firstK, endK);
if(headString == null ||
(headString = headString.trim()) == null ||
headString.length() == 0){
ok = false;
}
else{
int spaceI = headString.indexOf(' ');
if(spaceI < 0 ){
//no attr
this.setName(headString);
}
else{
int i = spaceI;
this.setName(headString.substring(0, i));
int headLen = headString.length();
int state = 0;
int nameS = -1;
int nameE = -1;
int valueS = -1;
int valueE = -1;
String attrName = null;
String attrValue = null;
while( ++i < headLen){
char c = headString.charAt(i);
switch(state){
case 0:// null
if(c != ' '){
nameS = i;
state = 1;
}
break;
case 1:// name
if(c == '='){
nameE = i;
attrName = headString.substring(nameS, nameE);
if(attrName == null || attrName.length() == 0){
ok = false;
}
state = 2;
}
break;
case 2:// name =
if(c == '"'){
valueS = i+1;
state = 3;
}
else if(c != ' '){
valueS = i;
state = 4;
}
break;
case 3:// name = "
if(c == '"'){
valueE = i;
attrValue = headString.substring(valueS, valueE);
if(attrValue == null){
ok = false;
}
else{
this.setAttr(attrName, attrValue);
state= 0;
}
}
break;
case 4: //name = xxx
if(c == ' '){
valueE = i;
attrValue = headString.substring(valueS, valueE);
if(attrValue == null){
ok = false;
}
else{
this.setAttr(attrName, attrValue);
state= 0;
}
}
break;
default:
ok = false;
break;
}
if(ok == false){
break;
}
}//while
//check state if necessary
}
}
}
catch(Exception e){
ok = false;
}
return ok;
}
protected boolean setTxtFromString(String xmlString,int firstK,int endK){
if(endK < firstK){
return false;
}
boolean ok = true;
try{
String txtString = xmlString.substring(firstK, endK);
if(txtString != null){
this.txt = txtString.trim();
}
}
catch(Exception e){
ok = false;
}
return ok;
}
protected boolean checkNameFromString(String xmlString,int firstK,int endK){
if(endK < firstK){
return false;
}
boolean ok = true;
try{
String nameString = xmlString.substring(firstK, endK);
ok = (nameString==null)?
((this.name==null)?true:false)
: ((this.name==null)?false:this.name.equals(nameString.trim()));
}
catch(Exception e){
ok = false;
}
return ok;
}
public int parseStr(String xmlString,int off) throws XMLParseException {
if(xmlString == null || xmlString.length() <= off){
throw new XMLParseException("invalid xml string");
}
int startOff = off;
int endOff = xmlString.length()-1;
//head part
boolean selfClosing = false;
boolean hasName = false;
int firstK = xmlString.indexOf('<', startOff);
if(firstK < 0 || (firstK+1) >= endOff){
throw new XMLParseException("head part no first tag");
}
int nextK = firstK+1;
while(nextK <= endOff){
if(xmlString.startsWith("!--", nextK)){
nextK = xmlString.indexOf("-->", nextK+3);
nextK+=3;
}
else if(xmlString.startsWith("?", nextK)){
nextK = xmlString.indexOf("?>", nextK+1);
nextK+=2;
}
else if(xmlString.startsWith("![CDATA[", nextK)){
nextK = xmlString.indexOf("]]>", nextK+8);
nextK+=3;
}
else if(xmlString.startsWith("!DOCTYPE", nextK)){
nextK = xmlString.indexOf(">", nextK+8);
nextK+=1;
}
else if(xmlString.startsWith("/", nextK)){
nextK = xmlString.indexOf(">", nextK+1);
nextK+=1;
}
else{
break;
}
firstK = xmlString.indexOf('<', nextK);
if(firstK < 0 || (firstK+1) >= endOff){
throw new XMLParseException("head part no first tag");
}
nextK = firstK+1;
}
char c=xmlString.charAt(nextK);
/*while(c == '!' ||
c == '?' ||
c == '/'){
firstK = xmlString.indexOf('<', nextK);
if(firstK < 0 || (firstK+1) >= endOff){
throw new XMLParseException("head part no first tag");
}
nextK = firstK+1;
c = xmlString.charAt(nextK);
}*/
while(nextK <= endOff ){
c = xmlString.charAt(nextK);
if(Character.isLetter(c)){
hasName = true;
break;
}
if(c=='>' || c=='/'){
break;
}
nextK++;
}
if(hasName == false){
throw new XMLParseException("head part no name");
}
firstK = nextK;
nextK = xmlString.indexOf('>', firstK);
if(nextK < 0 || nextK > endOff){
throw new XMLParseException("head part no end");
}
int endK = nextK;
if(xmlString.charAt(nextK-1) == '/'){
selfClosing = true;
endK--;
}
if(this.setNameAndAttrFromString(xmlString, firstK, endK) == false){
throw new XMLParseException("head part setNameAndAttr error");
}
nextK++;
//body part
if(selfClosing == true){
return nextK;
}
boolean hasChild = true;
firstK = xmlString.indexOf('<', nextK);
if(firstK < 0 || (firstK+1) >= endOff){
throw new XMLParseException("body part no end tag");
}
if(xmlString.charAt(firstK+1) == '/'){
hasChild = false;
}
if(hasChild == true){
do{
XMLNode child = new XMLNode(this);
nextK = child.parseStr(xmlString, firstK);
firstK = xmlString.indexOf('<', nextK);
if(firstK < 0 || (firstK+1) >= endOff){
throw new XMLParseException("body part no end");
}
if(xmlString.charAt(firstK+1) == '/'){
hasChild = false;
}
}while(hasChild == true);
}
else{
if(this.setTxtFromString(xmlString, nextK, firstK) == false){
throw new XMLParseException("body part set text error");
}
}
//end part
nextK = xmlString.indexOf('>',firstK);
if(nextK < 0 || nextK > endOff){
throw new XMLParseException("end part no end tag");
}
if(this.checkNameFromString(xmlString, firstK+2, nextK) == false){
throw new XMLParseException("end part checkName error");
}
nextK++;
return nextK;
}
public String toString(){
return null;
}
public String getTxt(){
return this.txt;
}
}