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