/*
* FSMPDA.java
*
* Copyright (c) 2010-2011, Ontotext (www.ontotext.com).
*
* This file is part of GATE (see http://gate.ac.uk/), and is free
* software, licenced under the GNU Library General Public License,
* Version 2, June 1991 (in the distribution as file licence.html,
* and also available at http://gate.ac.uk/gate/licence.html).
*
*
* $Id$
*/
package com.ontotext.jape.pda;
import static gate.jape.KleeneOperator.Type.OPTIONAL;
import static gate.jape.KleeneOperator.Type.PLUS;
import static gate.jape.KleeneOperator.Type.RANGE;
import static gate.jape.KleeneOperator.Type.STAR;
import gate.fsm.FSM;
import gate.jape.BasicPatternElement;
import gate.jape.ComplexPatternElement;
import gate.jape.ConstraintGroup;
import gate.jape.KleeneOperator;
import gate.jape.LeftHandSide;
import gate.jape.PatternElement;
import gate.jape.PrioritisedRuleList;
import gate.jape.Rule;
import gate.jape.SinglePhaseTransducer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.ontotext.jape.automaton.Automaton;
import com.ontotext.jape.automaton.AutomatonBuildHelp;
import com.ontotext.jape.automaton.ClosedHashOfStrings;
import com.ontotext.jape.automaton.Constants;
import com.ontotext.jape.automaton.GenericWholeArrray;
import com.ontotext.jape.automaton.TripleTransitions;
public class FSMPDA extends FSM {
private static final long serialVersionUID = 5672459102751022260L;
private transient ClosedHashOfStrings setOfBindingNames;
private transient TripleTransitions tripleTransitions;
private String[] arrayOfBindingNames;
private StatePDA initialState;
public String[] getBindingNames() {
return arrayOfBindingNames;
}
public FSMPDA(SinglePhaseTransducer spt) {
this();
setOfBindingNames = new ClosedHashOfStrings();
tripleTransitions = new TripleTransitions();
addRules(spt.getRules());
}
@Override
public StatePDA getInitialState(){
return initialState;
}
protected FSMPDA() {
initialState = new StatePDA();
}
@Override
protected void addRules(PrioritisedRuleList rules) {
Iterator<Rule> rulesEnum = rules.iterator();
while (rulesEnum.hasNext()) {
FSMPDA ruleFSM = spawn(rulesEnum.next());
initialState.addTransition(new TransitionPDA(null, ruleFSM.initialState), tripleTransitions);
}
arrayOfBindingNames = setOfBindingNames.getCopyOfStrings();
setOfBindingNames = null;
AutomatonBuildHelp help = new AutomatonBuildHelp(tripleTransitions);
Automaton aut = new Automaton(help, GenericWholeArrray.TYPE_SHORT);
tripleTransitions.addAll(aut, help);
tripleTransitions.setTheInitialState(aut, initialState.getIndex());
aut = aut.determinize(tripleTransitions.getFinalitites()).minimize();
allStates = aut.toFSM(tripleTransitions);
int i = aut.getInitialState();
if (i == Constants.NO) {
initialState = new StatePDA();
allStates = new StatePDA[1];
allStates[0] = initialState;
} else {
initialState = allStates[i];
}
tripleTransitions = null;
}
protected StatePDA[] allStates;
public FSMPDA(Rule rule, ClosedHashOfStrings setOfBindingNames,
TripleTransitions tripleTransitions) {
this();
this.setOfBindingNames = setOfBindingNames;
this.tripleTransitions = tripleTransitions;
setRule(rule);
}
@Override
protected void setRule(Rule rule) {
LeftHandSide lhs = rule.getLHS();
PatternElement[][] constraints = lhs.getConstraintGroup()
.getPatternElementDisjunction();
// the rectangular array constraints is a disjunction of sequences of
// constraints = [[PE]:[PE]...[PE] ||
// [PE]:[PE]...[PE] ||
// ...
// [PE]:[PE]...[PE] ]
// The current and the next state for the current ROW.
StatePDA currentRowState, nextRowState;
StatePDA finalState = new StatePDA();
PatternElement currentPattern;
for (int i = 0; i < constraints.length; i++) {
// for each row we have to create a sequence of states that will
// accept
// the sequence of annotations described by the restrictions on that
// row.
// The final state of such a sequence will always be a final state
// which
// will have associated the right hand side of the rule used for
// this
// constructor.
// For each row we will start from the initial state.
currentRowState = initialState;
for (int j = 0; j < constraints[i].length; j++) {
// parse the sequence of constraints:
// For each basic pattern element add a new state and link it to
// the
// currentRowState.
// The case of kleene operators has to be considered!
currentPattern = constraints[i][j];
StatePDA insulator = new StatePDA();
currentRowState.addTransition(
new TransitionPDA(null, insulator), tripleTransitions);
currentRowState = insulator;
if (currentPattern instanceof BasicPatternElement) {
// the easy case
nextRowState = new StatePDA();
currentRowState.addTransition(
new TransitionPDA(
(BasicPatternElement) currentPattern,
nextRowState), tripleTransitions);
currentRowState = nextRowState;
} else if (currentPattern instanceof ComplexPatternElement) {
// the current pattern is a complex pattern element
// ..it will probaly be converted into a sequence of states
// itself.
currentRowState = convertComplexPE(currentRowState,
(ComplexPatternElement) currentPattern);
} else {
// we got an unknown kind of pattern
throw new RuntimeException("Strange looking pattern: "
+ currentPattern);
}
} // for j
// link the end of the current row to the final state using
// an empty transition.
currentRowState.addTransition(new TransitionPDA(null, finalState),
tripleTransitions);
finalState.setAction(rule.getRHS(), tripleTransitions);
finalState.setFileIndex(rule.getPosition());
finalState.setPriority(rule.getPriority());
} // for i
}
protected FSMPDA(ComplexPatternElement cpe) {
this();
finalState = convertComplexPE(initialState, cpe);
((StatePDA) finalState).setItFinal(tripleTransitions);
}
@Override
protected FSMPDA spawn(Rule r) {
return new FSMPDA(r, setOfBindingNames, tripleTransitions);
}
@Override
protected FSMPDA spawn(ComplexPatternElement currentPattern) {
return new FSMPDA(currentPattern);
}
private StatePDA convertComplexPE(StatePDA startState,
ComplexPatternElement cpe) {
String bindingName = cpe.getBindingName();
KleeneOperator kleeneOp = cpe.getKleeneOp();
KleeneOperator.Type type = kleeneOp.getType();
StatePDA innerStartState;
if (bindingName != null) {
innerStartState = new StatePDA();
} else {
innerStartState = startState;
}
StatePDA innerEndState = generateStates(innerStartState, cpe);
StatePDA endState;
if (bindingName != null) {
endState = new StatePDA();
startState.addTransition(new TransitionPDA(null, innerStartState,
TransitionPDA.TYPE_OPENING_ROUND_BRACKET),
tripleTransitions);
if (type != RANGE) {
innerEndState.addTransition(new TransitionPDA(null, endState,
setOfBindingNames.put(bindingName)), tripleTransitions);
}
} else {
endState = innerEndState;
}
// now take care of the kleene operator
if (type == OPTIONAL) {
// allow to skip everything via a null transition
startState.addTransition(new TransitionPDA(null, endState),
tripleTransitions);
} else if (type == PLUS) {
// allow to return to innerStartState from innerEndState
innerEndState
.addTransition(new TransitionPDA(null, innerStartState),
tripleTransitions);
} else if (type == STAR) {
// allow to skip everything via a null transition
startState.addTransition(new TransitionPDA(null, endState),
tripleTransitions);
// allow to return to innerStartState from innerEndState
innerEndState
.addTransition(new TransitionPDA(null, innerStartState),
tripleTransitions);
} else if (type == RANGE) {
Integer min = kleeneOp.getMin();
Integer max = kleeneOp.getMax();
List<StatePDA> startStateList;
if (bindingName != null) {
startStateList = null;
} else {
// in this case keep a list of the start states for each
// possible optional sets so can make
// direct transitions from them to the final end state
startStateList = new ArrayList<StatePDA>();
}
if (min == null || min == 0) {
// if min is empty or 0, allow to skip everything via a null
// transition
if (bindingName != null) {
startState.addTransition(new TransitionPDA(null, endState),
tripleTransitions);
} else {
startStateList.add(innerStartState);
}
} else if (min > 1) {
// add min-1 copies of the set of states for the CPE. It's -1
// because
// one set was already added by the first generateStates call
for (int i = 1; i < min; i++) {
// the end state of the previous set always moves up to be
// the
// start state of the next set.
innerStartState = innerEndState;
innerEndState = generateStates(innerStartState, cpe);
}
}
if (max == null) {
// if there is no defined max, allow to return to startState any
// number of times. Start state may be the original start or, if
// min > 1, then it's the start of the last set of states added.
// Example: A range with min 3 and max = unbounded will look
// like
// this:
// v------|
// start1...end1->start2...end2->start3...end3->...
//
innerEndState.addTransition(new TransitionPDA(null,
innerStartState), tripleTransitions);
} else if (max > min) {
// there are some optional state sets. Make a copy of the state
// set for each.
int numCopies = max - min;
// if min == 0 then reduce numCopies by one since we already
// added
// one set of states that are optional
if (min == 0)
numCopies--;
for (int i = 1; i <= numCopies; i++) {
innerStartState = innerEndState;
if (bindingName != null) {
innerStartState.addTransition(new TransitionPDA(null,
endState, setOfBindingNames.put(bindingName)),
tripleTransitions);
} else {
startStateList.add(innerStartState);
}
innerEndState = generateStates(innerStartState, cpe);
}
}
if (bindingName != null) {
innerEndState.addTransition(new TransitionPDA(null, endState,
setOfBindingNames.put(bindingName)), tripleTransitions);
} else {
// each of the optional stages can transition directly to the
// final end
for (StatePDA state : startStateList) {
state.addTransition(new TransitionPDA(null, innerEndState),
tripleTransitions);
}
endState = innerEndState;
}
} // end if type == RANGE
return endState;
}
private StatePDA generateStates(StatePDA startState,
ComplexPatternElement cpe) {
ConstraintGroup constraintGroup = cpe.getConstraintGroup();
PatternElement[][] constraints = constraintGroup
.getPatternElementDisjunction();
// the rectangular array constraints is a disjunction of sequences of
// constraints = [[PE]:[PE]...[PE] ||
// [PE]:[PE]...[PE] ||
// ...
// [PE]:[PE]...[PE] ]
// The current and the next state for the current ROW.
StatePDA currentRowState, nextRowState, endState = new StatePDA();
PatternElement currentPattern;
for (int i = 0; i < constraints.length; i++) {
// for each row we have to create a sequence of states that will
// accept
// the sequence of annotations described by the restrictions on that
// row.
// The final state of such a sequence will always be a finale state
// which
// will have associated the right hand side of the rule used for
// this
// constructor.
// For each row we will start from the initial state.
currentRowState = startState;
for (int j = 0; j < (constraints[i]).length; j++) {
// parse the sequence of constraints:
// For each basic pattern element add a new state and link it to
// the
// currentRowState.
// The case of kleene operators has to be considered!
StatePDA insulator = new StatePDA();
currentRowState.addTransition(
new TransitionPDA(null, insulator), tripleTransitions);
currentRowState = insulator;
currentPattern = constraints[i][j];
if (currentPattern instanceof BasicPatternElement) {
// the easy case
nextRowState = new StatePDA();
currentRowState.addTransition(
new TransitionPDA(
(BasicPatternElement) currentPattern,
nextRowState), tripleTransitions);
currentRowState = nextRowState;
} else if (currentPattern instanceof ComplexPatternElement) {
// the current pattern is a complex pattern element
// ..it will probaly be converted into a sequence of states
// itself.
currentRowState = convertComplexPE(currentRowState,
(ComplexPatternElement) currentPattern);
} else {
// we got an unknown kind of pattern
throw new RuntimeException("Strange looking pattern:"
+ currentPattern);
}
} // for j
// link the end of the current row to the general end state
// using
// an empty transition.
currentRowState.addTransition(new TransitionPDA(null, endState),
tripleTransitions);
} // for i
return endState;
}
}