1   /*
2    *  SinglePhaseTransducer.java - transducer class
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Hamish Cunningham, 24/07/98
12   *
13   *  $Id: SinglePhaseTransducer.java,v 1.64 2002/05/14 09:43:17 valyt Exp $
14   */
15  
16  
17  package gate.jape;
18  
19  import java.io.*;
20  
21  import gate.annotation.*;
22  import gate.util.*;
23  import gate.*;
24  import gate.fsm.*;
25  import gate.gui.*;
26  import gate.creole.*;
27  import gate.event.*;
28  import java.util.*;
29  
30  /**
31    * Represents a complete CPSL grammar, with a phase name, options and
32    * rule set (accessible by name and by sequence).
33    * Implements a transduce method taking a Document as input.
34    * Constructs from String or File.
35    */
36  public class SinglePhaseTransducer
37  extends Transducer implements JapeConstants, java.io.Serializable
38  {
39    /** Debug flag */
40    private static final boolean DEBUG = false;
41  
42    /** Construction from name. */
43    public SinglePhaseTransducer(String name) {
44      this.name = name;
45      rules = new PrioritisedRuleList();
46      finishedAlready = false;
47    } // Construction from name
48  
49    /** Type of rule application (constants defined in JapeConstants). */
50    private int ruleApplicationStyle = BRILL_STYLE;
51  
52    /** Set the type of rule application (types defined in JapeConstants). */
53    public void setRuleApplicationStyle(int style) {
54      ruleApplicationStyle = style;
55    }
56  
57    /** The list of rules in this transducer. Ordered by priority and
58      * addition sequence (which will be file position if they come from
59      * a file).
60      */
61    private PrioritisedRuleList rules;
62  
63    FSM fsm;
64  
65    public FSM getFSM(){
66      return fsm;
67    }
68  
69    /** Add a rule. */
70    public void addRule(Rule rule) {
71      rules.add(rule);
72    } // addRule
73  
74    /** The values of any option settings given. */
75    private java.util.HashMap optionSettings = new java.util.HashMap();
76  
77    /** Add an option setting. If this option is set already, the new
78      * value overwrites the previous one.
79      */
80    public void setOption(String name, String setting) {
81      optionSettings.put(name, setting);
82    } // setOption
83  
84    /** Get the value for a particular option. */
85    public String getOption(String name) {
86      return (String) optionSettings.get(name);
87    } // getOption
88  
89    /** Whether the finish method has been called or not. */
90    private boolean finishedAlready;
91  
92    /** Finish: replace dynamic data structures with Java arrays; called
93      * after parsing.
94      */
95    public void finish(){
96      // both MPT and SPT have finish called on them by the parser...
97      if(finishedAlready) return;
98      else finishedAlready = true;
99  
100     //each rule has a RHS which has a string for java code
101     //those strings need to be compiled now
102     Map actionClasses = new HashMap(rules.size());
103     for(Iterator i = rules.iterator(); i.hasNext(); ){
104       Rule rule = (Rule)i.next();
105       rule.finish();
106       actionClasses.put(rule.getRHS().getActionClassName(),
107                         rule.getRHS().getActionClassString());
108     }
109     try{
110       Javac.loadClasses(actionClasses);
111     }catch(Exception e){
112       Err.prln("Compile error:\n" + e.getMessage());
113 //e.printStackTrace();
114     }
115 
116     //build the finite state machine transition graph
117     fsm = new FSM(this);
118     //clear the old style data structures
119     rules.clear();
120     rules = null;
121   } // finish
122 
123 //dam: was
124 //  private void addAnnotationsByOffset(Map map, SortedSet keys, Set annotations){
125   private void addAnnotationsByOffset(/*Map map,*/ SimpleSortedSet keys, Set annotations){
126     Iterator annIter = annotations.iterator();
127     while(annIter.hasNext()){
128       Annotation ann = (Annotation)annIter.next();
129       //ignore empty annotations
130       long offset = ann.getStartNode().getOffset().longValue();
131       if(offset == ann.getEndNode().getOffset().longValue())
132         continue;
133 //dam: was
134 /*
135 //      Long offset = ann.getStartNode().getOffset();
136 
137       List annsAtThisOffset = null;
138       if(keys.add(offset)){
139         annsAtThisOffset = new LinkedList();
140         map.put(offset, annsAtThisOffset);
141       }else{
142         annsAtThisOffset = (List)map.get(offset);
143       }
144       annsAtThisOffset.add(ann);
145 */
146 //dam: end
147       keys.add(offset, ann);
148     }
149   }//private void addAnnotationsByOffset()
150 
151 
152   /**
153     * Transduce a document using the annotation set provided and the current
154     * rule application style.
155     */
156   public void transduce(Document doc, AnnotationSet inputAS,
157                         AnnotationSet outputAS) throws JapeException,
158                                                        ExecutionException {
159     interrupted = false;
160     fireProgressChanged(0);
161 
162     //the input annotations will be read from this map
163     //maps offset to list of annotations
164 
165 //dam was
166 /*
167     Map annotationsByOffset = new HashMap();
168 
169     SortedSet offsets = new TreeSet();
170 */
171 //dam: now
172     SimpleSortedSet offsets = new SimpleSortedSet();
173     SimpleSortedSet annotationsByOffset = offsets;
174 //dam: end
175 
176     //select only the annotations of types specified in the input list
177 //    Out.println("Input:" + input);
178     if(input.isEmpty())
179     {
180 //dam: was
181 //        addAnnotationsByOffset(annotationsByOffset, offsets, inputAS);
182 //dam: now
183         addAnnotationsByOffset(offsets, inputAS);
184 //dam: end
185     } else {
186       Iterator typesIter = input.iterator();
187       AnnotationSet ofOneType = null;
188       while(typesIter.hasNext()){
189         ofOneType = inputAS.get((String)typesIter.next());
190         if(ofOneType != null){
191 //dam: was
192 //        addAnnotationsByOffset(annotationsByOffset, offsets, ofOneType);
193 //dam: now
194           addAnnotationsByOffset(offsets, ofOneType);
195 //dam: end
196         }
197       }
198     }
199 
200     if(annotationsByOffset.isEmpty()){
201       fireProcessFinished();
202       return;
203     }
204 
205     annotationsByOffset.sort();
206     //define data structures
207     //FSM instances that haven't blocked yet
208 //    java.util.LinkedList activeFSMInstances = new java.util.LinkedList();
209     java.util.ArrayList activeFSMInstances = new java.util.ArrayList();
210 
211     // FSM instances that have reached a final state
212     // This is a sorted set and the contained objects are sorted by the length
213     // of the document content covered by the matched annotations
214 //dam: was ArrayList has faster add and remove methods then LinkedList
215 //    java.util.LinkedList acceptingFSMInstances = new LinkedList();
216 //dam: now
217     java.util.ArrayList acceptingFSMInstances = new ArrayList();
218 //dam: end
219     FSMInstance currentFSM;
220 
221 
222     //find the first node of the document
223     Node startNode = ((Annotation)
224                       ((ArrayList)annotationsByOffset.
225                              get(offsets.first())).get(0)).
226                       getStartNode();
227 
228     //used to calculate the percentage of processing done
229     long lastNodeOff = doc.getContent().size().longValue();
230 
231     //the offset of the node where the matching currently starts
232     //the value -1 marks no more annotations to parse
233     long startNodeOff = startNode.getOffset().longValue();
234 
235     //used to decide when to fire progress events
236     long oldStartNodeOff = 0;
237 
238     //the big while for the actual parsing
239     while(startNodeOff != -1){
240 //Out.prln();
241 //Out.pr("Start: " + startNodeOff);
242       //while there are more annotations to parse
243       //create initial active FSM instance starting parsing from new startNode
244       //currentFSM = FSMInstance.getNewInstance(
245       currentFSM = new FSMInstance(
246                   fsm,
247                   fsm.getInitialState(),//fresh start
248                   startNode,//the matching starts form the current startNode
249                   startNode,//current position in AG is the start position
250                   new java.util.HashMap(),//no bindings yet!
251                   doc
252                   );
253 
254       // at this point ActiveFSMInstances should always be empty!
255       activeFSMInstances.clear();
256       acceptingFSMInstances.clear();
257 //dam: was used LinkedList
258 //      activeFSMInstances.addLast(currentFSM);
259 //dam: now used ArrayList
260       activeFSMInstances.add(currentFSM);
261 //dam: end
262 
263       //far each active FSM Instance, try to advance
264       whileloop2:
265       while(!activeFSMInstances.isEmpty()){
266         if(interrupted) throw new ExecutionInterruptedException(
267           "The execution of the \"" + getName() +
268           "\" Jape transducer has been abruptly interrupted!");
269 
270 //Out.pr(" <" + acceptingFSMInstances.size() + "/" +
271 //              activeFSMInstances.size() +">");
272         // take the first active FSM instance
273         currentFSM = (FSMInstance)activeFSMInstances.remove(0);
274 
275         // process the current FSM instance
276         if(currentFSM.getFSMPosition().isFinal()){
277           //the current FSM is in a final state
278 //dam: was LinkedList
279 //          acceptingFSMInstances.addLast(currentFSM.clone());
280 //dam: now
281           acceptingFSMInstances.add(currentFSM.clone());
282 //dam: end
283 //          //if we are in APPELT mode clear all the accepting instances
284 //          //apart from the longest one
285 //          if(ruleApplicationStyle == APPELT_STYLE &&
286 //             acceptingFSMInstances.size() > 1){
287 //            Object longestAcceptor = acceptingFSMInstances.last();
288 //            acceptingFSMInstances.clear();
289 //            acceptingFSMInstances.add(longestAcceptor);
290 //          }
291           //if we're only looking for the shortest stop here
292           if(ruleApplicationStyle == FIRST_STYLE) break whileloop2;
293         }
294 
295         //get all the annotations that start where the current FSM finishes
296 //<<< DAM: was using SortedSet
297 //        SortedSet offsetsTailSet = offsets.tailSet(
298 //=== DAM: now
299         SimpleSortedSet offsetsTailSet = offsets.tailSet(
300 //>>> DAM: end
301                                     currentFSM.getAGPosition().getOffset().longValue());
302         ArrayList paths; //was linkedList
303 
304 //<<< DAM: SortedSet speedup
305 /*
306         if(offsetsTailSet.isEmpty()){
307           paths = new ArrayList();
308         }else{
309           paths = (List)annotationsByOffset.get(offsetsTailSet.first());
310         }
311 */
312 //=== DAM: now
313         long theFirst = offsetsTailSet.first();
314         if(theFirst <0)
315           continue;
316 
317           paths = (ArrayList)annotationsByOffset.get(theFirst);
318 //        }
319 //System.out.println("Paths: " + paths + "\n^localInputIndex: " + localInputIndex);
320 //>>> DAM: end
321 
322 //        if(!paths.isEmpty()){
323         if(paths.isEmpty()) continue;
324           Iterator pathsIter = paths.iterator();
325           Annotation onePath;
326           State currentState = currentFSM.getFSMPosition();
327           Iterator transitionsIter;
328 //DAM: doit without intermediate FetureMap
329 //        FeatureMap features = null;//Factory.newFeatureMap();
330           //foreach possible annotation
331           while(pathsIter.hasNext()){
332             onePath = (Annotation)pathsIter.next();
333             transitionsIter = currentState.getTransitions().iterator();
334             Transition currentTransition;
335             Constraint[] currentConstraints;
336             transitionsWhile:
337             while(transitionsIter.hasNext()){
338               currentTransition = (Transition)transitionsIter.next();
339               //check if the current transition can use the curent annotation (path)
340               currentConstraints =
341                            currentTransition.getConstraints().getConstraints();
342               String annType;
343 //DAM: introduce index of the constaint to process
344               int currentConstraintsindex = -1;
345               //we assume that all annotations in a contraint are of the same type
346               for(int i = 0; i<currentConstraints.length; i++){
347                 annType = currentConstraints[i].getAnnotType();
348                 //if wrong type try next transition
349                 if(!annType.equals(onePath.getType()))continue transitionsWhile;
350 //DAM: doit without intermediate FetureMap
351 //                features.clear();
352 //                features.putAll(currentConstraints[i].getAttributeSeq());
353                 currentConstraintsindex = i;
354                 break;
355               }
356 // >>> was
357 //              if(onePath.getFeatures().entrySet().containsAll(features.entrySet())){
358 // >>> NASO, FeatArray optimization
359               if(onePath.getFeatures().subsumes(
360 //dam: was
361 //                features
362 //dam: now
363                 currentConstraints[currentConstraintsindex].getAttributeSeq()
364 //dam: end
365                 )){
366 // >>> end NASO
367                 //we have a match
368   //System.out.println("Match!");
369                 //create a new FSMInstance, advance it over the current annotation
370                 //take care of the bindings  and add it to ActiveFSM
371                 FSMInstance newFSMI = (FSMInstance)currentFSM.clone();
372                 newFSMI.setAGPosition(onePath.getEndNode());
373                 newFSMI.setFSMPosition(currentTransition.getTarget());
374                 //bindings
375                 java.util.Map binds = newFSMI.getBindings();
376                 java.util.Iterator labelsIter =
377                                    currentTransition.getBindings().iterator();
378                 String oneLabel;
379                 AnnotationSet boundAnnots, newSet;
380                 while(labelsIter.hasNext()){
381                   oneLabel = (String)labelsIter.next();
382                   boundAnnots = (AnnotationSet)binds.get(oneLabel);
383                   if(boundAnnots != null)
384                     newSet = new AnnotationSetImpl((AnnotationSet)boundAnnots);
385                   else
386                     newSet = new AnnotationSetImpl(doc);
387                   newSet.add(onePath);
388                   binds.put(oneLabel, newSet);
389 
390                 }//while(labelsIter.hasNext())
391                 activeFSMInstances.add(newFSMI);
392 //Out.pr("^(" + newFSMI.getStartAGPosition().getOffset() +
393 //                               "->" + newFSMI.getAGPosition().getOffset() + ")");
394               }//if match
395             }//while(transitionsIter.hasNext())
396           }//while(pathsIter.hasNext())
397 // dam: reverse the paths.isEmpty check
398 //        }//if(paths != null)
399 // dam
400       }//while(!activeFSMInstances.isEmpty())
401 
402 
403       //FIRE THE RULE
404 //dam: use long
405 //      Long lastAGPosition = null;
406 //dam: now
407       long lastAGPosition = -1;
408 //dam: end
409       if(acceptingFSMInstances.isEmpty()){
410         //no rule to fire, advance to the next input offset
411         lastAGPosition = startNodeOff + 1;
412       } else if(ruleApplicationStyle == BRILL_STYLE) {
413       //System.out.println("Brill acceptor");
414         // fire the rules corresponding to all accepting FSM instances
415         java.util.Iterator accFSMs = acceptingFSMInstances.iterator();
416         FSMInstance currentAcceptor;
417         RightHandSide currentRHS;
418         lastAGPosition = startNode.getOffset().longValue();
419 
420         while(accFSMs.hasNext()){
421           currentAcceptor = (FSMInstance) accFSMs.next();
422 
423           currentRHS = currentAcceptor.getFSMPosition().getAction();
424           currentRHS.transduce(doc, currentAcceptor.getBindings(),
425                                inputAS, outputAS, ontology);
426 //dam: use long
427 //          Long currentAGPosition = currentAcceptor.getAGPosition().getOffset();
428 //dam: now
429           long currentAGPosition = currentAcceptor.getAGPosition().getOffset().longValue();
430 //dam: end
431           if(currentAGPosition > lastAGPosition)
432             lastAGPosition = currentAGPosition;
433         }
434 
435       } else if(ruleApplicationStyle == APPELT_STYLE ||
436                 ruleApplicationStyle == FIRST_STYLE ||
437                 ruleApplicationStyle == ONCE_STYLE) {
438 
439 //System.out.println("Appelt acceptor");
440         // AcceptingFSMInstances is an ordered structure:
441         // just execute the longest (last) rule
442 
443         Collections.sort(acceptingFSMInstances, Collections.reverseOrder());
444 
445         FSMInstance currentAcceptor =(FSMInstance)acceptingFSMInstances.get(0);
446         if(isDebugMode()){
447           //see if we have any conflicts
448           Iterator accIter = acceptingFSMInstances.iterator();
449           FSMInstance anAcceptor;
450           List conflicts = new ArrayList();
451           while(accIter.hasNext()){
452             anAcceptor = (FSMInstance)accIter.next();
453             if(anAcceptor.equals(currentAcceptor)){
454               conflicts.add(anAcceptor);
455             }else{
456               break;
457             }
458           }
459           if(conflicts.size() > 1){
460             Out.prln("\nConflicts found during matching:" +
461                      "\n================================");
462             accIter = conflicts.iterator();
463             int i = 0;
464             while(accIter.hasNext()){
465               Out.prln(i++ + ") " + accIter.next().toString());
466             }
467           }
468         }
469 
470         RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();
471         currentRHS.transduce(doc, currentAcceptor.getBindings(),
472                              inputAS, outputAS, ontology);
473 
474         //if in ONCE mode stop after first match
475         if(ruleApplicationStyle == ONCE_STYLE) return;
476 
477         //advance in AG
478         lastAGPosition = currentAcceptor.getAGPosition().getOffset().longValue();
479       }
480 //      else if(ruleApplicationStyle == FIRST_STYLE) {
481 //        // AcceptingFSMInstances is an ordered structure:
482 //        // just execute the shortest (first) rule
483 //
484 //        FSMInstance currentAcceptor =(FSMInstance)acceptingFSMInstances.first();
485 //        RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();
486 //        currentRHS.transduce(doc, outputAS, currentAcceptor.getBindings());
487 //        //advance in AG
488 //        long lastAGPosition = currentAcceptor.getAGPosition().
489 //                              getOffset().longValue();
490 //        //advance the index on input
491 //        while(inputIndex < annotations.size() &&
492 //              ((Annotation)annotations.get(inputIndex)).
493 //              getStartNode().getOffset().longValue() < lastAGPosition){
494 //          inputIndex++;
495 //        }
496 //      }
497       else throw new RuntimeException("Unknown rule application style!");
498 
499 
500       //advance on input
501 //      SortedSet OffsetsTailSet = offsets.tailSet(lastAGPosition);
502       SimpleSortedSet OffsetsTailSet = offsets.tailSet(lastAGPosition);
503 //<<< DAM: isEmpty speedup
504 /*
505       if(OffsetsTailSet.isEmpty()){
506 */
507 //=== DAM: now
508         long theFirst = OffsetsTailSet.first();
509       if( theFirst < 0){
510 //>>> DAM: end
511         //no more input, phew! :)
512         startNodeOff = -1;
513         fireProcessFinished();
514       }else{
515 //<<< DAM: use long
516 /*
517         Long nextKey = (Long)OffsetsTailSet.first();
518 */
519 //=== DAM: now
520         long nextKey = theFirst;
521 //>>> DAM: end
522         startNode = ((Annotation)
523                       ((ArrayList)annotationsByOffset.get(nextKey)).get(0)). //nextKey
524                     getStartNode();
525         startNodeOff = startNode.getOffset().longValue();
526 
527         //eliminate the possibility for infinite looping
528         if(oldStartNodeOff == startNodeOff){
529 //Out.prln("");
530 //Out.pr("SKIP " + startNodeOff);
531           //we are about to step twice in the same place, ...skip ahead
532           lastAGPosition = startNodeOff + 1;
533           OffsetsTailSet = offsets.tailSet(lastAGPosition);
534 //<<< DAM: isEmpty speedup
535 /*
536           if(OffsetsTailSet.isEmpty()){
537 */
538 //=== DAM: now
539           theFirst = OffsetsTailSet.first();
540           if(theFirst < 0){
541 //>>> DAM: end
542             //no more input, phew! :)
543             startNodeOff = -1;
544             fireProcessFinished();
545           }else{
546 //<<< DAM: use long
547 //            nextKey = (Long)OffsetsTailSet.first();
548 //=== DAM: now
549             nextKey = theFirst;
550 //>>> DAM: end
551             startNode = ((Annotation)
552                           ((List)annotationsByOffset.get(theFirst)).get(0)).
553                         getStartNode();
554             startNodeOff =startNode.getOffset().longValue();
555           }
556 //Out.prln(" ->" + startNodeOff);
557         }//if(oldStartNodeOff == startNodeOff)
558 
559 
560         //fire the progress event
561         if(startNodeOff - oldStartNodeOff > 256){
562           if(isInterrupted()) throw new ExecutionInterruptedException(
563             "The execution of the \"" + getName() +
564             "\" Jape transducer has been abruptly interrupted!");
565 
566           fireProgressChanged((int)(100 * startNodeOff / lastNodeOff));
567           oldStartNodeOff = startNodeOff;
568         }
569       }
570     }//while(startNodeOff != -1)
571     fireProcessFinished();
572   } // transduce
573 
574 
575   /** Clean up (delete action class files, for e.g.). */
576   public void cleanUp() {
577 //    for(DListIterator i = rules.begin(); ! i.atEnd(); i.advance())
578 //      ((Rule) i.get()).cleanUp();
579   } // cleanUp
580 
581   /** A string representation of this object. */
582   public String toString() {
583     return toString("");
584   } // toString()
585 
586   /** A string representation of this object. */
587   public String toString(String pad) {
588     String newline = Strings.getNl();
589     String newPad = Strings.addPadding(pad, INDENT_PADDING);
590 
591     StringBuffer buf =
592       new StringBuffer(pad + "SPT: name(" + name + "); ruleApplicationStyle(");
593 
594     switch(ruleApplicationStyle) {
595       case APPELT_STYLE: buf.append("APPELT_STYLE); "); break;
596       case BRILL_STYLE:  buf.append("BRILL_STYLE); ");  break;
597       default: break;
598     }
599 
600     buf.append("rules(" + newline);
601     Iterator rulesIterator = rules.iterator();
602     while(rulesIterator.hasNext())
603       buf.append(((Rule) rulesIterator.next()).toString(newPad) + " ");
604 
605     buf.append(newline + pad + ")." + newline);
606 
607     return buf.toString();
608   } // toString(pad)
609 
610   //needed by fsm
611   public PrioritisedRuleList getRules() {
612     return rules;
613   }
614 
615   /**
616     * Adds a new type of input annotations used by this transducer.
617     * If the list of input types is empty this transducer will parse all the
618     * annotations in the document otherwise the types not found in the input
619     * list will be completely ignored! To be used with caution!
620     */
621   public void addInput(String ident) {
622     input.add(ident);
623   }
624   public synchronized void removeProgressListener(ProgressListener l) {
625     if (progressListeners != null && progressListeners.contains(l)) {
626       Vector v = (Vector) progressListeners.clone();
627       v.removeElement(l);
628       progressListeners = v;
629     }
630   }
631   public synchronized void addProgressListener(ProgressListener l) {
632     Vector v = progressListeners == null ? new Vector(2) : (Vector) progressListeners.clone();
633     if (!v.contains(l)) {
634       v.addElement(l);
635       progressListeners = v;
636     }
637   }
638 
639   /**
640     * Defines the types of input annotations that this transducer reads. If this
641     * set is empty the transducer will read all the annotations otherwise it
642     * will only "see" the annotations of types found in this list ignoring all
643     * other types of annotations.
644     */
645   java.util.Set input = new java.util.HashSet();
646   private transient Vector progressListeners;
647 
648   protected void fireProgressChanged(int e) {
649     if (progressListeners != null) {
650       Vector listeners = progressListeners;
651       int count = listeners.size();
652       for (int i = 0; i < count; i++) {
653         ((ProgressListener) listeners.elementAt(i)).progressChanged(e);
654       }
655     }
656   }
657   protected void fireProcessFinished() {
658     if (progressListeners != null) {
659       Vector listeners = progressListeners;
660       int count = listeners.size();
661       for (int i = 0; i < count; i++) {
662         ((ProgressListener) listeners.elementAt(i)).processFinished();
663       }
664     }
665   }
666   public int getRuleApplicationStyle() {
667     return ruleApplicationStyle;
668   }
669 
670   /*
671   private void writeObject(ObjectOutputStream oos) throws IOException {
672     Out.prln("writing spt");
673     oos.defaultWriteObject();
674     Out.prln("finished writing spt");
675   } // writeObject
676   */
677 
678 
679 } // class SinglePhaseTransducer
680 
681 /*
682 class SimpleSortedSet {
683 
684     static final int INCREMENT = 1023;
685     int[] theArray = new int[INCREMENT];
686     Object[] theObject = new Object[INCREMENT];
687     int tsindex = 0;
688     int size = 0;
689     public static int avesize = 0;
690     public static int maxsize = 0;
691     public static int avecount = 0;
692     public SimpleSortedSet()
693     {
694         avecount++;
695         java.util.Arrays.fill(theArray, Integer.MAX_VALUE);
696     }
697 
698     public Object get(int elValue)
699     {
700         int index = java.util.Arrays.binarySearch(theArray, elValue);
701         if (index >=0)
702             return theObject[index];
703         return null;
704     }
705 
706     public boolean add(int elValue, Object o)
707     {
708         int index = java.util.Arrays.binarySearch(theArray, elValue);
709         if (index >=0)
710         {
711             ((ArrayList)theObject[index]).add(o);
712             return false;
713         }
714         if (size == theArray.length)
715         {
716             int[] temp = new int[theArray.length + INCREMENT];
717             Object[] tempO = new Object[theArray.length + INCREMENT];
718             System.arraycopy(theArray, 0, temp, 0, theArray.length);
719             System.arraycopy(theObject, 0, tempO, 0, theArray.length);
720             java.util.Arrays.fill(temp, theArray.length, temp.length , Integer.MAX_VALUE);
721             theArray = temp;
722             theObject = tempO;
723         }
724         index = ~index;
725         System.arraycopy(theArray, index, theArray, index+1, size - index );
726         System.arraycopy(theObject, index, theObject, index+1, size - index );
727         theArray[index] = elValue;
728         theObject[index] = new ArrayList();
729         ((ArrayList)theObject[index]).add(o);
730         size++;
731         return true;
732     }
733     public int first()
734     {
735         if (tsindex >= size) return -1;
736         return theArray[tsindex];
737     }
738 
739     public Object getFirst()
740     {
741         if (tsindex >= size) return null;
742         return theObject[tsindex];
743     }
744 
745     public SimpleSortedSet tailSet(int elValue)
746     {
747         if (tsindex < theArray.length && elValue != theArray[tsindex])
748         {
749             if (tsindex<(size-1) && elValue > theArray[tsindex] &&
750                 elValue <= theArray[tsindex+1])
751                 {
752                     tsindex++;
753                    return this;
754                 }
755             int index = java.util.Arrays.binarySearch(theArray, elValue);
756             if (index < 0)
757                 index = ~index;
758             tsindex = index;
759         }
760         return this;
761     }
762 
763     public boolean isEmpty()
764     {
765         return size ==0;
766     }
767 };
768 */