BasicPatternElement.java
001 /*
002  *  BasicPatternElement.java - transducer class
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Hamish Cunningham, 24/07/98
013  *
014  *  $Id: BasicPatternElement.java 17597 2014-03-08 15:19:43Z markagreenwood $
015  */
016 
017 
018 package gate.jape;
019 
020 import gate.util.Pair;
021 import gate.util.Strings;
022 
023 import java.io.Serializable;
024 import java.util.ArrayList;
025 import java.util.HashMap;
026 import java.util.List;
027 import java.util.Map;
028 
029 
030 /**
031   * A pattern element within curly braces. Has a set of Constraint,
032   * which all must be satisfied at whatever position the element is being
033   * matched at.
034   */
035 public class BasicPatternElement
036 extends PatternElement implements JapeConstants, Serializable
037 {
038   private static final long serialVersionUID = -6515011025898779462L;
039 
040   /** A set of Constraint. Used during parsing. */
041   private List<Constraint> constraints1;
042 
043   /** A set of Constraint. Used during matching. */
044   private Constraint[] constraints2;
045 
046   /** A map of constraint annot type to constraint. Used during parsing. */
047   private Map<Object, Constraint> constraintsMap;
048 
049   /** Cache of the last position we failed at (-1 when none). */
050   private int lastFailurePoint = -1;
051 
052   /** The position of the next available annotation of the type required
053     * by the first constraint.
054     */
055   //private MutableInteger nextAvailable = new MutableInteger();
056 
057   private final boolean negationCompatMode;
058   
059   /** Construction. */
060   public BasicPatternElement(SinglePhaseTransducer spt) {
061     constraintsMap = new HashMap<Object, Constraint>();
062     constraints1 = new ArrayList<Constraint>();
063     lastFailurePoint = -1;
064     negationCompatMode = spt.isNegationCompatMode();
065   // construction
066 
067   /** Need cloning for processing of macro references. See comments on
068     <CODE>PatternElement.clone()</CODE>
069     */
070   @Override
071   public Object clone() {
072     BasicPatternElement newPE = (BasicPatternElementsuper.clone();
073     newPE.constraintsMap = new HashMap<Object, Constraint>(constraintsMap);
074     newPE.constraints1 = new ArrayList<Constraint>();
075     int consLen = constraints1.size();
076     for(int i = 0; i < consLen; i++)
077       newPE.constraints1.add(
078         (Constraint)constraints1.get(i).clone()
079       );
080 //    newPE.matchedAnnots = new AnnotationSetImpl((Document) null);
081 //    newPE.matchedAnnots.addAll(matchedAnnots);
082     return newPE;
083   // clone
084 
085   /** Add a constraint. Ensures that only one constraint of any given
086     * annotation type and negation state exists.
087     */
088   public void addConstraint(Constraint newConstraint) {
089     // find if we need to merge this new constraint with an existing one
090     Constraint existingConstraint = null;
091     String annotType = newConstraint.getAnnotType();
092     Pair typeNegKey = new Pair(annotType, newConstraint.isNegated());
093     if(negationCompatMode && newConstraint.isNegated()) {
094       // compatibility mode (pre GATE 7.0) where multiple negative constraints
095       // on the same annotation type do NOT get grouped together and AND'ed 
096       // before the negation is applied
097       existingConstraint = null;
098     else {
099       // positive constraint OR negated, but in default mode:
100       // if a constraint with the same negation state as this constraint is
101       // already mapped, put its attributes on the existing constraint, else
102       // add it
103       existingConstraint = constraintsMap.get(typeNegKey);
104     }      
105 
106     if(existingConstraint != null) {
107       existingConstraint.addAttributes(newConstraint.getAttributeSeq());
108     else {
109       constraintsMap.put(typeNegKey, newConstraint);
110       constraints1.add(newConstraint);      
111     }
112   // addConstraint
113 
114 
115   /**
116    * Indicates whether this constraint deals with only one type of annotation or
117    * multiple types.
118    */
119   public boolean isMultiType() {
120       return constraints2 != null ? constraints2.length > :
121              constraints1 != null ? constraints1.size() :
122              false;
123   }
124 
125   /** Finish: replace dynamic data structures with Java arrays; called
126     * after parsing.
127     */
128   @Override
129   public void finish() {
130     int j=0;
131     constraints2 = new Constraint[constraints1.size()];
132     for(Constraint c : constraints1 ) {
133       constraints2[j= c;
134       constraints2[j++].finish();
135     }
136     constraints1 = null;
137   // finish
138 
139 
140 
141   /** Create a string representation of the object. */
142   @Override
143   public String toString() {
144     StringBuffer result = new StringBuffer("{");
145     Constraint[] constraints = getConstraints();
146     for(int i = 0; i<constraints.length; i++){
147       result.append(constraints[i].shortDesc() ",");
148     }
149     result.setCharAt(result.length() -1'}');
150     return result.toString();
151   }
152 
153   /** Create a string representation of the object. */
154   @Override
155   public String toString(String pad) {
156     String newline = Strings.getNl();
157     String newPad = Strings.addPadding(pad, INDENT_PADDING);
158 
159     StringBuffer buf = new StringBuffer(pad +
160       "BPE: lastFailurePoint(" + lastFailurePoint + "); constraints("
161     );
162 
163     // constraints
164     if(constraints1 != null) {
165       for(int len = constraints1.size(), i = 0; i < len; i++)
166         buf.append(
167           newline + constraints1.get(i).getDisplayString(newPad)
168         );
169     else {
170       for(int len = constraints2.length, i = 0; i < len; i++)
171         buf.append(newline + constraints2[i].getDisplayString(newPad));
172     }
173 
174     // matched annots
175 //    buf.append(
176 //      newline + pad + "matchedAnnots: " + matchedAnnots +
177 //      newline + pad + ") BPE."
178 //    );
179 
180     return buf.toString();
181   // toString
182 
183   /**
184     * Returns a short description.
185     */
186   public String shortDesc() {
187     String res = "";
188     if(constraints1 != null) {
189       for(int len = constraints1.size(), i = 0; i < len; i++)
190         res += constraints1.get(i).toString();
191     else {
192       for(int len = constraints2.length, i = 0; i < len; i++)
193         res += constraints2[i].shortDesc();
194     }
195     return res;
196   }
197 
198   /**
199    * Get the current list of unfinished Constraint objects. This
200    * can only be used before the finish() method is used.
201    @return the array list of constraint objects. Will be null after
202    * the finish() method has been used.
203    */
204   public List<Constraint> getUnfinishedConstraints() {
205     return constraints1;
206   }
207 
208   /**
209    * Get the finished Constraint objects. Can only be used after the
210    * finish() method has been used.
211    @return an array of constraint objects. Will be null before the
212    * finish() method has been used.
213    */
214   public Constraint[] getConstraints(){
215     return constraints2;
216   }
217 // class BasicPatternElement