UNPKG

@maxgraph/core

Version:

maxGraph is a fully client side JavaScript diagramming library that uses SVG and HTML for rendering.

553 lines (552 loc) 23 kB
import type Codec from './Codec.js'; /** * 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. * * ```javascript * const obj = new Object(); * obj.foo = "Foo"; * obj.bar = "Bar"; * ``` * * This object is encoded into an XML node using the following. * * ```javascript * const enc = new Codec(); * const node = enc.encode(obj); * ``` * * The output of the encoding may be viewed using {@link GlobalConfig.logger} as follows. * * ```javascript * GlobalConfig.logger.show(); * GlobalConfig.logger.debug(mxUtils.getPrettyXml(node)); * ``` * * Finally, the result of the encoding looks as follows. * * ```javascript * <Object foo="Foo" bar="Bar"/> * ``` * * 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 * node name. * * ### 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 field name. This special attribute is * called "as" and hence, as is a reserved word that should not be used for a * field name. * * Consider the following example where foo is an object and bar is an atomic * property of foo. * * ```javascript * const obj = {foo: {bar: "Bar"}}; * ``` * * This will be mapped to the following XML structure by ObjectCodec. * * ```javascript * <Object> * <Object bar="Bar" as="foo"/> * </Object> * ``` * * In the above output, the inner Object node contains the as-attribute that * specifies the field name 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 field name, 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. * * ```javascript * const obj = ["Bar", {bar: "Bar"}]; * obj["bar"] = "Bar"; * obj["foo"] = {bar: "Bar"}; * ``` * * This array is represented by the following XML nodes. * * ```javascript * <Array bar="Bar"> * <add value="Bar"/> * <Object bar="Bar"/> * <Object bar="Bar" as="foo"/> * </Array> * ``` * * 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 look up the object in a table within {@link Codec}. The * {@link 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 field name is in {@link idrefs}, an array of strings that is used to configure * the {@link ObjectCodec}. * * 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 {@link isExcluded}, whose default implementation uses the array of excluded * field names passed to the ObjectCodec 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 {@link Codec.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, {@link Codec.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 `MyConstant.PROP`. * * ```javascript * <Object> * <add as="foo">MyConstant.PROP</add> * </Object> * ``` * * The resulting object has a field called foo with the value "myValue" (assuming that `MyConstant.PROP=myValue`). * Its XML representation looks as follows. * * ```javascript * <Object foo="left"/> * ``` * * 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 {@link allowEval} is true. * * @category Serialization with Codecs */ declare class ObjectCodec { private name?; constructor(template: any, exclude?: string[], idrefs?: string[], mapping?: { [key: string]: string; }); /** * Static global switch that specifies if expressions in arrays are allowed. * * **WARNING**: Enabling this switch carries a possible security risk. * * @default false */ static allowEval: boolean; /** * Holds the template object associated with this codec. */ template: any; /** * Array containing the variable names that should be ignored by the codec. */ exclude: string[]; /** * Array containing the variable names that should be * turned into or converted from references. See * {@link Codec.getId} and {@link Codec.getObject}. */ idrefs: string[]; /** * Maps from field names to XML attribute names. */ mapping: { [key: string]: string; }; /** * Maps from XML attribute names to fieldnames. */ reverse: { [key: string]: string; }; /** * Returns the name used for the node names 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 from what this returns. * * The default implementation returns the classname of the template class if no name is set. */ getName(): string; setName(name: string): void; /** * Returns a new instance of the template for this codec. */ cloneTemplate(): any; /** * Returns the field name for the given attribute name. * Looks up the value in the {@link reverse} mapping or returns * the input if there is no reverse mapping for the * given name. */ getFieldName(attributename: string): string; /** * Returns the attribute name for the given field name. * Looks up the value in the {@link mapping} or returns * the input if there is no mapping for the * given name. */ getAttributeName(fieldname: string | null): string | null; /** * Returns true if the given attribute is to be ignored by the codec. This * implementation returns true if the given field name is in {@link exclude} or * if the field name equals {@link ObjectIdentity.FIELD_NAME}. * * @param obj Object instance that contains the field. * @param attr Fieldname of the field. * @param value Value of the field. * @param 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. */ isExcluded(obj: any, attr: string, value: any, write?: boolean): boolean; /** * Returns true if the given field name is to be treated * as a textual reference (ID). This implementation returns * true if the given field name is in {@link idrefs}. * * @param obj Object instance that contains the field. * @param attr Field name of the field. * @param value Value of the field. * @param 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. */ isReference(obj: any, attr: string | null, value: any, write?: boolean): boolean; /** * Encodes the specified object and returns a node * representing then given object. Calls {@link beforeEncode} * after creating the node and {@link 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 {@link exclude} then it is ignored. * - If the variable name is in {@link idrefs} then {@link Codec.getId} * is used to replace the object with its ID. * - The variable name is mapped using {@link 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 {@link 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 {@link idrefs} or if an object * cannot be encoded, a warning is issued using {@link GlobalConfig.logger}. * * Returns the resulting XML node that represents the given * object. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object to be encoded. */ encode(enc: Codec, obj: any): Element | null; /** * Encodes the value of each member in then given obj into the given node using * {@link encodeValue}. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object to be encoded. * @param node XML node that contains the encoded object. */ encodeObject(enc: Codec, obj: any, node: Element): void; /** * Converts the given value according to the mappings * and id-refs in this codec and uses {@link writeAttribute} * to write the attribute into the given node. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object whose property is going to be encoded. * @param name XML node that contains the encoded object. * @param value Value of the property to be encoded. * @param node XML node that contains the encoded object. */ encodeValue(enc: Codec, obj: any, name: string | null, value: any, node: Element): void; /** * Writes the given value into node using {@link writePrimitiveAttribute} * or {@link writeComplexAttribute} depending on the type of the value. */ writeAttribute(enc: Codec, obj: any, name: string | null, value: any, node: Element): void; /** * Writes the given value as an attribute of the given node. */ writePrimitiveAttribute(enc: Codec, obj: any, name: string | null, value: any, node: Element): void; /** * Writes the given value as a child node of the given node. */ writeComplexAttribute(enc: Codec, obj: any, name: string | null, value: any, node: Element): void; /** * Converts true to "1" and false to "0" is {@link isBooleanAttribute} returns true. * All other values are not converted. * * @param enc {@link Codec} that controls the encoding process. * @param obj Objec to convert the attribute for. * @param name Name of the attribute to be converted. * @param value Value to be converted. */ convertAttributeToXml(enc: Codec, obj: any, name: string | null, value: any, node: Element): any; /** * Returns true if the given object attribute is a boolean value. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object to convert the attribute for. * @param name Name of the attribute to be converted. * @param value Value of the attribute to be converted. */ isBooleanAttribute(enc: Codec, obj: any, name: string | null, value: any): boolean; /** * Converts booleans and numeric values to the respective types. Values are * numeric if {@link isNumericAttribute} returns true. * * @param dec {@link Codec} that controls the decoding process. * @param attr XML attribute to be converted. * @param obj Objec to convert the attribute for. */ convertAttributeFromXml(dec: Codec, attr: any, obj: any): any; /** * Returns true if the given XML attribute is or should be a numeric value. * * @param dec {@link Codec} that controls the decoding process. * @param attr XML attribute to be converted. * @param obj Object to convert the attribute for. */ isNumericAttribute(dec: Codec, attr: any, obj: any): boolean; /** * Hook for subclassers to pre-process the object before * encoding. This returns the input object. The return * value of this function is used in {@link encode} to perform * the default encoding into the given node. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object to be encoded. * @param node XML node to encode the object into. */ beforeEncode(enc: Codec, obj: any, node?: Element): any; /** * 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 {@link encode}. * * @param enc {@link Codec} that controls the encoding process. * @param obj Object to be encoded. * @param node XML node that represents the default encoding. */ afterEncode(enc: Codec, obj: any, node: Element): Element; /** * 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 {@link template} and cached in {@link Codec.objects}. * * This implementation decodes all attributes and childs of a node * according to the following rules: * * - If the variable name is in {@link exclude} or if the attribute name is "id" * or "as" then it is ignored. * - If the variable name is in {@link idrefs} then {@link Codec.getObject} is used * to replace the reference with an object. * - The variable name is mapped using a reverse {@link 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 {@link 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: * * ```javascript * <Object> * <add as="hello"><![CDATA[ * function(arg1) { * mxUtils.alert('Hello '+arg1); * } * ]]></add> * </Object> * ``` * * If no object exists for an ID in {@link idrefs} a warning is issued * using {@link GlobalConfig.logger}. * * Returns the resulting object that represents the given XML node * or the object given to the method as the into parameter. * * @param dec {@link Codec} that controls the decoding process. * @param node XML node to be decoded. * @param into Optional object to encode the node into. */ decode(dec: Codec, node: Element, into?: any): any; /** * Calls {@link decodeAttributes} and {@link decodeChildren} for the given node. * * @param dec {@link Codec} that controls the decoding process. * @param node XML node to be decoded. * @param obj Object to encode the node into. */ decodeNode(dec: Codec, node: Element | null, obj: any): void; /** * Decodes all attributes of the given node using {@link decodeAttribute}. * * @param dec {@link Codec} that controls the decoding process. * @param node XML node to be decoded. * @param obj Object to encode the node into. */ decodeAttributes(dec: Codec, node: Element, obj: any): void; /** * Returns true if the given attribute should be ignored. This implementation * returns true if the attribute name is "as" or "id". * * @param dec {@link Codec} that controls the decoding process. * @param attr XML attribute to be decoded. * @param obj Objec to encode the attribute into. */ isIgnoredAttribute(dec: Codec, attr: any, obj?: any): boolean; /** * Reads the given attribute into the specified object. * * @param dec {@link Codec} that controls the decoding process. * @param attr XML attribute to be decoded. * @param obj Objec to encode the attribute into. */ decodeAttribute(dec: Codec, attr: any, obj?: any): void; /** * Decodes all children of the given node using {@link decodeChild}. * * @param dec {@link Codec} that controls the decoding process. * @param node XML node to be decoded. * @param obj Objec to encode the node into. */ decodeChildren(dec: Codec, node: Element, obj?: any): void; /** * Reads the specified child into the given object. * * @param dec {@link Codec} that controls the decoding process. * @param child XML child element to be decoded. * @param obj Objec to encode the node into. */ decodeChild(dec: Codec, child: Element, obj: any): void; /** * 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. */ getFieldTemplate(obj: any, fieldname: string, child: Element): any; /** * 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 field name as a * key. If the field name 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. */ addObjectValue(obj: any, fieldname: string, value: any, template: any): void; /** * 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. * * @param dec {@link Codec} that controls the encoding/decoding process. * @param node XML node to be checked. * @param into Optional object to pass-thru to the codec. */ processInclude(dec: Codec, node: Element, into?: any): boolean; /** * Hook for subclassers to pre-process the node for * the specified object and return the node to be * used for further processing by {@link 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 {@link decode} to perform * the default decoding into the given object. * * @param dec {@link Codec} that controls the decoding process. * @param node XML node to be decoded. * @param obj Object to encode the node into. */ beforeDecode(dec: Codec, node: Element, obj: any): Element | null; /** * 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 {@link decode}. * * @param dec {@link Codec} that controls the encoding process. * @param node XML node to be decoded. * @param obj Object that represents the default decoding. */ afterDecode(dec: Codec, node: Element | null, obj?: any): any; } export default ObjectCodec;