| /** |
| * Copyright (c) 2006-2015, JGraph Ltd |
| * Copyright (c) 2006-2015, Gaudenz Alder |
| */ |
| /** |
| * Class: mxObjectCodec |
| * |
| * Generic codec for JavaScript objects that implements a mapping between |
| * JavaScript objects and XML nodes that maps each field or element to an |
| * attribute or child node, and vice versa. |
| * |
| * Atomic Values: |
| * |
| * Consider the following example. |
| * |
| * (code) |
| * var obj = new Object(); |
| * obj.foo = "Foo"; |
| * obj.bar = "Bar"; |
| * (end) |
| * |
| * This object is encoded into an XML node using the following. |
| * |
| * (code) |
| * var enc = new mxCodec(); |
| * var node = enc.encode(obj); |
| * (end) |
| * |
| * The output of the encoding may be viewed using <mxLog> as follows. |
| * |
| * (code) |
| * mxLog.show(); |
| * mxLog.debug(mxUtils.getPrettyXml(node)); |
| * (end) |
| * |
| * Finally, the result of the encoding looks as follows. |
| * |
| * (code) |
| * <Object foo="Foo" bar="Bar"/> |
| * (end) |
| * |
| * In the above output, the foo and bar fields have been mapped to attributes |
| * with the same names, and the name of the constructor was used for the |
| * nodename. |
| * |
| * Booleans: |
| * |
| * Since booleans are numbers in JavaScript, all boolean values are encoded |
| * into 1 for true and 0 for false. The decoder also accepts the string true |
| * and false for boolean values. |
| * |
| * Objects: |
| * |
| * The above scheme is applied to all atomic fields, that is, to all non-object |
| * fields of an object. For object fields, a child node is created with a |
| * special attribute that contains the fieldname. This special attribute is |
| * called "as" and hence, as is a reserved word that should not be used for a |
| * fieldname. |
| * |
| * Consider the following example where foo is an object and bar is an atomic |
| * property of foo. |
| * |
| * (code) |
| * var obj = {foo: {bar: "Bar"}}; |
| * (end) |
| * |
| * This will be mapped to the following XML structure by mxObjectCodec. |
| * |
| * (code) |
| * <Object> |
| * <Object bar="Bar" as="foo"/> |
| * </Object> |
| * (end) |
| * |
| * In the above output, the inner Object node contains the as-attribute that |
| * specifies the fieldname in the enclosing object. That is, the field foo was |
| * mapped to a child node with an as-attribute that has the value foo. |
| * |
| * Arrays: |
| * |
| * Arrays are special objects that are either associative, in which case each |
| * key, value pair is treated like a field where the key is the fieldname, or |
| * they are a sequence of atomic values and objects, which is mapped to a |
| * sequence of child nodes. For object elements, the above scheme is applied |
| * without the use of the special as-attribute for creating each child. For |
| * atomic elements, a special add-node is created with the value stored in the |
| * value-attribute. |
| * |
| * For example, the following array contains one atomic value and one object |
| * with a field called bar. Furthermore it contains two associative entries |
| * called bar with an atomic value, and foo with an object value. |
| * |
| * (code) |
| * var obj = ["Bar", {bar: "Bar"}]; |
| * obj["bar"] = "Bar"; |
| * obj["foo"] = {bar: "Bar"}; |
| * (end) |
| * |
| * This array is represented by the following XML nodes. |
| * |
| * (code) |
| * <Array bar="Bar"> |
| * <add value="Bar"/> |
| * <Object bar="Bar"/> |
| * <Object bar="Bar" as="foo"/> |
| * </Array> |
| * (end) |
| * |
| * The Array node name is the name of the constructor. The additional |
| * as-attribute in the last child contains the key of the associative entry, |
| * whereas the second last child is part of the array sequence and does not |
| * have an as-attribute. |
| * |
| * References: |
| * |
| * Objects may be represented as child nodes or attributes with ID values, |
| * which are used to lookup the object in a table within <mxCodec>. The |
| * <isReference> function is in charge of deciding if a specific field should |
| * be encoded as a reference or not. Its default implementation returns true if |
| * the fieldname is in <idrefs>, an array of strings that is used to configure |
| * the <mxObjectCodec>. |
| * |
| * Using this approach, the mapping does not guarantee that the referenced |
| * object itself exists in the document. The fields that are encoded as |
| * references must be carefully chosen to make sure all referenced objects |
| * exist in the document, or may be resolved by some other means if necessary. |
| * |
| * For example, in the case of the graph model all cells are stored in a tree |
| * whose root is referenced by the model's root field. A tree is a structure |
| * that is well suited for an XML representation, however, the additional edges |
| * in the graph model have a reference to a source and target cell, which are |
| * also contained in the tree. To handle this case, the source and target cell |
| * of an edge are treated as references, whereas the children are treated as |
| * objects. Since all cells are contained in the tree and no edge references a |
| * source or target outside the tree, this setup makes sure all referenced |
| * objects are contained in the document. |
| * |
| * In the case of a tree structure we must further avoid infinite recursion by |
| * ignoring the parent reference of each child. This is done by returning true |
| * in <isExcluded>, whose default implementation uses the array of excluded |
| * fieldnames passed to the mxObjectCodec constructor. |
| * |
| * References are only used for cells in mxGraph. For defining other |
| * referencable object types, the codec must be able to work out the ID of an |
| * object. This is done by implementing <mxCodec.reference>. For decoding a |
| * reference, the XML node with the respective id-attribute is fetched from the |
| * document, decoded, and stored in a lookup table for later reference. For |
| * looking up external objects, <mxCodec.lookup> may be implemented. |
| * |
| * Expressions: |
| * |
| * For decoding JavaScript expressions, the add-node may be used with a text |
| * content that contains the JavaScript expression. For example, the following |
| * creates a field called foo in the enclosing object and assigns it the value |
| * of <mxConstants.ALIGN_LEFT>. |
| * |
| * (code) |
| * <Object> |
| * <add as="foo">mxConstants.ALIGN_LEFT</add> |
| * </Object> |
| * (end) |
| * |
| * The resulting object has a field called foo with the value "left". Its XML |
| * representation looks as follows. |
| * |
| * (code) |
| * <Object foo="left"/> |
| * (end) |
| * |
| * This means the expression is evaluated at decoding time and the result of |
| * the evaluation is stored in the respective field. Valid expressions are all |
| * JavaScript expressions, including function definitions, which are mapped to |
| * functions on the resulting object. |
| * |
| * Expressions are only evaluated if <allowEval> is true. |
| * |
| * Constructor: mxObjectCodec |
| * |
| * 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: |
| * |
| * (code) |
| * var mapping = new Object(); |
| * mapping['variableName'] = 'attribute-name'; |
| * (end) |
| * |
| * Parameters: |
| * |
| * template - Prototypical instance of the object to be |
| * encoded/decoded. |
| * exclude - Optional array of fieldnames to be ignored. |
| * idrefs - Optional array of fieldnames to be converted to/from |
| * references. |
| * mapping - Optional mapping from field- to attributenames. |
| */ |
| function mxObjectCodec(template, exclude, idrefs, mapping) |
| { |
| this.template = template; |
| |
| this.exclude = (exclude != null) ? exclude : []; |
| this.idrefs = (idrefs != null) ? idrefs : []; |
| this.mapping = (mapping != null) ? mapping : []; |
| |
| this.reverse = new Object(); |
| |
| for (var i in this.mapping) |
| { |
| this.reverse[this.mapping[i]] = i; |
| } |
| }; |
| |
| /** |
| * Variable: allowEval |
| * |
| * Static global switch that specifies if expressions in arrays are allowed. |
| * Default is false. NOTE: Enabling this carries a possible security risk. |
| */ |
| mxObjectCodec.allowEval = false; |
| |
| /** |
| * Variable: template |
| * |
| * Holds the template object associated with this codec. |
| */ |
| mxObjectCodec.prototype.template = null; |
| |
| /** |
| * Variable: exclude |
| * |
| * Array containing the variable names that should be |
| * ignored by the codec. |
| */ |
| mxObjectCodec.prototype.exclude = null; |
| |
| /** |
| * Variable: idrefs |
| * |
| * Array containing the variable names that should be |
| * turned into or converted from references. See |
| * <mxCodec.getId> and <mxCodec.getObject>. |
| */ |
| mxObjectCodec.prototype.idrefs = null; |
| |
| /** |
| * Variable: mapping |
| * |
| * Maps from from fieldnames to XML attribute names. |
| */ |
| mxObjectCodec.prototype.mapping = null; |
| |
| /** |
| * Variable: reverse |
| * |
| * Maps from from XML attribute names to fieldnames. |
| */ |
| mxObjectCodec.prototype.reverse = null; |
| |
| /** |
| * Function: getName |
| * |
| * Returns the name used for the nodenames and lookup of the codec when |
| * classes are encoded and nodes are decoded. For classes to work with |
| * this the codec registry automatically adds an alias for the classname |
| * if that is different than what this returns. The default implementation |
| * returns the classname of the template class. |
| */ |
| mxObjectCodec.prototype.getName = function() |
| { |
| return mxUtils.getFunctionName(this.template.constructor); |
| }; |
| |
| /** |
| * Function: cloneTemplate |
| * |
| * Returns a new instance of the template for this codec. |
| */ |
| mxObjectCodec.prototype.cloneTemplate = function() |
| { |
| return new this.template.constructor(); |
| }; |
| |
| /** |
| * Function: getFieldName |
| * |
| * Returns the fieldname for the given attributename. |
| * Looks up the value in the <reverse> mapping or returns |
| * the input if there is no reverse mapping for the |
| * given name. |
| */ |
| mxObjectCodec.prototype.getFieldName = function(attributename) |
| { |
| if (attributename != null) |
| { |
| var mapped = this.reverse[attributename]; |
| |
| if (mapped != null) |
| { |
| attributename = mapped; |
| } |
| } |
| |
| return attributename; |
| }; |
| |
| /** |
| * Function: getAttributeName |
| * |
| * Returns the attributename for the given fieldname. |
| * Looks up the value in the <mapping> or returns |
| * the input if there is no mapping for the |
| * given name. |
| */ |
| mxObjectCodec.prototype.getAttributeName = function(fieldname) |
| { |
| if (fieldname != null) |
| { |
| var mapped = this.mapping[fieldname]; |
| |
| if (mapped != null) |
| { |
| fieldname = mapped; |
| } |
| } |
| |
| return fieldname; |
| }; |
| |
| /** |
| * Function: isExcluded |
| * |
| * Returns true if the given attribute is to be ignored by the codec. This |
| * implementation returns true if the given fieldname is in <exclude> or |
| * if the fieldname equals <mxObjectIdentity.FIELD_NAME>. |
| * |
| * Parameters: |
| * |
| * obj - Object instance that contains the field. |
| * attr - Fieldname of the field. |
| * value - Value of the field. |
| * write - Boolean indicating if the field is being encoded or decoded. |
| * Write is true if the field is being encoded, else it is being decoded. |
| */ |
| mxObjectCodec.prototype.isExcluded = function(obj, attr, value, write) |
| { |
| return attr == mxObjectIdentity.FIELD_NAME || |
| mxUtils.indexOf(this.exclude, attr) >= 0; |
| }; |
| |
| /** |
| * Function: isReference |
| * |
| * 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 <idrefs>. |
| * |
| * Parameters: |
| * |
| * obj - Object instance that contains the field. |
| * attr - Fieldname of the field. |
| * value - Value of the field. |
| * write - Boolean indicating if the field is being encoded or decoded. |
| * Write is true if the field is being encoded, else it is being decoded. |
| */ |
| mxObjectCodec.prototype.isReference = function(obj, attr, value, write) |
| { |
| return mxUtils.indexOf(this.idrefs, attr) >= 0; |
| }; |
| |
| /** |
| * Function: encode |
| * |
| * 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: |
| * |
| * - If the variable name is in <exclude> then it is ignored. |
| * - If the variable name is in <idrefs> then <mxCodec.getId> |
| * is used to replace the object with its ID. |
| * - The variable name is mapped using <mapping>. |
| * - If obj is an array and the variable name is numeric |
| * (ie. an index) then it is not encoded. |
| * - 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. |
| * - Else, if <encodeDefaults> is true or the value differs |
| * from the template value, then ... |
| * - ... if obj is not an array, then the value is mapped to |
| * an attribute. |
| * - ... 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. |
| * |
| * If no ID exists for a variable in <idrefs> or if an object |
| * cannot be encoded, a warning is issued using <mxLog.warn>. |
| * |
| * Returns the resulting XML node that represents the given |
| * object. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Object to be encoded. |
| */ |
| mxObjectCodec.prototype.encode = function(enc, obj) |
| { |
| var node = enc.document.createElement(this.getName()); |
| |
| obj = this.beforeEncode(enc, obj, node); |
| this.encodeObject(enc, obj, node); |
| |
| return this.afterEncode(enc, obj, node); |
| }; |
| |
| /** |
| * Function: encodeObject |
| * |
| * Encodes the value of each member in then given obj into the given node using |
| * <encodeValue>. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Object to be encoded. |
| * node - XML node that contains the encoded object. |
| */ |
| mxObjectCodec.prototype.encodeObject = function(enc, obj, node) |
| { |
| enc.setAttribute(node, 'id', enc.getId(obj)); |
| |
| for (var i in obj) |
| { |
| var name = i; |
| var value = obj[name]; |
| |
| if (value != null && !this.isExcluded(obj, name, value, true)) |
| { |
| if (mxUtils.isInteger(name)) |
| { |
| name = null; |
| } |
| |
| this.encodeValue(enc, obj, name, value, node); |
| } |
| } |
| }; |
| |
| /** |
| * Function: encodeValue |
| * |
| * Converts the given value according to the mappings |
| * and id-refs in this codec and uses <writeAttribute> |
| * to write the attribute into the given node. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Object whose property is going to be encoded. |
| * name - XML node that contains the encoded object. |
| * value - Value of the property to be encoded. |
| * node - XML node that contains the encoded object. |
| */ |
| mxObjectCodec.prototype.encodeValue = function(enc, obj, name, value, node) |
| { |
| if (value != null) |
| { |
| if (this.isReference(obj, name, value, true)) |
| { |
| var tmp = enc.getId(value); |
| |
| if (tmp == null) |
| { |
| mxLog.warn('mxObjectCodec.encode: No ID for ' + |
| this.getName() + '.' + name + '=' + value); |
| return; // exit |
| } |
| |
| value = tmp; |
| } |
| |
| var defaultValue = this.template[name]; |
| |
| // Checks if the value is a default value and |
| // the name is correct |
| if (name == null || enc.encodeDefaults || defaultValue != value) |
| { |
| name = this.getAttributeName(name); |
| this.writeAttribute(enc, obj, name, value, node); |
| } |
| } |
| }; |
| |
| /** |
| * Function: writeAttribute |
| * |
| * Writes the given value into node using <writePrimitiveAttribute> |
| * or <writeComplexAttribute> depending on the type of the value. |
| */ |
| mxObjectCodec.prototype.writeAttribute = function(enc, obj, name, value, node) |
| { |
| if (typeof(value) != 'object' /* primitive type */) |
| { |
| this.writePrimitiveAttribute(enc, obj, name, value, node); |
| } |
| else /* complex type */ |
| { |
| this.writeComplexAttribute(enc, obj, name, value, node); |
| } |
| }; |
| |
| /** |
| * Function: writePrimitiveAttribute |
| * |
| * Writes the given value as an attribute of the given node. |
| */ |
| mxObjectCodec.prototype.writePrimitiveAttribute = function(enc, obj, name, value, node) |
| { |
| value = this.convertAttributeToXml(enc, obj, name, value, node); |
| |
| if (name == null) |
| { |
| var child = enc.document.createElement('add'); |
| |
| if (typeof(value) == 'function') |
| { |
| child.appendChild(enc.document.createTextNode(value)); |
| } |
| else |
| { |
| enc.setAttribute(child, 'value', value); |
| } |
| |
| node.appendChild(child); |
| } |
| else if (typeof(value) != 'function') |
| { |
| enc.setAttribute(node, name, value); |
| } |
| }; |
| |
| /** |
| * Function: writeComplexAttribute |
| * |
| * Writes the given value as a child node of the given node. |
| */ |
| mxObjectCodec.prototype.writeComplexAttribute = function(enc, obj, name, value, node) |
| { |
| var child = enc.encode(value); |
| |
| if (child != null) |
| { |
| if (name != null) |
| { |
| child.setAttribute('as', name); |
| } |
| |
| node.appendChild(child); |
| } |
| else |
| { |
| mxLog.warn('mxObjectCodec.encode: No node for ' + this.getName() + '.' + name + ': ' + value); |
| } |
| }; |
| |
| /** |
| * Function: convertAttributeToXml |
| * |
| * Converts true to "1" and false to "0" is <isBooleanAttribute> returns true. |
| * All other values are not converted. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Objec to convert the attribute for. |
| * name - Name of the attribute to be converted. |
| * value - Value to be converted. |
| */ |
| mxObjectCodec.prototype.convertAttributeToXml = function(enc, obj, name, value) |
| { |
| // Makes sure to encode boolean values as numeric values |
| if (this.isBooleanAttribute(enc, obj, name, value)) |
| { |
| // Checks if the value is true (do not use the value as is, because |
| // this would check if the value is not null, so 0 would be true) |
| value = (value == true) ? '1' : '0'; |
| } |
| |
| return value; |
| }; |
| |
| /** |
| * Function: isBooleanAttribute |
| * |
| * Returns true if the given object attribute is a boolean value. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Objec to convert the attribute for. |
| * name - Name of the attribute to be converted. |
| * value - Value of the attribute to be converted. |
| */ |
| mxObjectCodec.prototype.isBooleanAttribute = function(enc, obj, name, value) |
| { |
| return (typeof(value.length) == 'undefined' && (value == true || value == false)); |
| }; |
| |
| /** |
| * Function: convertAttributeFromXml |
| * |
| * Converts booleans and numeric values to the respective types. Values are |
| * numeric if <isNumericAttribute> returns true. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * attr - XML attribute to be converted. |
| * obj - Objec to convert the attribute for. |
| */ |
| mxObjectCodec.prototype.convertAttributeFromXml = function(dec, attr, obj) |
| { |
| var value = attr.value; |
| |
| if (this.isNumericAttribute(dec, attr, obj)) |
| { |
| value = parseFloat(value); |
| } |
| |
| return value; |
| }; |
| |
| /** |
| * Function: isNumericAttribute |
| * |
| * Returns true if the given XML attribute is a numeric value. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * attr - XML attribute to be converted. |
| * obj - Objec to convert the attribute for. |
| */ |
| mxObjectCodec.prototype.isNumericAttribute = function(dec, attr, obj) |
| { |
| return mxUtils.isNumeric(attr.value); |
| }; |
| |
| /** |
| * Function: beforeEncode |
| * |
| * Hook for subclassers to pre-process the object before |
| * encoding. This returns the input object. The return |
| * value of this function is used in <encode> to perform |
| * the default encoding into the given node. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Object to be encoded. |
| * node - XML node to encode the object into. |
| */ |
| mxObjectCodec.prototype.beforeEncode = function(enc, obj, node) |
| { |
| return obj; |
| }; |
| |
| /** |
| * Function: afterEncode |
| * |
| * Hook for subclassers to post-process the node |
| * for the given object after encoding and return the |
| * post-processed node. This implementation returns |
| * the input node. The return value of this method |
| * is returned to the encoder from <encode>. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * obj - Object to be encoded. |
| * node - XML node that represents the default encoding. |
| */ |
| mxObjectCodec.prototype.afterEncode = function(enc, obj, node) |
| { |
| return node; |
| }; |
| |
| /** |
| * Function: decode |
| * |
| * Parses the given node into the object or returns a new object |
| * representing the given node. |
| * |
| * Dec is a reference to the calling decoder. It is used to decode |
| * complex objects and resolve references. |
| * |
| * If a node has an id attribute then the object cache is checked for the |
| * object. If the object is not yet in the cache then it is constructed |
| * using the constructor of <template> and cached in <mxCodec.objects>. |
| * |
| * This implementation decodes all attributes and childs of a node |
| * according to the following rules: |
| * |
| * - If the variable name is in <exclude> or if the attribute name is "id" |
| * or "as" then it is ignored. |
| * - If the variable name is in <idrefs> then <mxCodec.getObject> is used |
| * to replace the reference with an object. |
| * - The variable name is mapped using a reverse <mapping>. |
| * - If the value has a child node, then the codec is used to create a |
| * child object with the variable name taken from the "as" attribute. |
| * - If the object is an array and the variable name is empty then the |
| * value or child object is appended to the array. |
| * - If an add child has no value or the object is not an array then |
| * the child text content is evaluated using <mxUtils.eval>. |
| * |
| * For add nodes where the object is not an array and the variable name |
| * is defined, the default mechanism is used, allowing to override/add |
| * methods as follows: |
| * |
| * (code) |
| * <Object> |
| * <add as="hello"><![CDATA[ |
| * function(arg1) { |
| * mxUtils.alert('Hello '+arg1); |
| * } |
| * ]]></add> |
| * </Object> |
| * (end) |
| * |
| * If no object exists for an ID in <idrefs> a warning is issued |
| * using <mxLog.warn>. |
| * |
| * Returns the resulting object that represents the given XML node |
| * or the object given to the method as the into parameter. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * node - XML node to be decoded. |
| * into - Optional objec to encode the node into. |
| */ |
| mxObjectCodec.prototype.decode = function(dec, node, into) |
| { |
| var id = node.getAttribute('id'); |
| var obj = dec.objects[id]; |
| |
| if (obj == null) |
| { |
| obj = into || this.cloneTemplate(); |
| |
| if (id != null) |
| { |
| dec.putObject(id, obj); |
| } |
| } |
| |
| node = this.beforeDecode(dec, node, obj); |
| this.decodeNode(dec, node, obj); |
| |
| return this.afterDecode(dec, node, obj); |
| }; |
| |
| /** |
| * Function: decodeNode |
| * |
| * Calls <decodeAttributes> and <decodeChildren> for the given node. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * node - XML node to be decoded. |
| * obj - Objec to encode the node into. |
| */ |
| mxObjectCodec.prototype.decodeNode = function(dec, node, obj) |
| { |
| if (node != null) |
| { |
| this.decodeAttributes(dec, node, obj); |
| this.decodeChildren(dec, node, obj); |
| } |
| }; |
| |
| /** |
| * Function: decodeAttributes |
| * |
| * Decodes all attributes of the given node using <decodeAttribute>. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * node - XML node to be decoded. |
| * obj - Objec to encode the node into. |
| */ |
| mxObjectCodec.prototype.decodeAttributes = function(dec, node, obj) |
| { |
| var attrs = node.attributes; |
| |
| if (attrs != null) |
| { |
| for (var i = 0; i < attrs.length; i++) |
| { |
| this.decodeAttribute(dec, attrs[i], obj); |
| } |
| } |
| }; |
| |
| /** |
| * Function: isIgnoredAttribute |
| * |
| * Returns true if the given attribute should be ignored. This implementation |
| * returns true if the attribute name is "as" or "id". |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * attr - XML attribute to be decoded. |
| * obj - Objec to encode the attribute into. |
| */ |
| mxObjectCodec.prototype.isIgnoredAttribute = function(dec, attr, obj) |
| { |
| return attr.nodeName == 'as' || attr.nodeName == 'id'; |
| }; |
| |
| /** |
| * Function: decodeAttribute |
| * |
| * Reads the given attribute into the specified object. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * attr - XML attribute to be decoded. |
| * obj - Objec to encode the attribute into. |
| */ |
| mxObjectCodec.prototype.decodeAttribute = function(dec, attr, obj) |
| { |
| if (!this.isIgnoredAttribute(dec, attr, obj)) |
| { |
| var name = attr.nodeName; |
| |
| // Converts the string true and false to their boolean values. |
| // This may require an additional check on the obj to see if |
| // the existing field is a boolean value or uninitialized, in |
| // which case we may want to convert true and false to a string. |
| var value = this.convertAttributeFromXml(dec, attr, obj); |
| var fieldname = this.getFieldName(name); |
| |
| if (this.isReference(obj, fieldname, value, false)) |
| { |
| var tmp = dec.getObject(value); |
| |
| if (tmp == null) |
| { |
| mxLog.warn('mxObjectCodec.decode: No object for ' + |
| this.getName() + '.' + name + '=' + value); |
| return; // exit |
| } |
| |
| value = tmp; |
| } |
| |
| if (!this.isExcluded(obj, name, value, false)) |
| { |
| //mxLog.debug(mxUtils.getFunctionName(obj.constructor)+'.'+name+'='+value); |
| obj[name] = value; |
| } |
| } |
| }; |
| |
| /** |
| * Function: decodeChildren |
| * |
| * Decodes all children of the given node using <decodeChild>. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * node - XML node to be decoded. |
| * obj - Objec to encode the node into. |
| */ |
| mxObjectCodec.prototype.decodeChildren = function(dec, node, obj) |
| { |
| var child = node.firstChild; |
| |
| while (child != null) |
| { |
| var tmp = child.nextSibling; |
| |
| if (child.nodeType == mxConstants.NODETYPE_ELEMENT && |
| !this.processInclude(dec, child, obj)) |
| { |
| this.decodeChild(dec, child, obj); |
| } |
| |
| child = tmp; |
| } |
| }; |
| |
| /** |
| * Function: decodeChild |
| * |
| * Reads the specified child into the given object. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * child - XML child element to be decoded. |
| * obj - Objec to encode the node into. |
| */ |
| mxObjectCodec.prototype.decodeChild = function(dec, child, obj) |
| { |
| var fieldname = this.getFieldName(child.getAttribute('as')); |
| |
| if (fieldname == null || !this.isExcluded(obj, fieldname, child, false)) |
| { |
| var template = this.getFieldTemplate(obj, fieldname, child); |
| var value = null; |
| |
| if (child.nodeName == 'add') |
| { |
| value = child.getAttribute('value'); |
| |
| if (value == null && mxObjectCodec.allowEval) |
| { |
| value = mxUtils.eval(mxUtils.getTextContent(child)); |
| } |
| } |
| else |
| { |
| value = dec.decode(child, template); |
| } |
| |
| this.addObjectValue(obj, fieldname, value, template); |
| } |
| }; |
| |
| /** |
| * Function: getFieldTemplate |
| * |
| * Returns the template instance for the given field. This returns the |
| * value of the field, null if the value is an array or an empty collection |
| * if the value is a collection. The value is then used to populate the |
| * field for a new instance. For strongly typed languages it may be |
| * required to override this to return the correct collection instance |
| * based on the encoded child. |
| */ |
| mxObjectCodec.prototype.getFieldTemplate = function(obj, fieldname, child) |
| { |
| var template = obj[fieldname]; |
| |
| // Non-empty arrays are replaced completely |
| if (template instanceof Array && template.length > 0) |
| { |
| template = null; |
| } |
| |
| return template; |
| }; |
| |
| /** |
| * Function: addObjectValue |
| * |
| * Sets the decoded child node as a value of the given object. If the |
| * object is a map, then the value is added with the given fieldname as a |
| * key. If the fieldname is not empty, then setFieldValue is called or |
| * else, if the object is a collection, the value is added to the |
| * collection. For strongly typed languages it may be required to |
| * override this with the correct code to add an entry to an object. |
| */ |
| mxObjectCodec.prototype.addObjectValue = function(obj, fieldname, value, template) |
| { |
| if (value != null && value != template) |
| { |
| if (fieldname != null && fieldname.length > 0) |
| { |
| obj[fieldname] = value; |
| } |
| else |
| { |
| obj.push(value); |
| } |
| //mxLog.debug('Decoded '+mxUtils.getFunctionName(obj.constructor)+'.'+fieldname+': '+value); |
| } |
| }; |
| |
| /** |
| * Function: processInclude |
| * |
| * Returns true if the given node is an include directive and |
| * executes the include by decoding the XML document. Returns |
| * false if the given node is not an include directive. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the encoding/decoding process. |
| * node - XML node to be checked. |
| * into - Optional object to pass-thru to the codec. |
| */ |
| mxObjectCodec.prototype.processInclude = function(dec, node, into) |
| { |
| if (node.nodeName == 'include') |
| { |
| var name = node.getAttribute('name'); |
| |
| if (name != null) |
| { |
| try |
| { |
| var xml = mxUtils.load(name).getDocumentElement(); |
| |
| if (xml != null) |
| { |
| dec.decode(xml, into); |
| } |
| } |
| catch (e) |
| { |
| // ignore |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * Function: beforeDecode |
| * |
| * Hook for subclassers to pre-process the node for |
| * the specified object and return the node to be |
| * used for further processing by <decode>. |
| * The object is created based on the template in the |
| * calling method and is never null. This implementation |
| * returns the input node. The return value of this |
| * function is used in <decode> to perform |
| * the default decoding into the given object. |
| * |
| * Parameters: |
| * |
| * dec - <mxCodec> that controls the decoding process. |
| * node - XML node to be decoded. |
| * obj - Object to encode the node into. |
| */ |
| mxObjectCodec.prototype.beforeDecode = function(dec, node, obj) |
| { |
| return node; |
| }; |
| |
| /** |
| * Function: afterDecode |
| * |
| * Hook for subclassers to post-process the object after |
| * decoding. This implementation returns the given object |
| * without any changes. The return value of this method |
| * is returned to the decoder from <decode>. |
| * |
| * Parameters: |
| * |
| * enc - <mxCodec> that controls the encoding process. |
| * node - XML node to be decoded. |
| * obj - Object that represents the default decoding. |
| */ |
| mxObjectCodec.prototype.afterDecode = function(dec, node, obj) |
| { |
| return obj; |
| }; |