package org.lemsml.jlems.io.xmlio;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.lemsml.jlems.core.logging.E;
import org.lemsml.jlems.core.serial.WrapperElement;
import org.lemsml.jlems.core.sim.ContentError;
import org.lemsml.jlems.core.type.Attribute;
import org.lemsml.jlems.core.type.BodyValued;
import org.lemsml.jlems.core.type.Component;
import org.lemsml.jlems.core.type.Inheritor;
import org.lemsml.jlems.core.type.LemsCollection;
import org.lemsml.jlems.core.xml.XMLElement;
public class XMLSerializer {
static HashMap<String, String> defaultAttributeMap = new HashMap<String, String>();
static {
defaultAttributeMap.put("eXtends", "extends");
}
HashMap<String, String> attributeMap = defaultAttributeMap;
boolean conciseTags;
boolean quoteStrings;
List<Component> refComponents = new ArrayList<Component>();
Boolean includeRefComponents = false;
public XMLSerializer() {
conciseTags = true;
quoteStrings = true;
}
public XMLSerializer(Boolean includeRefComponents) {
conciseTags = true;
quoteStrings = true;
this.includeRefComponents = includeRefComponents;
}
public void setConciseTags(boolean b) {
conciseTags = b;
}
public void setQuoteStrings(boolean b) {
quoteStrings = b;
}
public static XMLSerializer newInstance() {
return new XMLSerializer();
}
public static String serialize(Object ob) throws ContentError {
return getSerialization(ob);
}
public static String getSerialization(Object ob) throws ContentError {
return newInstance().writeObject(ob);
}
public String writeObject(Object obj) throws ContentError {
XMLElement xe = makeXMLElement(null, null, null, obj);
String serializeObject = xe.serialize();
// If includeRedComponents = true, serialise ref component too
// Probably because it is a resolved lems component
if (this.includeRefComponents){
this.includeRefComponents = false;
for (Component refComponent : refComponents){
serializeObject += makeXMLElement(null, null, null, refComponent).serialize();
}
}
return serializeObject;
}
public XMLElement makeXMLElement(XMLElement dest, Object parent, String knownAs, Object ob) throws ContentError {
XMLElement ret = null;
if (ob instanceof String) {
ret = new XMLElement("String");
ret.setBody((String) ob);
} else if (ob instanceof Component) {
// TODO knownAs intead of null?
ret = makeComponentElement(null, (Component) ob);
} else if (ob instanceof LemsCollection) {
LemsCollection<?> lc = (LemsCollection<?>) ob;
if (lc.size() > 0) {
ret = new WrapperElement("anon");
for (Object child : lc) {
if (parent instanceof Inheritor && ((Inheritor) parent).inherited(child)) {
// we inherited it from the parent of the object that
// contains the list
// dont need to write it out again
} else {
XMLElement che = makeXMLElement(dest, null, null, child);
if (che != null) {
ret.add(che);
}
}
}
}
} else {
String tag = "error";
if (knownAs != null) {
tag = knownAs;
} else {
tag = ob.getClass().getName();
if (conciseTags) {
int ilast = tag.lastIndexOf(".");
if (ilast >= 0) {
tag = tag.substring(ilast + 1, tag.length());
}
}
}
String stag = tag;
ret = new XMLElement(stag);
if (ob instanceof BodyValued) {
String sb = ((BodyValued) ob).getBodyValue();
if (sb != null) {
ret.setBody(sb);
}
}
Field[] flds = ob.getClass().getFields();
for (int i = 0; i < flds.length; i++) {
String fieldName = flds[i].getName();
Object wk = null;
try {
wk = flds[i].get(ob);
} catch (Exception e) {
E.warning("failed to get field " + fieldName + " in " + ob);
}
if (Modifier.isFinal(flds[i].getModifiers())) {
wk = null;
// not settable
} else if (Modifier.isPublic(flds[i].getModifiers())) {
if (fieldName.startsWith("p_") || fieldName.startsWith("r_")) {
// leave it out all the same - local private and
// reference elements
} else {
// export map may set it null if it shouldn't be
// exported
if (fieldName != null) {
if (wk instanceof Double) {
setAttribute(ret, fieldName, "" + ((Double) wk).doubleValue());
} else if (wk instanceof Integer) {
setAttribute(ret, fieldName, "" + ((Integer) wk).intValue());
} else if (wk instanceof Boolean) {
setAttribute(ret, fieldName, (((Boolean) wk).booleanValue() ? "true" : "false"));
} else if (wk instanceof String) {
setAttribute(ret, fieldName, (String) wk);
} else if (wk instanceof double[]) {
setAttribute(ret, fieldName, makeString((double[]) wk));
} else if (wk instanceof int[]) {
setAttribute(ret, fieldName, makeString((int[]) wk));
} else if (wk instanceof boolean[]) {
setAttribute(ret, fieldName, makeString((boolean[]) wk));
} else if (wk instanceof String[]) {
setAttribute(ret, fieldName, makeString((String[]) wk));
} else if (wk != null) {
// should child elements be known by the field
// name in the parent, or their element type?
// XMLElement xe = makeXMLElement(ob, fieldName,
// wk);
XMLElement xe = makeXMLElement(ret, ob, null, wk);
if (xe == null) {
// must have been something we don't want
} else if (xe instanceof WrapperElement) {
for (XMLElement sub : xe.getElements()) {
ret.add(sub);
}
} else {
ret.add(xe);
}
}
}
}
}
}
}
return ret;
}
private void setAttribute(XMLElement ret, String fieldName, String avalue) {
String value = avalue;
String anm = fieldName;
if (attributeMap.containsKey(anm)) {
anm = attributeMap.get(anm);
}
ret.addAttribute(anm, value);
}
private XMLElement makeComponentElement(String tagName, Component cpt) {
String typeName = cpt.getTypeName();
XMLElement ret = null;
if (tagName == null) {
ret = new XMLElement(typeName);
} else {
ret = new XMLElement(tagName);
ret.addAttribute("type", typeName);
}
String enm = cpt.getExtendsName();
if (enm != null) {
ret.addAttribute("extends", enm);
}
if (cpt.getID() != null) {
ret.addAttribute("id", cpt.getID());
}
// ParamValues are the parsed ones - could put them back in with units
// added?
// for (ParamValue pv : cpt.getParamValues()) {
// ret.addAttribute(pv.getName(), pv.parseValue(s));
// }
HashSet<String> atts = new HashSet<String>();
HashMap<String, Component> refHM = cpt.getRefComponents();
if (refHM != null) {
for (String s : refHM.keySet()) {
Component tgt = refHM.get(s);
ret.addAttribute(s, tgt.getID());
atts.add(s);
// Add Ref Components to list to serialise them
if (includeRefComponents)
refComponents.add(tgt);
}
}
for (Attribute att : cpt.getAttributes()) {
String sa = att.getName();
if (!atts.contains(sa)) {
ret.addAttribute(sa, att.getValue().toString());
}
}
for (Component cch : cpt.getStrictChildren()) {
ret.add(makeComponentElement(null, cch));
}
HashMap<String, Component> chm = cpt.getChildHM();
if (chm != null) {
for (String s : chm.keySet()) {
Component c = chm.get(s);
XMLElement xe = makeComponentElement(s, c);
ret.add(xe);
}
}
return ret;
}
private String makeString(double[] wk) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (double d : wk) {
if (!first) {
sb.append(",");
}
sb.append("" + d);
first = false;
}
return sb.toString();
}
private String makeString(int[] wk) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (int d : wk) {
if (!first) {
sb.append(",");
}
sb.append("" + d);
first = false;
}
return sb.toString();
}
// lots to go wrong here... TODO
private String makeString(String[] wk) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String d : wk) {
if (!first) {
sb.append(",");
}
sb.append("" + d);
first = false;
}
return sb.toString();
}
private String makeString(boolean[] wk) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (boolean b : wk) {
if (!first) {
sb.append(",");
}
sb.append("" + (b ? "true" : "false"));
first = false;
}
return sb.toString();
}
}