1098 lines
30 KiB
JavaScript
1098 lines
30 KiB
JavaScript
/**
|
|
* 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);
|
|
|
|
if (isNaN(value) || !isFinite(value))
|
|
{
|
|
value = 0;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Function: isNumericAttribute
|
|
*
|
|
* Returns true if the given XML attribute is or should be 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)
|
|
{
|
|
// Handles known numeric attributes for generic objects
|
|
var result = (obj.constructor == mxGeometry &&
|
|
(attr.name == 'x' || attr.name == 'y' ||
|
|
attr.name == 'width' || attr.name == 'height')) ||
|
|
(obj.constructor == mxPoint &&
|
|
(attr.name == 'x' || attr.name == 'y')) ||
|
|
mxUtils.isNumeric(attr.value);
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
try
|
|
{
|
|
this.addObjectValue(obj, fieldname, value, template);
|
|
}
|
|
catch (e)
|
|
{
|
|
throw new Error(e.message + ' for ' + child.nodeName);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
};
|