fr.inria.oak.paxquery.common.xml.navigation.NavigationTreePatternNode.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.oak.paxquery.common.xml.navigation.NavigationTreePatternNode.java

Source

/*******************************************************************************
 * Copyright (C) 2013, 2014, 2015 by Inria and Paris-Sud University
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package fr.inria.oak.paxquery.common.xml.navigation;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import fr.inria.oak.paxquery.common.predicates.BasePredicate.PredicateType;

/**
 * Class that represents each node of a {@link NavigationTreePattern}.
 * 
 */
public final class NavigationTreePatternNode implements Serializable, Comparable<NavigationTreePatternNode> {

    /**
     * Universal version identifier for PatternNode class
     */
    private static final long serialVersionUID = -5271882998433575092L;

    private final String startNamespaceDelimiter;
    private final String endNamespaceDelimiter;

    private static final Log logger = LogFactory.getLog(NavigationTreePatternNode.class);

    /**
     * This variable keeps a global counter of nodes and it is used
     * to number uniquely the nodes of several patterns.
     */
    public static AtomicInteger globalNodeCounter = new AtomicInteger(1);

    /* Node related properties */
    /**
     * If true, this node represents an attribute, and though, only attributes match.
     * Otherwise, this node represents an element, and though, only elements match.
     */
    private boolean isAttribute;

    /**
     * If matchingVariables.size() > 0, this node (either an attribute or an element) is assigned to one or more variables in the query. 
     * Otherwise, this node (either an attribute or an element) is not assigned to any variable.
     * For example, variable $i would be stored in matchingVariables of name? for the sentence $i := <whatever>/name 
     */
    private ArrayList<Variable> matchingVariables;

    /**
     * The TreePattern this PatternNode belongs to.
     */
    private NavigationTreePattern navigationTreePattern;

    /**
     * If the xam is built from a XAM, this is the hash code of the XAM node
     * from which it originated.
     */
    private int nodeCode;

    /* ID related properties */
    /**
     * If true, the ID of this element is needed. When {@link #storesID} is set to
     * true, needs ID is always set to true.
     */
    private boolean storesID;

    /**
     * If true, the ID of this element is (R)equired, must be known in order to access
     * the data stored in the xam.
     */
    private boolean requiresID;

    /**
     * If true, "ID i" was specified for this node in the XAM file, so the identity ID for
     * this node should be stored. 
     */
    private boolean identityID;

    /**
     * If true, "ID o" was specified for this node in the XAM file, so the order preserving ID for
     * this node should be stored. 
     */
    private boolean orderID;

    /**
     * If true, "ID s" was specified for this node in the XAM file, so the structural ID for
     * this node should be stored. 
     */
    private boolean structID;

    /**
     * If true, "ID u" was specified for this node in the XAM file, so the update ID for
     * this node should be stored. 
     */
    private boolean updateID;

    /* Tag related properties */
    /**
     * If true, the tag of this element is stored.
     */
    private boolean storesTag;

    /**
     * If true, the Tag of this element is (R)equired, must be known in order to access
     * the data stored in the xam.
     */
    private boolean requiresTag;

    /**
     * If true, there is a selection on tag.
     */
    private boolean selectOnTag;

    /**
     * The namespace of this node.
     */
    private String namespace;

    /**
     * The tag of this node ([Tag="a"]), "" for the xam root, "*" for any tag.
     */
    private String tag;

    /* Value related properties */
    /**
     * If true, the value of this element is stored.
     */
    private boolean storesValue;

    /**
     * If true, the value of this element is (R)equired, must be known in order to access
     * the data stored in the xam.
     */
    private boolean requiresVal;

    /**
     * If true, there is a selection on value.
     */
    private boolean selectOnValue;

    private PredicateType selectOnValuePredicate;

    /**
     * If selectOnValue is true, this is what the value should be ([Val="a"]).
     */
    private String stringValue;

    /**
     * If selectOnValue is true, this is what the value should be ([Val=a]).
     */
    private double doubleValue;

    /* Content related properties */
    /**
     * If true, the content of this element is stored.
     */
    private boolean storesContent;

    /* Edges to other nodes */
    /**
     * Edge connecting this node to its parent.
     */
    private NavigationTreePatternEdge parentEdge;

    /**
     * Edges connecting this node to its children.
     */
    private ArrayList<NavigationTreePatternEdge> edges;

    /**
     * top returning node
     * -1: not computed yet
     * 0: false
     * 1: true
     */
    private int isTopReturningNode;

    /**
     * 
     */
    private int nestingDepth;

    /**
     * True if this node has been obtained by navigating inside a Cont attribute
     */
    private boolean virtual;

    /**
     * Creates a new node with no children. All properties are set to false
     * by default.
     * 
     * @param namespace the namespace associated to this node
     * @param tag the tag associated to this node
     * @param nodeCode the code associated to this node
     */
    public NavigationTreePatternNode(String namespace, String tag, int nodeCode, String startNamespaceDelimiter,
            String endNamespaceDelimiter) {
        this.namespace = namespace;
        this.tag = tag;
        this.storesTag = false;
        if (tag == null || tag.compareTo("*") == 0) {
            this.namespace = "";
            this.tag = "*";
        } else {
            this.selectOnTag = true;
        }
        this.edges = (new ArrayList<NavigationTreePatternEdge>());
        this.matchingVariables = new ArrayList<Variable>();
        this.storesContent = false;
        this.storesValue = false;
        this.storesID = false;
        this.requiresID = false;
        this.requiresTag = false;
        this.requiresVal = false;
        this.isTopReturningNode = -1;
        this.nodeCode = nodeCode;

        this.startNamespaceDelimiter = startNamespaceDelimiter;
        this.endNamespaceDelimiter = endNamespaceDelimiter;
    }

    /**
     * Creates a new node with no children. All properties are set to false
     * by default.
     * 
     * @param namespace the namespace associated to this node
     * @param tag the tag associated to this node
     * @param nodeCode the code associated to this node
     * @param treePattern the TreePattern this node belongs to
     */
    public NavigationTreePatternNode(String namespace, String tag, int nodeCode, String startNamespaceDelimiter,
            String endNamespaceDelimiter, NavigationTreePattern navigationTreePattern) {
        this.namespace = namespace;
        this.tag = tag;
        this.storesTag = false;
        if (tag == null || tag.compareTo("*") == 0) {
            this.namespace = "";
            this.tag = "*";
        } else {
            this.selectOnTag = true;
        }
        this.edges = (new ArrayList<NavigationTreePatternEdge>());
        this.matchingVariables = new ArrayList<Variable>();
        this.storesContent = false;
        this.storesValue = false;
        this.storesID = false;
        this.requiresID = false;
        this.requiresTag = false;
        this.requiresVal = false;
        this.isTopReturningNode = -1;
        this.nodeCode = nodeCode;

        this.startNamespaceDelimiter = startNamespaceDelimiter;
        this.endNamespaceDelimiter = endNamespaceDelimiter;

        this.navigationTreePattern = navigationTreePattern;
    }

