View Javadoc

1   /*
2    * Zemucan: A Syntax Assistant for DB2
3    * Copyright (C) 2009, 2010 Andres Gomez Casanova
4    *
5    * This file is part of Zemucan.
6    *
7    * Zemucan is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as published by
9    * the Free Software Foundation; either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * Zemucan is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public License
18   * along with this library; if not, see <http://www.gnu.org/licenses/>.
19   *
20   * Contact:
21   * a n g o c a  at  y a h o o  dot  c o m
22   * Cra. 45 No 61 - 31, Bogota, Colombia.
23   *
24   * Author:   $LastChangedBy: angoca $:
25   * Date:     $LastChangedDate: 2011-03-06 10:15:32 -0500 (dom, 06 mar 2011) $:
26   * Revision: $LastChangedRevision: 1913 $:
27   * URL:      $HeadURL: https://zemucan.svn.sourceforge.net/svnroot/zemucan/branches/zemucan_v1/source-code/graph/src/main/java/name/angoca/zemucan/core/graph/model/Graph.java $:
28   */
29  package name.angoca.zemucan.core.graph.model;
30  
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  
38  import name.angoca.zemucan.AbstractZemucanException;
39  import name.angoca.zemucan.ParameterNullException;
40  import name.angoca.zemucan.tools.Constants;
41  import name.angoca.zemucan.tools.configurator.Configurator;
42  
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  /**
47   * This class models the graph that represents the read grammar. The
48   * construction of the graph is done in two phases: add nodes, and associate
49   * node.
50   * <p>
51   * The first phase only permits to add nodes to the graph. This is done with the
52   * method addNode. Try to establish a relation between nodes is not permitted in
53   * this phase.
54   * <p>
55   * The second phase permits to establish relation between nodes, once they have
56   * been defined. In this phase is not possible to add more nodes.
57   * <p>
58   * In order to change the phase, the method 'firstPhaseFinished' has to be
59   * called. Once this method is called, is not possible to go back.
60   * <p>
61   * The method 'secondPhaseFinished' before to return the StartingNode, it checks
62   * the graph if it is stable and there are not inconsistencies. Also, at this
63   * phase, the extra nodes are added. The extra nodes include the help node and
64   * the about node.
65   * <p>
66   * This object is immutable, once it is created, their properties cannot be
67   * changed. This is the reason because there are not setter methods. The getters
68   * methods are for tests purposes and have a default visibility.
69   * <p>
70   * <b>Control Version</b>
71   * <p>
72   * <ul>
73   * <li>1.0.0 Class creation.</li>
74   * <li>1.0.1 The name attribute is used for logging.</li>
75   * <li>1.1.0 GraphState.</li>
76   * <li>1.2.0 Merge and simplify graph.</li>
77   * <li>1.2.1 Extra nodes as part of first phase.</li>
78   * <li>1.3.0 GetNodes.</li>
79   * <li>1.3.1 Extra nodes addition fixed.</li>
80   * <li>1.4.0 Some methods were changed to static.</li>
81   * <li>1.4.1 Exception deleted.</li>
82   * <li>1.4.2 About and license tokens.</li>
83   * <li>1.5.0 GraphNode hierarchy.</li>
84   * <li>1.6.0 Next Node: Create / Remove relation from nodes. Simplify graph, add
85   * node returning the created one.</li>
86   * </ul>
87   *
88   * @author Andres Gomez Casanova <a
89   *         href="mailto:a n g o c a at y a h o o dot c o m" >(AngocA)</a>
90   * @version 1.6.0 2010-09-15
91   */
92  public class Graph {
93  
94      /**
95       * Logger.
96       */
97      private static final Logger LOGGER = LoggerFactory.getLogger(Graph.class);
98  
99      /**
100      * Change the ascendancy associations of a pair of nodes, to the other one.
101      * <p>
102      * The nodes are not the same.
103      *
104      * @param n1
105      *            Node to add the other node's associations.
106      * @param n2
107      *            The associations of this node will be taken out. After this
108      *            process it will be deleted.
109      * @param node
110      *            Node to relocate the associations.
111      * @throws AbstractGraphException
112      *             If one node is null or there is a problem with the
113      *             associations.
114      */
115     private static void changeAscendancyNodeRelations(
116             final AbstractGraphNode/* ! */n1, final AbstractGraphNode/* ! */n2,
117             final AbstractGraphNode /* ! */node) throws AbstractGraphException {
118         assert n1 != null;
119         assert n2 != null;
120         assert node != null;
121         assert n1 != n2;
122         assert n2 != node;
123 
124         // First removes (If it's Starting or Ending Node)
125         node.removeChild(n2);
126         n2.removeParent(node);
127         // Then, adds relations.
128         n1.addParent(node);
129         node.addChild(n1);
130     }
131 
132     /**
133      * Change the descendancy associations of a pair of nodes, to the other one.
134      * <p>
135      * The nodes are not the same.
136      *
137      * @param n1
138      *            Node to add the other node's associations.
139      * @param n2
140      *            The associations of this node will be taken out. After this
141      *            process this node will be deleted.
142      * @param node
143      *            Node to relocate the associations.
144      * @throws AbstractGraphException
145      *             If one node is null or there is a problem with the
146      *             associations.
147      */
148     private static void changeDescendancyNodeRelations(
149             final AbstractGraphNode/* ! */n1, final AbstractGraphNode/* ! */n2,
150             final AbstractGraphNode /* ! */node) throws AbstractGraphException {
151         assert n1 != null;
152         assert n2 != null;
153         assert node != null;
154         assert n1 != n2;
155         assert n2 != node;
156 
157         // First adds the parent relation.
158         node.addParent(n1);
159         
160         // Then removes the relation.
161         node.removeParent(n2);
162         n2.removeChild(node);
163         
164         // Finally add the child relation.
165         n1.addChild(node);
166     }
167 
168     /**
169      * Checks the children of a given node to see if there are two children that
170      * represents the same word.
171      *
172      * @param node
173      *            Node to check its children.
174      * @return true if there are two children representing the same word.
175      * @throws AbstractZemucanException
176      */
177     static boolean checkChildren(final AbstractGraphNode/* ! */node)
178             throws AbstractZemucanException {
179         assert node != null;
180 
181         boolean ret = false;
182         // Checks the names of the nodes in a sorted way.
183         if (node.getChildren().size() > 1) {
184             Graph.LOGGER.debug("Fusioning children in {}", node.getId()); //$NON-NLS-1$
185             // Sort the children
186             final List<AbstractGraphNode> children = new ArrayList<AbstractGraphNode>();
187             children.addAll(node.getChildren());
188             Collections.sort(children);
189             AbstractGraphNode n1 = children.get(0);
190             Graph.LOGGER.debug("Analyzing to fusion: {}", n1.getId()); //$NON-NLS-1$
191             // This condition changes dynamically, so it is calculated
192             // for each iteration.
193             for (int i = 1; i < children.size(); i += 1) {
194                 final AbstractGraphNode n2 = children.get(i);
195                 if ((n2 != null)
196                         && (n1 instanceof TextualGraphNode)
197                         && (n2 instanceof TextualGraphNode)
198                         && ((TextualGraphNode) n1).getName().equals(
199                                 ((TextualGraphNode) n2).getName())) {
200                     // Fusion the nodes.
201                     Graph.LOGGER.debug("Fusionning nodes {} and {}",
202                             n1.getId(), n2.getId());
203                     Graph.nodeFusion(n1, n2);
204                     ret = true;
205                 } else if (n2 != null) {
206                     // Second token, becomes first token.
207                     n1 = n2;
208                 } else {
209                     // Error.
210                     assert false;
211                 }
212             }
213         } else {
214             Graph.LOGGER.debug("No children to fusion in {}", node.getId()); //$NON-NLS-1$
215         }
216         return ret;
217     }
218 
219     /**
220      * Recursive method to check that all the ways of a graph goes to
221      * EndingNode.
222      *
223      * @param node
224      *            Node to tests its ways.
225      */
226     private static void goBackward(final AbstractGraphNode /* ! */node) {
227         assert node != null;
228 
229         if (!node.isGoToEndingNode()) {
230             node.setGoToEndingNode(true);
231             final List<AbstractGraphNode> parents = node.getParents();
232             for (final AbstractGraphNode graphNode : parents) {
233                 Graph.goBackward(graphNode);
234             }
235         }
236     }
237 
238     /**
239      * Recursive method to check that all the ways of a graph comes from
240      * StartingNode.
241      *
242      * @param node
243      *            Node to tests its ways.
244      */
245     private static void goForward(final AbstractGraphNode /* ! */node) {
246         assert node != null;
247 
248         if (!node.isComeFromStartingNode()) {
249             node.setComeFromStartingNode(true);
250             final List<AbstractGraphNode> children = node.getChildren();
251             for (final AbstractGraphNode graphNode : children) {
252                 Graph.goForward(graphNode);
253             }
254         }
255     }
256 
257     /**
258      * Fusion two nodes into one.
259      * <p>
260      * The nodes are not the same.
261      *
262      * @param n1
263      *            First node to merge, and resulting node.
264      * @param n2
265      *            Second node to merge. It will be deleted.
266      * @throws AbstractGraphException
267      *             If there is a problem changing the associations.
268      */
269     static void nodeFusion(final AbstractGraphNode/* ! */n1,
270             final AbstractGraphNode/* ! */n2)
271             throws AbstractZemucanException {
272         assert n1 != null;
273         assert n2 != null;
274         assert n1 != n2;
275 
276         // Fusion of children. The size decrease in each iteration.
277         while (n2.getChildren().size() >= 1) {
278             final AbstractGraphNode node = n2.getChildren().get(0);
279             Graph.changeDescendancyNodeRelations(n1, n2, node);
280         }
281         // Fusion of parents.
282         while (n2.getParents().size() >= 1) {
283             final AbstractGraphNode node = n2.getParents().get(0);
284             Graph.changeAscendancyNodeRelations(n1, n2, node);
285         }
286     }
287 
288     /**
289      * Ending node of the graph.
290      */
291     private EndingNode endingNode;
292 
293     /**
294      * Determines if the graph has to include the extra nodes.
295      */
296     private final boolean extraNodes;
297 
298     /**
299      * This parameter permits to determine the state of the graph. If the graph
300      * has all the nodes, and it waits for the second phase when the relations
301      * are established. Or if the graph has a valid structure.
302      */
303     private GraphState graphState;
304 
305     /**
306      * The name of the graph. This aids to identify the graph when merging
307      * graphs.
308      */
309     private String name;
310 
311     /**
312      * Set of nodes. In this array, at the first phase only reads the nodes but
313      * they do not have relations. The second phase associates the nodes.
314      */
315     private Map<String, AbstractGraphNode> nodes;
316 
317     /**
318      * Starting node of the graph.
319      */
320     private StartingNode startingNode;
321 
322     /**
323      * Creates a graph with a given name. The name works as identifier when
324      * dealing with several graphs.
325      *
326      * @param graphName
327      *            Name of this graph.
328      * @param addExtraNodes
329      *            Determines if the graph has to include some extra predefined
330      *            nodes.
331      * @throws ParameterNullException
332      *             If the graph name is not given.
333      */
334     public Graph(final String/* ! */graphName, final boolean addExtraNodes)
335             throws ParameterNullException {
336         if (graphName == null) {
337             throw new ParameterNullException("name"); //$NON-NLS-1$
338         }
339 
340         this.name = graphName;
341 
342         this.nodes = new HashMap<String, AbstractGraphNode>();
343         this.graphState = GraphState.FirstPhase;
344 
345         this.extraNodes = addExtraNodes;
346 
347         Graph.LOGGER.info("'{}' graph started.", graphName); //$NON-NLS-1$
348     }
349 
350     /**
351      * Adds the about information, license and help node to the graph. It can be
352      * called several times, but it will add the nodes just once.
353      *
354      * @throws AbstractZemucanException
355      *             If there is a problem adding the associations.
356      */
357     private void addExtraNodes() throws AbstractGraphException {
358         if ((this.startingNode != null) && (this.endingNode != null)) {
359             // About node
360             final GraphNode aboutCaller;
361             final String aboutNode = Configurator.getInstance().getProperty(
362                     Constants.ABOUT_TOKEN_PROPERTY);
363             if (aboutNode == null) {
364                 aboutCaller = new GraphNode(Constants.ABOUT_TOKEN_ID,
365                         Constants.ABOUT_TOKEN_VALUE, this);
366             } else {
367                 aboutCaller = new GraphNode(Constants.ABOUT_TOKEN_ID,
368                         aboutNode, this);
369             }
370             final AboutNode about = new AboutNode(this);
371 
372             // Adds the nodes to the list.
373             this.nodes.put(this.name + ':' + Constants.ABOUT_TOKEN_ID,
374                     aboutCaller);
375             this.nodes.put(this.name + ':' + Constants.ABOUT_CONTENT_TOKEN_ID,
376                     about);
377 
378             // Adds double relations
379             this.startingNode.addChild(aboutCaller);
380             aboutCaller.addParent(this.startingNode);
381             aboutCaller.addChild(about);
382             about.addParent(aboutCaller);
383             about.addChild(this.endingNode);
384             this.endingNode.addParent(about);
385 
386             // Help node
387             final GraphNode helpCaller;
388             final String helpNode = Configurator.getInstance().getProperty(
389                     Constants.HELP_TOKEN_PROPERTY);
390             if (helpNode == null) {
391                 helpCaller = new GraphNode(Constants.HELP_TOKEN_ID,
392                         Constants.HELP_TOKEN_VALUE, this);
393             } else {
394                 helpCaller = new GraphNode(Constants.HELP_TOKEN_ID, helpNode,
395                         this);
396             }
397             final HelpNode help = new HelpNode(this);
398 
399             // Adds the nodes to the list.
400             this.nodes.put(this.name + ':' + Constants.HELP_TOKEN_ID,
401                     helpCaller);
402             this.nodes.put(this.name + ':' + Constants.HELP_CONTENT_TOKEN_ID,
403                     help);
404 
405             // Adds double relations
406             this.startingNode.addChild(helpCaller);
407             helpCaller.addParent(this.startingNode);
408             helpCaller.addChild(help);
409             help.addParent(helpCaller);
410             help.addChild(this.endingNode);
411             this.endingNode.addParent(help);
412 
413             // License node
414             final GraphNode licenseCaller;
415             final String licenseNode = Configurator.getInstance().getProperty(
416                     Constants.LICENSE_TOKEN_PROPERTY);
417             if (licenseNode == null) {
418                 licenseCaller = new GraphNode(Constants.LICENSE_TOKEN_ID,
419                         Constants.LICENSE_TOKEN_VALUE, this);
420             } else {
421                 licenseCaller = new GraphNode(Constants.LICENSE_TOKEN_ID,
422                         licenseNode, this);
423             }
424             final LicenseNode license = new LicenseNode(this);
425 
426             // Adds the nodes to the list.
427             this.nodes.put(this.name + ':' + Constants.LICENSE_TOKEN_ID,
428                     licenseCaller);
429             this.nodes.put(
430                     this.name + ':' + Constants.LICENSE_CONTENT_TOKEN_ID,
431                     license);
432 
433             // Adds double relations
434             this.startingNode.addChild(licenseCaller);
435             licenseCaller.addParent(this.startingNode);
436             licenseCaller.addChild(license);
437             license.addParent(licenseCaller);
438             license.addChild(this.endingNode);
439             this.endingNode.addParent(license);
440         } else {
441             Graph.LOGGER
442                     .info("Extra nodes could not be added, there were not " + //$NON-NLS-1$
443                             "StartingNode or EndingNode."); //$NON-NLS-1$
444         }
445     }
446 
447     /**
448      * Adds a new node in the graph. The given parameters permit to build the
449      * node before to add it. This method does not establishes associations.
450      *
451      * @param nodeId
452      *            Id of the node.
453      * @param nodeName
454      *            Name of the node.
455      * @param reserved
456      *            if the node represents a reserved word.
457      * @return The created node.
458      * @throws AbstractGraphException
459      *             If the graph state is invalid, or if the graph name is
460      *             invalid, or if the node id already exists in the graph.
461      */
462     public final AbstractGraphNode addNode(final String/* ! */nodeId,
463             final String/* ! */nodeName, final boolean reserved)
464             throws AbstractGraphException {
465         if (!this.graphState.equals(GraphState.FirstPhase)) {
466             throw new InvalidGraphStateException(this.graphState,
467                     GraphState.FirstPhase);
468         }
469 
470         Graph.LOGGER.debug("Adding node '{}' ({}, {}) in '{}' graph", //$NON-NLS-1$
471                 new String[] { nodeId, nodeName, Boolean.toString(reserved),
472                         this.name });
473 
474         // Creates a new node with its parameters
475         AbstractGraphNode node = null;
476 
477         if (nodeId.equals(Constants.STARTING_NODE)) {
478             if (nodeName.equals(Constants.STARTING_NODE) && reserved) {
479                 // It's the starting node
480                 node = new StartingNode(this);
481                 this.startingNode = (StartingNode) node;
482             } else {
483                 throw new InvalidStartingNodeException();
484             }
485         } else if (nodeId.equals(Constants.ENDING_NODE)) {
486             if (nodeName.equals(Constants.ENDING_NODE) && reserved) {
487                 // It's the ending node
488                 node = new EndingNode(this);
489                 this.endingNode = (EndingNode) node;
490             } else {
491                 throw new InvalidEndingNodeException();
492             }
493         } else if (nodeId.startsWith(Constants.NEXT_NODE) && reserved) {
494             // It's a next node.
495             node = new NextNode(nodeName, this);
496         } else {
497             // It's a normal node
498             if (reserved) {
499                 node = new GraphNode(nodeId, nodeName, this);
500             } else {
501                 node = new NonReservedGraphNode(nodeId, nodeName, this);
502             }
503 
504         }
505 
506         // Adds the node to list
507         if (this.nodes.containsKey(this.name + ':' + nodeId)) {
508             throw new DuplicatedNodeException(nodeId);
509         }
510         this.nodes.put(this.name + ':' + nodeId, node);
511 
512         Graph.LOGGER.debug("Addition {}", this);
513 
514         assert node != null;
515 
516         return node;
517     }
518 
519     /**
520      * Creates a relation between two nodes. It calls implicitly the method that
521      * creates a relation between two node ids.
522      *
523      * @param parentNode
524      *            Origin of the relation.
525      * @param childNode
526      *            Target of the relation.
527      * @throws AbstractZemucanException
528      *             If there is a problem while creating the relation.
529      */
530     public void addRelation(final AbstractGraphNode parentNode,
531             final AbstractGraphNode childNode)
532             throws AbstractZemucanException {
533         final int size = this.name.length() + 1;
534         String parentId = parentNode.getId();
535         String childId = childNode.getId();
536         if (parentId.startsWith(this.name)) {
537             parentId = parentId.substring(size);
538         }
539         if (childId.startsWith(this.name)) {
540             childId = childId.substring(size);
541         }
542         this.addRelation(parentId, childId);
543         Graph.LOGGER.debug("Relation {}", parentNode);
544     }
545 
546     /**
547      * Adds a relation between two nodes, giving the members of the relation,
548      * the parent and the child.
549      *
550      * @param parentId
551      *            Id of the parent node.
552      * @param childId
553      *            Id of the child node.
554      * @throws AbstractZemucanException
555      *             if the parent or the child does not exist in the set of
556      *             nodes. Or if the state of the graph is invalid. Or when
557      *             trying to make an invalid relation between two nodes. If a
558      *             parameter is null.
559      */
560     public final void addRelation(final String/* ! */parentId,
561             final String/* ! */childId) throws AbstractZemucanException {
562         Graph.LOGGER.debug(
563                 "Adding relation in graph '{}' between '{}' and '{}'", //$NON-NLS-1$
564                 new String[] { this.name, parentId, childId });
565 
566         if (parentId == null) {
567             throw new ParameterNullException("parentId"); //$NON-NLS-1$
568         }
569         if (childId == null) {
570             throw new ParameterNullException("childId"); //$NON-NLS-1$
571         }
572         if (this.graphState.equals(GraphState.FirstPhase)) {
573             throw new InvalidGraphStateException(this.graphState,
574                     GraphState.SecondPhase);
575         }
576         if (this.graphState.equals(GraphState.Validated)) {
577             this.graphState = GraphState.SecondPhase;
578         }
579         // XXX dumpGraphKeys();
580         final String key = this.name + ':' + parentId;
581         final AbstractGraphNode parent = this.nodes.get(key);
582         if (parent == null) {
583             throw new NotExistingNodeException(parentId);
584         }
585 
586         // Retrieves the child and established the relation.
587         final AbstractGraphNode childNode = this.nodes.get(this.name + ':'
588                 + childId);
589         if (childNode == null) {
590             throw new NotExistingChildNodeException(parentId, childId);
591         }
592 
593         parent.addChild(childNode);
594         childNode.addParent(parent);
595     }
596 
597     /**
598      * Checks the graph searching nodes that represents the same word for a
599      * common parent.
600      *
601      * @throws DuplicatedNodeNameException
602      *             If a double node is found.
603      */
604     public final void checkDuplicates() throws DuplicatedNodeNameException {
605         final Iterator<AbstractGraphNode> it = this.nodes.values().iterator();
606         while (it.hasNext()) {
607             final AbstractGraphNode node = it.next();
608             final int size = node.getChildren().size();
609             if (size >= 2) {
610                 final List<AbstractGraphNode> children = new ArrayList<AbstractGraphNode>();
611                 children.addAll(node.getChildren());
612                 Collections.sort(children);
613                 AbstractGraphNode n0 = children.get(0);
614                 for (int i = 1; i < size; i += 1) {
615                     final AbstractGraphNode n1 = children.get(i);
616                     if ((n0 instanceof TextualGraphNode)
617                             && (n1 instanceof TextualGraphNode)) {
618                         final TextualGraphNode textualn0 = (TextualGraphNode) n0;
619                         final TextualGraphNode textualn1 = (TextualGraphNode) n1;
620                         if (textualn0.getName().equals(textualn1.getName())) {
621                             throw new DuplicatedNodeNameException(node.getId(),
622                                     textualn0.getName(), n0.getId(), n1.getId());
623                         }
624                     }
625                     n0 = n1;
626                 }
627             }
628         }
629     }
630 
631     /**
632      * Scans the set of nodes searching if someone has StartingNode as Child.
633      *
634      * @return Always returns true because is used when assertions are
635      *         activated.
636      * @throws AbstractZemucanException
637      *             When a node is detected that references the StartingNode.
638      *             When a node is detected that if referenced by the EndingNode.
639      */
640     private boolean checkNoParentsForStartingNode()
641             throws AbstractZemucanException {
642         for (final AbstractGraphNode node : this.nodes.values()) {
643             // Redundant check that cannot be tested.
644             final List<AbstractGraphNode> ways = node.getChildren();
645             for (final AbstractGraphNode graphNode : ways) {
646                 if (graphNode instanceof StartingNode) {
647                     // It cannot be tested, because GraphNode checks
648                     // the child node when adding relation
649                     throw new ReferencingStartingNodeException(node.getId());
650                 }
651             }
652 
653             // Redundant check that cannot be tested.
654             final List<AbstractGraphNode> parents = node.getParents();
655             for (final AbstractGraphNode graphNode : parents) {
656                 if (graphNode instanceof EndingNode) {
657                     // It cannot be tested, because EndingNode always
658                     // throws an exception when trying to add a child.
659                     throw new ReferencingEndingNodeException(node.getId());
660                 }
661             }
662         }
663         return true;
664     }
665 
666     /*
667      * (non-Javadoc)
668      * @see java.lang.Object#equals(java.lang.Object)
669      */
670     @Override
671     public final boolean equals(final Object obj) {
672         boolean result = false;
673         if (obj instanceof Graph) {
674             final Graph graph = (Graph) obj;
675             if (this.name.equals(graph.name)
676                     && (this.extraNodes == graph.extraNodes)
677                     && (this.graphState == graph.graphState)
678                     && (this.startingNode != null)
679                     && (graph.startingNode != null)
680                     && this.startingNode.equals(graph.startingNode)
681                     && (this.endingNode != null) && (graph.endingNode != null)
682                     && this.endingNode.equals(graph.endingNode)
683                     && (this.nodes.size() == graph.nodes.size())) {
684                 result = true;
685                 for (final String key : this.nodes.keySet()) {
686                     final AbstractGraphNode node = graph.nodes.get(key);
687                     if (!this.exist(key, node)) {
688                         result = false;
689                     }
690                 }
691             }
692         }
693         return result;
694     }
695 
696     /**
697      * Checks if a given key and a node, exist in the current set of nodes.
698      *
699      * @param key
700      *            key of the graph in the set of nodes.
701      * @param node
702      *            Node.
703      * @return If the node exists in the set of nodes.
704      */
705     private boolean exist(final String/* ! */key,
706             final AbstractGraphNode/* ? */node) {
707         assert key != null;
708         assert !key.equals(""); //$NON-NLS-1$
709 
710         boolean result = false;
711         final AbstractGraphNode graphNode = this.nodes.get(key);
712         if ((graphNode != null) && graphNode.equals(node)) {
713             result = true;
714         }
715         return result;
716     }
717 
718     /**
719      * This parameter change the state of the graph. The first phase permits to
720      * add nodes but it does not permit to establish relation between them. The
721      * second phase permits to create associations between nodes when all the
722      * nodes are defined in the graph.
723      *
724      * @throws AbstractZemucanException
725      *             If there is a problem adding the extra associations.
726      */
727     public final void firstPhaseFinished() throws AbstractZemucanException {
728         if (!this.graphState.equals(GraphState.FirstPhase)) {
729             throw new InvalidGraphStateException(this.graphState,
730                     GraphState.FirstPhase);
731         }
732 
733         // Adding extra nodes.
734         if (this.extraNodes) {
735             this.addExtraNodes();
736         }
737 
738         this.graphState = GraphState.SecondPhase;
739 
740         Graph.LOGGER.info("'{}' - First Phase finished.", this.name); //$NON-NLS-1$
741     }
742 
743     /**
744      * Returns the EndingNode. This method can be called only when the graph has
745      * a state of validated; it means that the graph is after the SecondPhase.
746      *
747      * @return EndingNode if the graph is validated.
748      * @throws InvalidGraphStateException
749      *             If the graph is not in Validated state.
750      */
751     final EndingNode/* ! */getEndingNode() throws InvalidGraphStateException {
752         if (!this.graphState.equals(GraphState.Validated)) {
753             throw new InvalidGraphStateException(this.graphState,
754                     GraphState.Validated);
755         }
756         return this.endingNode;
757     }
758 
759     /**
760      * Retrieves the graph state: FirstPhase, SecondPhase or validated.
761      *
762      * @return Returns the state of the graph.
763      */
764     final GraphState/* ! */getGraphState() {
765         return this.graphState;
766     }
767 
768     /**
769      * Returns the name of the graph.
770      *
771      * @return Graph's name.
772      */
773     public final String/* ! */getName() {
774         return this.name;
775     }
776 
777     /**
778      * Returns the set of nodes that compose the graph.
779      *
780      * @return Set of nodes.
781      */
782     final Map<String, AbstractGraphNode> /* <!,!>! */getNodes() {
783         return this.nodes;
784     }
785 
786     /**
787      * Returns the StartingNode. This method can be executed only after the
788      * second phase has been finished (The graph has to be in Validated state.)
789      *
790      * @return StartingNode if the graph is validated..
791      * @throws InvalidGraphStateException
792      *             If the graph is in an invalid state to call this method.
793      */
794     public final StartingNode getStartingNode()
795             throws InvalidGraphStateException {
796         if (!this.graphState.equals(GraphState.Validated)) {
797             throw new InvalidGraphStateException(this.graphState,
798                     GraphState.Validated);
799         }
800         return this.startingNode;
801     }
802 
803     /*
804      * (non-Javadoc)
805      * @see java.lang.Object#hashCode()
806      */
807     @Override
808     public final int hashCode() {
809         int result = this.name.hashCode() - GraphState.FirstPhase.hashCode();
810         if (this.extraNodes) {
811             result *= 2;
812         }
813         if (this.startingNode != null) {
814             result -= this.startingNode.hashCode();
815         } else {
816             result += 1;
817         }
818         if (this.endingNode != null) {
819             result -= this.endingNode.hashCode();
820         } else {
821             result += 2;
822         }
823         if (this.nodes.size() != 0) {
824             result /= this.nodes.size();
825         }
826 
827         return result;
828     }
829 
830     /**
831      * Merge the current graph in the given graph. The current one will be
832      * emptied.
833      * <p>
834      * Both graphs (this and the given one) have to be validated.
835      * <p>
836      * The given graph can't be the same.
837      *
838      * @param graph
839      *            Graph that will have all the information of the current graph.
840      * @throws AbstractZemucanException
841      *             If there is a problem making the fusion between nodes.
842      */
843     public final void merge(final Graph graph)
844             throws AbstractZemucanException {
845         assert graph != this;
846         assert !this.name.equals(graph.name);
847         assert this.graphState == GraphState.Validated;
848         assert graph.graphState == GraphState.Validated;
849 
850         // Fusion Starting and Ending nodes.
851         Graph.nodeFusion(graph.startingNode, this.startingNode);
852         this.nodes.remove(this.startingNode.getId());
853         Graph.nodeFusion(graph.endingNode, this.endingNode);
854         this.nodes.remove(this.endingNode.getId());
855 
856 
857         Object[] keys = this.nodes.keySet().toArray();
858         int i = 0;
859         int len = keys.length;
860         while (i < len) {
861             final String id = (String) keys[i];
862             AbstractGraphNode node = this.nodes.remove(id);
863             graph.nodes.put(id, node);
864             i += 1;
865         }
866 
867         graph.name = "merged:{" + graph.name + ',' + this.name + '}'; //$NON-NLS-1$
868 
869         // Rename the keys for all nodes
870         final Map<String, AbstractGraphNode> newNodes = new HashMap<String, AbstractGraphNode>();
871         keys = graph.nodes.keySet().toArray();
872         i = 0;
873         len = keys.length;
874         while (i < len) {
875             final String id = (String) keys[i];
876             AbstractGraphNode node = graph.nodes.remove(id);
877             node.addGraphName(graph.name);
878             newNodes.put(node.getId(), node);
879             i += 1;
880         }
881         graph.nodes = newNodes;
882     }
883 
884     /**
885      * Removes a relation between two nodes. It calls implicitly the method that
886      * removes a relation between two node ids.
887      *
888      * @param parentNode
889      *            Origin of the relation.
890      * @param childNode
891      *            Target of the relation.
892      * @throws AbstractZemucanException
893      *             If there is a problem while removing the relation.
894      */
895     public void removeRelation(final AbstractGraphNode parentNode,
896             final AbstractGraphNode childNode)
897             throws AbstractZemucanException {
898         final int size = this.name.length() + 1;
899         String parentId = parentNode.getId();
900         String childId = childNode.getId();
901         if (parentId.startsWith(this.name)) {
902             parentId = parentId.substring(size);
903         }
904         if (childId.startsWith(this.name)) {
905             childId = childId.substring(size);
906         }
907         this.removeRelation(parentId, childId);
908         Graph.LOGGER.debug("Relation {}", parentNode);
909     }
910 
911     /**
912      * Removes a relation between two nodes, giving the members of the relation,
913      * the parent and the child.
914      *
915      * @param parentId
916      *            Id of the parent node.
917      * @param childId
918      *            Id of the child node.
919      * @throws AbstractZemucanException
920      *             if the parent or the child does not exist in the set of
921      *             nodes. Or if the state of the graph is invalid. Or when
922      *             trying to remove an invalid relation between two nodes. If a
923      *             parameter is null.
924      */
925     private void removeRelation(final String parentId, final String childId)
926             throws AbstractZemucanException {
927         Graph.LOGGER.debug(
928                 "Removing relation in graph '{}' between '{}' and '{}'", //$NON-NLS-1$
929                 new String[] { this.name, parentId, childId });
930 
931         if (parentId == null) {
932             throw new ParameterNullException("parentId"); //$NON-NLS-1$
933         }
934         if (childId == null) {
935             throw new ParameterNullException("childId"); //$NON-NLS-1$
936         }
937         if (this.graphState.equals(GraphState.FirstPhase)) {
938             throw new InvalidGraphStateException(this.graphState,
939                     GraphState.SecondPhase);
940         }
941         if (this.graphState.equals(GraphState.Validated)) {
942             this.graphState = GraphState.SecondPhase;
943         }
944 
945         final AbstractGraphNode parent = this.nodes.get(this.name + ':'
946                 + parentId);
947         if (parent == null) {
948             throw new NotExistingNodeException(parentId);
949         }
950 
951         // Retrieves the child and established the relation.
952         final AbstractGraphNode childNode = this.nodes.get(this.name + ':'
953                 + childId);
954         if (childNode == null) {
955             throw new NotExistingChildNodeException(parentId, childId);
956         }
957 
958         parent.removeChild(childNode);
959         childNode.removeParent(parent);
960     }
961 
962     /**
963      * Process the graph. Checks the graph structure as part of the finalization
964      * of the second phase. Optionally, it adds the extra nodes, depending on
965      * the parameter when creating the graph, and finally it returns the
966      * startingNode.
967      * <p>
968      * Checks that there is not nodes that references to StartingNode. Also,
969      * that EndingNode does not reference any node.
970      *
971      * @return The startingNode of the graph.
972      * @throws AbstractZemucanException
973      *             When a node is detected that references the StartingNode. Or
974      *             when a node is detected that if referenced by the EndingNode.
975      *             Or if a node does not come from StartingNode. Or if a node
976      *             does not go to EndingNode. Or if the first phase has not
977      *             finished. Or if the graph is in an invalid state.
978      */
979     public final StartingNode/* ! */secondPhaseFinished()
980             throws AbstractZemucanException {
981         if (this.graphState.equals(GraphState.FirstPhase)) {
982             throw new InvalidGraphStateException(this.graphState,
983                     GraphState.SecondPhase);
984         }
985 
986         // Reset value of nodes.
987         for (final AbstractGraphNode node : this.nodes.values()) {
988             node.setComeFromStartingNode(false);
989             node.setGoToEndingNode(false);
990         }
991 
992         assert this.checkNoParentsForStartingNode();
993 
994         if (this.startingNode == null) {
995             throw new StartingNodeNotDefinedException();
996         }
997         Graph.goForward(this.startingNode);
998 
999         if (this.endingNode == null) {
1000             throw new EndingNodeNotDefinedException();
1001         }
1002         Graph.goBackward(this.endingNode);
1003 
1004         for (final AbstractGraphNode node : this.nodes.values()) {
1005             if (!node.isComeFromStartingNode()) {
1006                 throw new NodeNotComesFromStartingNodeException(node.getId());
1007             } else if (!node.isGoToEndingNode()) {
1008                 throw new NodeNotGoesToEndingNodeException(node.getId());
1009             }
1010         }
1011 
1012         this.checkDuplicates();
1013 
1014         if ((this.startingNode.getChildren().size() == 1)
1015                 && this.startingNode.getChildren().get(0) instanceof EndingNode) {
1016             throw new EmptyGrammarException();
1017         }
1018 
1019         Graph.LOGGER.info("'{}' - Second phase finished.", this.name); //$NON-NLS-1$
1020 
1021         this.graphState = GraphState.Validated;
1022 
1023         assert this.startingNode != null;
1024         return this.startingNode;
1025     }
1026 
1027     /**
1028      * Simplify the graph, by merging nodes that represents the same tokens and
1029      * replacing the next nodes.
1030      *
1031      * @throws AbstractZemucanException
1032      *             If there is a problem add when merging the nodes or replacing
1033      *             the next nodes.
1034      */
1035     public final void simplifyGraph() throws AbstractZemucanException {
1036         Graph.LOGGER.debug("-> Simplyfing graph");
1037 
1038         // Replaces the NextNode nodes.
1039         Graph.LOGGER.debug("-> Remplacing next nodes");
1040         Iterator<AbstractGraphNode> ite = this.nodes.values().iterator();
1041         while (ite.hasNext()) {
1042             final AbstractGraphNode parentNode = ite.next();
1043             Graph.LOGGER.debug("parent {}", parentNode.getId());
1044             final List<AbstractGraphNode> children = parentNode.getChildren();
1045 
1046             if (!(parentNode instanceof EndingNode) && children.size() == 0) {
1047                 // ERROR
1048                 throw new RuntimeException("Node without children.");
1049             }
1050             int size = children.size();
1051             for (int i = 0; i < size; i++) {
1052                 final AbstractGraphNode childNode = children.get(i);
1053                 Graph.LOGGER.debug("child   {}", childNode.getId());
1054                 if (childNode instanceof NextNode) {
1055                     final List<AbstractGraphNode> grandChildren = childNode
1056                             .getChildren();
1057                     // Adds a relation between parent and grand child.
1058                     for (int j = 0; j < grandChildren.size(); j++) {
1059                         final AbstractGraphNode grandChildNode = grandChildren
1060                                 .get(j);
1061                         this.addRelation(parentNode, grandChildNode);
1062                         this.removeRelation(parentNode, childNode);
1063                     }
1064                     // Restart the iterator.
1065                     ite = this.nodes.values().iterator();
1066                     i = children.size();
1067                     Graph.LOGGER.debug("Restarted");
1068                 }
1069             }
1070         }
1071         // Removes the next nodes and their relations.
1072         Graph.LOGGER.debug("-> Removing next nodes and their iterations");
1073         final Object[] set = this.nodes.keySet().toArray();
1074         for (int i = 0; i < set.length; i++) {
1075             final String key = (String) set[i];
1076             final AbstractGraphNode node = this.nodes.get(key);
1077             if (node instanceof NextNode) {
1078                 final List<AbstractGraphNode> children = node.getChildren();
1079                 for (int j = 0; j < children.size(); j++) {
1080                     final AbstractGraphNode childNode = children.get(j);
1081                     this.removeRelation(node, childNode);
1082                 }
1083                 this.nodes.remove(key);
1084             }
1085         }
1086 
1087         // Fusions the common nodes.
1088         Graph.LOGGER.debug("-> Fusionning common nodes");
1089         Iterator<AbstractGraphNode> it = this.nodes.values().iterator();
1090         while (it.hasNext()) {
1091             final AbstractGraphNode node = it.next();
1092             if (Graph.checkChildren(node)) {
1093                 // If there is a fusion, the iterator has to restart.
1094                 it = this.nodes.values().iterator();
1095             }
1096         }
1097 
1098         // Remove empty nodes.
1099         Graph.LOGGER.debug("-> Removing empty nodes");
1100         final Object[] keys = this.nodes.keySet().toArray();
1101         for (final Object key2 : keys) {
1102             final String key = (String) key2;
1103 
1104             final AbstractGraphNode node = this.nodes.get(key);
1105 
1106             int childrenSize = node.getChildren().size();
1107             int parentsSize = node.getParents().size();
1108             Graph.LOGGER.debug("node {}, parents {} children {}", new Object[] {
1109                     node.getId(), parentsSize, childrenSize });
1110             if ((childrenSize == 0) && (parentsSize == 0)) {
1111                 this.nodes.remove(key);
1112             }
1113         }
1114     }
1115 
1116     /*
1117      * (non-Javadoc)
1118      * @see java.lang.Object#toString()
1119      */
1120     @Override
1121     public final String toString() {
1122         String res = this.name + ":[" + this.graphState + '-'; //$NON-NLS-1$
1123         if (this.extraNodes) {
1124             res += "WithExtraNodes"; //$NON-NLS-1$
1125         } else {
1126             res += "WithoutExtraNodes"; //$NON-NLS-1$
1127         }
1128         res += "{" + this.startingNode + '-' + this.endingNode //$NON-NLS-1$
1129                 + "}(" + this.nodes.size() + ":\n"; //$NON-NLS-1$ //$NON-NLS-2$
1130         final List<String> children = new ArrayList<String>();
1131         children.addAll(this.nodes.keySet());
1132         Collections.sort(children);
1133         for (int i = 0; i < children.size(); i++) {
1134             final String key = children.get(i);
1135             final AbstractGraphNode node = this.nodes.get(key);
1136             String nodeString;
1137             if (node instanceof TextualGraphNode) {
1138                 nodeString = key + '-'
1139                         + ((TextualGraphNode) this.nodes.get(key)).getName()
1140                         + '\n';
1141             } else {
1142                 // TODO v1.0 omitir el id o la key, pero no ambas
1143                 nodeString = key + '-' + this.nodes.get(key).getId() + '\n';
1144             }
1145             res += nodeString;
1146         }
1147         res += ')';
1148         res += ']';
1149         return res;
1150     }
1151 }