001package org.maltparser.core.syntaxgraph.node;
002
003import java.util.NoSuchElementException;
004import java.util.SortedSet;
005import java.util.TreeSet;
006
007import org.maltparser.core.exception.MaltChainedException;
008import org.maltparser.core.helper.SystemLogger;
009import org.maltparser.core.symbol.SymbolTable;
010import org.maltparser.core.syntaxgraph.SyntaxGraphException;
011import org.maltparser.core.syntaxgraph.TokenStructure;
012import org.maltparser.core.syntaxgraph.edge.Edge;
013import org.maltparser.core.syntaxgraph.headrules.Direction;
014import org.maltparser.core.syntaxgraph.headrules.HeadRules;
015
016public class NonTerminal extends GraphNode implements PhraseStructureNode, NonTerminalNode {
017        public final static int INDEX_OFFSET = 10000000;
018        protected final SortedSet<PhraseStructureNode> children;
019        protected PhraseStructureNode parent;
020        protected int index;
021        
022        public NonTerminal() throws MaltChainedException {
023                super();
024                index = -1;
025                children = new TreeSet<PhraseStructureNode>();
026        }
027        
028        public void addIncomingEdge(Edge in) throws MaltChainedException {
029                super.addIncomingEdge(in);
030                if (in.getType() == Edge.PHRASE_STRUCTURE_EDGE && in.getSource() instanceof PhraseStructureNode) {
031                        parent = (PhraseStructureNode)in.getSource();
032                }
033        }
034        
035        public void removeIncomingEdge(Edge in) throws MaltChainedException {
036                super.removeIncomingEdge(in);
037                if (in.getType() == Edge.PHRASE_STRUCTURE_EDGE && in.getSource() instanceof PhraseStructureNode) {
038                        if (in.getSource() == parent) {
039                                this.parent = null;
040                        }
041                }
042        }
043        
044        public void addOutgoingEdge(Edge out) throws MaltChainedException {
045                super.addOutgoingEdge(out);
046                if (out.getType() == Edge.PHRASE_STRUCTURE_EDGE && out.getTarget() instanceof PhraseStructureNode) {
047                        children.add((PhraseStructureNode)out.getTarget());
048//                      boolean notSorted = true;
049//                      PhraseStructureNode prev = children.first();
050//                      for (PhraseStructureNode node : children) {
051//                              if (prev != node) {
052//                                      if (node.compareTo(prev) == -1) {
053//                                              notSorted = false;
054//                                              System.out.println("NS");
055//                                              break;
056//                                      }
057//                              } 
058//                              prev = node;
059//                      }
060//                      if (notSorted == false) {
061//                              SortedSet<PhraseStructureNode> tmp = new TreeSet<PhraseStructureNode>(children);
062//                              children.clear();
063//                              for (PhraseStructureNode node : tmp) {
064//                                      children.add(node);
065//                              }
066//                      }
067                }
068        }
069        
070        public void removeOutgoingEdge(Edge out) throws MaltChainedException {
071                super.removeOutgoingEdge(out);
072                if (out.getType() == Edge.PHRASE_STRUCTURE_EDGE && out.getTarget() instanceof PhraseStructureNode) {
073                        children.remove(out.getTarget());
074                }
075        }
076        
077        public PhraseStructureNode getParent() {
078                return parent;
079        }
080        
081        public Edge getParentEdge() throws MaltChainedException {
082                for (Edge e : incomingEdges) {
083                        if (e.getSource() == parent && e.getType() == Edge.PHRASE_STRUCTURE_EDGE) {
084                                return e;
085                        }
086                }
087                return null;
088        }
089        
090        public String getParentEdgeLabelSymbol(SymbolTable table) throws MaltChainedException {
091                for (Edge e : incomingEdges) {
092                        if (e.getSource() == parent && e.getType() == Edge.PHRASE_STRUCTURE_EDGE) {
093                                return e.getLabelSymbol(table);
094                        }
095                }
096                return null;
097        }
098
099        public int getParentEdgeLabelCode(SymbolTable table) throws MaltChainedException {
100                for (Edge e : incomingEdges) {
101                        if (e.getSource() == parent && e.getType() == Edge.PHRASE_STRUCTURE_EDGE) {
102                                return e.getLabelCode(table);
103                        }
104                }
105                return -1;
106        }
107        
108        public boolean hasParentEdgeLabel(SymbolTable table) throws MaltChainedException {
109                for (Edge e : incomingEdges) {
110                        if (e.getSource() == parent && e.getType() == Edge.PHRASE_STRUCTURE_EDGE) {
111                                return e.hasLabel(table);
112                        }
113                }
114                return false;
115        }
116        
117        public int getHeight() {
118                int max = -1;
119                for (PhraseStructureNode node : children) {
120                        if (node instanceof Token) {
121                                if (max < 0) {
122                                        max = 0;
123                                }
124                        } else {
125                                int nodeheight = ((NonTerminalNode)node).getHeight();
126                                if (max < nodeheight) {
127                                        max = nodeheight;
128                                }
129                        }
130                }
131                return max + 1;
132        }
133        
134        public SortedSet<PhraseStructureNode> getChildren() {
135                return new TreeSet<PhraseStructureNode>(children);
136        }
137
138        public PhraseStructureNode getChild(int index) {
139                if (index >= 0 && index < children.size()) {
140                        int i = 0;
141                        for (PhraseStructureNode node : children) {
142                                if (i == index) {
143                                        return node;
144                                }
145                                i++;
146                        }
147//                      return children.toArray(new PhraseStructureNode[children.size()])[index];
148                }
149                return null;
150        }
151        
152        public PhraseStructureNode getLeftChild() {
153                for (PhraseStructureNode node : children) {
154                        return node;
155                }
156                return null;
157        }
158        
159        public PhraseStructureNode getRightChild() {
160                int n = children.size();
161                int i = 1;
162                for (PhraseStructureNode node : children) {
163                        if (i == n) {
164                                return node;
165                        }
166                        i++;
167                }
168                return null;
169        }
170        
171        public int nChildren() {
172                return children.size();
173        }
174        
175        public boolean hasNonTerminalChildren() {
176                for (PhraseStructureNode node : children) {
177                        if (node instanceof NonTerminal) {
178                                return true;
179                        }
180                }
181                return false;
182        }
183        
184        public boolean hasTerminalChildren() {
185                for (PhraseStructureNode node : children) {
186                        if (node instanceof Token) {
187                                return true;
188                        }
189                }
190                return false;
191        }
192        
193        public TokenNode getLexicalHead(HeadRules headRules) throws MaltChainedException {
194                return identifyHead(headRules);
195        }
196
197        public PhraseStructureNode getHeadChild(HeadRules headRules) throws MaltChainedException {
198                return identifyHeadChild(headRules);
199        }
200        
201        public TokenNode getLexicalHead() throws MaltChainedException {
202                return identifyHead(null);
203        }
204
205        public PhraseStructureNode getHeadChild() throws MaltChainedException {
206                return identifyHeadChild(null);
207        }
208        
209        private PhraseStructureNode identifyHeadChild(HeadRules headRules) throws MaltChainedException {
210                PhraseStructureNode headChild = (headRules == null)?null:headRules.getHeadChild(this);
211                if (headChild == null) {
212                        Direction direction = (headRules == null)?Direction.LEFT:headRules.getDefaultDirection(this);
213                        if (direction == Direction.LEFT) {
214                                if ((headChild = leftmostTerminalChild()) == null) {
215                                        headChild = leftmostNonTerminalChild();
216                                }
217                        } else {
218                                if ((headChild = rightmostTerminalChild()) == null) {
219                                        headChild = rightmostNonTerminalChild();
220                                }
221                        }
222                }
223                return headChild;
224        }
225        
226        public TokenNode identifyHead(HeadRules headRules) throws MaltChainedException {
227                PhraseStructureNode headChild = identifyHeadChild(headRules);
228                TokenNode lexicalHead = null;
229                if (headChild instanceof NonTerminalNode) {
230                        lexicalHead = ((NonTerminalNode)headChild).identifyHead(headRules);
231                } else if (headChild instanceof TokenNode) {
232                        lexicalHead = (TokenNode)headChild;
233                }
234                for (PhraseStructureNode node : children) {
235                        if (node != headChild && node instanceof NonTerminalNode) {
236                                ((NonTerminalNode)node).identifyHead(headRules);
237                        }
238                }
239                return lexicalHead;
240        }
241        
242        public int getIndex() {
243                return index;
244        }
245        
246        public int getCompareToIndex() {
247                return index + INDEX_OFFSET;
248        }
249        
250        private PhraseStructureNode leftmostTerminalChild() {
251                for (PhraseStructureNode node : children) {
252                        if (node instanceof TokenNode) {
253                                return node;
254                        }
255                }
256                return null;
257        }
258        
259        private PhraseStructureNode leftmostNonTerminalChild() {
260                for (PhraseStructureNode node : children) {
261                        if (node instanceof NonTerminalNode) {
262                                return node;
263                        }
264                }
265                return null;
266        }
267        
268        private PhraseStructureNode rightmostTerminalChild() {
269                try {
270                        if (children.last() instanceof TokenNode) {
271                                return children.last();
272                        }
273                } catch (NoSuchElementException e) { }
274                
275                PhraseStructureNode candidate = null;
276                for (PhraseStructureNode node : children) {
277                        if (node instanceof TokenNode) {
278                                candidate = node;
279                        }
280                }
281                return candidate;
282        }
283        
284        private PhraseStructureNode rightmostNonTerminalChild() {
285                try {
286                        if (children.last() instanceof NonTerminalNode) {
287                                return children.last();
288                        }
289                } catch (NoSuchElementException e) { }
290                
291                PhraseStructureNode candidate = null;
292                for (PhraseStructureNode node : children) {
293                        if (node instanceof NonTerminalNode) {
294                                candidate = node;
295                        }
296                }
297                return candidate;
298        }
299        
300//      protected void getPhraseDominationSet(SortedSet<PhraseStructureNode> dominationSet) {
301//              for (PhraseStructureNode node : children) {
302//                      if (node instanceof TerminalNode) {
303//                              dominationSet.add(node);
304//                      } else {
305//                              ((NonTerminal)node).getPhraseDominationSet(dominationSet);
306//                      }
307//              }
308//      }
309//      
310//      private SortedSet<PhraseStructureNode> getPhraseDominationSet() {
311//              SortedSet<PhraseStructureNode> dominationSet = new TreeSet<PhraseStructureNode>();
312//              getPhraseDominationSet(dominationSet);
313//              return dominationSet;
314//      }
315        
316
317        
318        public boolean isContinuous() {
319                int lcorner = getLeftmostProperDescendant().getIndex();
320                int rcorner = getRightmostProperDescendant().getIndex();
321                
322                if (lcorner == rcorner) {
323                        return true;
324                }
325                
326                TokenNode terminal = ((TokenStructure)getBelongsToGraph()).getTokenNode(lcorner);
327                while (terminal.getIndex() != rcorner) {
328                        PhraseStructureNode tmp = terminal.getParent();
329                        while (true) {
330                                if (tmp == this) {
331                                        break;
332                                }
333                                if (tmp == null) {
334                                        return false;
335                                }
336                                tmp = tmp.getParent();
337                        }
338                        terminal = terminal.getTokenNodeSuccessor();
339                }
340                
341                return true;
342        }
343
344        public boolean isContinuousExcludeTerminalsAttachToRoot() {
345                int lcorner = getLeftmostProperDescendant().getIndex();
346                int rcorner = getRightmostProperDescendant().getIndex();
347                
348                if (lcorner == rcorner) {
349                        return true;
350                }
351                
352                TokenNode terminal = ((TokenStructure)getBelongsToGraph()).getTokenNode(lcorner);
353                while (terminal.getIndex() != rcorner) {
354                        if (terminal.getParent() != null && terminal.getParent().isRoot()) {
355                                terminal = terminal.getTokenNodeSuccessor();
356                                continue;
357                        }
358                        PhraseStructureNode tmp = terminal.getParent();
359                        while (true) {
360                                if (tmp == this) {
361                                        break;
362                                }
363                                if (tmp == null) {
364                                        return false;
365                                }
366                                tmp = tmp.getParent();
367                        }
368                        terminal = terminal.getTokenNodeSuccessor();
369                }
370                return true;
371        }
372        
373        @Override
374        public boolean isRoot() {
375                return false;
376        }
377
378        
379        public ComparableNode getLeftmostProperDescendant() {
380                NonTerminalNode node = this;
381                PhraseStructureNode candidate = null;
382                while (node != null) {
383                        candidate = node.getLeftChild();
384                        if (candidate == null || candidate instanceof TokenNode) {
385                                return candidate;
386                        }
387                        node = (NonTerminalNode)candidate;
388                }
389                return null;
390        }
391        
392        public ComparableNode getRightmostProperDescendant() {
393                NonTerminalNode node = this;
394                PhraseStructureNode candidate = null;
395                while (node != null) {
396                        candidate = node.getRightChild();
397                        if (candidate == null || candidate instanceof TokenNode) {
398                                return candidate;
399                        }
400                        node = (NonTerminalNode)candidate;
401                }
402                return null;
403        }
404        
405        public ComparableNode getLeftmostDescendant() throws MaltChainedException {
406                return getLeftmostProperDescendant();
407        }
408        
409        public ComparableNode getRightmostDescendant() throws MaltChainedException {
410                return getRightmostProperDescendant();
411        }
412        
413//      public void reArrangeChildrenAccordingToLeftAndRightProperDesendant() throws MaltChainedException {
414//              int i = 0;
415//              int leftMostCorner = -1;
416//              int leftMostCornerChildIndex = -1;
417//              while (i < children.size()) {   
418//                      if (leftMostCorner == -1) {
419//                              leftMostCorner = getChild(i).getLeftmostProperDescendantIndex();
420//                              leftMostCornerChildIndex = i;
421//                      } else if (getChild(i) instanceof NonTerminal && getChild(i).getLeftmostProperDescendantIndex() < leftMostCorner) {
422//                              NonTerminalNode child = (NonTerminal)getChild(i);
423//                              PhraseStructureNode leftMostCornerChild = getChild(leftMostCornerChildIndex);
424//                              if (leftMostCornerChild.getParent() != null) {
425//                                      LabelSet labelSet = leftMostCornerChild.getParentEdge().getLabelSet();
426//                                      ((PhraseStructure)getBelongsToGraph()).removePhraseStructureEdge(this, leftMostCornerChild);
427//                                      Edge e = ((PhraseStructure)getBelongsToGraph()).addPhraseStructureEdge(child, leftMostCornerChild);
428//                                      e.addLabel(labelSet);
429//                              }
430//                              child.reArrangeChildrenAccordingToLeftAndRightProperDesendant();
431//                              i = -1;
432//                              leftMostCorner = -1;
433//                              leftMostCornerChildIndex = -1;
434//                      } else {
435//                              leftMostCorner = getChild(i).getLeftmostProperDescendantIndex();
436//                              leftMostCornerChildIndex = i;
437//                      }
438//                      i++;
439//              }
440//
441//              for (int j = 0, n=children.size(); j < n; j++) {
442//                      if (getChild(j) instanceof NonTerminalNode) {
443//                              ((NonTerminalNode)getChild(j)).reArrangeChildrenAccordingToLeftAndRightProperDesendant();
444//                      }
445//              }
446//      }
447        
448        @Override
449        public void setIndex(int index) throws MaltChainedException {
450                if (index > 0) { 
451                        this.index = index; //INDEX_OFFSET+index;
452                } else {
453                        throw new SyntaxGraphException("The index must be a positive index");
454                }
455                
456        }
457
458        public void clear() throws MaltChainedException {
459                super.clear();
460                children.clear();
461                parent = null;
462                index = -1;
463        }
464        
465        @Override
466        public int compareTo(ComparableNode o) {
467                final int BEFORE = -1;
468            final int EQUAL = 0;
469            final int AFTER = 1;
470            if ( this == o ) return EQUAL;
471            try {
472                    final int thisLCorner = this.getLeftmostProperDescendantIndex();
473                    final int thatLCorner = (o instanceof TokenNode)?o.getCompareToIndex():o.getLeftmostProperDescendantIndex();
474                    final int thisRCorner = this.getRightmostProperDescendantIndex();
475                    final int thatRCorner = (o instanceof TokenNode)?o.getCompareToIndex():o.getRightmostProperDescendantIndex();
476
477//                  if (thisLCorner == -1 || thatLCorner == -1) {
478//                          if (thisRCorner < thatRCorner) return BEFORE;
479//                          if (thisRCorner > thatRCorner) return AFTER;
480//                  }
481//                  if (thisRCorner == -1 || thatRCorner == -1) {
482//                          if (thisLCorner < thatLCorner) return BEFORE;
483//                          if (thisLCorner > thatLCorner) return AFTER;
484//                  }
485                    
486                    if (thisLCorner != -1 && thatLCorner != -1 && thisRCorner != -1 && thatRCorner != -1) {
487                            if (thisLCorner < thatLCorner && thisRCorner < thatRCorner) return BEFORE;
488                            if (thisLCorner > thatLCorner && thisRCorner > thatRCorner) return AFTER;
489                            if (thisLCorner > thatLCorner && thisRCorner < thatRCorner) return BEFORE;
490                            if (thisLCorner < thatLCorner && thisRCorner > thatRCorner) return AFTER;   
491                    } else {
492                            if (thisLCorner != -1 && thatLCorner != -1) {
493                                    if (thisLCorner < thatLCorner) return BEFORE;
494                                    if (thisLCorner > thatLCorner) return AFTER;
495                            }
496                            if (thisRCorner != -1 && thatRCorner != -1) {
497                                    if (thisRCorner < thatRCorner) return BEFORE;
498                                    if (thisRCorner > thatRCorner) return AFTER;
499                            }
500                    }
501
502                
503                
504//                  final int thisLCorner = this.getLeftmostDescendantIndex();
505//                  final int thatLCorner = (o instanceof TerminalNode)?o.getCompareToIndex():o.getLeftmostDescendantIndex();
506//                  final int thisRCorner = this.getRightmostDescendantIndex();
507//                  final int thatRCorner = (o instanceof TerminalNode)?o.getCompareToIndex():o.getRightmostDescendantIndex();
508//
509//                  if (thisLCorner == -1 || thatLCorner == -1) {
510//                          if (thisRCorner < thatRCorner) return BEFORE;
511//                          if (thisRCorner > thatRCorner) return AFTER;
512//                  }
513//                  if (thisRCorner == -1 || thatRCorner == -1) {
514//                          if (thisLCorner < thatLCorner) return BEFORE;
515//                          if (thisLCorner > thatLCorner) return AFTER;
516//                  }
517//                  if (thisLCorner < thatLCorner && thisRCorner < thatRCorner) return BEFORE;
518//                  if (thisLCorner > thatLCorner && thisRCorner > thatRCorner) return AFTER;
519//                  if (thisLCorner > thatLCorner && thisRCorner < thatRCorner) return BEFORE;
520//                  if (thisLCorner < thatLCorner && thisRCorner > thatRCorner) return AFTER;
521                
522//                  System.out.println(thisLCorner + " " + thatLCorner + " " +thisRCorner + " " + thatRCorner);
523                
524//                  int thisCorner = this.getLeftmostDescendantIndex();
525//                  int thatCorner = (o instanceof TerminalNode)?o.getCompareToIndex():o.getLeftmostDescendantIndex();
526//                  if (thisCorner != -1 && thatCorner != -1) {
527//                          if (thisCorner < thatCorner) return BEFORE;
528//                          if (thisCorner > thatCorner) return AFTER;
529//                  }
530//                  thisCorner = this.getRightmostDescendantIndex();
531//                  thatCorner = (o instanceof TerminalNode)?o.getCompareToIndex():o.getRightmostDescendantIndex();
532//                  if (thisCorner != -1 && thatCorner != -1) {
533//                          if (thisCorner < thatCorner) return BEFORE;
534//                          if (thisCorner > thatCorner) return AFTER;
535//                  }
536        } catch (MaltChainedException e) {
537                        if (SystemLogger.logger().isDebugEnabled()) {
538                                SystemLogger.logger().debug("",e);
539                        } else {
540                                SystemLogger.logger().error(e.getMessageChain());
541                        }
542                        System.exit(1);
543        }
544            if (this.getCompareToIndex() < o.getCompareToIndex()) return BEFORE;
545            if (this.getCompareToIndex() > o.getCompareToIndex()) return AFTER;
546                return super.compareTo(o);
547        }
548        
549        public boolean equals(Object obj) {
550                if (this == obj) return true;
551                if (!(obj instanceof NonTerminal)) return false;
552                return super.equals(obj);
553        }
554        
555        public int hashCode() {
556                return 31 * 7 + super.hashCode();
557        }
558        
559        public String toString() {
560                final StringBuilder sb = new StringBuilder();
561                sb.append(getIndex());
562                sb.append('\t');
563
564                for (SymbolTable table : getLabelTypes()) {
565                        try {
566                                sb.append(getLabelSymbol(table));
567                        } catch (MaltChainedException e) {
568                                System.err.println("Print error : "+e.getMessageChain());
569                        }
570                        sb.append('\t');
571                }
572
573                return sb.toString();
574        }
575        
576}