    /**
     * Compare method for pattern nodes based on their namespace and tag.
     * 
     * @return the result of the comparison
     */
    public int compareTo(NavigationTreePatternNode pn) {
        int result = nodeExtendedString().compareTo(pn.nodeExtendedString());
        if (result == 0) {
            return treeToString(PrintingLevel.EXTENDED).compareTo(pn.treeToString(PrintingLevel.EXTENDED));
        }
        return result;
    }

    public void setVirtual(boolean isVirtual) {
        this.virtual = isVirtual;
    }

    /**
     * Counts the nodes in this subtree.
     * 
     * @return the number of xam nodes
     */
    public int getNumberOfNodes() {
        if (this.edges == null) {
            return 1;
        }
        if (this.edges.size() == 0) {
            return 1;
        }
        int aux = 1;
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            aux += ((NavigationTreePatternEdge) it.next()).n2.getNumberOfNodes();
        }
        return aux;
    }

    /**
     * Counts the nodes in this subtree that return ID, Cont and Val.
     * 
     * @return the number of xam nodes
     */
    public int getNumberOfReturningNodes() {
        int aux = 0;
        if (this.storesContent || this.storesID || this.storesValue) {
            aux++;
        }
        if (this.edges == null) {
            return aux;
        }
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            aux += ((NavigationTreePatternEdge) it.next()).n2.getNumberOfReturningNodes();
        }
        return aux;
    }

    public int pruneAllButPathTo(NavigationTreePatternNode n1) {
        // Parameters.logger.info("pruneAllButPathTo " + n1.tag + " in " +
        // this.tag);
        if (this.nodeCode == n1.nodeCode) {
            this.edges = (new ArrayList<NavigationTreePatternEdge>());
            // Parameters.logger.info("I am the target, erasing children");
            return 1;
        }
        ArrayList<NavigationTreePatternEdge> newEdges = (new ArrayList<NavigationTreePatternEdge>());
        Iterator<NavigationTreePatternEdge> it = this.edges.iterator();
        while (it.hasNext()) {
            NavigationTreePatternEdge thisEdge = it.next();
            NavigationTreePatternNode child = thisEdge.n2;
            // Parameters.logger.info("Moving to child " + child.tag);
            int k = child.pruneAllButPathTo(n1);
            // Parameters.logger.info("Child " + child.tag + " returned " + k);
            if (k == 1) {
                newEdges.add(thisEdge);
                this.edges = newEdges;
                // Parameters.logger.info("Copying child " + child.tag + ",
                // returning 1");
                return 1;
            }
        }
        // Parameters.logger.info("Erasing all children, returning");
        this.edges = newEdges;
        return 2;
    }

    /**
     * Returns the node of the subtree whose {@link #nodeCode} is 
     * equal to the value introduced as parameter.
     * 
     * @param nx2
     * @return
     */
    public NavigationTreePatternNode locate(Integer nx2) {
        // Parameters.logger.info("Locate " + nx2 + " on " + this.tag + " " +
        // this.XAMNodeHashCode);
        if (this.nodeCode == nx2.intValue()) {
            return this;
        } else {
            Iterator<NavigationTreePatternEdge> it = this.edges.iterator();
            while (it.hasNext()) {
                NavigationTreePatternNode n2 = it.next().n2.locate(nx2);
                if (n2 != null) {
                    return n2;
                }
            }
        }
        return null;
    }

    /**
     * Recursive method that removes the storage requirements of the subtree
     * starting from this node, skipping the nodes with the tag specified in
     * the parameter.
     * 
     * @param tagOfTuplesToKeep tag of the nodes whose storage requirements
     * we want to preserve
     */
    protected void parseUnrequiredData(String tagOfTuplesToKeep) {
        if (!this.tag.equals(tagOfTuplesToKeep)) {
            this.removeRequirementsToStoreData();
        }
        for (NavigationTreePatternEdge edge : this.edges) {
            edge.n2.parseUnrequiredData(tagOfTuplesToKeep);
        }
    }

    /**
     * Removes the storage requirements from this node. It sets to false
     * {@link #storesID}, {@link #storesTag}, {@link #storesValue} and
     * {@link #storesContent}.
     */
    private void removeRequirementsToStoreData() {
        this.storesID = false;
        this.storesValue = false;
        this.storesContent = false;
        this.storesTag = false;
    }

    /* Copy methods */
    /**
     * Recursive method that returns the copy of the subtree starting from
     * this node. This method will preserve the {@link #nodeCode} of the
     * nodes of the subtree.
     * 
     * @return the root of the subtree copied
     */
    public NavigationTreePatternNode deepCopy() {
        NavigationTreePatternNode aux = new NavigationTreePatternNode(this.namespace, this.tag, this.nodeCode,
                this.startNamespaceDelimiter, this.endNamespaceDelimiter);

        aux.setStoresContent(this.storesContent);
        aux.setStoresID(this.storesID);
        aux.setStoresTag(this.storesTag);
        aux.setStoresValue(this.storesValue);

        aux.setRequiresID(this.requiresID);
        aux.setRequiresTag(this.requiresTag);
        aux.setRequiresVal(this.requiresVal);

        aux.setNamespace(this.namespace);
        aux.setSelectOnTag(this.selectOnTag, this.namespace, this.tag);
        if (this.selectOnValue && this.stringValue == null)
            aux.setSelectOnValue(this.selectOnValue, this.selectOnValuePredicate, this.doubleValue);
        else
            aux.setSelectOnValue(this.selectOnValue, this.selectOnValuePredicate, this.stringValue);

        aux.identityID = this.identityID;
        aux.orderID = this.orderID;
        aux.updateID = this.updateID;
        aux.structID = this.structID;

        aux.isAttribute = this.isAttribute;

        aux.virtual = this.virtual;

        aux.edges = (new ArrayList<NavigationTreePatternEdge>());
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            NavigationTreePatternEdge pe = (NavigationTreePatternEdge) it.next();
            NavigationTreePatternNode childCopy = pe.n2.deepCopy();
            aux.addEdge(childCopy, pe.isParent(), pe.isNested(), pe.isOptional());
        }
        return aux;
    }

    /**
     * Recursive method that returns the copy of the subtree starting from this
     * node, but replacing the old node introduced as the first parameters for
     * a new node introduced as the second parameter. This method will preserve
     * the {@link #nodeCode} of the nodes of the subtree.
     * 
     * @param oldNode the old node that we want to replace
     * @param newNode the new node that will replace the old node
     * @return the root of the subtree copied
     */
    public NavigationTreePatternNode deepCopyWithReplace(NavigationTreePatternNode oldNode,
            NavigationTreePatternNode newNode) {
        if (this == oldNode) {
            return newNode;
        }

        NavigationTreePatternNode aux = new NavigationTreePatternNode(this.namespace, this.tag, this.nodeCode,
                this.startNamespaceDelimiter, this.endNamespaceDelimiter);

        aux.storesContent = this.storesContent;
        aux.storesID = this.storesID;
        aux.storesTag = this.storesTag;
        aux.storesValue = this.storesValue;

        aux.setRequiresID(this.requiresID);
        aux.setRequiresTag(this.requiresTag);
        aux.setRequiresVal(this.requiresVal);

        aux.selectOnTag = this.selectOnTag;

        aux.selectOnValue = this.selectOnValue;
        if (aux.selectOnValue) {
            aux.stringValue = this.stringValue;
        }

        aux.identityID = this.identityID;
        aux.orderID = this.orderID;
        aux.updateID = this.updateID;
        aux.structID = this.structID;

        aux.isAttribute = this.isAttribute;

        aux.edges = new ArrayList<NavigationTreePatternEdge>();

        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            NavigationTreePatternEdge pe = it.next();
            NavigationTreePatternNode childCopy = null;
            if (pe.n2 == oldNode) {
                childCopy = newNode;
            } else {
                childCopy = pe.n2.deepCopyWithReplace(oldNode, newNode);
            }
            aux.addEdge(childCopy, pe.isParent(), pe.isNested(), pe.isOptional());
        }
        return aux;
    }

    /**
     * Copies a node. This method will preserve the {@link #nodeCode}
     * of the node.
     * 
     * @return the copy of the node
     */
    public NavigationTreePatternNode nodeCopy() {
        NavigationTreePatternNode aux = new NavigationTreePatternNode(this.namespace, this.tag, this.nodeCode,
                this.startNamespaceDelimiter, this.endNamespaceDelimiter);

        aux.storesContent = this.storesContent;
        aux.storesID = this.storesID;
        aux.storesTag = this.storesTag;
        aux.storesValue = this.storesValue;

        aux.setRequiresID(this.requiresID);
        aux.setRequiresTag(this.requiresTag);
        aux.setRequiresVal(this.requiresVal);

        aux.selectOnTag = this.selectOnTag;
        if (aux.selectOnTag) {
            aux.namespace = this.namespace;
            aux.tag = this.tag;
        }

        aux.selectOnValue = this.selectOnValue;
        if (aux.selectOnValue) {
            aux.stringValue = this.stringValue;
        }

        aux.identityID = this.identityID;
        aux.orderID = this.orderID;
        aux.updateID = this.updateID;
        aux.structID = this.structID;

        aux.isAttribute = this.isAttribute;

        aux.virtual = this.virtual;

        return aux;
    }

    /**
     * Method that copies the value of {@link #storesID}, {@link #storesValue} and
     * {@link #storesContent}, if they are true, from the node introduced in
     * the parameters to this node.
     * 
     * @param node the node whose storage parameters we will copy
     */
    public void copyAttributesFrom(NavigationTreePatternNode node) {
        if (!this.storesValue && node.storesValue) {
            this.storesValue = true;
        }
        if (!this.storesContent && node.storesContent) {
            this.storesContent = true;
        }
        if (!this.storesID && node.storesID) {
            this.storesID = true;
        }
    }

    /**
     * Copies all the children of the node introduced in the parameters
     * as the children of this node.
     * 
     * @param node the node whose children we want to copy
     */
    public void copyChildrenFrom(NavigationTreePatternNode node) {
        Iterator<NavigationTreePatternEdge> it = node.edges.iterator();
        while (it.hasNext()) {
            NavigationTreePatternEdge e = it.next();
            NavigationTreePatternNode child = e.n2;
            addEdge(child.deepCopy(), e.isParent(), e.isNested(), e.isOptional());
        }
    }

    /**
     * 
     * @param edge
     */
    public void copyVirtualChild(NavigationTreePatternEdge edge) {
        NavigationTreePatternNode childCopy = edge.n2.deepCopy();
        // marking childCopy and its subtree as virtual:
        Stack<NavigationTreePatternNode> st = new Stack<NavigationTreePatternNode>();
        st.push(childCopy);
        while (!st.empty()) {
            NavigationTreePatternNode pn = st.pop();
            // Parameters.logger.info("Set virtual node: " + pn.tag);
            pn.virtual = true;
            pn.nodeCode = NavigationTreePatternNode.globalNodeCounter.getAndIncrement();
            // virtual nodes obtained by navigation cannot store ID
            pn.storesID = false;
            Iterator<NavigationTreePatternEdge> pnChildren = pn.edges.iterator();
            while (pnChildren.hasNext()) {
                NavigationTreePatternEdge pnEdge = pnChildren.next();
                st.push(pnEdge.n2);
            }
        }
        addEdge(childCopy, edge.isParent(), edge.isNested(), edge.isOptional());
    }

    /* Auxiliary functions to getters and setters */

    /**
     * Returns true if there is some required field underneath this node.
     * 
     * @return if there is some required field below
     */
    public boolean requiresSomething() {
        if (this.requiresID || this.requiresTag || this.requiresVal) {
            // Parameters.logger.info(this.tag + " requires id: " + this.requiresID
            // +
            // " requires tag: " + this.requiresTag + " requires value: " +
            // this.requiresValue);
            return true;
        }
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            if (((NavigationTreePatternEdge) it.next()).n2.requiresSomething()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Recursive method that returns true if any node in the subtree
     * starting from this node stores the ID, the tag, the value or
     * the content.
     * 
     * @return if the subtree starting from this node stores something
     */
    public boolean deepStoresSomething() {
        if (this.storesID) {
            return true;
        }
        if (this.storesTag) {
            return true;
        }
        if (this.storesValue) {
            return true;
        }
        if (this.storesContent) {
            return true;
        }
        if (this.edges != null) {
            Iterator<NavigationTreePatternEdge> it = edges.iterator();
            while (it.hasNext()) {
                NavigationTreePatternNode child = ((NavigationTreePatternEdge) it.next()).n2;
                if (child.deepStoresSomething()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Recursive method that returns true if this node stores the ID,
     * the tag, the value or the content.
     * 
     * @return if this node stores something
     */
    public boolean nodeStoresSomething() {
        if (this.storesID || this.storesTag || this.storesValue || this.storesContent)
            return true;
        return false;
    }

    /**
     * Returns what properties - consisting of ID, Val and Cont - are stored for a node
     * 
     * @return a boolean array of size 3 where the 0th position represents ID, 1st Val and 3rd Cont.
     * These positions are set to <code>true</code> if they are stored for this node, otherwise <code>false</code>.
     */
    public boolean[] getStoredProperties() {
        boolean[] storedProperties = new boolean[3];
        if (this.storesID) {
            storedProperties[0] = true;
        } else {
            storedProperties[0] = false;
        }
        if (this.storesValue) {
            storedProperties[1] = true;
        } else {
            storedProperties[1] = false;
        }
        if (this.storesContent) {
            storedProperties[2] = true;
        } else {
            storedProperties[2] = false;
        }
        return storedProperties;
    }

    /**
     * Returns the columns required for this node
     * 
     * @param storedProperties      the boolean array returned from getStoredProperties
     *                         @see fr.inria.gemo.vip2p.xam.PatternNode#getStoredProperties()
     * @return                  the columns required for this node
     */
    public int[] getStoredColumns(boolean[] storedProperties) {
        ArrayList<Integer> columns = new ArrayList<Integer>();

        for (int i = 0; i < 3; i++) {
            if (storedProperties[i] == true) {
                columns.add(i);
            }
        }

        int[] columnsArray = new int[columns.size()];
        for (int j = 0; j < columns.size(); j++) {
            columnsArray[j] = columns.get(j);
        }
        return columnsArray;
    }

    public NavigationTreePatternNode getRoot(NavigationTreePatternNode n1) {
        if (n1.parentEdge == null) {
            return n1;
        } else {
            return getRoot(n1.parentEdge.n1);
        }
    }

    /**
     *  Returns the descendants of this node in BFS order
     *  
     *  @return all the descendant of the node in BFS order
     *  
     * */
    public ArrayList<NavigationTreePatternNode> getBFSOrderedDescendants() {
        ArrayList<NavigationTreePatternNode> nodes = new ArrayList<NavigationTreePatternNode>();

        //BFS uses Queue data structure
        Queue<NavigationTreePatternNode> q = new LinkedList<NavigationTreePatternNode>();
        q.add(this);
        nodes.add(this);

        while (!q.isEmpty()) {
            NavigationTreePatternNode n = q.remove();

            for (NavigationTreePatternNode child : n.getChildrenList()) {
                nodes.add(child);
                q.add(child);
            }
        }

        return nodes;
    }

    protected List<Boolean> getColumnsStoringValue() {
        List<Boolean> list = new ArrayList<Boolean>();

        if (this.storesID)
            list.add(false);
        if (this.storesTag)
            list.add(false);
        if (this.storesValue)
            list.add(true);
        if (this.storesContent)
            list.add(false);

        return list;
    }

    protected List<Boolean> getColumnsBelongingToAttribute() {
        List<Boolean> list = new ArrayList<Boolean>();

        if (this.isAttribute) {
            if (this.storesID)
                list.add(true);
            if (this.storesTag)
                list.add(true);
            if (this.storesValue)
                list.add(true);
            if (this.storesContent)
                list.add(true);
        } else {
            if (this.storesID)
                list.add(false);
            if (this.storesTag)
                list.add(false);
            if (this.storesValue)
                list.add(false);
            if (this.storesContent)
                list.add(false);
        }

        return list;
    }

    public LinkedList<NavigationTreePatternNode> getNodes() {
        if (this.edges == null) {
            return null;
        }
        if (this.edges.size() == 0) {
            return null;
        }
        LinkedList<NavigationTreePatternNode> nodes = new LinkedList<NavigationTreePatternNode>();
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            nodes = ((NavigationTreePatternEdge) it.next()).n2.getNodesRec(nodes);
        }
        return nodes;
    }

    private LinkedList<NavigationTreePatternNode> getNodesRec(LinkedList<NavigationTreePatternNode> nodes) {
        nodes.add(this);
        if (this.edges != null) {
            Iterator<NavigationTreePatternEdge> it = edges.iterator();
            while (it.hasNext()) {
                nodes = ((NavigationTreePatternEdge) it.next()).n2.getNodesRec(nodes);
            }
        }
        return nodes;
    }

    /**
     * Adds an edge connecting this node to a child node. It also marks the
     * child node as being existential, if this is the case.
     * 
     * @param child
     *            The child node.
     * @param parent
     *            Whether or not the edge is a parent-child edge (otherwise, it
     *            is ancestor-descendant).
     * @param nested
     * 
     */
    /*
    public void addEdge(NavigationTreePatternNode child, boolean parent, boolean nested, boolean optional) {
       NavigationTreePatternEdge e = new NavigationTreePatternEdge(this, child, parent, nested, optional);
        
       this.edges.add(e);
       child.parentEdge = e;
    }*/
    public void addEdge(NavigationTreePatternNode child, boolean parent, boolean nested, boolean optional) {
        NavigationTreePatternEdge e = new NavigationTreePatternEdge(this, child, parent, nested, optional);

        this.edges.add(e);
        child.parentEdge = e;

        if (nested)
            child.setNestingDepth(this.getNestingDepth() + 1);
        else
            child.setNestingDepth(this.getNestingDepth());

    }

    /**
     * 
     * @param pe
     */
    public void addEdge(NavigationTreePatternEdge pe) {
        if (this.edges == null)
            this.edges = new ArrayList<NavigationTreePatternEdge>();
        this.edges.add(pe);
        pe.n2.parentEdge = pe;
    }

    public void removeEdge(NavigationTreePatternEdge pe) {
        edges.remove(pe);
    }

    /**
     * 
     */
    public void cleanEdges() {
        this.edges = new ArrayList<NavigationTreePatternEdge>();
    }

    /**
     * This method returns a list of the children on the specific xam node
     * 
     * @return List of children.
     */
    public ArrayList<NavigationTreePatternNode> getChildrenList() {
        ArrayList<NavigationTreePatternNode> children = new ArrayList<NavigationTreePatternNode>();
        for (NavigationTreePatternEdge pe : edges) {
            NavigationTreePatternNode n2 = pe.n2;
            children.add(n2);
        }
        return children;
    }

    /**
     * Returns true if this node has a child with the same tag and the same parenthood relationship
     * @param descendantTag the tag of the (possibly) descendant node
     * @param directDescendant the relationship between both nodes: true means parent/chid (-), false means ancestor/descendant (=)
     * @return true if this node has a child with the same tag and the same parenthood relationship
     */
    public boolean hasChild(String descendantTag, boolean directDescendant) {
        for (NavigationTreePatternEdge edge : edges) {
            //the relationship and the tag must be the same
            if (edge.isParent() == directDescendant && edge.n2.tag.compareTo(descendantTag) == 0)
                return true;
        }
        return false;
    }

    /**
     * Returns the child whose tag is "descendantTag" and is connected as specified by "directDescendant" (whether a direct descendant or undirect descendant), or
     * null if no child with the same tag and relationship is found.
     * @param descendantTag the tag of the (possible) child
     * @param directDescendant whether the child is a direct descendant (true) or undirect descendant (false)
     * @return the child, or null if such child with such relationship was not found.
     */
    public NavigationTreePatternNode getChild(String descendantTag, boolean directDescendant) {
        for (NavigationTreePatternEdge edge : edges) {
            //the relationship and the tag must be the same
            if (edge.isParent() == directDescendant && edge.n2.tag.compareTo(descendantTag) == 0)
                return edge.n2;
        }
        return null;
    }

    /* Getters/setters */
    /* Node related getters/setters */
    /**
     * If true, this node represents an attribute, and though, only attributes match.
     * Otherwise, this node represents an element, and though, only elements match.
     * 
     * @return the isAttribute
     */
    public boolean isAttribute() {
        return this.isAttribute;
    }

    /**
     * If set to true, this node represents an attribute, and though, only attributes match.
     * Otherwise, this node represents an element, and though, only elements match.
     * 
     * @param isAttribute the isAttribute to set
     */
    public void setAttribute(boolean isAttribute) {
        this.isAttribute = isAttribute;
    }

    /**
     * Returns the list of variables to which this node is assigned.
     * @return An ArrayList<Variable> containing the variables this node is assigned to.
     */
    public ArrayList<Variable> getMatchingVariables() {
        return this.matchingVariables;
    }

    /**
     * Returns those variables to which this node is assigned that are marked as storing the ID of the node.
     * That is, those variables "var" such that var.dataType == Variable.VariableDataType.NodeID
     * @return An arrayList<Variable> containing those variables this node is assigned to that are marked as storing the ID of the node.
     */
    public ArrayList<Variable> getMatchingVariablesStoringID() {
        ArrayList<Variable> list = new ArrayList<Variable>();

        for (Variable var : matchingVariables) {
            if (var.dataType == Variable.VariableDataType.NodeID)
                list.add(var);
        }

        return list;
    }

    /**
     * Returns those variables to which this node is assigned that are marked as storing value.
     * That is, those variables "var" such that var.dataType == Variable.VariableDataType.Value
     * @return An ArrayList<Variable> containing those variables this node is assigned to that are marked as storing value.
     */
    public ArrayList<Variable> getMatchingVariablesStoringValue() {
        ArrayList<Variable> list = new ArrayList<Variable>();

        for (Variable var : matchingVariables) {
            if (var.dataType == Variable.VariableDataType.Value)
                list.add(var);
        }

        return list;
    }

    /**
     * Returns those variables to which this node is assigned that are marked as storing content.
     * That is, those variables "var" such that var.dataType == Variable.VariableDataType.Content
     * @return An ArrayList<Variable> containing those variables to which this node is assigned that are marked as storing content.
     */
    public ArrayList<Variable> getMatchingVariablesStoringContent() {
        ArrayList<Variable> list = new ArrayList<Variable>();

        for (Variable var : matchingVariables) {
            if (var.dataType == Variable.VariableDataType.Content)
                list.add(var);
        }

        return list;
    }

    /**
     * Returns true if this node is assigned to at least one variable marked as storing value.
     * That is, if this node is assigned to at least one variable "var" such that var.dataType == Variable.VariableDataType.Value
     * @return true if this node is assigned to at least one variable marked as storing value.
     */
    public boolean checkAnyMatchingVariableStoresValue() {
        for (Variable var : matchingVariables) {
            if (var.dataType == Variable.VariableDataType.Value)
                return true;
        }
        return false;
    }

    /**
     * Returns true if this node is assigned to at least one variable marked as storing content.
     * That is, if this node is assigned to at least one variable "var" such that var.dataType == Variable.VariableDataType.Content
     * @return true if this node is assigned to at least one variable marked as storing value.
     */
    public boolean checkAnyMatchingVariableStoresContent() {
        for (Variable var : matchingVariables) {
            if (var.dataType == Variable.VariableDataType.Content)
                return true;
        }
        return false;
    }

    /**
     * Returns the number of matching variables. That is, the number of variables assigned to this node.
     * @return the number of matching variables.
      */
    public int getMatchingVariablesSize() {
        return matchingVariables.size();
    }

    /**
     * Marks this node as assigned to those variables indicated by {@link #variables}.
     * @param variables variables to which this node will be assigned.
     */
    public void addMatchingVariables(Variable... variables) {
        for (Variable var : variables)
            matchingVariables.add(var);
    }

    /**
     * Removes all matching variables from this node. As a result, this node is not assigned to any variable any more.
      */
    public void clearMatchingVariables() {
        matchingVariables.clear();
    }

    /**
     * Removes the matching variable indicated by {@link #variables} (if present). As a result, this node is not assigned to those variables any more.
     */
    public void removeMatchingVariables(String... variableNames) {
        for (int i = matchingVariables.size() - 1; i >= 0; i--) {
            for (int j = 0; j < variableNames.length; j++) {
                if (matchingVariables.get(i).name.compareTo(variableNames[j]) == 0)
                    matchingVariables.remove(i);
            }
        }
    }

    /**
     * Returns the variable whose name is given by "variableName". 
     * @param variableName the name of the expected variable.
     * @return the variable whose name is given by "variableName". Null if the node is not assigned to such variable.
     */
    public Variable getMatchingVariable(String variableName) {
        for (int i = 0; i < matchingVariables.size(); i++) {
            if (matchingVariables.get(i).name.compareTo(variableName) == 0)
                return matchingVariables.get(i);
        }
        return null;
    }

    /**
     * Returns the TreePattern this node belongs to.
     * @return the TreePattern this node belongs to.
     */
    public NavigationTreePattern getTreePattern() {
        return navigationTreePattern;
    }

    /**
     * Assigns this node to a given TreePattern
     * @param treePattern the TreePattern to which this node is assigned.
     */
    public void setTreePattern(NavigationTreePattern navigationTreePattern) {
        this.navigationTreePattern = navigationTreePattern;
    }

    /**
     * If the xam is built from a XAM, this is the hash code of the XAM node
     * from which it originated.
     * 
     * @return the {@link #nodeCode}
     */
    public int getNodeCode() {
        return nodeCode;
    }

    /**
     * Set the hash code of the XAM node.
     * 
     * @param nodeCode the nodeCode to set
     */
    public void setNodeCode(int nodeCode) {
        this.nodeCode = nodeCode;
    }

    /* ID related getters/setters */
    /**
     * If true, the ID of this element is needed. When {@link #storesID} is set to
     * true, needs ID is always set to true.
     * 
     * @return the storesID
     */
    public boolean storesID() {
        return this.storesID;
    }

    /**
     * This method sets the variable {@link #storesID} to the given value.
     * 
     * @param storesID the storesID to set
     */
    public void setStoresID(boolean storesID) {
        this.storesID = storesID;
    }

    /**
     * If true, the ID of this element is (R)equired,must be known in order to access
     * the data stored in the xam.
     * @return the requiresID;
     */
    public boolean requiresID() {
        return this.requiresID;
    }

    /**
     * This method sets the variable {@link #requiresID} to the given value.
     * 
     * @param requireID the requireID to set
     */
    public void setRequiresID(boolean requiresID) {
        this.requiresID = requiresID;
    }

    /**
     * If true, "ID i" was specified for this node in the XAM file, so the identity ID for
     * this node should be stored. 
     * 
     * @return the identityID
     */
    public boolean isIdentityIDType() {
        return this.identityID;
    }

    /**
     * If true, "ID o" was specified for this node in the XAM file, so the order preserving ID for
     * this node should be stored.
     * 
     * @return the orderID
     */
    public boolean isOrderIDType() {
        return this.orderID;
    }

    /**
     * If true, "ID s" was specified for this node in the XAM file, so the structural ID for
     * this node should be stored. 
     * 
     * @return the structID
     */
    public boolean isStructIDType() {
        return this.structID;
    }

    /**
     * If true, "ID u" was specified for this node in the XAM file, so the update ID for
     * this node should be stored. 
     * 
     * @return the updateID
     */
    public boolean isUpdateIDType() {
        return this.updateID;
    }

    /**
     * This method sets which type of ID will be stored for this node.
     * 
     * @param identityID to set
     * @param orderID to set
     * @param structID to set
     * @param updateID to set
     */
    public void setIDType(boolean identityID, boolean orderID, boolean structID, boolean updateID) {
        this.identityID = identityID;
        this.orderID = orderID;
        this.structID = structID;
        this.updateID = updateID;
    }

    /* Tag related getters/setters */
    /**
     * If true, the tag of this element is stored.
     * 
     * @return the storesTag
     */
    public boolean storesTag() {
        return this.storesTag;
    }

    /**
     * If it is set to true, the tag of this element is stored. 
     * 
     * @param storesTag the storesTag to set
     */
    public void setStoresTag(boolean storesTag) {
        this.storesTag = storesTag;
    }

    /**
     * If true, the Tag of this element is (R)equired,must be known in order to access
     * the data stored in the xam.
     * @return the requiresID;
     */
    public boolean requiresTag() {
        return this.requiresTag;
    }

    /**
     * This method sets the variable {@link #requiresTag} to the given value.
     * 
     * @param requireTag the requireTag to set
     */
    public void setRequiresTag(boolean requiresTag) {
        this.requiresTag = requiresTag;
    }

    /**
     * If true, there is a selection on tag.
     * 
     * @return the selectOnTag
     */
    public boolean selectsTag() {
        return this.selectOnTag;
    }

    /**
     * If it is set to true, there is a selection on the namespace and
     * tag specified.
     * 
     * @param selectOnTag
     * @param namespace
     * @param tag
     */
    public void setSelectOnTag(boolean selectOnTag, String namespace, String tag) {
        this.selectOnTag = selectOnTag;
        if (selectOnTag) {
            this.namespace = namespace;
            this.tag = tag;
        } else {
            this.namespace = "";
            this.tag = "*";
        }
    }

    /**
     * Returns the namespace stored for this node.
     * 
     * @return the namespace
     */
    public String getNamespace() {
        return namespace;
    }

    /**
     * Sets the {@link #namespace} for this node.
     * 
     * @param namespace the namespace to set
     */
    public void setNamespace(String namespace) {
        this.namespace = namespace;

    }

    /**
     * Returns the tag stored for this node.
     * 
     * @return the tag
     */
    public String getTag() {
        return tag;
    }

    /**
     * Sets the {@link #tag} for this node.
     * 
     * @param tag the tag to set
     */
    public void setTag(String tag) {
        this.tag = tag;
    }

    /* Value related getters/setters */
    /**
     * If true, the value of this element is stored.
     * 
     * @return the storesValue
     */
    public boolean storesValue() {
        return this.storesValue;
    }

    /**
     * If set to true, the value of this element is stored.
     * 
     * @param storesValue the storesValue to set
     */
    public void setStoresValue(boolean storesValue) {
        this.storesValue = storesValue;
    }

    /**
     * If true, the value of this element is (R)equired,must be known in order to access
     * the data stored in the xam.
     * @return the requiresVal;
     */
    public boolean requiresVal() {
        return this.requiresVal;
    }

    /**
     * This method sets the variable {@link #requiresVal} to the given value.
     * 
     * @param requireID the requireID to set
     */
    public void setRequiresVal(boolean requiresVal) {
        this.requiresVal = requiresVal;
    }

    /**
     * If true, there is a selection on value.
     * 
     * @return the selectOnValue
     */
    public boolean selectsValue() {
        return this.selectOnValue;
    }

    public PredicateType getSelectOnValuePredicate() {
        return this.selectOnValuePredicate;
    }

    /**
     * Returns the {@link #value} for this node.
     * 
     * @return the value
     */
    public String getStringValue() {
        return this.stringValue;
    }

    public double getDoubleValue() {
        return this.doubleValue;
    }

    /**
     * If it is set to true, there is a selection on the value specified.
     * 
     * @param selectOnValue
     * @param value
     */
    public void setSelectOnValue(boolean selectOnValue, PredicateType predType, String value) {
        this.selectOnValue = selectOnValue;
        if (selectOnValue) {
            this.selectOnValuePredicate = predType;
            this.stringValue = value;
            this.doubleValue = -1;
        }
    }

    /**
     * If it is set to true, there is a selection on the value specified.
     * 
     * @param selectOnValue
     * @param value
     */
    public void setSelectOnValue(boolean selectOnValue, PredicateType predType, double value) {
        this.selectOnValue = selectOnValue;
        if (selectOnValue) {
            this.selectOnValuePredicate = predType;
            this.stringValue = null;
            this.doubleValue = value;
        }
    }

    /* Content related getters/setters */
    /**
     * If true, the content of this element is stored.
     * 
     * @return the storesContent
     */
    public boolean storesContent() {
        return this.storesContent;
    }

    /**
     * If set to true, the content of this element is stored.
     * 
     * @param storesContent the storesContent
     */
    public void setStoresContent(boolean storesContent) {
        this.storesContent = storesContent;
    }

    /* Edges related getters/setters */
    /**
     * Returns the edge that connects this node to its parent/ancestor.
     * 
     * @return the parentEdge
     */
    public NavigationTreePatternEdge getParentEdge() {
        return this.parentEdge;
    }

    /**
     * Returns whether this given node is linked to its parent through a nested edge
     * @return
     */
    public boolean isNested() {
        if (this.parentEdge == null)
            return false;
        return this.parentEdge.isNested();
    }

    /**
     * Returns the list of edges that connect this node to its children/descendants.
     * 
     * @return the edges
     */
    public ArrayList<NavigationTreePatternEdge> getEdges() {
        return this.edges;
    }

    /**
     * Sets the list of edges that connect this node to its children/descendants.
     * 
     * @param edges the edges
     */
    public void setEdges(ArrayList<NavigationTreePatternEdge> edges) {
        this.edges = edges;
    }

    /**
     * Returns true if this node is the top returning node of the tree pattern.
     * Assumes that the node returns at least an ID, so we don't check for that.
     * Also assumes that the node selects on the tag and does not return it.
     * @return isTopReturningNode
     */
    public boolean isTopReturningNode() {
        if (this.isTopReturningNode == 0) {
            return false;
        }
        if (this.isTopReturningNode == 1) {
            return true;
        }
        // otherwise, it is -1, and we need to look
        if (this.parentEdge == null) {
            return true;
        }
        NavigationTreePatternNode n2 = this.parentEdge.n1;
        Stack<NavigationTreePatternNode> sn = new Stack<NavigationTreePatternNode>();
        sn.push(n2);
        while (!sn.empty()) {
            NavigationTreePatternNode n3 = sn.pop();
            if (n3 == null) {
                this.isTopReturningNode = 1;
                return true;
            }
            if (n3.storesID || n3.storesValue || (n3.storesContent)) {
                this.isTopReturningNode = 0;
                return false;
            }
            if (n3.parentEdge != null) {
                sn.push(n3.parentEdge.n1);
            }
        }
        this.isTopReturningNode = 1;
        return true;
    }

    /**
     * The nested depth of this node.
     * 
     * @return the nestingDepth
     */
    public int getNestingDepth() {
        return this.nestingDepth;
    }

    /**
     * Builds the nested depth from this node.
     *//*
        public void setNestingDepth() {
        Stack<NavigationTreePatternNode> st = new Stack<NavigationTreePatternNode>();
        st.push(this);
        int currentDepth = 0;
        Stack<Integer> st2 = new Stack<Integer>();
        st2.push(new Integer(currentDepth));
            
        while (!st.empty()) {
         NavigationTreePatternNode x = (NavigationTreePatternNode) st.pop();
         Integer y = (Integer) st2.pop();
         x.nestingDepth = y.intValue();
            
         Iterator<NavigationTreePatternEdge> it = x.edges.iterator();
         while (it.hasNext()) {
            NavigationTreePatternEdge e = it.next();
            st.push(e.n2);
            if (e.isNested()) {
               st2.push(new Integer(x.nestingDepth + 1));
            } else {
               st2.push(y);
            }
         }
        }
        }*/
    public void setNestingDepth(int depth) {
        nestingDepth = depth;
    }

    /**
     * Returns true if this node is virtual.
     * 
     * @return the virtual
     */
    public boolean isVirtual() {
        return virtual;
    }

    /* Displaying and printing methods */
    /**
     * Recursive method that transforms the subtree starting from this node
     * into its equivalent in a XAM file.
     * 
     * @param nodeBuffer buffer that will be filled with the definition
     * of each node
     * @param edgeBuffer buffer that will be filled with the definition
     * of the edges (relationship between the nodes)
     * @param parentNo the node code of the last parent
     * @param availableNo the node code of the actual node
     * @return the next available node code
     */
    protected int toXAMString(StringBuffer nodeBuffer, StringBuffer edgeBuffer, int parentNo, int availableNo) {
        nodeBuffer.append("\n");
        int myCode = availableNo;
        int nextAvailable = availableNo + 1;
        // Parameters.logger.info("toXAMString on " + this.tag + " tag value: " +
        // this.tagValue
        // + " needs Tag: " + this.needsTag + " selects on tag: " +
        // this.selectOnTag);
        if (!this.isAttribute) {
            nodeBuffer.append("E: ");
        } else {
            nodeBuffer.append("A: ");
        }
        nodeBuffer.append(availableNo);
        nodeBuffer.append(" ");
        if (this.storesID) {
            nodeBuffer.append(" ID ");
            if (this.updateID) {
                nodeBuffer.append("u ");
            }
            if (this.structID || (!this.orderID && !this.updateID && !this.structID)) {
                nodeBuffer.append("s ");
            }
            if (this.orderID || this.identityID) {
                nodeBuffer.append("i ");
            }
        }
        if (this.requiresID) {
            nodeBuffer.append(" R ");
        }
        if (this.selectOnTag) {
            nodeBuffer.append("[Tag=\"");
            if (!this.namespace.equals(""))
                nodeBuffer.append(startNamespaceDelimiter + this.namespace + endNamespaceDelimiter);
            nodeBuffer.append(this.tag + "\"] ");
        }
        if (this.storesTag) {
            nodeBuffer.append("Tag ");
        }
        if (this.requiresTag) {
            nodeBuffer.append(" R ");
        }

        if (this.selectOnValue) {
            nodeBuffer.append("[Val=\"" + this.stringValue + "\"] ");
        }
        if (this.storesValue) {
            nodeBuffer.append("Val ");
        }
        if (this.requiresVal) {
            nodeBuffer.append(" R ");
        }
        if (this.storesContent) {
            nodeBuffer.append("Cont ");
        }
        Iterator<NavigationTreePatternEdge> itE = this.edges.iterator();
        while (itE.hasNext()) {
            NavigationTreePatternEdge pe = (NavigationTreePatternEdge) itE.next();
            NavigationTreePatternNode child = pe.n2;
            edgeBuffer.append(myCode + "," + nextAvailable + " ");
            if (pe.isParent()) {
                edgeBuffer.append("/");
            } else {
                edgeBuffer.append("//");
            }
            if (pe.isNested()) {
                edgeBuffer.append("n");
            }
            if (pe.isOptional()) {
                edgeBuffer.append("o");
            } else {
                edgeBuffer.append("j");
            }
            edgeBuffer.append("\n");
            nextAvailable = child.toXAMString(nodeBuffer, edgeBuffer, myCode, nextAvailable);
        }
        return nextAvailable;
    }

    /**
     * This method informally display the tree starting from this node.
     * Optional edges are marked with "?". Edges that lead to children
     * are nested within parenthesis in the parent.
     * We can specify several level of details while transforming the
     * tree into a String.
     * The levels of detail are defined in the {@link PrintingLevel}
     * enumeration.
     * 
     * @param level of detail
     * @return the tree representation
     */
    public String treeToString(PrintingLevel level) {
        StringBuffer sb = new StringBuffer();
        this.recTreeToString(sb, level);
        return new String(sb);
    }

    private void recTreeToString(StringBuffer sb, PrintingLevel level) {
        switch (level) {
        case SIMPLIFY:
            sb.append(nodeString());
            break;
        case EXTENDED:
            sb.append(nodeExtendedString());
            break;
        default:
            logger.error("Printing level not defined properly: I don't know how to print patterns!");
        }
        if (edges.size() > 1) {
            sb.append("(");
        }
        Iterator<NavigationTreePatternEdge> it = edges.iterator();
        while (it.hasNext()) {
            NavigationTreePatternEdge e = (NavigationTreePatternEdge) it.next();
            if (e.isParent()) {
                sb.append("/");
            } else {
                sb.append("//");
            }
            if (e.isNested())
                sb.append("N");
            if (e.isOptional())
                sb.append("O");
            sb.append(" ");
            e.n2.recTreeToString(sb, level);
            sb.append(" ");
        }
        if (edges.size() > 1) {
            sb.append(")");
        }

    }

    /**
     * Prints the node simplify representation.
     * 
     * @return the simplify node representation
     */
    public String toString() {
        return nodeString();
    }

    /*
     * Node simplify representation.
     * 
     * @return the simplify node representation
     */
    private String nodeString() {
        StringBuffer sb = new StringBuffer();
        if (virtual) {
            sb.append("~");
        }
        sb.append(this.isAttribute ? "@" : "");
        if (!this.namespace.equals(""))
            sb.append(this.namespace + ":");
        sb.append(this.tag);// + ":" + this.nodeCode);
        if (this.storesID) {
            sb.append("*");
        }
        if (this.storesContent) {
            sb.append("C");
        }
        if (this.storesValue) {
            sb.append("V");
        }
        if (this.selectOnValue) {
            if (this.stringValue != null)
                sb.append(" {=" + this.stringValue + "} ");
            else {
                sb.append(" {");
                sb.append(this.selectOnValuePredicate);
                sb.append(this.doubleValue);
                sb.append("}");
            }
        }
        return new String(sb);
    }

    /*
     * Extended node representation used for comparing uniquely
     * two nodes in the method {@link #compareTo(PatternNode)}.
     * 
     * @return the extended full node representation
     */
    private String nodeExtendedString() {
        StringBuffer sb = new StringBuffer();
        sb.append(this.virtual ? "~" : "");
        sb.append(this.isAttribute ? "@" : "");
        if (this.selectOnTag) {
            if (!this.namespace.equals(""))
                sb.append(this.namespace + ":");
            sb.append(this.tag);
            sb.append("(" + this.getNodeCode() + ")");
        }
        if (this.storesTag) {
            sb.append("_S");
        }
        if (this.requiresTag) {
            sb.append("R");
        }
        if (this.storesID) {
            sb.append("*");
        }
        if (this.requiresID) {
            sb.append("R");
        }
        if (this.selectOnValue)
            sb.append("#V{=" + this.stringValue + "}");
        if (this.storesValue) {
            sb.append("#V");
        }
        if (this.requiresVal) {
            sb.append("R");
        }
        if (this.storesContent)
            sb.append("#C");
        return new String(sb);
    }

    /**
      * The following method adds the code for the graphical representation of this
      * XAM node to the StringBuffer and then calls the recursiveDraw of its children.
     * 
     * @author Spyros ZOUPANOS
     */
    public void drawTree(StringBuffer sb, String backgroundColor, String foregroundColor) {
        sb.append(-this.hashCode() + " [ fontname=\"Lucida Grande\" fontcolor=\"white\" ] ; \n");

        for (int i = 0; i < edges.size(); i++)
            edges.get(i).n2.recursiveDrawTree(sb, -this.hashCode(), backgroundColor, foregroundColor);
    }

    //TODO: draw matching variables
    private void recursiveDrawTree(StringBuffer sb, int parentNodeCode, String backgroundColor,
            String foregroundColor) {
        sb.append(nodeCode + " [");

        if (storesContent)
            sb.append(" shape=\"trapezium\" color=\"" + foregroundColor + "\" style=\"filled\" fillcolor=\""
                    + backgroundColor + "\" ");
        else if (storesValue)
            sb.append(" color=\"" + foregroundColor + "\" style=\"filled\" fillcolor=\"" + backgroundColor + "\" ");
        else
            sb.append(" color=\"" + foregroundColor + "\" ");

        if (this.selectOnTag && this.tag.length() > 0) {
            sb.append(" label=\"");
            if (this.isAttribute)
                sb.append("@");
            if (this.namespace.equals(""))
                sb.append(this.tag);
            else if (this.namespace.length() > 15)
                sb.append(this.namespace.substring(0, 6) + "..."
                        + this.namespace.substring(this.namespace.length() - 6, this.namespace.length()) + ":"
                        + this.tag);
            else
                sb.append(this.namespace + ":" + this.tag);
            if (this.storesID)
                sb.append(" &bull;");
            if (this.selectOnValue) {
                sb.append("\\n[");
                sb.append(selectOnValuePredicate.toString() + " ");
                if (this.stringValue != null) {
                    sb.append("\\\"" + this.stringValue + "\\\"");
                } else {
                    sb.append(this.doubleValue);
                }
                sb.append("]");
            }
            sb.append("\" ");
        }

        sb.append("] ; \n");

        // if we want to design a parent-child edge, we add it one time
        sb.append(parentNodeCode + " -- " + nodeCode + " ");
        if (parentEdge.isOptional())
            sb.append("[style=\"dashed\"] ");
        if (parentEdge.isNested())
            sb.append("[headlabel=\"n\", labeldistance=3.5, labelangle=20] ");
        sb.append("; \n");
        if (!parentEdge.isParent()) {
            // if we want to design an ancestor-descendant edge, we add it one more time
            sb.append(parentNodeCode + " -- " + nodeCode + " ");
            if (parentEdge.isOptional())
                sb.append("[style=\"dashed\"] ");
            sb.append("; \n");
        }

        for (int i = 0; i < edges.size(); i++)
            edges.get(i).n2.recursiveDrawTree(sb, nodeCode, backgroundColor, foregroundColor);
    }

    /**
     * Returns the name of the columns that this node stores.
     * It is used at the ViP2P GUI.
     * 
     * @return the list of String
     */
    protected ArrayList<String> getColumnsName() {
        ArrayList<String> list = new ArrayList<String>();

        if (this.storesID)
            list.add(nodeCode + ".ID");
        if (this.storesTag)
            list.add(nodeCode + ".Tag");
        if (this.storesValue)
            list.add(nodeCode + ".Val");
        if (this.storesContent)
            list.add(nodeCode + ".Cont");

        return list;
    }

}