All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tailf.jnc.Element Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package com.tailf.jnc;

import org.xml.sax.InputSource;

import java.io.*;
import java.util.*;

/**
 * A configuration element sub-tree. Makes it possible to create and/or
 * manipulate an element tree. The element tree is then to be used to issue
 * NETCONF operations using the {@link NetconfSession} class.
 * 

* Example: * *

 * 
 * // start (Netconf) sessions towards our device
 * SSHConnection connection = new SSHConnection("127.0.0.1");
 * connection.authenticateWithPassword("admin", "pass");
 * SSHSession ssh = new SSHSession(connection);
 * NetconfSession session = new NetconfSession(ssh);
 * 
 * // get system configuration from session
 * Element sysConfig = session.getConfig("/system").first();
 * 
 * // manipulate the element tree
 * sysConfig.setValue("dns", "83.100.1.1");
 * sysConfig.setValue("gateway", "10.0.0.1");
 * 
 * // Write back the updated element tree
 * session.editConfig(sysConfig);
 * 
*/ public class Element implements Serializable { private static final long serialVersionUID = 1L; /** * The NETCONF namespace. "urn:ietf:params:xml:ns:netconf:base:1.0". */ public static final String NETCONF_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0"; public static final String OPERATION = "operation"; public static final String CREATE = "create"; public static final String DELETE = "delete"; public static final String REPLACE = "replace"; public static final String MERGE = "merge"; /** * The namespace this element name belongs to. */ public String namespace; /** * The name of the node. */ public String name; /** * The value of the element. */ public Object value; /** * Attributes on the node. ArrayList of Attribute. */ ArrayList attrs; /** * Prefix map are really xmlns attributes. For example: *

* xmlns="http:tail-f.com/aaa" or xmlns:aaa="http:tail-f.com/aaa" */ public PrefixMap prefixes; /** * Static prefix map that is always a default and will be resolved at root * level. For example the NETCONF prefix mapping "nc" is added here as a * default. */ public static final PrefixMap defaultPrefixes = new PrefixMap(new Prefix[] { new Prefix("nc", NETCONF_NAMESPACE), new Prefix("pl", Capabilities.NS_PARTIAL_LOCK), new Prefix("ncn", Capabilities.NS_NOTIFICATION) }); /** * Children to the node, if container element. */ protected NodeSet children = null; /** * The parent to this node. */ protected Element parent = null; /** * Constructor that creates a new element tree. An element consists of a * name that belongs to a namespace. * * @param ns Namespace * @param name Name of the element */ public Element(String ns, String name) { namespace = ns; this.name = name; } public Element getRootElement() { Element top = this; while (top.parent != null) { top = top.parent; } return top; } /** * Static method that creates a new configuration element tree given a * path. A prefix mapping for the namespace will be added to the top * element of the created sub-tree. A prefix map is used for resolving * prefix to namespace mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param namespace Namespace * @param pathStr A "path create" string * @return A new configuration element tree */ public static Element create(String namespace, String pathStr) throws JNCException { final PrefixMap prefixMap = new PrefixMap(); prefixMap.add(new Prefix("", namespace)); return Element.create(prefixMap, pathStr); } /** * Static method that creates a new configuration element tree, given a * path. A prefix mapping will be added to the top element of the created * sub-tree. A prefix map is used for resolving prefix to namespace * mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param prefix A prefix mapping. * @param pathStr A "path create" string * @return A new configuration element tree */ public static Element create(Prefix prefix, String pathStr) throws JNCException { final PrefixMap prefixMap = new PrefixMap(); prefixMap.add(prefix); return Element.create(prefixMap, pathStr); } /** * Static method that creates a new configuration element tree, given a * path. A prefix map will be added to the top element in the created * sub-tree. A prefix map is used for resolving prefix to namespace * mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param prefixMap Prefix mappings to be added * @param pathStr A "path create" string * @return A new configuration element tree */ public static Element create(PrefixMap prefixMap, String pathStr) throws JNCException { trace("create: \"" + pathStr + "\""); final PathCreate path = new PathCreate(pathStr); final Element t = path.eval(prefixMap); t.setPrefix(prefixMap); return t; } /** * Creates a new path. This is a value for mode in * {@link #createPath(int, String)}. */ public static final int CREATE_NEW = 1; /** * Creates a new path and merges with the existing nodes when possible. * This is a value for mode in {@link #createPath(int, String)}. */ public static final int CREATE_MERGE = 2; /** * Creates a new path and merges with the existing nodes when possible. * Several nodes may me matching the path, and a sub-tree will be created * for all of them. This is a value for mode in * {@link #createPath(int, String)}. */ public static final int CREATE_MERGE_MULTI = 3; /** * Creates a child element to the context node. * * @param name The name of the child element */ public Element createChild(String name) { final Element elem = new Element(namespace, name); addChild(elem); return elem; } /** * Creates a child element with specified value. * * @param name The name of the child element * @param value The value of the element */ public Element createChild(String name, Object value) { final Element elem = new Element(namespace, name); elem.setValue(value); addChild(elem); return elem; } /** * Creates a child element with specified value. * * @param namespace The namespace that the name belongs to * @param name The name of the child element * @param value The value of the element */ public Element createChild(String namespace, String name, Object value) { final Element elem = new Element(namespace, name); elem.setValue(value); addChild(elem); return elem; } /** * Returns the path for this element including an appended sub-path. * * @param subPath A sub-path to be appended to the current path */ public String getElementPath(String subPath) { return getElementPath() + "/" + subPath; } /** * Creates an element tree as a child to the context node from a create * path expression. *

* See {@link PathCreate} for more information about path create * expressions. * * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(String pathStr) throws JNCException { return createPath(CREATE_MERGE, null, pathStr); } /** * Creates an element tree as a child to the context node from a create * path expression. The mode parameter is one of: {@link #CREATE_NEW}, * {@link #CREATE_MERGE}, {@link #CREATE_MERGE_MULTI} * * @param mode The creation mode. * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(int mode, String pathStr) throws JNCException { return createPath(mode, null, pathStr); } /** * Creates an element tree as a child to the context node from a create * path expression. A prefix map containing the provided namespace will be * added to the context nodes prefix mapping. A prefix map is used for * resolving prefix to namespace mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param namespace Namespace. * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(String namespace, String pathStr) throws JNCException { final PrefixMap p = new PrefixMap(); p.add(new Prefix("", namespace)); return createPath(CREATE_MERGE, p, pathStr); } /** * Creates an element tree as a child to the context node from a create * path expression. A prefix containing the provided prefix mapping will be * added to the context nodes prefix mappings. A prefix map is used for * resolving prefix to namespace mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param prefix A prefix mapping * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(Prefix prefix, String pathStr) throws JNCException { final PrefixMap p = new PrefixMap(); p.add(prefix); return createPath(CREATE_MERGE, p, pathStr); } /** * Creates an element tree as a child to the context node from a create * path expression. A prefix map containing prefix mappings will be added * to the context nodes prefix mappings. A prefix map is used for resolving * prefix to namespace mappings for a given path. *

* See {@link PathCreate} for more information about path create * expressions. * * @param addPrefixes Prefix mappings * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(PrefixMap addPrefixes, String pathStr) throws JNCException { return createPath(CREATE_MERGE, addPrefixes, pathStr); } /** * Creates an element tree as a child to the context node. A prefix map * containing prefix mappings will be added to the context nodes prefix * mappings. A prefix map is used for resolving prefix to namespace * mappings for a given path. *

* The mode parameter is one of: {@link #CREATE_NEW}, {@link #CREATE_MERGE}, {@link #CREATE_MERGE_MULTI} *

* See {@link PathCreate} for more information about path create * expressions. * * @param mode The creation mode. * @param addPrefixes Prefix mappings * @param pathStr A "path create" string. * @return A new configuration element sub-tree that is a child of the * context node */ public Element createPath(int mode, PrefixMap addPrefixes, String pathStr) throws JNCException { trace("createPath: \"" + pathStr + "\""); final PathCreate path = new PathCreate(pathStr); if (addPrefixes != null) { setPrefix(addPrefixes); } if (mode == CREATE_MERGE || mode == CREATE_MERGE_MULTI) { /* Step down the locationsteps until we cannot go further */ NodeSet nodeSet = new NodeSet(), deepest; Element first_found = null; nodeSet.add(this); deepest = nodeSet; int step = 0; final int steps = path.steps(); while (nodeSet.size() > 0 && step < steps) { nodeSet = ((Path) path).evalStep(nodeSet, step); if (nodeSet.size() > 0) { deepest = nodeSet; if (first_found == null) { first_found = nodeSet.getElement(0); } } step++; } if (step == steps && nodeSet.size() > 0) { return first_found; /* path already exist */ } if (mode == CREATE_MERGE && deepest.size() > 1) { throw new JNCException(JNCException.PATH_CREATE_ERROR, "multiple nodes found by path: \"" + pathStr + "\""); } step--; // need to do last step again for (Element parent : deepest) { final PrefixMap prefixMap = parent.getContextPrefixMap(); for (int i = step; i < steps; i++) { final Element elem = path.evalStep(prefixMap, i, parent); parent.addChild(elem); parent = elem; if (first_found == null) { first_found = elem; } } } return first_found; } else { /* mode== CREATE_NEW */ final PrefixMap prefixMap = getContextPrefixMap(); final Element elem = path.eval(prefixMap); addChild(elem); return elem; } } /** * Sets the default prefix mapping on this node. xmlns= 'NAMESPACE' A * prefix mapping is used for resolving prefix to namespace mappings for a * given path. */ public void setDefaultPrefix() { setPrefix(new Prefix("", namespace)); } /** * Removes the default prefix mapping on this node (if any). xmlns= * 'NAMESPACE' A prefix mapping is used for resolving prefix to namespace * mappings for a given path. */ public void removeDefaultPrefix() { removePrefix(""); } /** * Sets a prefix mapping to the context node. A prefix map is used for * resolving prefix to namespace mappings for a given path. * * @param prefix String prefix to be used for the namespace. */ public void setPrefix(String prefix) { setPrefix(new Prefix(prefix, namespace)); } /** * Sets a prefix map to the context node. A prefix map is used for * resolving prefix to namespace mappings for a given path. * * @param prefix A prefix mapping */ public void setPrefix(Prefix prefix) { setPrefix(new PrefixMap(prefix)); } /** * Sets prefix mappings to the context node. A prefix map is used for * resolving prefix to namespace mappings for a given path. * * @param prefixMap Prefix mappings */ public void setPrefix(PrefixMap prefixMap) { if (prefixes == null) { prefixes = new PrefixMap(); } prefixes.set(prefixMap); } /** * Removes a prefix map from the context node. */ public void removePrefix(String prefix) { if (prefixes == null) { return; } prefixes.remove(prefix); } /* Parent and Children */ /** * Returns the parent node of this node. Or null if none. * * @return Parent configuration element node or null */ public Element getParent() { return parent; } /** * Adds child to children and makes this element the parent of child. * * @param child Child element to be added */ public void addChild(Element child) { if (children == null) { children = new NodeSet(); } children.add(child); child.parent = this; } /** * Inserts a child element and returns the position of (the first * occurrence of) the inserted child in the list of children. *

* Checks that child is not already in use. * * @param child Child element to be inserted * @throws JNCException If child is already a child of another element or * if child equals this element */ public int insertChild(Element child) throws JNCException { if (child.parent != null || child == this) { throw new JNCException(JNCException.ELEMENT_ALREADY_IN_USE, this); } addChild(child); return children.indexOf(child); } /** * Inserts a child element at a specific index in the list of children and * returns that index upon success. * * @param child Child element to be inserted * @param index Position in child list to insert child to. 0 is the first. * @throws JNCException If child is already a child of another element. */ public int insertChild(Element child, int index) throws JNCException { if (child.parent != null) { throw new JNCException(JNCException.ELEMENT_ALREADY_IN_USE, this); } if (children == null) { children = new NodeSet(); } child.parent = this; children.add(index, child); return children.indexOf(child); } /** * Inserts a child element at the correct position by providing structure * information (the names of all the children, in order). * * @param child Child element to be inserted * @param childrenNames The names of all children in order. * @throws JNCException If child is already a child of another element. */ public int insertChild(Element child, String[] childrenNames) throws JNCException { if (child.parent != null) { throw new JNCException(JNCException.ELEMENT_ALREADY_IN_USE, this); } if (children == null) { children = new NodeSet(); } child.parent = this; int pos = 0; int i = 0; while (pos < children.size()) { if (children.getElement(pos).name.equals(childrenNames[i])) { pos++; } else if (child.name.equals(childrenNames[i])) { break; } else { i++; } } children.add(pos, child); return pos; } /** * Inserts a child element first in the list of children. Always returns 0. * * @param child Child element to be inserted * @throws JNCException If child is already a child of another element. */ public int insertFirst(Element child) throws JNCException { return insertChild(child, 0); } /** * Inserts a child element last in the list of children. Returns the * position of the inserted child. * * @param child Child element to be inserted * @throws JNCException If child is already a child of another element or * if child equals this element */ public int insertLast(Element child) throws JNCException { return insertChild(child); } /** * Returns the position of this element in the the parent child list. '0' * is the first position. */ public int position() { return (parent == null) ? -1 : parent.children.indexOf(this); } /** * Deletes child node(s). All children matching the path string will be * deleted. An array of the deleted children is returned. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string for children that will be deleted * @return An array of the deleted element nodes. */ public NodeSet delete(String pathStr) throws JNCException { final NodeSet nodes = get(pathStr); if (nodes != null) { for (int i = 0; i < nodes.size(); i++) { nodes.getElement(i).delete(); } } return nodes; } /** * Deletes this node. Means that the parent will no longer have reference * to us. This node will be removed from our parents child list. This * method will not do anything if this node is the root node of a tree. */ public void delete() { if (parent != null) { parent.deleteChild(this); } } /** * Deletes a child node, provided it is present in the children list. * * @param child Child to delete */ public void deleteChild(Element child) { if (children == null) { return; } for (int i = 0; i < children.size(); i++) { if (child == children.getElement(i)) { children.remove(i); child.parent = null; break; } } } /** * Returns true if this node has any children, * false otherwise. * * @return true or false */ public boolean hasChildren() { return children != null && children.size() > 0; } /* Attibutes */ /** * Adds an attribute for this element. * */ public void addAttr(Attribute attr) { if (attrs == null) { attrs = new ArrayList(); } attrs.add(attr); } /** * Gets all attributes for this element. * * @return An array of configuration attributes or null */ public Attribute[] getAttrs() { if (attrs != null) { return attrs.toArray(new Attribute[attrs.size()]); } return null; } /** * Returns the string value of the named attribute. Returns * null if no such attribute is found or "" if no value is * given to the attribute. * * @param name The name of the attribute * @return String value of the attribute. */ public String getAttrValue(String name) { return getAttr(name).getValue(); } /** * Gets an Attribute * * @param name Lookup using the name of attribute */ public Attribute getAttr(String name) { if (attrs != null) { for (final Attribute attr : attrs) { if (attr.name.equals(name)) { return attr; } } } return null; // not found } /** * Sets an attribute on this element, treating xmlns attributes as prefix * maps. If the attribute already exists, its value is changed, otherwise * the attribute is added. * * @param name The name of the attribute * @param value The value of the attribute * @return The configuration attribute. */ public Attribute setAttr(String name, String value) { trace("setAttr: " + name + "=\"" + value + "\""); if (name.equals("xmlns")) { // it's an xmlns attribute - treat this as a prefix map final Prefix p = new Prefix("", value); setPrefix(p); return p; } else if (name.startsWith("xmlns:")) { final String prefix = name.substring(6); final Prefix p = new Prefix(prefix, value); setPrefix(p); return p; } else if (attrs == null) { attrs = new ArrayList(); } else { for (final Attribute attr : attrs) { if (attr.name.equals(name)) { attr.setValue(value); return attr; } } } final Attribute attr = new Attribute(namespace, name, value); addAttr(attr); return attr; } /** * Sets an attribute on this element, aware of its namespace. If an * attribute with this name and ns already exists, its value is changed. * Otherwise such an attribute is added. *

* If name starts with xmlns and ns starts with the xmlns namespace * (http://www.w3.org/2000/xmlns/), the value is set as a prefix map. * * @param ns The namespace that the attribute name belongs to * @param name The name of the attribute * @param value The value of the attribute * @return The configuration attribute. */ public Attribute setAttr(String ns, String name, String value) { trace("setAttr: (" + ns + ") " + name + "=\"" + value + "\""); if (name.startsWith("xmlns") && ns.startsWith(Prefix.XMLNS_NAMESPACE)) { return setAttr(name, value); } if (attrs == null) { attrs = new ArrayList(); } else { for (final Attribute attr : attrs) { if (attr.ns.equals(ns) && attr.name.equals(name)) { // Change existing attribute attr.setValue(value); return attr; } } } // Add new one final Attribute attr = new Attribute(ns, name, value); addAttr(attr); return attr; } /** * Removes an attribute with specified name. This method does not consider * namespace so note that it will remove the first attribute which matches * the name (and no other). * * @param name The name of the attribute to be removed. */ public void removeAttr(String name) { if (attrs != null) { for (int i = 0; i < attrs.size(); i++) { final Attribute attr = attrs.get(i); if (attr.name.equals(name)) { trace("removeAttr: " + name); attrs.remove(i); return; } } } } /** * Removes an attribute with specified namespace and name from this * element's attribute list. * * @param namespace the namespace the name belongs to. * @param name The name of the attribute to be removed. */ public void removeAttr(String namespace, String name) { if (attrs != null) { for (int i = 0; i < attrs.size(); i++) { final Attribute attr = attrs.get(i); if (attr.name.equals(name) && attr.ns.equals(namespace)) { trace("removeAttr: (" + namespace + ") " + name); attrs.remove(i); } } } } /* Values */ /** * Finds the value of child with specified name, if it exists. * * @param childName Name of child * @return Value of child, or null if none */ public Object getValueOfChild(String childName) { for (final Element child : children) { if (child.name.equals(childName)) { return child.getValue(); } } return null; } /** * Returns the value of this element. * * @return The value of the element. */ public Object getValue() { return value; } /** * Check if any nodes in this element matches a given path string. *

* See {@link Path} for more information about path expressions. * * @param pathStr The path to match * @return true if any node matches pathStr; * false otherwise. */ public boolean exists(String pathStr) throws JNCException { final NodeSet nodes = get(pathStr); return (nodes != null && !nodes.isEmpty()); } /** * Returns the value of a the first subnode matching a given path string, * or null if there are no matches. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find node * @return The value of the (first) found element or null */ public Object getValue(String pathStr) throws JNCException { final NodeSet nodes = get(pathStr); // Don't call exists, to avoid computing matches twice return (nodes != null && !nodes.isEmpty()) ? nodes.getElement(0) .getValue() : null; } /** * Returns the value(s) of nodes in a given path expression. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @return An array of the values of the element nodes found by the * expression (or null) */ public Object[] getValues(String pathStr) throws JNCException { final NodeSet nodes = get(pathStr); Object[] values = null; if (nodes != null) { values = new String[nodes.size()]; for (int i = 0; i < nodes.size(); i++) { values[i] = nodes.getElement(i).getValue(); } } return values; } /** * Returns the values of nodes in a given path expression. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @return A set with the values of the element nodes found by the * expression (or null) */ public Set getValuesAsSet(String pathStr) throws JNCException { final String[] valuesBefore = (String[]) getValues(pathStr); return new HashSet(Arrays.asList(valuesBefore)); } /** * Sets a new value for this node element. * * @param value Value to be set */ public void setValue(Object value) { trace("setValue: " + name + "=\"" + value + "\""); this.value = value; } /** * Sets value for all node elements matching specified path string. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @param value Value to be set */ public void setValue(String pathStr, Object value) throws JNCException { for (final Element elem : get(pathStr)) { elem.setValue(value); } } /** * Deletes value of node(s) *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes */ public void deleteValue(String pathStr) throws JNCException { for (final Element elem : get(pathStr)) { elem.deleteValue(); } } /** * Deletes the value for this node. */ public void deleteValue() { value = null; } /* Get */ /** * Returns first node that matches the path expression, or * null if no such node was found. *

* Example: * *

     *      Element full = NetconfSession:getConfig();
     * 
     *      Element first_host = full.getFirst("/hosts/host");
     *      Element last_host = full.getLast("/hosts/host");
     * 
* * See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @return The first element node found by the expression. */ public Element getFirst(String pathStr) throws JNCException { final NodeSet nodeSet = get(pathStr); if (nodeSet == null || nodeSet.isEmpty()) { return null; } return nodeSet.getElement(0); } /** * Returns the last node that matches the path expression, or * null if no such node was found. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @return The last element node found by the expression. */ public Element getLast(String pathStr) throws JNCException { final NodeSet nodeSet = get(pathStr); if (nodeSet == null || nodeSet.isEmpty()) { return null; } return nodeSet.get(nodeSet.size() - 1); } /** * Gets all nodes matching a given path expression. *

* Example: * *

     * Element full_config = session.get();
     * NodeSet calle_nodes = full_config.get("host[www='Calle']");
     * 
* * See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes * @return An array of the element nodes found by the expression. */ public NodeSet get(String pathStr) throws JNCException { final Path path = new Path(pathStr); return path.eval(this); } /** * Returns the children of this node. * * @return The children node set of this node or null */ public NodeSet getChildren() { return children; } /** * Get the children with specified name, from children list * * @param name Name of child * @return a NodeSet with all chldren that has the name */ public NodeSet getChildren(String name) { final NodeSet n = new NodeSet(); if (children != null) { for (int i = 0; i < children.size(); i++) { final Element elem = children.getElement(i); if (elem.name.equals(name)) { n.add(elem); } } } return n; } /** * Get the (first) child with specified name, from children list * * @param name Name of child * @return The found element or null */ public Element getChild(String name) { if (children != null) { for (int i = 0; i < children.size(); i++) { final Element elem = children.getElement(i); if (elem.name.equals(name)) { return elem; } } } return null; } /** * Clones the tree, making an exact copy. The entire tree is treated as if * it was created. * * @return A copy of the element sub-tree. */ @Override public Object clone() { final Element copy = new Element(namespace, name); // copy all children if (children != null) { if (copy.children == null) { copy.children = new NodeSet(); } for (int i = 0; i < children.size(); i++) { final Element child = children.getElement(i); final Element child_copy = (Element) child.clone(); copy.addChild(child_copy); } } cloneAttrs(copy); cloneValue(copy); return copy; } /** * Tries to find an element with the same namespace and name as x. * * @param child Element to compare against * @return the matching element if it exists; null otherwise. */ protected Element getChild(Element child) { if (children != null) { for (final Element other : children) { if (child.compare(other) >= 0) { return other; } } } return null; } /** * Clones the tree, making an exact copy. Does only clone this level. not * the children. * * @return A copy of the shallow element sub-tree. */ protected Element cloneShallow() { final Element copy = new Element(namespace, name); cloneAttrs(copy); cloneValue(copy); return copy; } /** * Operation flag to be used with {@link #merge(Element,int)}. */ public final static int OP_CREATE = 1; /** * Operation flag to be used with {@link #merge(Element,int)}. */ public final static int OP_DELETE = 2; /** * Operation flag to be used with {@link #merge(Element,int)}. */ public final static int OP_REPLACE = 3; /** * Operation flag to be used with {@link #merge(Element,int)}. */ public final static int OP_MERGE = 4; /** * Merges a subtree into a resulting target subtree. The 'op' parameter * controls how the nodes that are added in the target subtree should be * marked. Either OP_CREATE, OP_DELETE, OP_MERGE or OP_REPLACE. * * @param root Target subtree. Must start from root node. * @param op One of {@link #OP_CREATE}, {@link #OP_DELETE}, * {@link #OP_MERGE} or {@link #OP_REPLACE} * @return Resulting target subtrees in NodeSet * */ public Element merge(Element root, int op) throws JNCException { // make list of nodes down to this node from root final NodeSet list = new NodeSet(); Element a = this; while (a != null) { list.add(0, a); a = a.parent; } // pop first element and assert that it's the same as root Element x = list.getElement(0); list.remove(0); if (root == null) { if (list.size() >= 1) { root = x.cloneShallow(); } else { // special case. only one path in root. Differ already if (op == OP_CREATE) { final Element x1 = (Element) x.clone(); x1.markCreate(); return x1; } else if (op == OP_DELETE) { final Element x1 = x.cloneShallow(); x1.markDelete(); return x1; } else if (op == OP_REPLACE) { final Element x1 = (Element) x.clone(); x1.markReplace(); return x1; } else if (op == OP_MERGE) { final Element x1 = (Element) x.clone(); x1.markMerge(); return x1; } } } if (x.compare(root) == -1) { System.err.println(" x= " + x); System.err.println(" root= " + root); System.err.println(" compare: " + x.compare(root)); throw new JNCException(JNCException.ELEMENT_MISSING, x.getElementPath() + ", " + (root != null ? root.getElementPath() : null)); } // same now, go down Element parent = root; while (list.size() > 1) { // pop first element from list x = list.getElement(0); list.remove(0); Element child = parent != null ? parent.getChild(x) : null; if (child == null) { // need to create it child = x.cloneShallow(); parent.addChild(child); } // go down parent = child; } // last one if (list.size() > 0) { x = list.getElement(0); // list.remove(0); // no need if (op == OP_CREATE) { // we know it's unique final Element x1 = (Element) x.clone(); x1.markCreate(); parent.addChild(x1); } else if (op == OP_DELETE) { // we know it's unique, cut all children except keys final Element x1 = x.cloneShallow(); x1.markDelete(); parent.addChild(x1); } else if (op == OP_MERGE) { final Element x1 = (Element) x.clone(); x1.markMerge(); parent.addChild(x1); } else { // OP_REPLACE final Element x1 = (Element) x.clone(); x1.markReplace(); parent.addChild(x1); } } // done return root; } /** * Clones the attributes to the target copy. Used in clone methods of this * class and YangElement. * * @param copy The target copy to clone the attributes to */ protected Element cloneAttrs(Element copy) { // copy attrs if (attrs != null) { copy.attrs = new ArrayList(); for (int i = 0; i < attrs.size(); i++) { final Attribute attr = attrs.get(i); final Attribute copy_attr = (Attribute) attr.clone(); copy.attrs.add(copy_attr); } } // copy xmlns attrs if (prefixes != null) { copy.prefixes = (PrefixMap) prefixes.clone(); } return copy; } /** * clones the value to the target copy. Used in clone methods of this * class and YangElement. * * @param copy The target copy to clone the value field to */ protected Element cloneValue(Element copy) { if (value instanceof YangBaseType) { copy.value = ((YangBaseType) value).clone(); } else if (value != null) { copy.value = value; } return copy; } /* Mark operations. Uses the nc:operations attribute */ /** * Removes the operation attribute from a node. This is equivalent to: * removeAttr(Element.NETCONF_NAMESPACE,"operation"); see @removeAttr */ public void removeMark() { removeAttr(NETCONF_NAMESPACE, OPERATION); } /** * Sets the operation attribute from a node. This is equivalent to: * setAttr(Element.NETCONF_NAMESPACE,"operation",operation); see @setAttr */ public void setMark(String operation) { Element that = this; while (that!=null&&that.prefixes==null){ that = that.getParent(); } that.prefixes.merge(defaultPrefixes); setAttr(NETCONF_NAMESPACE, OPERATION, operation); } /** * Removes all operation attributes from a sub-tree. * */ public void removeMarks() { removeMark(); if (children != null) { for (int i = 0; i < children.size(); i++) { children.getElement(i).removeMarks(); } } } /** * Marks a node with operation delete. */ public void markDelete() { setMark(DELETE); } /** * Marks node(s) with operation delete. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes */ public void markDelete(String pathStr) throws JNCException { final Path path = new Path(pathStr); final NodeSet nodeSet = path.eval(this); if (nodeSet != null) { for (int i = 0; i < nodeSet.size(); i++) { nodeSet.getElement(i).markDelete(); } } } /** * Marks a node with operation replace. */ public void markReplace() { setMark(REPLACE); } /** * Marks node(s) with operation replace. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes */ public void markReplace(String pathStr) throws JNCException { final Path path = new Path(pathStr); final NodeSet nodeSet = path.eval(this); if (nodeSet != null) { for (int i = 0; i < nodeSet.size(); i++) { nodeSet.getElement(i).markReplace(); } } } /** * Marks a node with operation merge. */ public void markMerge() { setMark(MERGE); } /** * Marks node(s) with operation merge. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes */ public void markMerge(String pathStr) throws JNCException { final Path path = new Path(pathStr); final NodeSet nodeSet = path.eval(this); if (nodeSet != null) { for (int i = 0; i < nodeSet.size(); i++) { nodeSet.getElement(i).markMerge(); } } } /** * Marks a node with operation create */ public void markCreate() { setMark(CREATE); } /** * Marks node(s) with operation create. *

* See {@link Path} for more information about path expressions. * * @param pathStr Path string to find nodes */ public void markCreate(String pathStr) throws JNCException { final Path path = new Path(pathStr); final NodeSet nodeSet = path.eval(this); if (nodeSet != null) { for (int i = 0; i < nodeSet.size(); i++) { nodeSet.getElement(i).markCreate(); } } } /* Info methods */ /** * A qualified name is a prefixed name. This method will find the prefix of * this elements namespace and build a name in the format: "prefix:name". * If no prefix is found the prefix "unknown" will be used. * * @return The qualified name of the element. */ public String qualifiedName() { String qName; String prefix = prefix(); if (prefix == null) { prefix = ""; } if (prefix.equals("")) { qName = name; } else { qName = prefix + ":" + name; } return qName; } /** * Returns the prefix name of this element. * * @return The prefix name that the namespace of this element is bound to */ public String prefix() { return nsToPrefix(namespace); } /** * Returns a prefix map, as it is in the current context. The prefix map is * built up by traversing the parents. * * @return The prefix mappings available at this context node */ public PrefixMap getContextPrefixMap() { final PrefixMap p = new PrefixMap(); Element node = this; while (node != null) { if (node.prefixes != null) { p.merge(node.prefixes); } node = node.parent; } // merge in the default prefixes as well p.merge(defaultPrefixes); return p; } /** * Lookups a prefix and returns the associated namespace, traverses up the * parent links until the prefix is found. Returns null if the * prefix is not found. * * @param prefix Prefix string to lookup. * @return The namespace of the specified prefix in the context of this * node. */ public String lookupContextPrefix(String prefix) { Element node = this; while (node != null) { if (node.prefixes != null) { final String ns = node.prefixes.prefixToNs(prefix); if (ns != null) { return ns; } } node = node.parent; } // as a last resort use the default prefix map. return defaultPrefixes.prefixToNs(prefix); } /** * This method will find the prefix of a specified namespace, from the * given context node. If no prefix is found null will be * returned. * * @param ns Namespace string to lookup * @return The prefix string of the given namespace at the context node. */ public String nsToPrefix(String ns) { Element top = this; String prefix = null; while (prefix == null && top != null) { if (top.prefixes != null) { prefix = top.prefixes.nsToPrefix(ns); } top = top.parent; } if (prefix != null) { return prefix; } // as a last resort use the default prefix map return defaultPrefixes.nsToPrefix(ns); } /** * Returns the path as a string * * @return The path of element */ public String getElementPath() { Element top = this; String s = null; while (top != null) { if (top.namespace == null) { s = strConcat(top.name, s); } else { // top.namespace!=null if (top.parent != null && top.parent.namespace != null && top.namespace.equals(top.parent.namespace)) { s = strConcat(top.name, s); } else { s = strConcat(top.qualifiedName(), s); } } top = top.parent; } return "/" + s; } private String strConcat(String s1, String s2) { if (s2 != null) { return s1 + "/" + s2; } return s1; } /** * Compare if equal to another Object. This method does not compare * attributes and children. It will only compare: *

    *
  • name *
  • namespace *
  • value *
* * @param other Object to compare this element against. * @return true if other is an Element with same name, * namespace and value; false otherwise. */ @Override public boolean equals(Object other) { if (other instanceof Element) { final Element b = (Element) other; final boolean sameName = name.equals(b.name); final boolean sameNamespace = namespace.equals(b.namespace); if (sameName && sameNamespace) { if (value != null) { return value.equals(b.value); } else { return b.value == null; } } } return false; } /** * Returns a hash code value for this object. * * @return The sum of the hash codes of the name, namespace and value of * this element subtree node. */ @Override public int hashCode() { return (name.hashCode() + namespace.hashCode() + value.hashCode()); } /** * Compare two elements. Compares the name, namespace, and value. Returns: * *
    *
  • -1 - if the two containers keys are not equal, which means that they * are completely different. *
  • 0 - if the two elements are equal in name, namespace, and value. *
  • 1 - the two containers are the same except the value. *
* * @param b Element to compare this element against. */ public int compare(Element b) { final boolean sameName = name.equals(b.name); final boolean sameNamespace = namespace.equals(b.namespace); if (sameName && sameNamespace) { return equals(b) ? 0 : 1; } return -1; } /** * Builds a string with the name, value, namespace, prefixes, other * attributes, children and the path of this element subtree. * * @return String representation of this element */ @Override public String toString() { final StringBuffer s_attrs = new StringBuffer(); final StringBuffer s_children = new StringBuffer(); final StringBuffer res = new StringBuffer("Element{name=").append(name); if (value != null) { res.append(", value=").append(value); } res.append(", ns=").append(namespace); // Attributes if (prefixes != null) { for (final Prefix p : prefixes) { s_attrs.append(p.toXMLString()); s_attrs.append(", "); } } if (attrs != null) { for (final Attribute a : attrs) { s_attrs.append(a.toXMLString(this)); s_attrs.append(", "); } } if (s_attrs.length() >= ", ".length()) { s_attrs.delete(s_attrs.length() - 3, s_attrs.length()); res.append(", attrs=[").append(s_attrs); } // Children if (children != null) { for (final Element child : children) { s_children.append(child.name); s_children.append(", "); } } if (s_children.length() >= ", ".length()) { s_children.delete(s_children.length() - 3, s_children.length()); res.append("], children=[").append(s_children); } res.append("], path=").append(getElementPath()).append("}"); return res.toString(); } /** * This will format the tree as an XML string, which can be printed. The * XML code is nicely indented. * * @return This element sub-tree represented as an XML string */ public String toXMLString() { final StringBuffer s = new StringBuffer(); toXMLString(0, s); return s.toString(); } public String toJSONString(){ final StringBuffer s = new StringBuffer(); toJSONString(true,false,false,0, s); s.append("\n}"); return s.toString(); } private void toJSONString(boolean first,boolean isEnd,boolean isArray,int indent, StringBuffer s){ final boolean flag = hasChildren(); boolean isArrayChild=false; if(flag){ String name =null; for (final Element child : children) { if(name!=null&&name.equals(child.qualifiedName())){ isArrayChild =true; break; } name =child.qualifiedName(); } } final String qName = qualifiedName(); s.append(getIndentationSpacing(true, indent)); if(first){ if(isArray){ s.append("["); }else{ s.append("{");} } if(!isArray) { s.append("\n").append(getIndentationSpacing(true, indent)).append("\"").append(qName).append("\":"); } // add xmlns attributes (prefixes) // if (prefixes != null) { // for (final Prefix p : prefixes) { // s.append(" ").append(p.toXMLString()); // } // } // // add attributes // if (attrs != null) { // for (final Attribute attr : attrs) { // s.append(" ").append(attr.toXMLString(this)); // } // } indent++; // add children elements if any if (flag) { // s.append("").append(("\n")); int index=0; int endIndex =children.size()-1; for (final Element child : children) { child.toJSONString(index==0,index==endIndex,isArrayChild,indent, s); if(index!=endIndex) s.append(","); s.append("\n");//.append(getIndentationSpacing(true, indent)); index++; } } else { // add value if any if (value != null) { s.append(("\"")); final String stringValue = value.toString().replaceAll("&", "&"); s.append(getIndentationSpacing(false, indent)); s.append(stringValue).append(("\"")); } else { // self-closing tag s.append("null").append(("}")); return; } } if(flag){ if(isArrayChild ){ s.append(getIndentationSpacing(flag, indent)).append("]"); }else{ s.append(getIndentationSpacing(flag, indent)).append("}"); } } indent--; } private void toXMLString(int indent, StringBuffer s) { final boolean flag = hasChildren(); final String qName = qualifiedName(); s.append(new String(new char[indent * 2]).replace("\0", " ")); s.append("<").append(qName); // add xmlns attributes (prefixes) if (prefixes != null) { for (final Prefix p : prefixes) { s.append(" ").append(p.toXMLString()); } } // add attributes if (attrs != null) { for (final Attribute attr : attrs) { s.append(" ").append(attr.toXMLString(this)); } } indent++; // add children elements if any if (flag) { s.append(">").append(("\n")); for (final Element child : children) { child.toXMLString(indent, s); } } else { // add value if any if (value != null) { s.append(">").append(("")); final String stringValue = value.toString().replaceAll("&", "&"); s.append(getIndentationSpacing(false, indent)); s.append(stringValue).append(("")); } else { // self-closing tag s.append("/>").append(("")); return; } } indent--; s.append(getIndentationSpacing(flag, indent)).append("\n"); } /** * Gets indentation spacing for any given indent level. * * @param shouldIndent Whether or not there should be any indentation. * @param indent The indentation level. * @return A string with indent * 2 number of spaces if shouldIndent is * true; otherwise an empty string. */ private String getIndentationSpacing(boolean shouldIndent, int indent) { if (shouldIndent) { return new String(new char[indent * 2]).replace("\0", " "); } return ""; } /** * Encode to XML and send it to the provided stream. Similar to the * toXMLString(), but without the pretty printing. *

* Equivalent to calling encode(out, true, null); * * @param out Stream to send the encoded version of this element to. * @throws JNCException If a YangElement encode implementation fails */ protected void encode(Transport out) throws JNCException { encode(out, true, null); } /** * Encode to XML and send it to the provided stream. Similar to the * toXMLString(), but without the pretty printing. *

* Equivalent to calling encode(out, true, c); * * @param out Stream to send the encoded version of this element to. * @param c Capabilities, used by YangElement instances. * @throws JNCException If a YangElement encode implementation fails. */ protected void encode(Transport out, Capabilities c) throws JNCException { encode(out, true, c); } /** * Encode to XML and send it to the provided stream. Similar to the * toXMLString(), but without the pretty printing. *

* The newline_at_end argument controls whether a newline char is permitted * at the end or not. *

* Equivalent to calling encode(out, newline_at_end, null); * * @param out Stream to send the encoded version of this element to. * @param newline_at_end If 'true' a newline is printed at the end. * @throws JNCException If a YangElement encode implementation fails. */ protected void encode(Transport out, boolean newline_at_end) throws JNCException { encode(out, newline_at_end, null); } /** * Encode to XML and send it to the provided stream. Similar to the * toXMLString(), but without the pretty printing. *

* The newline_at_end argument controls whether a newline char is permitted * at the end or not. * * @param out Stream to send the encoded version of this element to. * @param newline_at_end If 'true' a newline is printed at the end. * @param c Capabilities, used by YangElement instances. * @throws JNCException If a YangElement encode implementation fails. */ protected void encode(Transport out, boolean newline_at_end, Capabilities capas) throws JNCException { final String qName = qualifiedName(); out.print("<" + qName); // add xmlns attributes (prefixes) if (prefixes != null) { for (final Prefix p : prefixes) { out.print(" "); p.encode(out); } } // add attributes if (attrs != null) { for (final Attribute attr : attrs) { out.print(" "); attr.encode(out, this); } } if (hasChildren()) { // add children elements if any out.println(">"); for (final Element child : children) { child.encode(out, true, capas); } } else if (value != null) { // otherwise, add value (if any) out.print(">" + Utils.escapeXml(value.toString())); } else { // self-closing tag out.print("/>" + (newline_at_end ? "\n" : "")); return; } out.print("" + (newline_at_end ? "\n" : "")); } /** * Return the full tagpath for this Element * * @return Absolute tagpath to this node */ public Tagpath tagpath() { Element e = this; int ix = 0; while (e != null) { e = e.parent; ix++; } final Tagpath tp = new Tagpath(ix); e = this; while (e != null) { tp.p[--ix] = e.name; e = e.parent; } return tp; } /** * Return an iterator for the children of this node. * * @return A new iterator over this element's children */ public ElementChildrenIterator iterator() { return new ElementChildrenIterator(children); } /** * Return an iterator for the children of a specified name of this node. *

* Example usage: * *

     * ElementChildrenIterator hostIter = config.iterator("host");
     * while (hostIter.hasNext()) {
     *     Element host = hostIter.next();
     *     System.out.println("Host: " + host);
     * }
     * 
* * @param name A filter name, return only children with this name. * @return A new iterator over this element's children with specified name */ public ElementChildrenIterator iterator(String name) { return new ElementChildrenIterator(children, name); } /** * Write this configuration tree to a file. The configuration tree is * written as XML text. * * @param filename File name. * @see #readFile(String) */ public void writeFile(String filename) throws IOException { final File file = new File(filename); final FileOutputStream fos = new FileOutputStream(file); final DataOutputStream dos = new DataOutputStream(fos); dos.writeBytes(toXMLString()); dos.close(); } /** * Read file with XML text and parse it into a configuration tree. * * @param filename File name. * @see #writeFile(String) */ public static Element readFile(String filename) throws JNCException { final XMLParser p = new XMLParser(); return p.readFile(filename); } /** * * @param xmlContent * @return * @throws JNCException */ public static Element readXml(String xmlContent) throws JNCException { final XMLParser p = new XMLParser(); return p.parse(new InputSource(new ByteArrayInputStream(xmlContent.getBytes()))); } /* help functions */ /** * Sets the debug level. 0 - no debug 1 - Element level: Element, Attribute * 2 - Session level: 3 - Parser level: Path, PathCreate, LocationStep, * Expr 4 - Other: Prefix, PrefixMap */ static final int DEBUG_LEVEL_ELEMENT = 1; static final int DEBUG_LEVEL_ATTRIBUTE = 1; static final int DEBUG_LEVEL_SESSION = 2; static final int DEBUG_LEVEL_TRANSPORT = 2; static final int DEBUG_LEVEL_PATH = 3; static final int DEBUG_LEVEL_PATHCREATE = 3; static final int DEBUG_LEVEL_EXPR = 3; static final int DEBUG_LEVEL_LOCATIONSTEP = 3; static final int DEBUG_LEVEL_PARSER = 3; static final int DEBUG_LEVEL_PREFIX = 4; static final int DEBUG_LEVEL_PREFIXMAP = 4; public static void setDebugLevel(int level) { debugLevel = level; } static int debugLevel = 0; /** * Printout trace if 'debug'-flag is enabled. */ private static void trace(String s) { if (debugLevel >= DEBUG_LEVEL_ELEMENT) { System.err.println("*Element: " + s); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy