001package org.maltparser.core.syntaxgraph;
002
003import java.util.Iterator;
004import java.util.NoSuchElementException;
005import java.util.Observable;
006import java.util.Set;
007import java.util.SortedMap;
008import java.util.SortedSet;
009import java.util.TreeMap;
010import java.util.TreeSet;
011
012
013import org.maltparser.core.exception.MaltChainedException;
014import org.maltparser.core.helper.SystemLogger;
015import org.maltparser.core.pool.ObjectPoolList;
016import org.maltparser.core.symbol.SymbolTable;
017import org.maltparser.core.symbol.SymbolTableHandler;
018import org.maltparser.core.syntaxgraph.ds2ps.LosslessMapping;
019import org.maltparser.core.syntaxgraph.edge.Edge;
020import org.maltparser.core.syntaxgraph.edge.GraphEdge;
021import org.maltparser.core.syntaxgraph.node.ComparableNode;
022import org.maltparser.core.syntaxgraph.node.DependencyNode;
023import org.maltparser.core.syntaxgraph.node.Node;
024import org.maltparser.core.syntaxgraph.node.NonTerminal;
025import org.maltparser.core.syntaxgraph.node.NonTerminalNode;
026import org.maltparser.core.syntaxgraph.node.PhraseStructureNode;
027import org.maltparser.core.syntaxgraph.node.Root;
028import org.maltparser.core.syntaxgraph.node.TokenNode;
029/**
030*
031*
032* @author Johan Hall
033*/
034public class MappablePhraseStructureGraph extends Sentence implements DependencyStructure, PhraseStructure {
035        private final ObjectPoolList<Edge> edgePool;
036        private final SortedSet<Edge> graphEdges;
037        private Root root;
038        private boolean singleHeadedConstraint;
039        private final SortedMap<Integer, NonTerminal> nonTerminalNodes;
040        private final ObjectPoolList<NonTerminal> nonTerminalPool;
041        private LosslessMapping mapping;
042        private RootLabels rootLabels;
043        
044        public MappablePhraseStructureGraph(SymbolTableHandler symbolTables) throws MaltChainedException {
045                super(symbolTables);
046                setSingleHeadedConstraint(true);
047                root = new Root();
048                root.setBelongsToGraph(this);
049                graphEdges = new TreeSet<Edge>();
050                edgePool = new ObjectPoolList<Edge>() {
051                        protected Edge create() { return new GraphEdge(); }
052                        public void resetObject(Edge o) throws MaltChainedException { o.clear(); }
053                };
054                
055                nonTerminalNodes = new TreeMap<Integer,NonTerminal>();
056                nonTerminalPool = new ObjectPoolList<NonTerminal>() {
057                        protected NonTerminal create() throws MaltChainedException { return new NonTerminal(); }
058                        public void resetObject(NonTerminal o) throws MaltChainedException { o.clear(); }
059                };
060                clear();
061        }
062
063        public DependencyNode addDependencyNode() throws MaltChainedException {
064                return addTokenNode();
065        }
066        
067        public DependencyNode addDependencyNode(int index) throws MaltChainedException {
068                if (index == 0) {
069                        return root;
070                }
071                return addTokenNode(index);
072        }
073        
074        public DependencyNode getDependencyNode(int index) throws MaltChainedException {
075                if (index == 0) {
076                        return root;
077                } 
078                return getTokenNode(index);
079        }
080        
081        public int nDependencyNode() {
082                return nTokenNode() + 1;
083        }
084        
085        public int getHighestDependencyNodeIndex() {
086                if (hasTokens()) {
087                        return getHighestTokenIndex();
088                }
089                return 0;
090        }
091        
092        public Edge addDependencyEdge(int headIndex, int dependentIndex) throws MaltChainedException {
093                DependencyNode head = null;
094                DependencyNode dependent = null;
095                if (headIndex == 0) {
096                        head = root;
097                } else if (headIndex > 0) {
098                        head = getOrAddTerminalNode(headIndex);
099                }
100                
101                if (dependentIndex > 0) {
102                        dependent = getOrAddTerminalNode(dependentIndex);
103                }
104                return addDependencyEdge(head, dependent);
105        }
106        
107        public Edge addDependencyEdge(DependencyNode head, DependencyNode dependent) throws MaltChainedException {
108                if (head == null || dependent == null || head.getBelongsToGraph() != this || dependent.getBelongsToGraph() != this) {
109                        throw new SyntaxGraphException("Head or dependent node is missing.");
110                } else if (!dependent.isRoot()) {
111                        if (singleHeadedConstraint && dependent.hasHead()) {
112                                throw new SyntaxGraphException("The dependent already have a head. ");
113                        }
114                        DependencyNode hc = ((DependencyNode)head).findComponent();
115                        DependencyNode dc = ((DependencyNode)dependent).findComponent();
116                        if (hc != dc) {
117                                link(hc, dc);
118                                numberOfComponents--;           
119                        }
120                        Edge e = edgePool.checkOut();
121                        e.setBelongsToGraph(this);
122                        e.setEdge((Node)head, (Node)dependent, Edge.DEPENDENCY_EDGE);
123                        graphEdges.add(e);
124                        return e;
125                } else {
126                        throw new SyntaxGraphException("Head node is not a root node or a terminal node.");
127                }
128        }
129        
130        public Edge moveDependencyEdge(int newHeadIndex, int dependentIndex) throws MaltChainedException {
131                DependencyNode newHead = null;
132                DependencyNode dependent = null;
133                if (newHeadIndex == 0) {
134                        newHead = root;
135                } else if (newHeadIndex > 0) {
136                        newHead = terminalNodes.get(newHeadIndex);
137                }
138                
139                if (dependentIndex > 0) {
140                        dependent = terminalNodes.get(dependentIndex);
141                }
142                return moveDependencyEdge(newHead, dependent);
143        }
144        
145        public Edge moveDependencyEdge(DependencyNode newHead, DependencyNode dependent) throws MaltChainedException {
146                if (dependent == null || !dependent.hasHead() || newHead.getBelongsToGraph() != this || dependent.getBelongsToGraph() != this) {
147                        return null;
148                }
149                Edge headEdge = dependent.getHeadEdge();
150
151                LabelSet labels = null;
152                if (headEdge.isLabeled()) { 
153                        labels = checkOutNewLabelSet();
154                        for (SymbolTable table : headEdge.getLabelTypes()) {
155                                labels.put(table, headEdge.getLabelCode(table));
156                        }
157                }
158                headEdge.clear();
159                headEdge.setBelongsToGraph(this);
160                headEdge.setEdge((Node)newHead, (Node)dependent, Edge.DEPENDENCY_EDGE);
161                if (labels != null) {
162                        headEdge.addLabel(labels);
163                        labels.clear();
164                        checkInLabelSet(labels);
165                }
166                return headEdge;
167        }
168        
169        public void removeDependencyEdge(int headIndex, int dependentIndex) throws MaltChainedException {
170                Node head = null;
171                Node dependent = null;
172                if (headIndex == 0) {
173                        head = root;
174                } else if (headIndex > 0) {
175                        head = terminalNodes.get(headIndex);
176                }
177                
178                if (dependentIndex > 0) {
179                        dependent = terminalNodes.get(dependentIndex);
180                }
181                removeDependencyEdge(head, dependent);
182        }
183        
184        protected void removeDependencyEdge(Node head, Node dependent) throws MaltChainedException {
185                if (head == null || dependent == null || head.getBelongsToGraph() != this || dependent.getBelongsToGraph() != this) {
186                        throw new SyntaxGraphException("Head or dependent node is missing.");
187                } else if (!dependent.isRoot()) {
188                        Iterator<Edge> ie = dependent.getIncomingEdgeIterator();
189                        while (ie.hasNext()) {
190                                Edge e = ie.next();
191                                if (e.getSource() == head) {
192                                        ie.remove();
193                                        graphEdges.remove(e);
194                                        edgePool.checkIn(e);
195                                }
196                        } 
197                } else {
198                        throw new SyntaxGraphException("Head node is not a root node or a terminal node.");
199                }
200        }
201
202        public Edge addSecondaryEdge(ComparableNode source, ComparableNode target) throws MaltChainedException {
203                if (source == null || target == null || source.getBelongsToGraph() != this || target.getBelongsToGraph() != this) {
204                        throw new SyntaxGraphException("Head or dependent node is missing.");
205                } else if (!target.isRoot()) {
206                        Edge e = edgePool.checkOut();
207                        e.setBelongsToGraph(this);
208                        e.setEdge((Node)source, (Node)target, Edge.SECONDARY_EDGE);
209                        graphEdges.add(e);
210                        return e;
211                }
212                return null;
213        }
214        
215        public void removeSecondaryEdge(ComparableNode source, ComparableNode target) throws MaltChainedException {
216                if (source == null || target == null || source.getBelongsToGraph() != this || target.getBelongsToGraph() != this) {
217                        throw new SyntaxGraphException("Head or dependent node is missing.");
218                } else if (!target.isRoot()) {
219                        Iterator<Edge> ie = ((Node)target).getIncomingEdgeIterator();
220                        while (ie.hasNext()) {
221                                Edge e = ie.next();
222                                if (e.getSource() == source) {
223                                        ie.remove();
224                                        graphEdges.remove(e);
225                                        edgePool.checkIn(e);
226                                }
227                        }
228                }
229        }
230        
231        public boolean hasLabeledDependency(int index) throws MaltChainedException {
232                return (getDependencyNode(index).hasHead() && getDependencyNode(index).getHeadEdge().isLabeled());
233        }
234        
235        public boolean isConnected() {
236                return (numberOfComponents == 1);
237        }
238        
239        public boolean isProjective()  throws MaltChainedException {
240                for (int i : terminalNodes.keySet()) {
241                        if (!terminalNodes.get(i).isProjective()) {
242                                return false;
243                        }
244                }
245                return true;
246        }
247        
248        public boolean isTree() {
249                return isConnected() && isSingleHeaded();
250        }
251        
252        public boolean isSingleHeaded() {
253                for (int i : terminalNodes.keySet()) {
254                        if (!terminalNodes.get(i).hasAtMostOneHead()) {
255                                return false;
256                        }
257                }
258                return true;
259        }
260        
261        public boolean isSingleHeadedConstraint() {
262                return singleHeadedConstraint;
263        }
264
265        public void setSingleHeadedConstraint(boolean singleHeadedConstraint) {
266                this.singleHeadedConstraint = singleHeadedConstraint;
267        }
268        
269        public int nNonProjectiveEdges() throws MaltChainedException {
270                int c = 0;
271                for (int i : terminalNodes.keySet()) {
272                        if (!terminalNodes.get(i).isProjective()) {
273                                c++;
274                        }
275                }
276                return c;
277        }
278        
279        public int nEdges() {
280                return graphEdges.size();
281        }
282        
283        public SortedSet<Edge> getEdges() {
284                return graphEdges;
285        }
286        
287        public SortedSet<Integer> getDependencyIndices() {
288                SortedSet<Integer> indices = new TreeSet<Integer>(terminalNodes.keySet());
289                indices.add(0);
290                return indices;
291        }
292        
293        protected DependencyNode link(DependencyNode x, DependencyNode y) throws MaltChainedException {
294                if (x.getRank() > y.getRank()) {
295                        y.setComponent(x);
296                } else {
297                        x.setComponent(y);
298                        if (x.getRank() == y.getRank()) {
299                                y.setRank(y.getRank()+1);
300                        }
301                        return y;
302                }
303                return x;
304        }
305        
306        public void linkAllTerminalsToRoot() throws MaltChainedException {
307                clear();
308
309                for (int i : terminalNodes.keySet()) {
310                        DependencyNode node = terminalNodes.get(i);
311                        addDependencyEdge(root,node);
312                }
313        }
314        
315        public void linkAllTreesToRoot() throws MaltChainedException {
316                for (int i : terminalNodes.keySet()) {
317                        if (!terminalNodes.get(i).hasHead()) {
318                                Edge e = addDependencyEdge(root,terminalNodes.get(i));
319                                mapping.updatePhraseStructureGraph(this, e, false);
320                        }
321                }
322        }
323        
324        public LabelSet getDefaultRootEdgeLabels() throws MaltChainedException {
325                if (rootLabels == null) {
326                        return null;
327                }
328                return rootLabels.getDefaultRootLabels();
329        }
330        
331        public String getDefaultRootEdgeLabelSymbol(SymbolTable table) throws MaltChainedException {
332                if (rootLabels == null) {
333                        return null;
334                }
335                return rootLabels.getDefaultRootLabelSymbol(table);
336        }
337        
338        public int getDefaultRootEdgeLabelCode(SymbolTable table) throws MaltChainedException {
339                if (rootLabels == null) {
340                        return -1;
341                }
342                return rootLabels.getDefaultRootLabelCode(table);
343        }
344        
345        public void setDefaultRootEdgeLabel(SymbolTable table, String defaultRootSymbol) throws MaltChainedException {
346                if (rootLabels == null) {
347                        rootLabels = new RootLabels();
348                }
349                rootLabels.setDefaultRootLabel(table, defaultRootSymbol);
350        }
351        
352        public void setDefaultRootEdgeLabels(String rootLabelOption, SortedMap<String, SymbolTable> edgeSymbolTables) throws MaltChainedException {
353                if (rootLabels == null) {
354                        rootLabels = new RootLabels();
355                }
356                rootLabels.setRootLabels(rootLabelOption, edgeSymbolTables);
357        }
358        
359        public void clear() throws MaltChainedException {
360                edgePool.checkInAll();
361                graphEdges.clear();
362                root.clear();
363                root.setBelongsToGraph(this);
364                nonTerminalPool.checkInAll();
365                nonTerminalNodes.clear();
366                if (mapping != null) {
367                        mapping.clear();        
368                }
369                super.clear();
370                
371                numberOfComponents++;
372        }
373        
374        public DependencyNode getDependencyRoot() {
375                return root;
376        }
377        
378        public PhraseStructureNode addTerminalNode() throws MaltChainedException {
379                return addTokenNode();
380        }
381        
382        public PhraseStructureNode addTerminalNode(int index) throws MaltChainedException {
383                return addTokenNode(index);
384        }
385        
386        public PhraseStructureNode getTerminalNode(int index) {
387                return getTokenNode(index);
388        }
389        
390        public int nTerminalNode() {
391                return nTokenNode();
392        }
393        
394        public PhraseStructureNode addNonTerminalNode(int index) throws MaltChainedException {
395                NonTerminal node = nonTerminalPool.checkOut();
396                node.setIndex(index);
397                node.setBelongsToGraph(this);
398                nonTerminalNodes.put(index,node);
399                return node;
400        }
401        
402        public PhraseStructureNode addNonTerminalNode() throws MaltChainedException {
403                int index = getHighestNonTerminalIndex();
404                if (index > 0) {
405                        return addNonTerminalNode(index+1);
406                }
407                return addNonTerminalNode(1);
408        }
409        
410        public PhraseStructureNode getNonTerminalNode(int index) throws MaltChainedException {
411                return nonTerminalNodes.get(index);
412        }
413        
414        public int getHighestNonTerminalIndex() {
415                try {
416                        return nonTerminalNodes.lastKey();
417                } catch (NoSuchElementException e) {
418                        return 0;
419                }
420        }
421        
422        public Set<Integer> getNonTerminalIndices() {
423                return new TreeSet<Integer>(nonTerminalNodes.keySet());
424        }
425        
426        public boolean hasNonTerminals() {
427                return !nonTerminalNodes.isEmpty();
428        }
429        
430        public int nNonTerminals() {
431                return nonTerminalNodes.size();
432        }
433        
434        public PhraseStructureNode getPhraseStructureRoot() {
435                return root;
436        }
437        
438        public Edge addPhraseStructureEdge(PhraseStructureNode parent, PhraseStructureNode child) throws MaltChainedException {
439                if (parent == null || child == null) {
440                        throw new MaltChainedException("Parent or child node is missing in sentence "+getSentenceID());
441                } else if (parent.getBelongsToGraph() != this || child.getBelongsToGraph() != this) {
442                        throw new MaltChainedException("Parent or child node is not a member of the graph in sentence "+getSentenceID());
443                } else if (parent == child) {
444                        throw new MaltChainedException("It is not allowed to add a phrase structure edge connecting the same node in sentence "+getSentenceID());
445                } else if (parent instanceof NonTerminalNode && !child.isRoot()) {
446                        Edge e = edgePool.checkOut();
447                        e.setBelongsToGraph(this);
448                        e.setEdge((Node)parent, (Node)child, Edge.PHRASE_STRUCTURE_EDGE);
449                        graphEdges.add(e);
450                        return e;
451                } else {
452                        throw new MaltChainedException("Parent or child node is not of correct node type.");
453                }
454        }
455        
456        public void update(Observable  o, Object arg)  {
457                if (o instanceof Edge && mapping != null) {
458                        try {
459                                mapping.update(this, (Edge)o, arg);
460                        } catch (MaltChainedException ex) {
461                                if (SystemLogger.logger().isDebugEnabled()) {
462                                        SystemLogger.logger().debug("",ex);
463                                } else {
464                                        SystemLogger.logger().error(ex.getMessageChain());
465                                }
466                                System.exit(1);
467                        }
468                }
469        }
470
471        public LosslessMapping getMapping() {
472                return mapping;
473        }
474
475        public void setMapping(LosslessMapping mapping) {
476                this.mapping = mapping;
477        }
478
479        public void addLabel(Element element, String labelFunction, String label) throws MaltChainedException {
480                super.addLabel(element, labelFunction, label);
481        }
482        
483        public void removePhraseStructureEdge(PhraseStructureNode parent, PhraseStructureNode child) throws MaltChainedException {
484                if (parent == null || child == null) {
485                        throw new MaltChainedException("Parent or child node is missing.");
486                } else if (parent instanceof NonTerminalNode && !child.isRoot()) {
487                        for (Edge e : graphEdges) {
488                                if (e.getSource() == parent && e.getTarget() == child) {
489                                        e.clear();
490                                        graphEdges.remove(e);
491                                        if (e instanceof GraphEdge) {
492                                                edgePool.checkIn(e);
493                                        }
494                                }
495                        }
496                } else {
497                        throw new SyntaxGraphException("Head node is not a root node or a terminal node.");
498                }
499        }
500        
501        public boolean isContinuous() {
502                for (int index : nonTerminalNodes.keySet()) {
503                        NonTerminalNode node = nonTerminalNodes.get(index);
504
505                        if (!node.isContinuous()) {
506                                return false;
507                        }
508                }
509                return true;
510        }
511        
512        public boolean isContinuousExcludeTerminalsAttachToRoot() {
513                for (int index : nonTerminalNodes.keySet()) {
514                        NonTerminalNode node = nonTerminalNodes.get(index);
515                        if (!node.isContinuousExcludeTerminalsAttachToRoot()) {
516                                return false;
517                        }
518                }
519                return true;
520        }
521        
522//      public void makeContinuous() throws MaltChainedException {
523//              if (root != null) {
524//                      root.reArrangeChildrenAccordingToLeftAndRightProperDesendant();
525//              }
526//      }
527        
528        public String toStringTerminalNode(TokenNode node) {
529                final StringBuilder sb = new StringBuilder();
530                final DependencyNode depnode = node;
531
532                sb.append(node.toString().trim());
533                if (depnode.hasHead()) {
534                        sb.append('\t');
535                        try {
536                                sb.append(depnode.getHead().getIndex());
537                                sb.append('\t');
538                                sb.append(depnode.getHeadEdge().toString());
539                        } catch (MaltChainedException e) {
540                                System.err.println(e);
541                        }
542                }
543                sb.append('\n');
544
545                return sb.toString();
546        }
547        
548        public String toStringNonTerminalNode(NonTerminalNode node) {
549                final StringBuilder sb = new StringBuilder();
550
551                sb.append(node.toString().trim());
552                sb.append('\n');
553                Iterator<Edge> ie = ((Node)node).getOutgoingEdgeIterator();
554                while (ie.hasNext()) {
555                        Edge e = ie.next();
556                        if (e.getTarget() instanceof TokenNode) {
557                                sb.append("   T");
558                                sb.append(e.getTarget().getIndex());
559                        }
560                        if (e.getTarget() instanceof NonTerminalNode) {
561                                sb.append("   N");
562                                sb.append(e.getTarget().getIndex());
563                        }
564                        sb.append('\t');
565                        sb.append(e.toString());
566                        sb.append('\n');
567                }
568                return sb.toString();
569        }
570        
571        public String toString() {
572                StringBuilder sb = new StringBuilder();
573                for (int index : terminalNodes.keySet()) {
574                        sb.append(toStringTerminalNode(terminalNodes.get(index)));
575                }
576                sb.append('\n');
577                sb.append(toStringNonTerminalNode((NonTerminalNode)getPhraseStructureRoot()));
578                for (int index : nonTerminalNodes.keySet()) {
579                        sb.append(toStringNonTerminalNode(nonTerminalNodes.get(index)));
580                }
581                return sb.toString();
582        }
583}