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    }