package pl.shockah;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XMLObject {
public static XMLObject deserialize(String text) {
text = text.replace("\t","").replace("\r","");
String[] lines = text.split("\\r?\\n");
for (int i = 0; i < lines.length; i++) lines[i] = lines[i].trim();
text = StringTools.implode(lines,"");
Pattern p = Pattern.compile("\\Q<![CDATA[\\E(.*?)\\Q]]>\\E");
Matcher m; while ((m = p.matcher(text)).find()) text = new StringBuilder(text).replace(m.start(),m.end(),StringTools.unescapeHTML(m.group(1))).toString();
XMLObject x = new XMLObject();
return deserialize(x,text) ? x : null;
}
private static boolean deserialize(XMLObject x, String text) {
int i = 0;
char c;
while (true) {
if (i > text.length()-1) break;
while (text.charAt(i) == ' ' && i > text.length()-1) i++;
if (i > text.length()-1) break;
int openCount = 0;
StringBuilder tagOpen = new StringBuilder();
while (true) {
tagOpen.append(text.charAt(i++));
if (tagOpen.length() == 1 && tagOpen.charAt(0) != '<') {
i--;
tagOpen = null;
break;
}
if (tagOpen.charAt(tagOpen.length()-1) == '<') openCount++;
if (tagOpen.charAt(tagOpen.length()-1) == '>') {
openCount--;
if (openCount == 0) {
if ((tagOpen.length() >= 3 && tagOpen.charAt(1) == '!') || (tagOpen.length() >= 4 && tagOpen.charAt(1) == '?' && tagOpen.charAt(tagOpen.length()-2) == '?')) {
tagOpen = new StringBuilder();
break;
}
break;
}
}
}
if (tagOpen == null) return false;
if (tagOpen.length() == 0) continue;
XMLObject newx = new XMLObject();
x.elements.add(newx);
StringBuilder tagName = new StringBuilder();
for (int _i = 1; _i < tagOpen.length(); _i++) if ((c = tagOpen.charAt(_i)) != '>') tagName.append(c);
boolean noClose = false;
if (tagName.charAt(tagName.length()-1) == '/') {
tagName.deleteCharAt(tagName.length()-1);
noClose = true;
}
String[] args = tagName.toString().trim().split(" ");
String arg = args.length == 1 ? "" : StringTools.implode(args,1," ");
int i2 = 0;
while (i2 <= arg.length()-1) {
while (arg.charAt(i2) == ' ') i2++;
StringBuilder key = new StringBuilder();
while ((c = arg.charAt(i2++)) != '=' && c != ' ') key.append(c);
if (c == ' ') {
newx.attributes.put(key.toString(),null);
break;
}
StringBuilder value = new StringBuilder(); char _start = arg.charAt(i2++);
while ((c = arg.charAt(i2++)) != _start) value.append(c);
newx.attributes.put(StringTools.unescapeHTML(key.toString()),StringTools.unescapeHTML(value.toString()));
}
String _end = "</"+(newx.name = StringTools.unescapeHTML(args[0]))+">";
if (!noClose) {
StringBuilder tagInside = new StringBuilder();
if (text.charAt(i) != '<' || text.charAt(i+1) != '/') {
while (true) {
tagInside.append(text.charAt(i++));
if (tagInside.length() >= _end.length() && tagInside.toString().endsWith(_end)) {
tagInside.delete(tagInside.length()-_end.length(),tagInside.length());
break;
}
}
} else i += _end.length();
StringBuilder _tag = new StringBuilder(tagInside.toString().trim());
if (_tag.length() != 0 && _tag.charAt(0) == '<') tagInside = _tag;
if (tagInside.length() != 0 && tagInside.charAt(0) == '<') {
deserialize(newx,tagInside.toString().trim());
} else {
newx.value = StringTools.unescapeHTML(tagInside.toString());
if (newx.value.isEmpty()) newx.value = null;
}
}
}
return true;
}
private String name = null, value = null;
private Map<String,String> attributes = Collections.synchronizedMap(new TreeMap<String,String>());
private List<XMLObject> elements = Collections.synchronizedList(new ArrayList<XMLObject>());
public String getName() {return name;}
public int countAttributes() {return attributes.size();}
public boolean existsAttribute(String key) {return attributes.containsKey(key);}
public String getAttribute(String key) {return attributes.get(key);}
public Map<String,String> getAllAttributes() {return new TreeMap<String,String>(attributes);}
public int countElements() {return elements.size();}
public XMLObject getBaseElement() {return getAllElements().get(0);}
public XMLObject getElement(String name) {
for (XMLObject e : elements)
if (e.name.equals(name))
return e;
return null;
}
public ArrayList<XMLObject> getElements(String name) {
ArrayList<XMLObject> list = new ArrayList<XMLObject>();
for (XMLObject e : elements) if (e.name.equals(name)) list.add(e);
return list;
}
public ArrayList<XMLObject> getAllElements() {return new ArrayList<XMLObject>(elements);}
public String getValue() {return value;}
public void print() {
print(0);
}
private void print(int spaces) {
String s = ""; for (int i = 0; i < spaces; i++) s += " ";
if (name == null) {
for (XMLObject e : elements) e.print(name == null ? 0 : spaces+2);
} else {
System.out.print(s+"<"+StringTools.escapeHTML(name));
for (String key : attributes.keySet()) {
String v = attributes.get(key); char c = v.contains("\"") ? '\'' : '"';
System.out.print(" "+key+"="+c+StringTools.escapeHTML(attributes.get(key))+c);
}
if (countElements() != 0 || value != null) System.out.print(">");
if (value != null) {
System.out.print(StringTools.escapeHTML(value));
System.out.println("</"+StringTools.escapeHTML(name)+">");
} else {
if (countElements() == 0) {
System.out.println(" />");
} else {
System.out.println();
for (XMLObject e : elements) e.print(name == null ? 0 : spaces+2);
System.out.println(s+"</"+StringTools.escapeHTML(name)+">");
}
}
}
}
@Override
public String toString() {
return getName()+'='+getValue();
}
/*public String serialize() {
return serialize(new StringBuilder()).toString();
}
private StringBuilder serialize(StringBuilder sb) {
sb.append(isArray() ? "[" : "{");
int i = 0;
for (String key : elements.keySet()) {
if (i++ != 0) sb.append(",");
if (!isArray()) sb.append("\""+key.replace("\"","\\\"")+"\":");
Object e = elements.get(key);
if (e instanceof XMLObject) ((XMLObject)e).serialize(sb);
else if (e instanceof String) sb.append("\""+((String)e).replace("\"","\\\"")+"\"");
}
sb.append(isArray() ? "]" : "}");
return sb;
}*/
}