    001    package org.maltparser.parser.algorithm.twoplanar;
    002
    003    import java.util.Stack;
    004
    005    import org.maltparser.core.exception.MaltChainedException;
    006    import org.maltparser.core.syntaxgraph.DependencyStructure;
    007    import org.maltparser.core.syntaxgraph.edge.Edge;
    008    import org.maltparser.core.syntaxgraph.node.DependencyNode;
    009    import org.maltparser.parser.ParserConfiguration;
    010    import org.maltparser.parser.TransitionSystem;
    011    import org.maltparser.parser.history.GuideUserHistory;
    012    import org.maltparser.parser.history.History;
    013    import org.maltparser.parser.history.action.ComplexDecisionAction;
    014    import org.maltparser.parser.history.action.GuideUserAction;
    015    import org.maltparser.parser.transition.TransitionTable;
    016    /**
    017     * @author Carlos Gomez Rodriguez
    018     *
    019     */
    020    public class TwoPlanar extends TransitionSystem {
    021            protected static final int SHIFT = 1;
    022            protected static final int SWITCH = 2;
    023            protected static final int RIGHTARC = 3;
    024            protected static final int LEFTARC = 4;
    025            protected static final int REDUCE = 5;
    026            protected static final int REDUCEBOTH = 6;
    027
    028
    029
    030
    031            public TwoPlanar() throws MaltChainedException {
    032                    super();
    033            }
    034
    035            public void apply(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
    036                    TwoPlanarConfig planarConfig = (TwoPlanarConfig)config;
    037                    Stack<DependencyNode> activeStack = planarConfig.getActiveStack();
    038                    Stack<DependencyNode> inactiveStack = planarConfig.getInactiveStack();
    039                    Stack<DependencyNode> input = planarConfig.getInput();
    040                    currentAction.getAction(actionContainers);
    041                    Edge e = null;
    042                    int actionCode = transActionContainer.getActionCode();
    043                    switch ( actionCode ) {
    044                    case LEFTARC:
    045                            e = planarConfig.getDependencyStructure().addDependencyEdge(input.peek().getIndex(), activeStack.peek().getIndex());
    046                            addEdgeLabels(e);
    047                            break;
    048                    case RIGHTARC:
    049                            e = planarConfig.getDependencyStructure().addDependencyEdge(activeStack.peek().getIndex(), input.peek().getIndex());
    050                            addEdgeLabels(e);
    051                            break;
    052                    case SWITCH:
    053                            planarConfig.switchStacks();
    054                            if ( planarConfig.reduceAfterSwitch() )
    055                            {
    056                                    planarConfig.getActiveStack().pop();
    057                            }
    058                            break;
    059                    case REDUCE:
    060                            activeStack.pop();
    061                            break;
    062                    case REDUCEBOTH:
    063                            activeStack.pop();
    064                            inactiveStack.pop();
    065                            break;
    066                    default: //SHIFT
    067                            DependencyNode n = input.pop();
    068                            activeStack.push(n);
    069                            inactiveStack.push(n);
    070                            break;
    071                    }
    072                    planarConfig.setLastAction(actionCode);
    073            }
    074
    075
    076            public GuideUserAction getDeterministicAction(GuideUserHistory history, ParserConfiguration config) throws MaltChainedException {
    077                    TwoPlanarConfig theConfig = (TwoPlanarConfig)config;
    078                    if (theConfig.getRootHandling() != TwoPlanarConfig.NORMAL && theConfig.getActiveStack().peek().isRoot()) {
    079                            return updateActionContainers(history, TwoPlanar.SHIFT, null);
    080                    }
    081                    return null;
    082            }
    083
    084            protected void addAvailableTransitionToTable(TransitionTable ttable) throws MaltChainedException {
    085                    ttable.addTransition(SHIFT, "SH", false, null);
    086                    ttable.addTransition(SWITCH, "SW", false, null);
    087                    ttable.addTransition(REDUCE, "RE", false, null);
    088                    ttable.addTransition(REDUCEBOTH, "RB", false, null);
    089                    ttable.addTransition(RIGHTARC, "RA", true, null);
    090                    ttable.addTransition(LEFTARC, "LA", true, null);
    091            }
    092
    093            protected void initWithDefaultTransitions(GuideUserHistory history) throws MaltChainedException {
    094                    GuideUserAction currentAction = new ComplexDecisionAction((History)history);
    095
    096                    transActionContainer.setAction(SHIFT);
    097                    transActionContainer.setAction(REDUCE);
    098                    transActionContainer.setAction(SWITCH); //TODO it seems like a good idea to do this, but I don't know what it actually does
    099                    transActionContainer.setAction(REDUCEBOTH); //TODO same as above
    100                    for (int i = 0; i < arcLabelActionContainers.length; i++) {
    101                            arcLabelActionContainers[i].setAction(-1);
    102                    }
    103                    currentAction.addAction(actionContainers);
    104            }
    105
    106            public String getName() {
    107                    return "two-planar arc-eager";
    108            }
    109
    110            public boolean permissible(GuideUserAction currentAction, ParserConfiguration config) throws MaltChainedException {
    111                    currentAction.getAction(actionContainers);
    112                    int trans = transActionContainer.getActionCode();
    113                    TwoPlanarConfig planarConfig = (TwoPlanarConfig)config;
    114                    DependencyNode activeStackPeek = planarConfig.getActiveStack().peek();
    115                    DependencyNode inactiveStackPeek = planarConfig.getInactiveStack().peek();
    116                    DependencyNode inputPeek = planarConfig.getInput().peek();
    117                    DependencyStructure dg = planarConfig.getDependencyGraph();
    118                    //int rootHandling = planarConfig.getRootHandling();
    119                    boolean singleHeadConstraint = planarConfig.requiresSingleHead();
    120                    boolean noCoveredRootsConstraint = planarConfig.requiresNoCoveredRoots();
    121                    boolean acyclicityConstraint = planarConfig.requiresAcyclicity();
    122                    //boolean connectednessConstraintOnReduce = planarConfig.requiresConnectednessCheckOnReduce();
    123                    //boolean connectednessConstraintOnShift = planarConfig.requiresConnectednessCheckOnShift();
    124                    if ((trans == LEFTARC || trans == RIGHTARC) && !isActionContainersLabeled()) {
    125                            return false;
    126                    }
    127                    //if ((trans == LEFTARC || trans == REDUCE) && stackPeek.isRoot()) {
    128                    //      return false;
    129                    //}
    130                    if (trans == LEFTARC) {
    131                            //avoid making root child of something
    132                            if ( activeStackPeek.isRoot() )
    133                                    return false;
    134                            //enforce single-head constraint if present
    135                            if ( activeStackPeek.hasHead() && singleHeadConstraint )
    136                                    return false;
    137                            //avoid two links being created from and to the same node
    138                            if ( activeStackPeek.hasHead() && dg.getTokenNode(activeStackPeek.getIndex()).getHead().getIndex() == inputPeek.getIndex() )
    139                                    return false;
    140                            //enforce acyclicity constraint if present
    141                            if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
    142                                    return false;
    143                    }
    144                    if (trans == RIGHTARC) {
    145                            //enforce single-head constraint if present
    146                            if ( inputPeek.hasHead() && singleHeadConstraint )
    147                                    return false;
    148                            //avoid two links being created from and to the same node
    149                            if ( inputPeek.hasHead() && dg.getTokenNode(inputPeek.getIndex()).getHead().getIndex() == activeStackPeek.getIndex() )
    150                                    return false;
    151                            //enforce acyclicity constraint if present
    152                            if ( acyclicityConstraint && activeStackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() )
    153                                    return false;
    154                    }
    155                    if (trans == REDUCE) {
    156                            //do not reduce the dummy root
    157                            if ( activeStackPeek.isRoot() )
    158                                    return false;
    159                            //enforce no-covered-roots constraint if present
    160                            if ( !activeStackPeek.hasHead() && noCoveredRootsConstraint )
    161                                    return false;
    162                            //TODO does this line still make sense? (from Nivre arc-eager)
    163                            //if ( !stackPeek.hasHead() && rootHandling == PlanarConfig.STRICT )
    164                            //      return false;
    165                            //enforce connectedness constraint if present
    166                            /*
    167                            if ( connectednessConstraintOnReduce )
    168                            {
    169                                    boolean path1 = ( stackPeek.findComponent().getIndex() == inputPeek.findComponent().getIndex() );
    170                                    boolean path2;
    171                                    if ( planarConfig.getStack().size() < 2 ) path2=false;
    172                                    else
    173                                    {
    174                                            DependencyNode stackPrev = planarConfig.getStack().get(planarConfig.getStack().size()-2);
    175                                            path2 = stackPrev.findComponent().getIndex() == stackPeek.findComponent().getIndex();
    176                                    }
    177                                    return path1 || path2;
    178                            }
    179                            */
    180                    }
    181                    if ( trans == SHIFT )
    182                    {
    183                            /*
    184                            if ( connectednessConstraintOnShift && planarConfig.getInput().size() == 1 ) //last word
    185                            {
    186                                    boolean path = ( planarConfig.getDependencyGraph().getTokenNode(1).findComponent().getIndex() == inputPeek.findComponent().getIndex() ); //require connection to 1st
    187                                    return path;
    188                            }
    189                            */
    190                    }
    191                    if (trans == REDUCEBOTH) {
    192                            //do not reduce the dummy root
    193                            if ( activeStackPeek.isRoot() || inactiveStackPeek.isRoot() )
    194                                    return false;
    195                            //enforce no-covered-roots constraint if present
    196                            if ( (!activeStackPeek.hasHead() || inactiveStackPeek.hasHead()) && noCoveredRootsConstraint )
    197                                    return false;
    198
    199                            //TODO remove this:
    200                            //not using this transition at the moment, so
    201                            return false;
    202                    }
    203                    if ( trans == SWITCH )
    204                    {
    205                            if ( planarConfig.reduceAfterSwitch() )
    206                            {
    207                                    if ( inactiveStackPeek.isRoot() )
    208                                            return false;
    209                                    //enforce no-covered-roots constraint if present
    210                                    if ( !inactiveStackPeek.hasHead() && noCoveredRootsConstraint )
    211                                            return false;
    212                            }
    213                            else
    214                            {
    215                                    if ( planarConfig.getLastAction() == SWITCH ) return false;
    216                            }
    217                    }
    218                    return true;
    219            }
    220
    221            public GuideUserAction defaultAction(GuideUserHistory history, ParserConfiguration configuration) throws MaltChainedException {
    222                    return updateActionContainers(history, TwoPlanar.SHIFT, null);
    223            }
    224
    225
    226    }