/* * Copyright (c) 2009 - 2011, Valentin Tablan. * * SPTBuilder.java * * 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). * * Valentin Tablan, 2 Aug 2009 * * $Id: SPTBuilder.java 71 2009-08-04 06:39:03Z valyt $ */ package gate.jape.plus; import gate.creole.ResourceInstantiationException; import gate.jape.Constraint; import gate.jape.JapeConstants; import gate.jape.RightHandSide; import gate.jape.Rule; import gate.jape.SinglePhaseTransducer; import gate.jape.constraint.ConstraintPredicate; import gate.jape.constraint.ContainsPredicate; import gate.jape.constraint.WithinPredicate; import gate.jape.plus.Predicate.PredicateType; import gate.jape.plus.SPTBase.MatchMode; import gate.jape.plus.SPTBase.State; import gate.jape.plus.SPTBase.Transition; import gate.jape.plus.Transducer.SPTData; import gate.util.GateClassLoader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import cern.colt.list.IntArrayList; import cern.colt.map.OpenIntIntHashMap; import com.ontotext.jape.pda.FSMPDA; import com.ontotext.jape.pda.StatePDA; import com.ontotext.jape.pda.TransitionPDA; /** * An utility class for converting a default JAPE transducer into a JAPE-Plus transducer. */ public class SPTBuilder { /** * Flag used when debugging: set to true to enable dumping the generated code * to disk and printing of additional information. */ private static final boolean DEBUG = false; public static final String GENERATED_CLASS_PACKAGE = "japephases"; private static final String[] TABS = new String[]{"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t"}; /** * Stores the states for the optimised transducer. */ protected List<SPTBase.State> newStates; /** * Stores the rules in the the transducer. */ protected Rule[] rules; /** * Stores the types of annotations actually used in the grammar. */ protected List<String> annotationTypes; /** * Stores the list of predicates for each annotation type. */ protected Map<String, List<Predicate>> predicatesByType; /** * Holds the mapping between input states (old states, represented through their * ID) and new state (represented as their index in the {@link #newStates} array). */ protected OpenIntIntHashMap oldToNewStates; public SPTData buildSPT(SinglePhaseTransducer oldSpt, GateClassLoader classLoader) throws ResourceInstantiationException{ annotationTypes = new ArrayList<String>(); predicatesByType = new HashMap<String, List<Predicate>>(); newStates = new ArrayList<SPTBase.State>(); oldToNewStates = new OpenIntIntHashMap(); rules = new Rule[oldSpt.getRules().size()]; rules = ((List<Rule>) oldSpt.getRules()).toArray(rules); //TODO not sure why this is being called from here but it needs to use the right classloader oldSpt.finish(classLoader); // FSM fsm = new FSM(oldSpt); FSMPDA fsm = (FSMPDA) oldSpt.getFSM(); createNewStates(fsm); createNewTransitions(fsm); optimisePredicates(); StringBuilder sptCode = new StringBuilder(); String className = "Phase" + oldSpt.getName() + Long.toString( System.currentTimeMillis(), Character.MAX_RADIX).toUpperCase(); writeClassHeader(className, sptCode); writeConstructor(className, oldSpt, 1, sptCode); writeDuplicate(className, 1, sptCode); writeAdvanceInstanceMethod(1, sptCode); for(int stateId = 0; stateId < newStates.size(); stateId++) { writeStateMethod(stateId, 1, sptCode); } writeClassFooter(sptCode); //prepare the parameters for calling the constructor //predicates Predicate[][] predicatesByTypeArray = new Predicate[ annotationTypes.size()][]; for(int i = 0; i < predicatesByTypeArray.length; i++){ String annType = annotationTypes.get(i); List<Predicate> preds = predicatesByType.get(annType); if(preds != null){ predicatesByTypeArray[i] = new Predicate[preds.size()]; predicatesByTypeArray[i] = preds.toArray( predicatesByTypeArray[i]); }else{ predicatesByTypeArray[i] = new Predicate[0]; } } // when debugging, this will dump the generated sources where Eclipse can // see them. if(DEBUG) { try { File srcDir = new File("plugins/JAPE_Plus/test/src/japephases/"); srcDir.mkdirs(); FileWriter writer = new FileWriter(new File(srcDir, className + ".java")); writer.write(sptCode.toString()); writer.close(); } catch(IOException e1) { e1.printStackTrace(); } // also print out the predicates used StringBuilder str = new StringBuilder(); for(int type = 0; type < predicatesByTypeArray.length; type++) { str.append(annotationTypes.get(type)).append(": "); for(Predicate pred : predicatesByTypeArray[type]) { str.append(pred.toString()).append(" "); } str.append("\n"); } str.append("\n\n"); System.out.print(str.toString()); } SPTData sptData = new SPTData(GENERATED_CLASS_PACKAGE + "." + className, sptCode.toString(), oldSpt.generateControllerEventBlocksCode(GENERATED_CLASS_PACKAGE,className+"CEAB"), rules, predicatesByTypeArray, oldSpt.input); //cleanup annotationTypes = null; newStates = null; oldToNewStates = null; predicatesByType = null; rules = null; return sptData; } protected void writeClassHeader(String className, StringBuilder out) { out.append("package ").append(GENERATED_CLASS_PACKAGE).append(";\n"); out.append("import gate.jape.Rule;\n"); out.append("import gate.jape.JapeException;\n"); out.append("import gate.jape.plus.Predicate;\n"); out.append("import gate.jape.plus.SPTBase;\n"); out.append("import gate.creole.ResourceInstantiationException;\n"); out.append("import cern.colt.list.IntArrayList;\n"); out.append("import static gate.jape.plus.SPTBase.MatchMode.*;\n\n"); out.append("public class ").append(className).append(" extends SPTBase {\n\n"); out.append("\t// default serialisation ID\n"); out.append("\tprivate static final long serialVersionUID = 1L;\n\n"); } protected void writeConstructor(String className, SinglePhaseTransducer spt, int tabs, StringBuilder out) { out.append(TABS[tabs]).append("public ").append(className).append(" (" ).append("Rule[] rules, Predicate[][] predicatesByType) {\n"); tabs++; out.append(TABS[tabs]).append("super (\n"); tabs++; // phaseName out.append(TABS[tabs]).append('"').append(className).append('"').append(", // phase name\n"); // binding names out.append(TABS[tabs]).append("// binding names\n"); out.append(TABS[tabs]).append("new String[]{"); boolean first = true; for(String str : ((FSMPDA)spt.getFSM()).getBindingNames()) { if(first) { first = false; } else { out.append(", "); } out.append("\"").append(str).append("\""); } out.append("},\n"); // annotationTypes out.append(TABS[tabs]).append("// annotationTypes\n"); out.append(TABS[tabs]).append("new String[]{"); first = true; for(String str : annotationTypes) { if(first) { first = false; } else { out.append(", "); } out.append("\"").append(str).append("\""); } out.append("},\n"); // debugMode out.append(TABS[tabs]).append(spt.isDebugMode()).append(",").append(" // debugMode\n"); // groupMatchingMode out.append(TABS[tabs]).append(spt.isMatchGroupMode()).append(",").append(" // groupMatchingMode\n"); // match style String matchStyle = MatchMode.APPELT.name(); // default is APPELT if(spt.getRuleApplicationStyle() == JapeConstants.ALL_STYLE){ matchStyle = MatchMode.ALL.name(); } else if(spt.getRuleApplicationStyle() == JapeConstants.BRILL_STYLE){ matchStyle = MatchMode.BRILL.name(); }else if(spt.getRuleApplicationStyle() == JapeConstants.FIRST_STYLE){ matchStyle = MatchMode.FIRST.name(); }else if(spt.getRuleApplicationStyle() == JapeConstants.ONCE_STYLE){ matchStyle = MatchMode.ONCE.name(); } out.append(TABS[tabs]).append(matchStyle).append(",").append(" // matching style\n"); out.append(TABS[tabs]).append("rules, predicatesByType\n"); tabs--; out.append(TABS[tabs]).append(");\n"); // end super() tabs--; out.append(TABS[tabs]).append("}\n\n"); // end constructor } private void writeDuplicate(String className, int tabs, StringBuilder out) { out.append(TABS[tabs]).append("@Override\n"); out.append(TABS[tabs]).append("protected ").append(className).append(" duplicate() throws ResourceInstantiationException {\n"); tabs++; out.append(TABS[tabs]).append(className).append(" copy = new ").append(className).append("(copyRules(), predicatesByType);\n"); out.append(TABS[tabs]).append("copy.inputAnnotationTypes = this.inputAnnotationTypes;\n"); out.append(TABS[tabs]).append("if(actionblocks != null) {\n"); tabs++; out.append(TABS[tabs]).append("try{\n"); tabs++; out.append(TABS[tabs]).append("copy.actionblocks = actionblocks.getClass().newInstance();\n"); tabs--; out.append(TABS[tabs]).append("} catch (Exception e) {\n"); tabs++; out.append(TABS[tabs]).append("throw new ResourceInstantiationException(e);\n"); tabs--; out.append(TABS[tabs]).append("}\n"); tabs--; out.append(TABS[tabs]).append("}\n"); out.append(TABS[tabs]).append("return copy;\n"); tabs--; out.append(TABS[tabs]).append("}\n\n"); // end method } protected void writeAdvanceInstanceMethod(int tabs, StringBuilder out) { out.append(TABS[tabs]).append("@Override\n"); out.append(TABS[tabs]).append("protected final boolean advanceInstance(FSMInstance instance) throws JapeException {\n"); tabs++; out.append(TABS[tabs]).append("switch(instance.state) {\n"); tabs++; for(int stateId = 0; stateId < newStates.size(); stateId++) { out.append(TABS[tabs]).append("case ").append(stateId).append(":\n"); tabs++; out.append(TABS[tabs]).append("if(state").append(stateId).append("(instance)) return true;\n"); out.append(TABS[tabs]).append("break;\n"); tabs--; } tabs--; out.append(TABS[tabs]).append("}\n"); out.append(TABS[tabs]).append("return false;\n"); tabs--; out.append(TABS[tabs]).append("}\n\n"); } protected void writeStateMethod(int stateId, int tabs, StringBuilder out) { out.append(TABS[tabs]).append("private final boolean state").append( stateId).append('(').append("FSMInstance instance").append(") throws JapeException {\n"); tabs++; State state = newStates.get(stateId); // if final state if(state.rule >= 0) { out.append(TABS[tabs]).append( "// current instance is in a final state\n"); out.append(TABS[tabs]).append( "FSMInstance newInstance = instance.clone();\n"); out.append(TABS[tabs]).append( "newInstance.rule = ").append(state.rule).append(";\n"); out.append(TABS[tabs]).append( "acceptingInstances.add(newInstance);\n"); out.append(TABS[tabs]).append( "if (matchMode == MatchMode.FIRST || matchMode == MatchMode.ONCE) {\n"); tabs++; out.append(TABS[tabs]).append("// we're done!\n"); out.append(TABS[tabs]).append("return true;\n"); tabs--; out.append(TABS[tabs]).append("}\n"); } // for each transition for(int transId = 0; transId < state.transitions.length; transId++) { Transition transition = state.transitions[transId]; if(transition.type == TransitionPDA.TYPE_OPENING_ROUND_BRACKET){ // opening-round-bracket transition out.append(TABS[tabs]).append( "{ // transition block: opening-round-bracket transition\n"); tabs++; out.append(TABS[tabs]).append( "FSMInstance nextInstance = instance.clone();\n"); out.append(TABS[tabs]).append( "nextInstance.pushNewEmptyBindingSet();\n"); out.append(TABS[tabs]).append( "nextInstance.state = ").append(transition.nextState).append(";\n"); out.append(TABS[tabs]).append( "activeInstances.addLast(nextInstance);\n"); tabs--; out.append(TABS[tabs]).append("} // end transition block\n"); } else if(transition.type != TransitionPDA.TYPE_CONSTRAINT){ // closing-round-bracket transition out.append(TABS[tabs]).append( "{ // transition block: closing-round-bracket transition\n"); tabs++; out.append(TABS[tabs]).append( "FSMInstance nextInstance = instance.clone();\n"); out.append(TABS[tabs]).append( "nextInstance.popBindingSet(bindingNames[").append(transition.type).append("]);\n"); out.append(TABS[tabs]).append( "nextInstance.state = ").append(transition.nextState).append(";\n"); out.append(TABS[tabs]).append( "activeInstances.addLast(nextInstance);\n"); tabs--; out.append(TABS[tabs]).append("} // end transition block\n"); } else { // constrained transition out.append(TABS[tabs]).append("s").append(stateId).append("t").append( transId).append(": ").append( "do { // transition block: constrained transition\n"); tabs++; writeConstrainedTransitionBlock(stateId, transId, tabs, out); tabs--; out.append(TABS[tabs]).append( "} while (false); // end transition block\n\n"); } } out.append(TABS[tabs]).append("return false;\n"); tabs--; out.append(TABS[tabs]).append("}\n\n"); } protected void writeConstrainedTransitionBlock(int stateId, int transId, int tabs, StringBuilder out) { State state = newStates.get(stateId); Transition transition = state.transitions[transId]; out.append(TABS[tabs]).append( "IntArrayList[] annotsForConstraints = new IntArrayList[").append( transition.constraints.length).append("];\n"); // unfurl, the constraints for(int constrId = 0; constrId < transition.constraints.length; constrId++) { // String blockLabel = "s" + stateId + "t" + transId + "c" + constrId; // out.append(TABS[tabs]).append(blockLabel).append(": do{ // constraint block\n"); out.append(TABS[tabs]).append("{ // constraint block\n"); tabs++; int[] constraint = transition.constraints[constrId]; out.append(TABS[tabs]).append("final int[] constraint = new int[] {"); boolean first = true; for(int elem : constraint) { if(first) first = false; else out.append(", "); out.append(elem); } out.append("};\n"); out.append(TABS[tabs]); // if there are any predicates to check, we need a break label if(constraint.length > 2) out.append("annotations: "); out.append("for(int annIdx = instance.annotationIndex;\n"); tabs++; tabs++; out.append(TABS[tabs]).append("annIdx < annotation.length &&\n"); out.append(TABS[tabs]).append("annIdx < annotationNextOffset[instance.annotationIndex];\n"); out.append(TABS[tabs]).append("annIdx++) {\n"); tabs--; out.append(TABS[tabs]).append( "if(constraint[0] == annotationType[annIdx]) {\n"); tabs++; out.append(TABS[tabs]).append("// type matched, now check predicates:\n"); // unfurl the predicates for(int predIdx = 2; predIdx < constraint.length; predIdx++) { out.append(TABS[tabs]).append( "if(!checkPredicate(annIdx, ").append(constraint[predIdx]).append(")) {\n"); tabs++; out.append(TABS[tabs]).append( "// one predicate failed -> move to next annotation\n"); out.append(TABS[tabs]).append("continue annotations;\n"); tabs--; out.append(TABS[tabs]).append("}\n"); } out.append(TABS[tabs]).append( "// if we got this far, all predicates succeeded, so this\n"); out.append(TABS[tabs]).append( "// annotation matches -> add it to the list for the current\n"); out.append(TABS[tabs]).append("// constraint\n"); out.append(TABS[tabs]).append("if(annotsForConstraints[").append( constrId).append("] == null) {\n"); tabs++; out.append(TABS[tabs]).append("annotsForConstraints[").append( constrId).append("] = new IntArrayList();\n"); tabs--; out.append(TABS[tabs]).append("}\n"); out.append(TABS[tabs]).append("annotsForConstraints[").append( constrId).append("].add(annIdx);\n"); tabs--; out.append(TABS[tabs]).append("}\n"); tabs--; out.append(TABS[tabs]).append("}\n"); out.append(TABS[tabs]).append("// we just finished checking one constraint\n"); if(constraint[1] < 0){ out.append(TABS[tabs]).append("// constraint is negated\n"); out.append(TABS[tabs]).append("if(annotsForConstraints[").append( constrId).append("] == null){\n"); tabs++; out.append(TABS[tabs]).append( "// no annotations matched -> constraint succeeds!\n"); out.append(TABS[tabs]).append("annotsForConstraints[").append( constrId).append("] = new IntArrayList();\n"); out.append(TABS[tabs]).append( "// no annotation is bound though!\n"); out.append(TABS[tabs]).append("annotsForConstraints[").append( constrId).append("].add(-1);\n"); tabs--; out.append(TABS[tabs]).append("}else{\n"); tabs++; out.append(TABS[tabs]).append( "// annotation were matched -> so the negated constraint fails!\n"); out.append(TABS[tabs]).append("break ").append("s").append( stateId).append("t").append(transId).append(";\n"); tabs--; out.append(TABS[tabs]).append("}\n"); }else{ out.append(TABS[tabs]).append("if(annotsForConstraints[").append( constrId).append("] == null) {\n"); tabs++; out.append(TABS[tabs]).append("// current constraint matched nothing -> transition failed.\n"); out.append(TABS[tabs]).append("break ").append("s").append( stateId).append("t").append(transId).append(";\n"); tabs--; out.append(TABS[tabs]).append("}\n"); } tabs--; out.append(TABS[tabs]).append("} // end constraint block\n"); } out.append(TABS[tabs]).append( "// we finished checking all constraints, and they all succeeded\n"); out.append(TABS[tabs]).append( "// -> apply the transition with all possible bindings combinations\n"); out.append(TABS[tabs]).append( "// a next step is a set of bound annotations, one for each constraint\n"); out.append(TABS[tabs]).append( "generateAllNewInstances(instance, ").append( transition.nextState).append(", annotsForConstraints);\n"); } protected void writeClassFooter(StringBuilder out) { out.append("}\n"); } /** * Generates new states for all the old states in the provided FSM. Stores the newly * created states into the {@link #newStates} list, and populates the mapping * between old state IDs and new state IDs in {@link #oldToNewStates}. * @param the {@link FSMPDA} from which the old states are obtained. */ protected void createNewStates(FSMPDA fsm){ LinkedList<StatePDA> oldStatesQueue = new LinkedList<StatePDA>(); oldStatesQueue.add(fsm.getInitialState()); while(oldStatesQueue.size() > 0){ StatePDA anOldState = oldStatesQueue.removeFirst(); if(oldToNewStates.containsKey(anOldState.getIndex())){ //state already converted } else { //new state required SPTBase.State newState = new SPTBase.State(); newStates.add(newState); oldToNewStates.put(anOldState.getIndex(), newStates.size() -1); //queue all old states reachable from this state for(gate.fsm.Transition anOldTransition : anOldState.getTransitions()){ oldStatesQueue.add((StatePDA)anOldTransition.getTarget()); } //if state is final, set the rule value newState.rule = -1; if(anOldState.isFinal()){ RightHandSide rhs = anOldState.getAction(); for(int i = 0; i < rules.length; i++){ if(rules[i].getRHS() == rhs){ newState.rule = i; break; } } } } } } /** * Parses the provided FSMPDA and converts the old transitions to new ones. The * {@link #newStates} list and the {@link #oldToNewStates} mapping should * already be populated before this method is called. * @param fsm * @throws ResourceInstantiationException */ protected void createNewTransitions(FSMPDA fsm) throws ResourceInstantiationException{ LinkedList<StatePDA> oldStatesQueue = new LinkedList<StatePDA>(); oldStatesQueue.add(fsm.getInitialState()); IntArrayList visitedOldStates = new IntArrayList(); while(oldStatesQueue.size() > 0){ StatePDA anOldState = oldStatesQueue.removeFirst(); if(visitedOldStates.contains(anOldState.getIndex())){ //state already processed -> nothing to do }else{ if(!oldToNewStates.containsKey(anOldState.getIndex())){ throw new ResourceInstantiationException( "State mapping error: " + "old state not associated with a new state!"); } SPTBase.State newState = newStates.get(oldToNewStates.get( anOldState.getIndex())); //now process all transitions List<SPTBase.Transition> newTransitions = new LinkedList<SPTBase.Transition>(); for(gate.fsm.Transition t : anOldState.getTransitions()){ TransitionPDA anOldTransition = (TransitionPDA) t; if(!visitedOldStates.contains(anOldTransition.getTarget().getIndex())){ oldStatesQueue.add((StatePDA) anOldTransition.getTarget()); } if(!oldToNewStates.containsKey(anOldTransition.getTarget().getIndex())){ throw new ResourceInstantiationException( "State mapping error: " + "old target state not associated with a new state!"); } int newStateTarget = oldToNewStates.get(anOldTransition.getTarget().getIndex()); SPTBase.Transition newTransition = new SPTBase.Transition(); newTransitions.add(newTransition); newTransition.nextState = newStateTarget; newTransition.type = anOldTransition.getType(); if(newTransition.type != TransitionPDA.TYPE_CONSTRAINT){ continue; } Constraint[] oldConstraints = anOldTransition.getConstraints(). getConstraints(); List<int[]> newConstraints = new ArrayList<int[]>(); for(int i = 0; i< oldConstraints.length; i++){ String annType = oldConstraints[i].getAnnotType(); int annTypeInt = annotationTypes.indexOf(annType); if(annTypeInt < 0){ annotationTypes.add(annType); annTypeInt = annotationTypes.size() -1; } int[] newConstraint = new int[oldConstraints[i]. getAttributeSeq().size() + 2]; newConstraints.add(newConstraint); newConstraint[0] = annTypeInt; newConstraint[1] = oldConstraints[i].isNegated() ? -1 : 0; int predId = 2; for(ConstraintPredicate oldPredicate : oldConstraints[i].getAttributeSeq()){ newConstraint[predId++] = convertPredicate(annType, oldPredicate); } } //now save the new constraints newTransition.constraints = new int[newConstraints.size()][]; newTransition.constraints = newConstraints.toArray( newTransition.constraints); } //convert the transitions list to an array newState.transitions = new SPTBase.Transition[newTransitions.size()]; newState.transitions = newTransitions.toArray(newState.transitions); //finally, mark the old state as visited. visitedOldStates.add(anOldState.getIndex()); } } } protected int convertPredicate(String annotationType, ConstraintPredicate oldPredicate) throws ResourceInstantiationException{ Predicate newPredicate = new Predicate(); newPredicate.annotationAccessor = oldPredicate.getAccessor(); String operator = oldPredicate.getOperator(); if(operator == ConstraintPredicate.EQUAL){ newPredicate.type = PredicateType.EQ; } else if(operator == ConstraintPredicate.GREATER){ newPredicate.type = PredicateType.GT; } else if(operator == ConstraintPredicate.GREATER_OR_EQUAL){ newPredicate.type = PredicateType.GE; } else if(operator == ConstraintPredicate.LESSER){ newPredicate.type = PredicateType.LT; } else if(operator == ConstraintPredicate.LESSER_OR_EQUAL){ newPredicate.type = PredicateType.LE; } else if(operator == ConstraintPredicate.NOT_EQUAL){ newPredicate.type = PredicateType.NOT_EQ; } else if(operator == ConstraintPredicate.NOT_REGEXP_FIND){ newPredicate.type = PredicateType.REGEX_NOT_FIND; } else if(operator == ConstraintPredicate.NOT_REGEXP_MATCH){ newPredicate.type = PredicateType.REGEX_NOT_MATCH; } else if(operator == ConstraintPredicate.REGEXP_FIND){ newPredicate.type = PredicateType.REGEX_FIND; } else if(operator == ConstraintPredicate.REGEXP_MATCH){ newPredicate.type = PredicateType.REGEX_MATCH; } else if(operator == ContainsPredicate.OPERATOR){ newPredicate.type = PredicateType.CONTAINS; } else if(operator == WithinPredicate.OPERATOR){ newPredicate.type = PredicateType.WITHIN; } else { // make it into a custom predicate newPredicate.type = PredicateType.CUSTOM; newPredicate.featureValue = oldPredicate; } if(newPredicate.type == PredicateType.CONTAINS) { String containedAnnType = null; List<Integer> containedPredicates = new LinkedList<Integer>(); // convert the value ContainsPredicate contPredicate = (ContainsPredicate)oldPredicate; Object value = oldPredicate.getValue(); if(value == null) { // just annotation type containedAnnType = contPredicate.getAnnotType(); } else if(value instanceof String) { // a simple annotation type containedAnnType = (String)value; } else if (value instanceof Constraint) { Constraint constraint = (Constraint)value; containedAnnType = constraint.getAnnotType(); for(ConstraintPredicate pred : constraint.getAttributeSeq()) { containedPredicates.add(convertPredicate(containedAnnType, pred)); } } int[] newPredValue = new int[2 + containedPredicates.size()]; newPredValue[0] = annotationTypes.indexOf(containedAnnType); if(newPredValue[0] == -1) { annotationTypes.add(containedAnnType); newPredValue[0] = annotationTypes.size() -1; } // contains predicates are always positive newPredValue[1] = 1; int predIdx = 2; for(Integer predId : containedPredicates) { newPredValue[predIdx++] = predId; } newPredicate.featureValue = newPredValue; } else if(newPredicate.type == PredicateType.WITHIN) { String containedAnnType = null; List<Integer> containedPredicates = new LinkedList<Integer>(); // convert the value WithinPredicate contPredicate = (WithinPredicate)oldPredicate; Object value = oldPredicate.getValue(); if(value == null) { // just annotation type containedAnnType = contPredicate.getAnnotType(); } else if(value instanceof String) { // a simple annotation type containedAnnType = (String)value; } else if (value instanceof Constraint) { Constraint constraint = (Constraint)value; containedAnnType = constraint.getAnnotType(); for(ConstraintPredicate pred : constraint.getAttributeSeq()) { containedPredicates.add(convertPredicate(containedAnnType, pred)); } } int[] newPredValue = new int[2 + containedPredicates.size()]; newPredValue[0] = annotationTypes.indexOf(containedAnnType); if(newPredValue[0] == -1) { annotationTypes.add(containedAnnType); newPredValue[0] = annotationTypes.size() -1; } // contains predicates are always positive newPredValue[1] = 1; int predIdx = 2; for(Integer predId : containedPredicates) { newPredValue[predIdx++] = predId; } newPredicate.featureValue = newPredValue; } else if(newPredicate.type == PredicateType.CUSTOM) { // do nothing } else { // for all other types of predicates, copy the value newPredicate.featureValue = (Serializable) oldPredicate.getValue(); } //now see if this is a new predicate or not List<Predicate> predsOfType = predicatesByType.get(annotationType); if(predsOfType == null){ predsOfType = new ArrayList<Predicate>(); predicatesByType.put(annotationType, predsOfType); } for(int i = 0; i < predsOfType.size(); i++){ if(predsOfType.get(i).equals(newPredicate)){ return i; } } //we have a new predicate newPredicate.alsoFalse = new int[0]; newPredicate.alsoTrue = new int[0]; newPredicate.converselyFalse = new int[0]; newPredicate.converselyTrue = new int[0]; predsOfType.add(newPredicate); return predsOfType.size() -1; } protected void optimisePredicates(){ for(List<Predicate> preds : predicatesByType.values()){ IntArrayList[] alsoTrue = new IntArrayList[preds.size()]; IntArrayList[] alsoFalse = new IntArrayList[preds.size()]; IntArrayList[] convTrue = new IntArrayList[preds.size()]; IntArrayList[] convFalse = new IntArrayList[preds.size()]; for(int i = 0; i < preds.size(); i++){ alsoTrue[i] = new IntArrayList(preds.size()); alsoFalse[i] = new IntArrayList(preds.size()); convTrue[i] = new IntArrayList(preds.size()); convFalse[i] = new IntArrayList(preds.size()); } for(int i = 0; i < preds.size() -1; i++){ Predicate one = preds.get(i); for(int j = i +1; j < preds.size(); j++){ Predicate other = preds.get(j); switch(one.type){ case EQ: switch(other.type){ case EQ: if(one.annotationAccessor.equals(other.annotationAccessor)){ if(one.featureValue.equals(other.featureValue)){ alsoTrue[i].add(j); alsoTrue[j].add(i); }else{ convFalse[i].add(j); convFalse[j].add(i); } } break; default: } break; default: } }//for j }//for i for(int i = 0; i< preds.size(); i++){ Predicate pred = preds.get(i); pred.alsoTrue = Arrays.copyOfRange(alsoTrue[i].elements(), 0, alsoTrue[i].size()); pred.alsoFalse = Arrays.copyOfRange(alsoFalse[i].elements(), 0, alsoFalse[i].size()); pred.converselyTrue = Arrays.copyOfRange(convTrue[i].elements(), 0, convTrue[i].size()); pred.converselyFalse = Arrays.copyOfRange(convFalse[i].elements(), 0, convFalse[i].size()); } }//for preds } }