package javaforce.webui;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import javaforce.*;
/**
* XML is a TreeModel data model that encapsules a complete XML file.<br> Each
* XML tag (element) is treated as a node in the tree. Once read() it can be
* viewed and edited with a JTree. Then you can write() it back to a file. XML
* will monitor changes made and update nodes as needed. The read() functions
* include a callback interface so you can further tweak the layout of the XML
* tree.<br> Typical XML Tag:<br> <name [attributes...]> content |
* children </name><br> Singleton XML Tag: (no children)<br> <name
* [attributes...] /><br> Caveats:<br> Only leaf nodes can contain actual
* data (content) (in other words @XmlMixed is not supported).<br>
* Mixed tags are read, but when written the
* content is lost.<br> There must be only one root tag.<br> Support for the
* standard XML header is provided (see header).<br> readClass() and
* writeClass() support : int, short, byte, float, double, boolean, String,
* Color and Custom Classes.<br> Arrays of any of these.<br> All classes and
* fields MUST be public. static and transient members are skipped. No special
* annotations are required.
*/
public class XML implements TreeModelListener {
private TreeModel treemodel;
private boolean useContentForName = false;
private boolean useNameAttributeForName = false;
private boolean useUniqueNames = true;
private boolean fireEvents = true;
private boolean ignoreEvents = false;
private class XMLTagPart {
private String content;
private String attrs;
public XMLTagPart() {
content = "";
attrs = "";
}
}
/**
* XMLEvent is an interface for a callback handler used during XML loading.
*/
public interface XMLEvent {
public void XMLTagAdded(XMLTag tag);
public void XMLTagRenamed(XMLTag tag);
};
/**
* XMLAttr is one attribute that is listed in each XML tag.
*/
public class XMLAttr {
public String name, value;
public XMLAttr() {
name = "";
value = "";
}
};
/**
* XMLTag is one node in the tree that represents one XML element or 'tag'.
*
* @param name the XML tag name
* @param attrs an ArrayList of XMLAttr
* @param uname the unique name of the tag. Usually equals name unless another
* child with the same parent has the same name. JTree uses uname to display
* the tags.
* @param content the data within the tags head/tail
* @param isLeaf set to force JTree to view node as a leaf
* @param isNotLeaf set to force JTree to view node that is expandable (even
* if it has no children)
* @param isReadOnly ignores edits from JTree
* @param useContentForName causes JTree to use content for display
*/
public class XMLTag extends TreeNode {
public String name = "";
public ArrayList<XMLAttr> attrs;
public String uname = ""; //unique name (usually same as name)
public String content = "";
/**
* Tag is a singleton - no content - ie: <tag attrs.../>
*/
public boolean isSingle = false;
public boolean isNotLeaf = false;
public boolean isLeaf = false;
public boolean isReadOnly = false;
public boolean useContentForName = false;
/**
* Constructs a new XMLTag
*/
public XMLTag() {
attrs = new ArrayList<XMLAttr>();
}
/**
* Returns the unique name of the tag.
*/
public String toString() {
return getName();
}
/**
* Returns the parent of the tag. This method just overrides the default
* method but returns XMLTag.
*/
public XMLTag getParent() {
if (this == treemodel.getRoot()) {
return null; //in case setRoot() moved the root up
}
return (XMLTag) super.getParent();
}
/**
* Returns true if the node is a leaf. This method just overrides the
* default method and allows better leaf control.
*/
public boolean isLeaf() {
if (isNotLeaf) {
return false;
}
if (isLeaf) {
return true;
}
return (getChildCount() == 0);
}
/**
* Returns a unique name for this node.
*/
public String getName() {
if (useContentForName) {
return content;
}
if (useNameAttributeForName) {
String argName = getArg("name");
if (argName != null) {
return argName;
}
}
if (useUniqueNames) {
return uname;
} else {
return name;
}
}
/**
* Sets the name for this node.
*/
public void setName(String newName) {
//check if name="..." is in attrs, else use name (and update uname)
XMLAttr attr;
boolean ok = false;
for (Iterator i = attrs.iterator(); i.hasNext();) {
attr = (XMLAttr) i.next();
if (attr.name.equals("name")) {
attr.value = newName;
ok = true;
break;
}
}
if (!ok) {
name = newName;
}
setuname(this);
}
/**
* Returns the child tag at index. This method just overrides the default
* method but returns XMLTag.
*/
public XMLTag getChildAt(int index) {
return (XMLTag) super.getChildAt(index);
}
/**
* Returns value of argument.
*/
public String getArg(String name) {
for (int a = 0; a < attrs.size(); a++) {
if (attrs.get(a).name.equals(name)) {
return attrs.get(a).value;
}
}
return null;
}
/**
* Sets argument to value.
*/
public void setArg(String name, String value) {
for (int a = 0; a < attrs.size(); a++) {
if (attrs.get(a).name.equals(name)) {
attrs.get(a).value = value;
return;
}
}
XMLAttr attr = new XMLAttr();
attr.name = name;
attr.value = value;
attrs.add(attr);
}
/**
* Returns the content of this node.
*/
public String getContent() {
return content;
}
/**
* Sets content.
*/
public void setContent(String newContent) {
content = newContent;
}
};
/**
* The header tag.<br> <?xml version="1.0" encoding="UTF-8" ?>
*/
public XMLTag header = new XMLTag();
/**
* The root tag.
*/
public XMLTag root = new XMLTag();
/**
* Constructs a new XML object.
*/
public XML() {
treemodel = new TreeModel(root);
treemodel.addTreeModelListener(this);
treemodel.setRoot(root);
}
/**
* Returns the TreeModel that can be passed to WebUI Tree constructor.
*/
public TreeModel getTreeModel() {
return treemodel;
}
private final int XML_OPEN = 1;
private final int XML_DATA = 2;
private final int XML_CLOSE = 3;
private final int XML_SINGLE = 4;
private int type;
private int line, pos;
private byte buf[];
private XMLTagPart readtag() {
boolean quote = false, isAttrs = false;
char ch;
XMLTagPart tag = new XMLTagPart();
type = XML_DATA;
while (pos < buf.length) {
ch = (char) buf[pos++];
if (ch == '\n') line++;
switch (type) {
case XML_OPEN:
case XML_CLOSE:
case XML_SINGLE:
if (ch == '\"') {
quote = !quote;
}
if (!quote) {
if (ch == '/') {
if (tag.content.length() == 0) {
type = XML_CLOSE;
} else {
type = XML_SINGLE;
}
continue;
}
if (ch == '>') {
break;
}
}
if ((ch == ' ') && (!isAttrs)) {
isAttrs = true;
}
if (isAttrs) {
tag.attrs += ch;
} else {
tag.content += ch;
}
continue;
case XML_DATA:
if ((ch == '<') && (!quote)) {
if (tag.content.length() > 0) {
pos--;
break;
}
type = XML_OPEN;
continue;
}
if (ch == '\"') {
if (quote) {
quote = false;
} else {
quote = true;
}
}
tag.content += ch;
continue;
}
break;
}
if (tag.content.length() == 0) {
return null; //EOF
}
if (type == XML_DATA) {
tag.content = decodeSafe(tag.content);
}
return tag;
}
private void string2attrs(XMLTag tag, String attrs) {
//search for name="value"
char ca[] = attrs.toCharArray();
XMLAttr attr;
String name, value;
int length = attrs.length();
int ep;
tag.attrs.clear();
for (int a = 0; a < length; a++) {
if (ca[a] == ' ') {
continue; //skip spaces
}
ep = attrs.indexOf('=', a);
if (ep == -1) {
return;
}
name = "";
for (int b = a; b < ep; b++) {
name += ca[b];
}
a = ep + 1;
value = "";
if (ca[a] == '\"') {
a++;
ep = attrs.indexOf('\"', a);
if (ep == -1) {
return;
}
for (int b = a; b < ep; b++) {
value += ca[b];
}
a = ep + 1;
} else if (ca[a] == '\'') {
a++;
ep = attrs.indexOf('\'', a);
if (ep == -1) {
return;
}
for (int b = a; b < ep; b++) {
value += ca[b];
}
a = ep + 1;
} else {
ep = attrs.indexOf(' ', a);
if (ep == -1) {
ep = length - 1;
}
if (ep <= a) {
return;
}
for (int b = a; b < ep; b++) {
value += ca[b];
}
a = ep + 1;
}
attr = new XMLAttr();
attr.name = name;
attr.value = value;
tag.attrs.add(attr);
}
}
private void setuname(XMLTag tag) {
XMLTag parent = tag.getParent();
String uname = tag.name;
XMLAttr attr;
for (Iterator i = tag.attrs.iterator(); i.hasNext();) {
attr = (XMLAttr) i.next();
if (attr.name.equalsIgnoreCase("name")) {
uname = attr.value;
break;
}
}
String orguname = uname;
if (parent == null) {
tag.uname = uname;
changedTag(tag);
return;
}
boolean ok;
int idx = 1;
int size = parent.getChildCount();
while (true) {
ok = true;
for (int a = 0; a < size; a++) {
XMLTag child = (XMLTag) parent.getChildAt(a);
if (child == tag) {
continue;
}
if (child.getName().equalsIgnoreCase(uname)) {
ok = false;
break;
}
}
if (ok) {
break;
}
uname = orguname + idx;
idx++;
}
tag.uname = uname;
changedTag(tag);
}
/**
* Reads the entire tree from a XML file from filename. No call handler is
* used.
*
* @param filename name of file to load XML data from
*/
public boolean read(String filename) {
return read(filename, null);
}
/**
* Reads the entire tree from a XML file from filename.
*
* @param filename name of file to load XML data from
* @param event callback handler to process each loaded XML tag
*/
public boolean read(String filename, XMLEvent event) {
FileInputStream fis;
boolean ret;
try {
fis = new FileInputStream(filename);
ret = read(fis, event);
fis.close();
} catch (Exception e) {
JFLog.log(e);
return false;
}
return ret;
}
/**
* Reads the entire tree from a XML file from the InputStream. No callback
* handler is used.
*
* @param in InputStream to load XML data from
*/
public boolean read(InputStream in) {
return read(in, null);
}
/**
* Reads the entire tree from a XML file from the InputStream.
*
* @param in InputStream to load XML data from
* @param event callback handler to process each loaded XML tag
*/
public boolean read(InputStream is, XMLEvent event) {
this.event = event;
try {
buf = JF.readAll(is);
} catch (Exception e) {
e.printStackTrace();
return false;
}
deleteAll();
type = XML_DATA;
XMLTagPart tagpart;
XMLTag tag = null, newtag;
boolean bRoot = false;
boolean bHeader = false;
line = 1;
while (true) {
tagpart = readtag();
if (tagpart == null) {
break;
}
switch (type) {
case XML_OPEN:
if (tagpart.content.startsWith("?xml")) {
if (bHeader) {
JFLog.log("XML:Multiple headers found");
return false;
} //already read the XML header
header.name = tagpart.content;
header.uname = header.name;
string2attrs(header, tagpart.attrs);
if (event != null) {
event.XMLTagAdded(header);
}
break;
}
if (tagpart.content.startsWith("!--")) {
//ignore comments
break;
}
//no break
case XML_SINGLE:
if (tag == null) {
//root tag
if (bRoot) {
JFLog.log("XML:Multiple root tags found");
return false;
} //already found a root tag
bRoot = true;
root.name = tagpart.content;
root.uname = root.name;
string2attrs(root, tagpart.attrs);
if (event != null) {
event.XMLTagAdded(root);
}
changedTag(root);
tag = root;
} else {
newtag = new XMLTag();
newtag.name = tagpart.content;
string2attrs(newtag, tagpart.attrs);
addTag(tag, newtag);
if (type == XML_SINGLE) {
newtag.isSingle = true;
} else {
tag = newtag;
}
}
break;
case XML_CLOSE:
if (tag == null) {
JFLog.log("XML:tag closed but never opened:line=" + line);
return false;
} //bad xml file
if (!tagpart.content.equalsIgnoreCase(tag.name)) {
JFLog.log("XML:tag closed doesn't match open:tag.open=" + tag.name + ":tag.close=" + tagpart.content + ":line=" + line );
return false;
} //unmatched closing tag
tag = (XMLTag) tag.getParent();
break;
case XML_DATA:
if (tag == null) {
continue; //could happen after header and before root tag
}
tag.content += tagpart.content;
break;
}
}
buf = null;
if (tag != null) {
JFLog.log("XML:tag left open");
return false;
} //tag left open
return true;
}
private String attrs2string(XMLTag tag) {
XMLAttr attr;
int size = tag.attrs.size();
String str = "", tmp;
for (int a = 0; a < size; a++) {
attr = tag.attrs.get(a);
tmp = " " + attr.name + "=\"" + attr.value + "\"";
str += tmp;
}
return str;
}
private void writestr(OutputStream out, String str) {
try {
out.write(str.getBytes("UTF-8"));
} catch (Exception e) {
}
}
private int indent;
private void writetag(OutputStream out, XMLTag tag) {
String tmp;
tmp = "";
for (int a = 0; a < indent; a++) {
tmp += ' ';
}
writestr(out, tmp);
int size = tag.getChildCount();
String attrs;
if (size > 0) {
//write open tag w/ attrs + content
attrs = attrs2string(tag);
tmp = "<" + tag.name + attrs + ">\n";
writestr(out, tmp);
indent += 2;
//write children
for (int a = 0; a < size; a++) {
writetag(out, (XMLTag) tag.getChildAt(a));
}
//write close tag
indent -= 2;
tmp = "";
for (int a = 0; a < indent; a++) {
tmp += ' ';
}
writestr(out, tmp);
tmp = "</" + tag.name + ">\n";
writestr(out, tmp);
} else {
attrs = attrs2string(tag);
if (tag.isSingle) {
tmp = "<" + tag.name + attrs + "/>\n";
} else {
tmp = "<" + tag.name + attrs + ">" + encodeSafe(tag.content) + "</" + tag.name + ">\n";
}
writestr(out, tmp);
}
}
/**
* Writes the entire tree as a XML file to the filename.
*/
public boolean write(String filename) {
FileOutputStream fos;
boolean ret;
try {
fos = new FileOutputStream(filename);
ret = write(fos);
fos.close();
} catch (Exception e) {
JFLog.log(e);
return false;
}
return ret;
}
/**
* Writes the entire tree as a XML file to the OutputStream.
*/
public boolean write(OutputStream os) {
BufferedOutputStream bos = new BufferedOutputStream(os);
String tmp, attrs;
if (root.name.length() == 0) {
return false;
}
if (header.name.length() > 0) {
attrs = attrs2string(header);
tmp = "<" + header.name + attrs + ">\n";
writestr(bos, tmp);
}
//write root header
attrs = attrs2string(root);
tmp = "<" + root.name + attrs + ">\n";
writestr(bos, tmp);
int size = root.getChildCount();
indent = 2;
for (int a = 0; a < size; a++) {
writetag(bos, (XMLTag) root.getChildAt(a));
}
//write root tail
tmp = "</" + root.name + ">\n";
writestr(bos, tmp);
try {
bos.flush();
} catch (Exception e) {
} //must flush or it's lost
return true;
}
private void clearTag(XMLTag tag) {
tag.name = "";
tag.attrs = new ArrayList<XMLAttr>();
tag.uname = "";
tag.content = "";
}
/**
* Deletes the entire tree and resets the root and header tags.
*/
public void deleteAll() {
deleteTag(root);
clearTag(header);
changedTag(header);
clearTag(root);
changedTag(root);
}
/**
* Deletes a node from the parent. Also deletes all it's children.
*/
public boolean deleteTag(XMLTag tag) {
//remove children first
while (tag.getChildCount() > 0) {
deleteTag((XMLTag) tag.getChildAt(0));
}
//now remove itself from parent
if (tag.getParent() == null) {
return true; //can not delete root/header tag itself
}
treemodel.removeNodeFromParent(tag);
return true;
}
private XMLEvent event = null;
/**
* Creates an empty node that can be inserted into the tree using addTag().
*/
public XMLTag createTag() {
return new XMLTag(); //must call addTag() to add to the tree
}
/**
* Adds the node to the targetParent.
*/
public XMLTag addTag(XMLTag targetParent, XMLTag tag) {
treemodel.insertNodeInto(tag, targetParent, targetParent.getChildCount());
setuname(tag);
if (event != null) {
event.XMLTagAdded(tag);
}
return tag;
}
/**
* Adds node with the name, attrs and content specified. If another node
* already exists with the same name the new node's unique name will differ.
*/
public XMLTag addTag(XMLTag targetParent, String name, String attrs, String content) {
XMLTag newtag = new XMLTag();
newtag.name = name;
newtag.content = content;
string2attrs(newtag, attrs);
return addTag(targetParent, newtag);
}
/**
* Adds (a non-existing) or sets (an existing) node with the name, attrs and
* content specified.
*/
public XMLTag addSetTag(XMLTag targetParent, String name, String attrs, String content) {
XMLTag child;
int len = targetParent.getChildCount();
for (int a = 0; a < len; a++) {
child = targetParent.getChildAt(a);
if (child.name.equals(name)) {
setTag(child, name, attrs, content);
return child;
}
}
return addTag(targetParent, name, attrs, content);
}
/**
* Notify the treemodel that you changed a node.
*/
public void changedTag(XMLTag tag) {
if (!fireEvents) return;
ignoreEvents = true;
treemodel.nodeChanged(tag);
ignoreEvents = false;
}
/**
* Sets the name, attrs and contents of the true root node.
*/
public void setRoot(String name, String attrs, String content) {
root.name = name;
root.uname = name;
string2attrs(root, attrs);
root.content = content;
changedTag(root);
}
/**
* Returns the unique name of a node.
*/
public String getName(XMLTag tag) {
return tag.getName();
}
/**
* Sets the name of a node. It's unique name may differ when shown in a tree.
*/
public void setName(XMLTag tag, String newName) {
tag.setName(newName);
}
/**
* Returns a node based on the TreePath path;
*/
public XMLTag getTag(TreePath path) {
return getTag(path.getPath());
}
/**
* Returns a node based on the objs[] path. Relative to virtual root tag. (see
* setRoot())
*/
public XMLTag getTag(Object objs[]) {
XMLTag tag = (XMLTag) treemodel.getRoot(), child;
String name;
if (objs == null || objs.length == 0) {
return null;
}
name = tag.getName();
if (!name.equals(objs[0].toString())) {
return null;
}
int idx = 1;
boolean ok;
int cnt;
while (idx < objs.length) {
ok = false;
cnt = tag.getChildCount();
for (int i = 0; i < cnt; i++) {
child = (XMLTag) tag.getChildAt(i);
name = child.getName();
if (name.equals(objs[idx].toString())) {
ok = true;
idx++;
tag = child;
break;
}
}
if (!ok) {
return null; //next path element not found
}
}
return tag;
}
/**
* Sets the name, attrs and content for an existing node.
*/
public void setTag(XMLTag tag, String name, String attrs, String content) {
tag.name = name;
string2attrs(tag, attrs);
tag.content = content;
if (tag.getParent() != null) {
setuname(tag);
} else {
tag.uname = name;
changedTag(tag);
}
if (event != null) {
event.XMLTagAdded(tag);
}
}
/**
* Sets the root node for the tree. This doesn't effect the true root node.
* This is usefull in hiding parts of a XML file from view when viewed in a
* JTree.
*/
public void setRoot(XMLTag newRoot) {
treemodel.setRoot(newRoot);
}
/**
* Forces getName() to return the tags content instead of the actual name.
*
* @param state
*/
public void setUseContentForName(boolean state) {
useContentForName = state;
}
/**
* Forces getName() to return the tags content instead of the actual name.
*/
public boolean getUseContentForName() {
return useContentForName;
}
/**
* Forces getName() to return the tags attribute 'name' (if available)
*
* @param state
*/
public void setUseAttributeNameForName(boolean state) {
useNameAttributeForName = state;
}
/**
* Forces getName() to return the tags content instead of the actual name.
*/
public boolean getUseAttributeNameForName() {
return useNameAttributeForName;
}
/**
* Forces getName() to return unique names.
*
* @param state
*/
public void setUseUniqueNames(boolean state) {
useUniqueNames = state;
}
/**
* Forces getName() to return the tags content instead of the actual name.
*/
public boolean getUseUniqueNames() {
return useUniqueNames;
}
/**
* Writes all children of tag to object.
*/
public void writeClass(XMLTag tag, Object obj) {
Class<?> c = obj.getClass();
Class<?> fc, fcc;
Field f;
int childCnt = tag.getChildCount();
XMLTag child;
Object i, array[], newArray[];
String name, typeString;
for (int a = 0; a < childCnt; a++) {
try {
child = tag.getChildAt(a);
int childChildcnt = child.getChildCount();
name = child.getName();
f = c.getField(name);
if (f == null) {
JFLog.log("XML:field not found:" + name);
continue;
}
if (childChildcnt > 0) {
fc = f.getType();
if (fc.isArray()) {
fcc = fc.getComponentType();
i = fcc.newInstance();
writeClass(child, i);
array = (Object[]) f.get(obj);
if ((array == null) || (array.length == 0)) {
newArray = (Object[]) Array.newInstance(fcc, 1);
newArray[0] = i;
} else {
newArray = Arrays.copyOf(array, array.length + 1);
newArray[array.length] = i;
}
f.set(obj, newArray);
} else {
Object childObject = fc.newInstance();
writeClass(child, childObject);
f.set(obj, childObject);
}
} else {
typeString = f.toGenericString();
if (typeString.indexOf(" int ") != -1) {
f.setInt(obj, Integer.valueOf(child.content));
} else if (typeString.indexOf(" short ") != -1) {
f.setShort(obj, (short) JF.atoi(child.content));
} else if (typeString.indexOf(" byte ") != -1) {
f.setByte(obj, (byte) JF.atoi(child.content));
} else if (typeString.indexOf(" float ") != -1) {
f.setFloat(obj, Float.valueOf(child.content));
} else if (typeString.indexOf(" double ") != -1) {
f.setDouble(obj, Double.valueOf(child.content));
} else if (typeString.indexOf(" boolean ") != -1) {
f.setBoolean(obj, child.content.equalsIgnoreCase("true"));
} else if (typeString.indexOf(" java.lang.String ") != -1) {
f.set(obj, child.content);
} else if (typeString.indexOf(" javaforce.webui.Color ") != -1) {
f.set(obj, new Color(JF.atox(child.content)));
} else if (typeString.indexOf(" int[] ") != -1) {
int array2[] = (int[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new int[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = Integer.valueOf(child.content);
f.set(obj, array2);
} else if (typeString.indexOf(" short[] ") != -1) {
short array2[] = (short[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new short[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = Short.valueOf(child.content);
f.set(obj, array2);
} else if (typeString.indexOf(" byte[] ") != -1) {
byte array2[] = (byte[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new byte[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = Byte.valueOf(child.content);
f.set(obj, array2);
} else if (typeString.indexOf(" float[] ") != -1) {
float array2[] = (float[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new float[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = Float.valueOf(child.content);
f.set(obj, array2);
} else if (typeString.indexOf(" double[] ") != -1) {
double array2[] = (double[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new double[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = Double.valueOf(child.content);
f.set(obj, array2);
} else if (typeString.indexOf(" boolean[] ") != -1) {
boolean array2[] = (boolean[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new boolean[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = child.content.equalsIgnoreCase("true");
f.set(obj, array2);
} else if (typeString.indexOf(" java.lang.String[] ") != -1) {
String array2[] = (String[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new String[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = child.content;
f.set(obj, array2);
} else if (typeString.indexOf(" java.awt.Color[] ") != -1) {
Color array2[] = (Color[]) f.get(obj);
int idx;
if (array2 == null) {
idx = 0;
array2 = new Color[1];
} else {
idx = array2.length;
array2 = Arrays.copyOf(array2, array2.length + 1);
}
array2[idx] = new Color(JF.atox(child.content));
f.set(obj, array2);
} else {
name = child.getName();
f = c.getField(name);
fc = f.getType();
Object childObject = fc.newInstance();
writeClass(child, childObject);
f.set(obj, childObject);
}
}
} catch (Exception e) {
JFLog.log(e);
}
}
}
/**
* Writes entire XML tree to object.
*/
public void writeClass(Object obj) {
writeClass(root, obj);
}
/**
* Reads all fields from an object and places into tag.
*/
public void readClass(XMLTag tag, Object obj) {
Class<?> c = obj.getClass(), fc;
Field fs[];
String name, typeString;
try {
fs = c.getFields();
} catch (Exception e) {
JFLog.log(e);
return;
}
int fieldcnt = fs.length;
boolean isArray;
Object array[];
for (int a = 0; a < fieldcnt; a++) {
try {
if (fs[a].get(obj) == null) {
continue;
}
fc = fs[a].getType();
isArray = fc.isArray();
name = fs[a].getName();
if (isArray) {
typeString = fs[a].toGenericString();
if (typeString.indexOf(" static ") != -1) {
continue; //ignore static fields
}
if (typeString.indexOf(" transient ") != -1) {
continue; //ignore transient fields
}
array = (Object[]) fs[a].get(obj);
if (typeString.indexOf(" int[] ") != -1) {
Integer array2[] = (Integer[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", "" + array2[b].intValue());
}
} else if (typeString.indexOf(" short[] ") != -1) {
Short array2[] = (Short[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", "" + array2[b].shortValue());
}
} else if (typeString.indexOf(" byte[] ") != -1) {
Byte array2[] = (Byte[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", "" + array2[b].byteValue());
}
} else if (typeString.indexOf(" boolean[] ") != -1) {
Boolean array2[] = (Boolean[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", array2[b].booleanValue() ? "true" : "false");
}
} else if (typeString.indexOf(" float[] ") != -1) {
Float array2[] = (Float[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", "" + array2[b].floatValue());
}
} else if (typeString.indexOf(" double[] ") != -1) {
Double array2[] = (Double[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", "" + array2[b].doubleValue());
}
} else if (typeString.indexOf(" java.lang.String[] ") != -1) {
String array2[] = (String[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", array2[b]);
}
} else if (typeString.indexOf(" javaforce.webui.Color[] ") != -1) {
Color array2[] = (Color[]) array;
for (int b = 0; b < array.length; b++) {
addTag(tag, name, "", Integer.toHexString(array2[b].getRGB()).substring(2));
}
} else {
//go deeper into object
for (int b = 0; b < array.length; b++) {
readClass(addTag(tag, name, "", ""), array[b]);
}
}
} else {
typeString = fs[a].toGenericString();
if (typeString.indexOf(" static ") != -1) {
continue; //ignore static fields
}
if (typeString.indexOf(" transient ") != -1) {
continue; //ignore transient fields
}
if (typeString.indexOf(" int ") != -1) {
addTag(tag, name, "", "" + fs[a].getInt(obj));
} else if (typeString.indexOf(" short ") != -1) {
addTag(tag, name, "", "" + fs[a].getShort(obj));
} else if (typeString.indexOf(" byte ") != -1) {
addTag(tag, name, "", "" + fs[a].getByte(obj));
} else if (typeString.indexOf(" boolean ") != -1) {
addTag(tag, name, "", fs[a].getBoolean(obj) ? "true" : "false");
} else if (typeString.indexOf(" float ") != -1) {
addTag(tag, name, "", "" + fs[a].getFloat(obj));
} else if (typeString.indexOf(" double ") != -1) {
addTag(tag, name, "", "" + fs[a].getDouble(obj));
} else if (typeString.indexOf(" java.lang.String ") != -1) {
addTag(tag, name, "", (String) fs[a].get(obj));
} else if (typeString.indexOf(" java.awt.Color ") != -1) {
addTag(tag, name, "", Integer.toHexString(((Color) fs[a].get(obj)).getRGB()).substring(2));
} else {
//go deeper into object
readClass(addTag(tag, name, "", ""), fs[a].get(obj));
}
}
} catch (Exception e) {
JFLog.log(e);
}
}
}
/**
* Reads entire XML tree from object (deletes entire tree first).<br>
*
* @param rootName = name to assign to root tag.
*/
public void readClass(String rootName, Object obj) {
this.deleteAll();
root.setName(rootName);
readClass(root, obj);
}
public void setEventListener(XMLEvent event) {
this.event = event;
}
private String encodeSafe(String in) {
return in.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); //order matters here
}
private String decodeSafe(String in) {
return in.replaceAll(">", ">").replaceAll("<", "<").replaceAll("&", "&");
}
//interface TreeModelListener
public void treeNodesChanged(TreeModelEvent e) {
if (ignoreEvents) return;
XMLTag parent = (XMLTag) (e.getTreePath().getLastPathComponent());
int indices[] = e.getChildIndices();
if (indices == null || indices.length == 0) {
return;
}
/*
* If the event lists children, then the changed
* node is the child of the node we've already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
int index = indices[0];
XMLTag tag = (XMLTag) (parent.getChildAt(index));
if (tag.isReadOnly) {
return;
}
if (tag.getUserObject() == null) {
return;
}
fireEvents = false; //prevent inf loop (setName will call changedTag which calls treemodel.nodeChanged)
tag.setName(tag.getUserObject().toString());
fireEvents = true;
if (event != null) {
event.XMLTagRenamed(tag);
}
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
};