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.62 2002/02/27 15:11:16 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);
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 
438 //System.out.println("Appelt acceptor");
439         // AcceptingFSMInstances is an ordered structure:
440         // just execute the longest (last) rule
441 
442         Collections.sort(acceptingFSMInstances, Collections.reverseOrder());
443 
444         FSMInstance currentAcceptor =(FSMInstance)acceptingFSMInstances.get(0);
445         if(isDebugMode()){
446           //see if we have any conflicts
447           Iterator accIter = acceptingFSMInstances.iterator();
448           FSMInstance anAcceptor;
449           List conflicts = new ArrayList();
450           while(accIter.hasNext()){
451             anAcceptor = (FSMInstance)accIter.next();
452             if(anAcceptor.equals(currentAcceptor)){
453               conflicts.add(anAcceptor);
454             }else{
455               break;
456             }
457           }
458           if(conflicts.size() > 1){
459             Out.prln("\nConflicts found during matching:" +
460                      "\n================================");
461             accIter = conflicts.iterator();
462             int i = 0;
463             while(accIter.hasNext()){
464               Out.prln(i++ + ") " + accIter.next().toString());
465             }
466           }
467         }
468 
469         RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();
470         currentRHS.transduce(doc, currentAcceptor.getBindings(),
471                              inputAS, outputAS);
472         //advance in AG
473         lastAGPosition = currentAcceptor.getAGPosition().getOffset().longValue();
474       }
475 //      else if(ruleApplicationStyle == FIRST_STYLE) {
476 //        // AcceptingFSMInstances is an ordered structure:
477 //        // just execute the shortest (first) rule
478 //
479 //        FSMInstance currentAcceptor =(FSMInstance)acceptingFSMInstances.first();
480 //        RightHandSide currentRHS = currentAcceptor.getFSMPosition().getAction();
481 //        currentRHS.transduce(doc, outputAS, currentAcceptor.getBindings());
482 //        //advance in AG
483 //        long lastAGPosition = currentAcceptor.getAGPosition().
484 //                              getOffset().longValue();
485 //        //advance the index on input
486 //        while(inputIndex < annotations.size() &&
487 //              ((Annotation)annotations.get(inputIndex)).
488 //              getStartNode().getOffset().longValue() < lastAGPosition){
489 //          inputIndex++;
490 //        }
491 //      }
492       else throw new RuntimeException("Unknown rule application style!");
493 
494 
495       //advance on input
496 //      SortedSet OffsetsTailSet = offsets.tailSet(lastAGPosition);
497       SimpleSortedSet OffsetsTailSet = offsets.tailSet(lastAGPosition);
498 //<<< DAM: isEmpty speedup
499 /*
500       if(OffsetsTailSet.isEmpty()){
501 */
502 //=== DAM: now
503         long theFirst = OffsetsTailSet.first();
504       if( theFirst < 0){
505 //>>> DAM: end
506         //no more input, phew! :)
507         startNodeOff = -1;
508         fireProcessFinished();
509       }else{
510 //<<< DAM: use long
511 /*
512         Long nextKey = (Long)OffsetsTailSet.first();
513 */
514 //=== DAM: now
515         long nextKey = theFirst;
516 //>>> DAM: end
517         startNode = ((Annotation)
518                       ((ArrayList)annotationsByOffset.get(nextKey)).get(0)). //nextKey
519                     getStartNode();
520         startNodeOff = startNode.getOffset().longValue();
521 
522         //eliminate the possibility for infinite looping
523         if(oldStartNodeOff == startNodeOff){
524 //Out.prln("");
525 //Out.pr("SKIP " + startNodeOff);
526           //we are about to step twice in the same place, ...skip ahead
527           lastAGPosition = startNodeOff + 1;
528           OffsetsTailSet = offsets.tailSet(lastAGPosition);
529 //<<< DAM: isEmpty speedup
530 /*
531           if(OffsetsTailSet.isEmpty()){
532 */
533 //=== DAM: now
534           theFirst = OffsetsTailSet.first();
535           if(theFirst < 0){
536 //>>> DAM: end
537             //no more input, phew! :)
538             startNodeOff = -1;
539             fireProcessFinished();
540           }else{
541 //<<< DAM: use long
542 //            nextKey = (Long)OffsetsTailSet.first();
543 //=== DAM: now
544             nextKey = theFirst;
545 //>>> DAM: end
546             startNode = ((Annotation)
547                           ((List)annotationsByOffset.get(theFirst)).get(0)).
548                         getStartNode();
549             startNodeOff =startNode.getOffset().longValue();
550           }
551 //Out.prln(" ->" + startNodeOff);
552         }//if(oldStartNodeOff == startNodeOff)
553 
554 
555         //fire the progress event
556         if(startNodeOff - oldStartNodeOff > 256){
557           if(isInterrupted()) throw new ExecutionInterruptedException(
558             "The execution of the \"" + getName() +
559             "\" Jape transducer has been abruptly interrupted!");
560 
561           fireProgressChanged((int)(100 * startNodeOff / lastNodeOff));
562           oldStartNodeOff = startNodeOff;
563         }
564       }
565     }//while(startNodeOff != -1)
566     fireProcessFinished();
567   } // transduce
568 
569 
570   /** Clean up (delete action class files, for e.g.). */
571   public void cleanUp() {
572 //    for(DListIterator i = rules.begin(); ! i.atEnd(); i.advance())
573 //      ((Rule) i.get()).cleanUp();
574   } // cleanUp
575 
576   /** A string representation of this object. */
577   public String toString() {
578     return toString("");
579   } // toString()
580 
581   /** A string representation of this object. */
582   public String toString(String pad) {
583     String newline = Strings.getNl();
584     String newPad = Strings.addPadding(pad, INDENT_PADDING);
585 
586     StringBuffer buf =
587       new StringBuffer(pad + "SPT: name(" + name + "); ruleApplicationStyle(");
588 
589     switch(ruleApplicationStyle) {
590       case APPELT_STYLE: buf.append("APPELT_STYLE); "); break;
591       case BRILL_STYLE:  buf.append("BRILL_STYLE); ");  break;
592       default: break;
593     }
594 
595     buf.append("rules(" + newline);
596     Iterator rulesIterator = rules.iterator();
597     while(rulesIterator.hasNext())
598       buf.append(((Rule) rulesIterator.next()).toString(newPad) + " ");
599 
600     buf.append(newline + pad + ")." + newline);
601 
602     return buf.toString();
603   } // toString(pad)
604 
605   //needed by fsm
606   public PrioritisedRuleList getRules() {
607     return rules;
608   }
609 
610   /**
611     * Adds a new type of input annotations used by this transducer.
612     * If the list of input types is empty this transducer will parse all the
613     * annotations in the document otherwise the types not found in the input
614     * list will be completely ignored! To be used with caution!
615     */
616   public void addInput(String ident) {
617     input.add(ident);
618   }
619   public synchronized void removeProgressListener(ProgressListener l) {
620     if (progressListeners != null && progressListeners.contains(l)) {
621       Vector v = (Vector) progressListeners.clone();
622       v.removeElement(l);
623       progressListeners = v;
624     }
625   }
626   public synchronized void addProgressListener(ProgressListener l) {
627     Vector v = progressListeners == null ? new Vector(2) : (Vector) progressListeners.clone();
628     if (!v.contains(l)) {
629       v.addElement(l);
630       progressListeners = v;
631     }
632   }
633 
634   /**
635     * Defines the types of input annotations that this transducer reads. If this
636     * set is empty the transducer will read all the annotations otherwise it
637     * will only "see" the annotations of types found in this list ignoring all
638     * other types of annotations.
639     */
640   java.util.Set input = new java.util.HashSet();
641   private transient Vector progressListeners;
642 
643   protected void fireProgressChanged(int e) {
644     if (progressListeners != null) {
645       Vector listeners = progressListeners;
646       int count = listeners.size();
647       for (int i = 0; i < count; i++) {
648         ((ProgressListener) listeners.elementAt(i)).progressChanged(e);
649       }
650     }
651   }
652   protected void fireProcessFinished() {
653     if (progressListeners != null) {
654       Vector listeners = progressListeners;
655       int count = listeners.size();
656       for (int i = 0; i < count; i++) {
657         ((ProgressListener) listeners.elementAt(i)).processFinished();
658       }
659     }
660   }
661   public int getRuleApplicationStyle() {
662     return ruleApplicationStyle;
663   }
664 
665   /*
666   private void writeObject(ObjectOutputStream oos) throws IOException {
667     Out.prln("writing spt");
668     oos.defaultWriteObject();
669     Out.prln("finished writing spt");
670   } // writeObject
671   */
672 
673 
674 } // class SinglePhaseTransducer
675 
676 /*
677 class SimpleSortedSet {
678 
679     static final int INCREMENT = 1023;
680     int[] theArray = new int[INCREMENT];
681     Object[] theObject = new Object[INCREMENT];
682     int tsindex = 0;
683     int size = 0;
684     public static int avesize = 0;
685     public static int maxsize = 0;
686     public static int avecount = 0;
687     public SimpleSortedSet()
688     {
689         avecount++;
690         java.util.Arrays.fill(theArray, Integer.MAX_VALUE);
691     }
692 
693     public Object get(int elValue)
694     {
695         int index = java.util.Arrays.binarySearch(theArray, elValue);
696         if (index >=0)
697             return theObject[index];
698         return null;
699     }
700 
701     public boolean add(int elValue, Object o)
702     {
703         int index = java.util.Arrays.binarySearch(theArray, elValue);
704         if (index >=0)
705         {
706             ((ArrayList)theObject[index]).add(o);
707             return false;
708         }
709         if (size == theArray.length)
710         {
711             int[] temp = new int[theArray.length + INCREMENT];
712             Object[] tempO = new Object[theArray.length + INCREMENT];
713             System.arraycopy(theArray, 0, temp, 0, theArray.length);
714             System.arraycopy(theObject, 0, tempO, 0, theArray.length);
715             java.util.Arrays.fill(temp, theArray.length, temp.length , Integer.MAX_VALUE);
716             theArray = temp;
717             theObject = tempO;
718         }
719         index = ~index;
720         System.arraycopy(theArray, index, theArray, index+1, size - index );
721         System.arraycopy(theObject, index, theObject, index+1, size - index );
722         theArray[index] = elValue;
723         theObject[index] = new ArrayList();
724         ((ArrayList)theObject[index]).add(o);
725         size++;
726         return true;
727     }
728     public int first()
729     {
730         if (tsindex >= size) return -1;
731         return theArray[tsindex];
732     }
733 
734     public Object getFirst()
735     {
736         if (tsindex >= size) return null;
737         return theObject[tsindex];
738     }
739 
740     public SimpleSortedSet tailSet(int elValue)
741     {
742         if (tsindex < theArray.length && elValue != theArray[tsindex])
743         {
744             if (tsindex<(size-1) && elValue > theArray[tsindex] &&
745                 elValue <= theArray[tsindex+1])
746                 {
747                     tsindex++;
748                    return this;
749                 }
750             int index = java.util.Arrays.binarySearch(theArray, elValue);
751             if (index < 0)
752                 index = ~index;
753             tsindex = index;
754         }
755         return this;
756     }
757 
758     public boolean isEmpty()
759     {
760         return size ==0;
761     }
762 };
763 */