📄 mxobjectcodec.java
字号:
/** * $Id: mxObjectCodec.java,v 1.19 2008/10/22 13:33:45 gaudenz Exp $ * Copyright (c) 2006, Gaudenz Alder */package com.mxgraph.io;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.util.Collection;import java.util.HashSet;import java.util.Hashtable;import java.util.Iterator;import java.util.Map;import java.util.Set;import org.w3c.dom.Element;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import com.mxgraph.util.mxUtils;/** * Generic codec for Java objects. See below for a detailed description of * the encoding/decoding scheme. * * Note: Since booleans are numbers in JavaScript, all boolean values are * encoded into 1 for true and 0 for false. */public class mxObjectCodec{ /** * Immutable empty set. */ private static Set EMPTY_SET = new HashSet(); /** * Holds the template object associated with this codec. */ Object template; /** * Array containing the variable names that should be ignored by the codec. */ Set exclude; /** * Array containing the variable names that should be turned into or * converted from references. See <mxCodec.getId> and <mxCodec.getObject>. */ Set idrefs; /** * Maps from from fieldnames to XML attribute names. */ Map mapping; /** * Maps from from XML attribute names to fieldnames. */ Map reverse; /** * Constructs a new codec for the specified template object. */ public mxObjectCodec(Object template) { this(template, null, null, null); } /** * Constructs a new codec for the specified template object. The variables * in the optional exclude array are ignored by the codec. Variables in the * optional idrefs array are turned into references in the XML. The * optional mapping may be used to map from variable names to XML * attributes. The argument is created as follows: * * @param template Prototypical instance of the object to be encoded/decoded. * @param exclude Optional array of fieldnames to be ignored. * @param idrefs Optional array of fieldnames to be converted to/from references. * @param mapping Optional mapping from field- to attributenames. */ public mxObjectCodec(Object template, String[] exclude, String[] idrefs, Map mapping) { this.template = template; if (exclude != null) { this.exclude = new HashSet(); for (int i = 0; i < exclude.length; i++) { this.exclude.add(exclude[i]); } } else { this.exclude = EMPTY_SET; } if (idrefs != null) { this.idrefs = new HashSet(); for (int i = 0; i < idrefs.length; i++) { this.idrefs.add(idrefs[i]); } } else { this.idrefs = EMPTY_SET; } if (mapping == null) { mapping = new Hashtable(); } this.mapping = mapping; reverse = new Hashtable(); Iterator it = mapping.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); reverse.put(e.getValue().toString(), e.getKey().toString()); } } /** * Returns the template object associated with this codec. * * @return Returns the template object. */ public Object getTemplate() { return template; } /** * Returns a new instance of the template object for representing the given * node. * * @param node XML node that the object is going to represent. * @return Returns a new template instance. */ protected Object cloneTemplate(Node node) { Object obj = null; try { obj = template.getClass().newInstance(); // Special case: Check if the collection // should be a map. This is if the first // child has an "as"-attribute. This // assumes that all childs will have // as attributes in this case. This is // required because in JavaScript, the // map and array object are the same. if (obj instanceof Collection) { node = node.getFirstChild(); if (node != null && node.hasAttributes() && node.getAttributes().getNamedItem("as") != null) { obj = new Hashtable(); } } } catch (InstantiationException e) { // ignore e.printStackTrace(); } catch (IllegalAccessException e) { // ignore e.printStackTrace(); } return obj; } /** * Returns true if the given attribute is to be ignored by the codec. This * implementation returns true if the given fieldname is in * {@link #exclude}. * * @param obj Object instance that contains the field. * @param attr Fieldname of the field. * @param value Value of the field. * @param isWrite Boolean indicating if the field is being encoded or * decoded. isWrite is true if the field is being encoded, else it is * being decoded. * @return Returns true if the given attribute should be ignored. */ public boolean isExcluded(Object obj, String attr, Object value, boolean isWrite) { return exclude.contains(attr); } /** * Returns true if the given fieldname is to be treated as a textual * reference (ID). This implementation returns true if the given fieldname * is in {@link #idrefs}. * * @param obj Object instance that contains the field. * @param attr Fieldname of the field. * @param value Value of the field. * @param isWrite Boolean indicating if the field is being encoded or * decoded. isWrite is true if the field is being encoded, else it is being * decoded. * @return Returns true if the given attribute should be handled as a * reference. */ public boolean isReference(Object obj, String attr, Object value, boolean isWrite) { return idrefs.contains(attr); } /** * Encodes the specified object and returns a node representing then given * object. Calls beforeEncode after creating the node and afterEncode * with the resulting node after processing. * * Enc is a reference to the calling encoder. It is used to encode complex * objects and create references. * * This implementation encodes all variables of an object according to the * following rules: * * <ul> * <li>If the variable name is in {@link #exclude} then it is ignored.</li> * <li>If the variable name is in {@link #idrefs} then * {@link mxCodec#getId(Object)} is used to replace the object with its ID. * </li> * <li>The variable name is mapped using {@link #mapping}.</li> * <li>If obj is an array and the variable name is numeric (ie. an index) then it * is not encoded.</li> * <li>If the value is an object, then the codec is used to create a child * node with the variable name encoded into the "as" attribute.</li> * <li>Else, if {@link com.mxgraph.io.mxCodec#isEncodeDefaults()} is true or * the value differs from the template value, then ... * <ul> * <li>... if obj is not an array, then the value is mapped to an * attribute.</li> * <li>... else if obj is an array, the value is mapped to an add child * with a value attribute or a text child node, if the value is a function. * </li> * </ul> * </li> * </ul> * * If no ID exists for a variable in {@link #idrefs} or if an object cannot be * encoded, a warning is printed to System.err. * * @param enc Codec that controls the encoding process. * @param obj Object to be encoded. * @return Returns the resulting XML node that represents the given object. */ public Node encode(mxCodec enc, Object obj) { String name = mxCodecRegistry.getName(obj.getClass()); Node node = enc.document.createElement(name); obj = beforeEncode(enc, obj, node); encodeObject(enc, obj, node); return afterEncode(enc, obj, node); } /** * Encodes the value of each member in then given obj * into the given node using {@link #encodeFields(mxCodec, Object, Node)} * and {@link #encodeElements(mxCodec, Object, Node)}. * * @param enc Codec that controls the encoding process. * @param obj Object to be encoded. * @param node XML node that contains the encoded object. */ protected void encodeObject(mxCodec enc, Object obj, Node node) { mxCodec.setAttribute(node, "id", enc.getId(obj)); encodeFields(enc, obj, node); encodeElements(enc, obj, node); } /** * Encodes the declared fields of the given object into the given node. * * @param enc Codec that controls the encoding process. * @param obj Object whose fields should be encoded. * @param node XML node that contains the encoded object. */ protected void encodeFields(mxCodec enc, Object obj, Node node) { Class type = obj.getClass(); while (type != null) { Field[] fields = type.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field f = fields[i]; if ((f.getModifiers() & Modifier.TRANSIENT) != Modifier.TRANSIENT) { String fieldname = f.getName(); Object value = getFieldValue(obj, fieldname); encodeValue(enc, obj, fieldname, value, node); } } type = type.getSuperclass(); } } /** * Encodes the child objects of arrays, maps and collections. * * @param enc Codec that controls the encoding process. * @param obj Object whose child objects should be encoded. * @param node XML node that contains the encoded object. */ protected void encodeElements(mxCodec enc, Object obj, Node node) { if (obj.getClass().isArray()) { Object[] tmp = (Object[]) obj; for (int i = 0; i < tmp.length; i++) { encodeValue(enc, obj, null, tmp[i], node); } } else if (obj instanceof Map) { Iterator it = ((Map) obj).entrySet().iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); encodeValue(enc, obj, String.valueOf(e.getKey()), e.getValue(), node); } } else if (obj instanceof Collection) { Iterator it = ((Collection) obj).iterator(); while (it.hasNext()) { Object value = it.next(); encodeValue(enc, obj, null, value, node); } } } /** * Converts the given value according to the mappings * and id-refs in this codec and uses * {@link #writeAttribute(mxCodec, Object, String, Object, Node)} * to write the attribute into the given node. * * @param enc Codec that controls the encoding process. * @param obj Object whose field is going to be encoded. * @param fieldname Name if the field to be encoded. * @param value Value of the property to be encoded. * @param node XML node that contains the encoded object. */ protected void encodeValue(mxCodec enc, Object obj, String fieldname, Object value, Node node) { if (value != null && !isExcluded(obj, fieldname, value, true)) { if (isReference(obj, fieldname, value, true)) { Object tmp = enc.getId(value); if (tmp == null) { System.err.println("mxObjectCodec.encode: No ID for " + mxCodecRegistry.getName(obj.getClass()) + "." + fieldname + "=" + value); return; // exit } value = tmp; } Object defaultValue = getFieldValue(template, fieldname); if (fieldname == null || enc.isEncodeDefaults() || defaultValue == null || !defaultValue.equals(value)) { writeAttribute(enc, obj, getAttributeName(fieldname), value, node); } } } /** * Returns true if the given object is a primitive value. * * @param value Object that should be checked. * @return Returns true if the given object is a primitive value. */ protected boolean isPrimitiveValue(Object value) { return value instanceof String || value instanceof Boolean || value instanceof Character || value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long || value instanceof Float || value instanceof Double || value.getClass().isPrimitive(); } /** * Writes the given value into node using writePrimitiveAttribute * or writeComplexAttribute depending on the type of the value. */ protected void writeAttribute(mxCodec enc, Object obj, String attr, Object value, Node node) { value = convertValueToXml(value); if (isPrimitiveValue(value)) { writePrimitiveAttribute(enc, obj, attr, value, node); } else { writeComplexAttribute(enc, obj, attr, value, node); } } /** * Writes the given value as an attribute of the given node. */ protected void writePrimitiveAttribute(mxCodec enc, Object obj, String attr, Object value, Node node) { if (attr == null || obj instanceof Map) { Node child = enc.document.createElement("add"); if (attr != null) { mxCodec.setAttribute(child, "as", attr); } mxCodec.setAttribute(child, "value", value); node.appendChild(child); } else { mxCodec.setAttribute(node, attr, value); } } /** * Writes the given value as a child node of the given node. */ protected void writeComplexAttribute(mxCodec enc, Object obj, String attr, Object value, Node node) { Node child = enc.encode(value); if (child != null) { if (attr != null) { mxCodec.setAttribute(child, "as", attr); } node.appendChild(child); } else { System.err.println("mxObjectCodec.encode: No node for " + mxCodecRegistry.getName(obj.getClass()) + "." + attr + ": " + value); } } /** * Converts true to "1" and false to "0". All other values are ignored. */ protected Object convertValueToXml(Object value) { if (value instanceof Boolean) { value = ((Boolean) value).booleanValue() ? "1" : "0"; } return value; } /** * Converts XML attribute values to object of the given type. */ protected Object convertValueFromXml(Class type, Object value) { if (value instanceof String && type.isPrimitive()) { String tmp = (String) value; if (type.equals(boolean.class)) { if (tmp.equals("1") || tmp.equals("0")) { tmp = (tmp.equals("1")) ? "true" : "false"; } value = new Boolean(tmp); } else if (type.equals(char.class)) { value = new Character(tmp.charAt(0)); } else if (type.equals(byte.class)) { value = new Byte(tmp); } else if (type.equals(short.class)) { value = new Short(tmp); } else if (type.equals(int.class)) { value = new Integer(tmp); } else if (type.equals(long.class)) { value = new Long(tmp); } else if (type.equals(float.class)) { value = new Float(tmp); } else if (type.equals(double.class)) { value = new Double(tmp); } } return value; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -