/* * BasicPatternElement.java - transducer class * * Copyright (c) 1998-2001, The University of Sheffield. * * 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). * * Hamish Cunningham, 24/07/98 * * Minor modifications made by Luc Plamondon, Universit� de Montr�al, 20/11/03: * - migrated original file to the ca.umontreal.iro.rali.gate.jape package. * - addConstraint: the list of attributes returned by the Constraint object is * a LinkedList, not a FeatureMap. * * $Id$ */ package ca.umontreal.iro.rali.gate.jape; import java.util.*; import gate.annotation.*; import gate.util.*; import gate.*; /** * A pattern element within curly braces. Has a set of Constraint, * which all must be satisfied at whatever position the element is being * matched at. */ public class BasicPatternElement extends PatternElement implements JapeConstants, java.io.Serializable { /** Debug flag */ private static final boolean DEBUG = false; /** A set of Constraint. Used during parsing. */ private ArrayList constraints1; /** A set of Constraint. Used during matching. */ private Constraint[] constraints2; /** A map of constraint annot type to constraint. Used during parsing. */ private HashMap constraintsMap; /** Cache of the last position we failed at (-1 when none). */ private int lastFailurePoint = -1; /** The position of the next available annotation of the type required * by the first constraint. */ //private MutableInteger nextAvailable = new MutableInteger(); /** The set of annotations we have matched. */ private AnnotationSet matchedAnnots; /** Access to the annotations that have been matched. */ public AnnotationSet getMatchedAnnots() { return matchedAnnots; } /** Construction. */ public BasicPatternElement() { constraintsMap = new HashMap(); constraints1 = new ArrayList(); lastFailurePoint = -1; //nextAvailable = new MutableInteger(); matchedAnnots = new AnnotationSetImpl((Document) null); } // construction /** Need cloning for processing of macro references. See comments on * <CODE>PatternElement.clone()</CODE> */ public Object clone() { BasicPatternElement newPE = (BasicPatternElement) super.clone(); newPE.constraintsMap = (HashMap) constraintsMap.clone(); newPE.constraints1 = new ArrayList(); int consLen = constraints1.size(); for(int i = 0; i < consLen; i++) newPE.constraints1.add( ((Constraint) constraints1.get(i)).clone() ); // newPE.matchedAnnots = new AnnotationSetImpl((Document) null); // newPE.matchedAnnots.addAll(matchedAnnots); return newPE; } // clone /** Add a constraint. Ensures that only one constraint of any given * annotation type and negation state exists. */ public void addConstraint(Constraint newConstraint) { /* if the constraint is already mapped, put it's attributes on the * existing constraint, else add it */ String annotType = newConstraint.getAnnotType(); Boolean negState = new Boolean(newConstraint.isNegated()); Pair typeNegKey = new Pair((Object) annotType, (Object) negState); Constraint existingConstraint = (Constraint) constraintsMap.get(typeNegKey); if(existingConstraint == null) { constraintsMap.put(typeNegKey, newConstraint); constraints1.add(newConstraint); } else { LinkedList newAttrs = newConstraint.getAttributeSeq(); LinkedList existingAttrs = existingConstraint.getAttributeSeq(); existingAttrs.addAll(newAttrs); //if(newConstraint.isNegated()) // existingConstraint.negate(); } } // addConstraint /** Finish: replace dynamic data structures with Java arrays; called * after parsing. */ public void finish() { int j=0; constraints2 = new Constraint[constraints1.size()]; for(Iterator i=constraints1.iterator(); i.hasNext(); ) { constraints2[j] = (Constraint) i.next(); constraints2[j++].finish(); } constraints1 = null; } // finish /** Reset: clear last failure point and matched annotations list. */ public void reset() { super.reset(); lastFailurePoint = -1; //nextAvailable.value = -1; matchedAnnots = new AnnotationSetImpl((Document) null); } // reset /** Multilevel rollback of the annotation cache. */ public void rollback(int arity) { //Debug.pr(this, "BPE rollback(" + arity + "), matchHistory.size() = " + // matchHistory.size()); //Debug.nl(this); for(int i=0; i<arity; i++) { matchedAnnots.removeAll((AnnotationSet) matchHistory.pop()); } } // rollback /** Does this element match the document at this position? */ public boolean matches ( Document doc, int position, MutableInteger newPosition ) { final int startingCacheSize = matchedAnnots.size(); AnnotationSet addedAnnots = new AnnotationSetImpl((Document) null); //Debug.pr(this, "BPE.matches: trying at position " + position); //Debug.nl(this); int rightmostEnd = -1; int end = doc.getContent().size().intValue(); MutableInteger nextAvailable = new MutableInteger(); int nextAvailOfFirstConstraint = -1; for(int len = constraints2.length, i = 0; i < len; i++) { Constraint constraint = constraints2[i]; String annotType = constraint.getAnnotType(); JdmAttribute[] constraintAttrs = constraint.getAttributeArray(); MutableBoolean moreToTry = new MutableBoolean(); if(DEBUG) { Out.println( "BPE.matches: selectAnn on lFP = " + lastFailurePoint + "; max(pos,lfp) = " + Math.max(position, lastFailurePoint) + "; annotType = " + annotType + "; attrs = " + constraintAttrs.toString() + Strings.getNl() ); for(int j=0; j<constraintAttrs.length; j++) Out.println( "BPE.matches attr: " + constraintAttrs[j].toString() ); } FeatureMap features = Factory.newFeatureMap(); for(int j = constraintAttrs.length - 1; j >= 0; j--) features.put(constraintAttrs[j].getName(), constraintAttrs[j].getValue()); AnnotationSet match = doc.getAnnotations().get( // this loses "April 2" on the frozen tests: // Math.max(nextAvailable.value, Math.max(position, lastFailurePoint)), annotType, features, new Long(Math.max(position, lastFailurePoint)) /*, nextAvailable, moreToTry */ ); if(DEBUG) Out.println( "BPE.matches: selectAnn returned " + match + ".... moreToTry = " + moreToTry.value + " nextAvailable = " + nextAvailable.value ); // store first constraint's next available if(nextAvailOfFirstConstraint == -1) nextAvailOfFirstConstraint = nextAvailable.value; // if there are no more annotations of this type, then we can // say that we failed this BPE and that we tried the whole document if(! moreToTry.value) { if(match != null) throw(new RuntimeException("BPE: no more annots but found one!")); lastFailurePoint = end; newPosition.value = end; } // selectNextAnnotation ensures that annotations matched will // all start >= position. we also need to ensure that second and // subsequent matches start <= to the rightmost end. otherwise // BPEs can match non-contiguous annotations, which is not the // intent. so we record the rightmostEnd, and reject annotations // whose leftmostStart is > this value. int matchEnd = -1; if(match != null) { matchEnd = match.lastNode().getOffset().intValue(); if(rightmostEnd == -1) { // first time through rightmostEnd = matchEnd; } else if(match.firstNode().getOffset().intValue() >= rightmostEnd) { // reject lastFailurePoint = matchEnd; match = null; } else { // this one is ok; reset rightmostEnd if(rightmostEnd < matchEnd) rightmostEnd = matchEnd; } } // match != null // negation if(constraint.isNegated()) { if(match == null) { //Debug.pr( // this, "BPE.matches: negating failed constraint" + Debug.getNl() //); continue; } else { // Debug.pr( // this, "BPE.matches: negating successful constraint, match = " + // match.toString() + Debug.getNl() //); lastFailurePoint = matchEnd; match = null; } } // constraint is negated if(match == null) { // clean up //Debug.pr(this, "BPE.matches: selectNextAnnotation returned null"); //Debug.nl(this); newPosition.value = Math.max(position + 1, nextAvailOfFirstConstraint); lastFailurePoint = nextAvailable.value; // we clear cached annots added this time, not all: maybe we were // applied under *, for example, and failure doesn't mean we should // purge the whole cache //for(int j = matchedAnnots.size() - 1; j >= startingCacheSize; j--) // matchedAnnots.removeNth(j); matchedAnnots.removeAll(addedAnnots); //Debug.pr( // this, "BPE.matches: false, newPosition.value(" + // newPosition.value + ")" + Debug.getNl() //); return false; } else { //Debug.pr(this,"BPE.matches: match= "+match.toString()+Debug.getNl()); matchedAnnots.addAll(match); addedAnnots.addAll(match); newPosition.value = Math.max(newPosition.value, matchEnd); } } // for each constraint // success: store the annots added this time matchHistory.push(addedAnnots); //Debug.pr(this, "BPE.matches: returning true" + Debug.getNl()); // under negation we may not have advanced... if(newPosition.value == position) newPosition.value++; return true; } // matches /** Create a string representation of the object. */ public String toString() { StringBuffer result = new StringBuffer("{"); Constraint[] constraints = getConstraints(); for(int i = 0; i<constraints.length; i++){ result.append(constraints[i].shortDesc() + ","); } result.setCharAt(result.length() -1, '}'); return result.toString(); } /** Create a string representation of the object. */ public String toString(String pad) { String newline = Strings.getNl(); String newPad = Strings.addPadding(pad, INDENT_PADDING); StringBuffer buf = new StringBuffer(pad + "BPE: lastFailurePoint(" + lastFailurePoint + "); constraints(" ); // constraints if(constraints1 != null) { for(int len = constraints1.size(), i = 0; i < len; i++) buf.append( newline + ((Constraint) constraints1.get(i)).toString(newPad) ); } else { for(int len = constraints2.length, i = 0; i < len; i++) buf.append(newline + constraints2[i].toString(newPad)); } // matched annots buf.append( newline + pad + "matchedAnnots: " + matchedAnnots + newline + pad + ") BPE." ); return buf.toString(); } // toString /** * Returns a short description. */ public String shortDesc() { String res = ""; if(constraints1 != null) { for(int len = constraints1.size(), i = 0; i < len; i++) res += ((Constraint) constraints1.get(i)).toString(); } else { for(int len = constraints2.length, i = 0; i < len; i++) res += constraints2[i].shortDesc(); } return res; } public Constraint[] getConstraints(){ return constraints2; } } // class BasicPatternElement