/// <summary>
        /// Look for a parent node of the given path (if it exists) and
        /// ask it to refresh.  This is necessary because filesystemwatcher
        /// doesn't always work over network shares.
        /// </summary>
        public void WatchParentOf(string path)
        {
            string      parent = Path.GetDirectoryName(path);
            WatcherNode node   = tree.NodeMap[parent] as WatcherNode;

            if (node != null)
            {
                node.UpdateLater();
            }
        }
    /**
     * Updates the state of the watcher, given reception of a new message.
     *
     * @param m
     *          The message that was received.
     */
    private void update(WatcherMessage m)
    {
        WatcherNode wn2;
        HashSet<WatcherNode> spawnedNodes;
        HashSet<WatcherNode> newNodes = new HashSet<WatcherNode>();
        int size = 0, atoms = 0;

        foreach (WatcherNode wn in m_nodes)
        {
            wn2 = new WatcherNode();
            wn2.setGamma(wn.getDelta());
            spawnedNodes = wn2.spawn(m);

            foreach (WatcherNode wn3 in spawnedNodes)
            {
                newNodes.Add(wn3);
            }
        }

        m_nodes = newNodes;

        // Updates statistics about maximum number of nodes
        size = m_nodes.Count;

        if (size > m_maxNodes)
        {
            m_maxNodes = size;
        }

        // Updates statistics about total number of formulae
        size = 0;
        atoms = 0;

        foreach (WatcherNode wn in m_nodes)
        {
            size += wn.getSize();
            atoms += wn.getAtoms();
        }

        if (size > m_maxFormulae)
        {
            m_maxFormulae = size;
        }

        if (atoms > m_maxAtoms)
        {
            m_maxAtoms = atoms;
        }
    }
    /**
     * Resets the watcher's state, i.e. considers the next message to be received
     * as the first one.
     */
    public override void reset()
    {
        WatcherNode wn = new WatcherNode();
        HashSet<Operator> hs = new HashSet<Operator>();

        hs.Add(m_formulaToWatch);
        wn.setDelta(hs);
        m_nodes.Clear();
        m_nodes.Add(wn);
        m_maxNodes = 1;
        m_maxFormulae = 0;
        m_maxAtoms = 0;
    }
    /**
     * Decomposes a watcher's node, given a received message.
     *
     * @param m
     *          The message on which to make the decomposition
     * @return A Set of WatcherNodes consisting of the resulting decomposition
     *         for that node. If the left-hand side of the node is empty,
     *         returns an empty set.
     */
    public HashSet<WatcherNode> spawn(WatcherMessage m)
    {
        // This method uses getClass to branch on the type of operator
        // used; TODO: redesign with method overriding
        HashSet<WatcherNode> spawnedSet, outSet = new HashSet<WatcherNode>();
        WatcherNode wn;
        Operator o2, o3;

        // Picks a formula and removes it from Gamma
        foreach (Operator o in m_gamma)
        {
            m_gamma.Remove(o);

            // Optimization from original algorithm: if formula can readily
          		// be decided in current state, bypass decomposition
            Outcome oc = isSatisfiedInCurrentState(o, m);

            if (oc == Outcome.TRUE)
            {
                // Formula is true: stop decomposition of this formula and
                // continue spawning
                return spawn(m);
            }

            else if (oc == Outcome.FALSE)
            {
                // Formula is false: stop branch and return empty set
                return outSet;
            }

            // Operator OR
            else if (o.GetType() == typeof(OperatorOr))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorOr)o).getLeftOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorOr)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator IMPLIES
            else if (o.GetType() == typeof(OperatorImplies))
            {
                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorImplies)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorImplies)o).getLeftOperand().getNegated();
                wn.addToGamma(o2.getNegatedNormalForm());
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator AND
            else if (o.GetType() == typeof(OperatorAnd))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorAnd)o).getLeftOperand();
                wn.addToGamma(o2);

                // Do for left operand
                o2 = ((OperatorAnd)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator X
            else if (o.GetType() == typeof(OperatorX))
            {
                wn = new WatcherNode(this);
                o2 = ((OperatorX)o).getOperand();
                wn.addToDelta(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator G
            else if (o.GetType() == typeof(OperatorG))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorG)o).getOperand();
                wn.addToGamma(o2);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator F
            else if (o.GetType() == typeof(OperatorF))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorF)o).getOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator U
            else if (o.GetType() == typeof(OperatorU))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorU)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator V
            else if (o.GetType() == typeof(OperatorV))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorV)o).getLeftOperand();
                wn.addToGamma(o2);
                o2 = ((OperatorV)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                o2 = ((OperatorV)o).getRightOperand();
                wn.addToGamma(o2);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator [p=x]
            else if (o.GetType() == typeof(FOForAll))
            {
                // Iterate over domain
                wn = new WatcherNode(this);

                // TODO: supposes that the string qualifier is an atom
                Atom p = new Atom(((FOForAll)o).getQualifier());
                Atom x = ((FOForAll)o).getQuantifiedVariable();
                HashSet<Atom> s = m.getDomain(p);

                foreach (Atom v in s)
                {
                    o2 = ((FOForAll)o).getOperand();
                    o3 = o2.evaluate(x, v);
                    wn.addToGamma(o3);
                }

                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator <p=x>
            else if (o.GetType() == typeof(FOExists))
            {
                // Iterate over domain
                // TODO: supposes that the string qualifier is an atom
                Atom p = new Atom(((FOExists)o).getQualifier());
                Atom x = ((FOExists)o).getQuantifiedVariable();
                HashSet<Atom> s = m.getDomain(p);

                foreach (Atom v in s)
                {
                    wn = new WatcherNode(this);
                    o2 = ((FOExists)o).getOperand();
                    o3 = o2.evaluate(x, v);
                    wn.addToGamma(o3);
                    spawnedSet = wn.spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }
                }

                // TODO (Dominic): Validate this ?
                return outSet;
            }

            // Operator =
            else if (o.GetType() == typeof(OperatorEquals))
            {
                // This should never happen! When down to the evaluation
                // of an equality, all variables should be evaluated!
                //assert(false);
                return outSet;
            }

            // Operator NOT
            else if (o.GetType() == typeof(OperatorNot))
            {
                // Do for operand
                wn = new WatcherNode(this);
                o2 = ((OperatorNot)o).getOperand();

                if (o2.GetType() != typeof(Constant))
                {
                    // This should not happen! Negations should be pushed
          			// to atoms
                    //assert(false);
                    return outSet;
                }

                // TODO: true and false are checked by comparing their
                // string representations; there should be a more graceful
                // way
                // to check for true and false
                if (((Constant)o).getSymbol() == Operator.m_trueAtom.getSymbol())
                {
                    // Constant TRUE, i.e. evaluates to FALSE: this branch
          			// does not return anything
          			// i.e. do nothing
                }

                else if (((Constant)o).getSymbol() == Operator.m_falseAtom.getSymbol())
                {
                    // Constant FALSE, i.e. evaluates to TRUE: just pass on
          			// the recursive evaluation
                    spawnedSet = spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }

                    return outSet;
                }

                else
                {
                    // This should never happen! All atoms should evaluate
          			// down
          			// to either true or false.
                    System.Diagnostics.Debug.Assert (false, "Unrecognized operator in watcher node");
                    return outSet;
                }
            }

            // Constants (true or false)
            else if (o.GetType() == typeof(Constant))
            {
                // TODO: true and false are checked by comparing their
                // string representations; there should be a more graceful
                // way
                // to check for true and false
                if (((Constant)o).getSymbol() == Operator.m_trueAtom.getSymbol())
                {
                    // Constant TRUE: just pass on the recursive evaluation
                    spawnedSet = spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }
                }

                else if (((Constant)o).getSymbol() == Operator.m_falseAtom.getSymbol())
                {
                    // This branch is stopped: this branch does not return
          			// anything
          			// i.e. do nothing
                    return outSet;
                }

                else
                {
                    // This should never happen! All atoms should evaluate
          			// down to either true or false.
                    //assert(false);
                    return outSet;
                }
            }
        }

        if (m_gamma.Count == 0)
        {
            // Gamma is empty: return a set with myself
            outSet.Add(this);
        }

        return outSet;
    }
 /**
  * Constructs a WatcherNode based on another WatcherNode. (NOTE: deep or
  * shallow copy?)
  *
  * @param wn
  *          The WatcherNode of which to make a copy.
  */
 public WatcherNode(WatcherNode wn)
     : base(wn.getGamma(), wn.getDelta())
 {
 }