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}