001    package jigcell.compare.data;
002    
003    import java.beans.DefaultPersistenceDelegate;
004    import java.beans.Encoder;
005    import java.beans.Statement;
006    import java.io.Reader;
007    import java.io.StringReader;
008    import jigcell.compare.IDataElement;
009    import jigcell.compare.IEditableDataElement;
010    import jigcell.compare.impl.Compare;
011    import jigcell.compare.impl.StreamingStringTokenizer;
012    
013    /**
014     * A helper implementation for editable data element classes.  Handles the parsing of a text representation of an element and provides a default
015     * implementation for nearly every method required by IEditableDataElement.  EditableDataElement is not synchronized and in particular does not
016     * protect against concurrent edits.
017     *
018     * <p>
019     * This code is licensed under the DARPA BioCOMP Open Source License.  See LICENSE for more details.
020     * </p>
021     *
022     * @author Nicholas Allen
023     */
024    
025    public abstract class EditableDataElement extends DataElement implements IEditableDataElement {
026    
027       /**
028        * Pattern indicating value is floating point
029        */
030    
031       protected final static char PATTERN_FP = '.';
032    
033       /**
034        * NaN
035        */
036    
037       protected final static Double DOUBLE_NaN = new Double (Double.NaN);
038    
039       /**
040        * -Infinity
041        */
042    
043       protected final static Double DOUBLE_NINFINITY = new Double (Double.NEGATIVE_INFINITY);
044    
045       /**
046        * +Infinity
047        */
048    
049       protected final static Double DOUBLE_PINFINITY = new Double (Double.POSITIVE_INFINITY);
050    
051       /**
052        * Message when unable to parse element text due to bad block nesting
053        */
054    
055       protected final static String MESSAGE_PARSEBLOCKERROR = "Unexpected '('";
056    
057       /**
058        * Message when unable to parse element text
059        */
060    
061       protected final static String MESSAGE_PARSEERROR = "Unable to fully parse expression";
062    
063       /**
064        * Message when unable to parse element text due to too much input
065        */
066    
067       protected final static String MESSAGE_PARSEEXTRAERROR = "Unexpected input following expression";
068    
069       /**
070        * Message when unable to parse element text due to unterminated literal
071        */
072    
073       protected final static String MESSAGE_PARSELITERALERROR = "Unable to find closing '\"'";
074    
075       /**
076        * All of the tokens used by the parser
077        */
078    
079       protected final static String TOKEN_ALL = "(,)\"";
080    
081       /**
082        * Token to separate block elements
083        */
084    
085       protected final static String TOKEN_BLOCKELEMENT = ",";
086    
087       /**
088        * Token to end a block
089        */
090    
091       protected final static String TOKEN_BLOCKEND = ")";
092    
093       /**
094        * Token to start a block
095        */
096    
097       protected final static String TOKEN_BLOCKSTART = "(";
098    
099       /**
100        * Token to delimit a literal
101        */
102    
103       protected final static String TOKEN_LITERAL = "\"";
104    
105       /**
106        * Prototypical instance of a subelement
107        */
108    
109       protected EditableDataElement prototypeChild;
110    
111       /**
112        * Persists the element using a flat string rather than trying to save the values table.
113        */
114    
115       protected static class EditableDataElementDelegate extends DefaultPersistenceDelegate {
116          protected void initialize (Class clazz, Object o1, Object o2, Encoder encoder) {
117             super.initialize (clazz, o1, o2, encoder);
118             encoder.writeStatement (new Statement (o1, "setValueInterpreted", new Object [] {((EditableDataElement) o1).toString ()}));
119          }
120       }
121    
122       static {
123          Compare.addDelegate (EditableDataElement.class, new EditableDataElementDelegate ());
124       }
125    
126       /**
127        * Generates an element from an existing DataElement.
128        *
129        * @param element DataElement
130        */
131    
132       public static IEditableDataElement createElement (IDataElement element) {
133          return element == null ? null : createElement (element.toString ());
134       }
135    
136       /**
137        * Generates the map for an element from a streaming input.
138        *
139        * @param element Element to populate
140        * @param reader Reader
141        */
142    
143       public static EditableDataElement createElement (EditableDataElement element, Reader reader) {
144          return createElement (element, new StreamingStringTokenizer (reader, TOKEN_ALL), -1);
145       }
146    
147       /**
148        * Generates an element from a text string.
149        *
150        * @param text String to parse
151        */
152    
153       public static IEditableDataElement createElement (String text) {
154          return createElement (new StringReader (text));
155       }
156    
157       /**
158        * Generates an element from streaming input.
159        *
160        * @param reader Reader
161        */
162    
163       public static IEditableDataElement createElement (Reader reader) {
164          return SparseTreeDataElement.createElement (reader);
165       }
166    
167       /**
168        * Generates an element from an existing DataElement.  If an element cannot be created, an empty element is returned.
169        *
170        * @param element DataElement
171        */
172    
173       public static IEditableDataElement createElementSafe (IDataElement element) {
174          return createElementSafe (element == null ? "" : element.toString ());
175       }
176    
177       /**
178        * Generates an element from a text string.  If an element cannot be created, an empty element in returned.
179        *
180        * @param text String to parse
181        */
182    
183       public static IEditableDataElement createElementSafe (String text) {
184          return createElementSafe (new StringReader (text == null ? "" : text));
185       }
186    
187       /**
188        * Generates an element from streaming input.  If an element cannot be created, an empty element in returned.
189        *
190        * @param reader Reader
191        */
192    
193       public static IEditableDataElement createElementSafe (Reader reader) {
194          return reader == null ? new SparseTreeDataElement () : SparseTreeDataElement.createElementSafe (reader);
195       }
196    
197       /**
198        * Generates an element from a boolean.
199        *
200        * @param value Value
201        */
202    
203       public static IEditableDataElement createScalarElement (boolean value) {
204          return createElement (value ? PATTERN_TRUE : PATTERN_FALSE);
205       }
206    
207       /**
208        * Generates an element from a double.
209        *
210        * @param value Value
211        */
212    
213       public static IEditableDataElement createScalarElement (double value) {
214          return createElement (String.valueOf (value));
215       }
216    
217       /**
218        * Generates an element from a long.
219        *
220        * @param value Value
221        */
222    
223       public static IEditableDataElement createScalarElement (long value) {
224          return createElement (String.valueOf (value));
225       }
226    
227       /**
228        * Generates an element from a string.
229        *
230        * @param value Value
231        */
232    
233       public static IEditableDataElement createScalarElement (String value) {
234          return createElement (TOKEN_LITERAL + value + TOKEN_LITERAL);
235       }
236    
237       /**
238        * Generates the map for an element from a text string.
239        *
240        * @param element Element to populate
241        * @param tokenizer Tokenizer to obtain input from
242        * @param pos Initial position to store element data at
243        */
244    
245       private static EditableDataElement createElement (EditableDataElement element, StreamingStringTokenizer tokenizer, long pos) {
246          boolean waiting = true;
247          while (tokenizer.hasNext ()) {
248             String text = tokenizer.next ().trim ().toLowerCase ();
249             if (text.length () == 0)
250                continue;
251             if (TOKEN_BLOCKELEMENT.equals (text)) {
252                if (pos == 0) {
253                   element.putDirect (1, element.getDirect (-1));
254                   element.removeDirect (-1);
255                   waiting = true;
256                   pos = 2;
257                } else if (pos == -1)
258                   pos = 2;
259                else if (waiting)
260                   pos++;
261                else
262                   waiting = true;
263                continue;
264             }
265             if (TOKEN_BLOCKSTART.equals (text)) {
266                if (pos == -1) {
267                   pos = 1;
268                   waiting = true;
269                } else {
270                   if (pos == 0 || !waiting)
271                      throw new IllegalArgumentException (MESSAGE_PARSEBLOCKERROR);
272                   element.putDirect (pos++,
273                      createElement ((element.prototypeChild == null ? element : element.prototypeChild).constructNew (), tokenizer, 1));
274                   waiting = false;
275                }
276                continue;
277             }
278             if (TOKEN_BLOCKEND.equals (text)) {
279                element.setLengthDirect (waiting ? pos : pos - 1);
280                return element;
281             }
282             if (pos == 0 || !waiting)
283                throw new IllegalArgumentException (MESSAGE_PARSEEXTRAERROR);
284             waiting = false;
285             if (TOKEN_LITERAL.equals (text)) {
286                StringBuffer value = new StringBuffer ();
287                while (true) {
288                   if (!tokenizer.hasNext ())
289                      throw new IllegalArgumentException (MESSAGE_PARSELITERALERROR);
290                   text = tokenizer.next ();
291                   if (TOKEN_LITERAL.equals (text))
292                      break;
293                   value.append (text);
294                }
295                element.putDirect (pos++, value.toString ());
296                continue;
297             }
298             try {
299                if (PATTERN_TRUE.equals (text))
300                   element.putDirect (pos++, Boolean.TRUE);
301                else if (PATTERN_FALSE.equals (text))
302                   element.putDirect (pos++, Boolean.FALSE);
303                else if (PATTERN_VOID.equals (text))
304                   pos++;
305                else if (PATTERN_PINFINITY.equals (text) || PATTERN_PINFINITY2.equals (text))
306                   element.putDirect (pos++, DOUBLE_PINFINITY);
307                else if (PATTERN_NINFINITY.equals (text) || PATTERN_NINFINITY2.equals (text))
308                   element.putDirect (pos++, DOUBLE_NINFINITY);
309                else if (PATTERN_NAN.equals (text))
310                   element.putDirect (pos++, DOUBLE_NaN);
311                else if (text.indexOf (PATTERN_FP) != -1)
312                   element.putDirect (pos++, Double.valueOf (text));
313                else
314                   element.putDirect (pos++, Long.valueOf (text));
315             } catch (Exception e) {
316                throw (IllegalArgumentException) new IllegalArgumentException (MESSAGE_PARSEERROR).initCause (e);
317             }
318          }
319          if (pos == 0) {
320             element.putDirect (0, element.getDirect (-1));
321             element.removeDirect (-1);
322             element.setLengthDirect (0);
323          } else if (pos == -1)
324             element.setLengthDirect (0);
325          else
326             element.setLengthDirect (waiting ? pos : pos - 1);
327          return element;
328       }
329    
330       /**
331        * {@inheritDoc}
332        */
333    
334       public void copyValue (long pos1, long pos2) {
335          putChecked (pos2, getChecked (pos1));
336          if (pos2 > getLength ())
337             setLengthDirect (pos2);
338       }
339    
340       /**
341        * {@inheritDoc}
342        */
343    
344       public boolean [] forceSlice (boolean slice [], long start, int length, long stride) {
345          boolean value [] = getSlice (slice, start, length, stride);
346          if (value != null)
347             return value;
348          value = slice == null || slice.length < length ? new boolean [length] : slice;
349          long pos = start;
350          for (int i = 0; i < length; pos += stride) {
351             Object item = getDirect (pos);
352             value [i++] = item == null ? false :
353                item instanceof Boolean ? item == Boolean.TRUE :
354                item instanceof Long ? ((Long) item).longValue () != 0 :
355                item instanceof Double ? ((Double) item).doubleValue () != 0.0 :
356                item instanceof String ? Boolean.valueOf ((String) item).booleanValue () : true;
357          }
358          return value;
359       }
360    
361       /**
362        * {@inheritDoc}
363        */
364    
365       public IDataElement [] forceSlice (IDataElement slice [], long start, int length, long stride) {
366          IDataElement value [] = getSlice (slice, start, length, stride);
367          if (value != null)
368             return value;
369          value = slice == null || slice.length < length ? new IDataElement [length] : slice;
370          long pos = start;
371          for (int i = 0; i < length; pos += stride) {
372             Object item = getDirect (pos);
373             if (item instanceof IDataElement) {
374                value [i++] = (IDataElement) item;
375                continue;
376             }
377             EditableDataElement element = constructNew ();
378             if (item != null) {
379                element.putDirect (1, item);
380                element.setLengthDirect (1);
381             }
382             value [i++] = element;
383          }
384          return value;
385       }
386    
387       /**
388        * {@inheritDoc}
389        */
390    
391       public double [] forceSlice (double slice [], long start, int length, long stride) {
392          double value [] = getSlice (slice, start, length, stride);
393          if (value != null)
394             return value;
395          value = slice == null || slice.length < length ? new double [length] : slice;
396          long pos = start;
397          for (int i = 0; i < length; pos += stride) {
398             Object item = getDirect (pos);
399             value [i++] = item == null ? Double.NaN :
400                item instanceof Double ? ((Double) item).doubleValue () :
401                item instanceof Long ? (double) ((Long) item).longValue () :
402                item instanceof Boolean ? item == Boolean.TRUE ? 1.0 : 0.0 :
403                item instanceof String ? Double.valueOf ((String) item).doubleValue () : Double.NaN;
404          }
405          return value;
406       }
407    
408       /**
409        * {@inheritDoc}
410        */
411    
412       public long [] forceSlice (long slice [], long start, int length, long stride) {
413          long value [] = getSlice (slice, start, length, stride);
414          if (value != null)
415             return value;
416          value = slice == null || slice.length < length ? new long [length] : slice;
417          long pos = start;
418          for (int i = 0; i < length; pos += stride) {
419             Object item = getDirect (pos);
420             value [i++] = item == null ? 0 :
421                item instanceof Long ? ((Long) item).longValue () :
422                item instanceof Double ? Math.round (((Double) item).doubleValue ()) :
423                item instanceof Boolean ? item == Boolean.TRUE ? 1 : 0 : item instanceof String ? Long.valueOf ((String) item).longValue () : 0;
424          }
425          return value;
426       }
427    
428       /**
429        * {@inheritDoc}
430        */
431    
432       public boolean getBooleanValue (long pos) {
433          return getDirect (pos) == Boolean.TRUE;
434       }
435    
436       /**
437        * {@inheritDoc}
438        */
439    
440       public long getIntegralValue (long pos) {
441          Object value = getDirect (pos);
442          return value != null && value instanceof Long ? ((Long) value).longValue () : 0;
443       }
444    
445       /**
446        * {@inheritDoc}
447        */
448    
449       public IDataElement getListValue (long pos) {
450          Object value = getDirect (pos);
451          return value instanceof IDataElement ? (IDataElement) value : null;
452       }
453    
454       /**
455        * {@inheritDoc}
456        */
457    
458       public String getLiteralValue (long pos) {
459          Object value = getDirect (pos);
460          return value instanceof String ? (String) value : null;
461       }
462    
463       /**
464        * The prototypical instance of a subelement.
465        */
466    
467       public EditableDataElement getPrototypeChild () {
468          return prototypeChild;
469       }
470    
471       /**
472        * {@inheritDoc}
473        */
474    
475       public double getRealValue (long pos) {
476          Object value = getDirect (pos);
477          return value != null && value instanceof Double ? ((Double) value).doubleValue () : Double.NaN;
478       }
479    
480       /**
481        * {@inheritDoc}
482        */
483    
484       public IDataElement.Type getType () {
485          return getLength () == 0 ? getType (0) : IDataElement.Type.MULTIPLE;
486       }
487    
488       /**
489        * {@inheritDoc}
490        */
491    
492       public IDataElement.Type getType (long pos) {
493          Object value = getDirect (pos);
494          return value == null ? IDataElement.Type.NONE :
495             value instanceof Long ? IDataElement.Type.INTEGRAL :
496             value instanceof Boolean ? IDataElement.Type.BOOLEAN :
497             value instanceof Double ? IDataElement.Type.REAL : value instanceof String ? IDataElement.Type.LITERAL : IDataElement.Type.MULTIPLE;
498       }
499    
500       /**
501        * {@inheritDoc}
502        */
503    
504       public void makeList () {
505          if (getLength () > 0)
506             return;
507          putDirect (1, getDirect (0));
508          setLengthDirect (1);
509       }
510    
511       /**
512        * {@inheritDoc}
513        */
514    
515       public void moveValue (long pos1, long pos2) {
516          putChecked (pos2, getChecked (pos1));
517          if (pos2 > getLength ())
518             setLengthDirect (pos2);
519          unsetValue (pos1);
520       }
521    
522       /**
523        * Sets the prototypical instance of a subelement
524        *
525        * @param prototypeChild Instance
526        */
527    
528       public void setPrototypeChild (EditableDataElement prototypeChild) {
529          this.prototypeChild = prototypeChild;
530       }
531    
532       /**
533        * {@inheritDoc}
534        */
535    
536       public void setValue (boolean value) {
537          clear ();
538          putDirect (0, value ? Boolean.TRUE : Boolean.FALSE);
539       }
540    
541       /**
542        * {@inheritDoc}
543        */
544    
545       public void setValue (IDataElement value) {
546          setValueInterpreted (value.toString ());
547       }
548    
549       /**
550        * {@inheritDoc}
551        */
552    
553       public void setValue (double value) {
554          clear ();
555          putDirect (0, new Double (value));
556       }
557    
558       /**
559        * {@inheritDoc}
560        */
561    
562       public void setValue (long value) {
563          clear ();
564          putDirect (0, getLongObject (value));
565       }
566    
567       /**
568        * {@inheritDoc}
569        */
570    
571       public void setValue (String value) {
572          clear ();
573          putDirect (0, value);
574       }
575    
576       /**
577        * {@inheritDoc}
578        */
579    
580       public void setValue (long pos, boolean value) {
581          putChecked (pos, value ? Boolean.TRUE : Boolean.FALSE);
582          if (pos > getLength ())
583             setLengthDirect (pos);
584       }
585    
586       /**
587        * {@inheritDoc}
588        */
589    
590       public void setValue (long pos, IDataElement value) {
591          putChecked (pos, value);
592          if (pos > getLength ())
593             setLengthDirect (pos);
594       }
595    
596       /**
597        * {@inheritDoc}
598        */
599    
600       public void setValue (long pos, double value) {
601          putChecked (pos, new Double (value));
602          if (pos > getLength ())
603             setLengthDirect (pos);
604       }
605    
606       /**
607        * {@inheritDoc}
608        */
609    
610       public void setValue (long pos, long value) {
611          putChecked (pos, getLongObject (value));
612          if (pos > getLength ())
613             setLengthDirect (pos);
614       }
615    
616       /**
617        * {@inheritDoc}
618        */
619    
620       public void setValue (long pos, String value) {
621          putChecked (pos, value);
622          if (pos > getLength ())
623             setLengthDirect (pos);
624       }
625    
626       /**
627        * {@inheritDoc}
628        */
629    
630       public void setValueInterpreted (String value) {
631          clear ();
632          createElement (this, new StringReader (value));
633       }
634    
635       /**
636        * {@inheritDoc}
637        */
638    
639       public void setValueInterpreted (long pos, String value) {
640          IDataElement element = createElement (constructNew (), new StringReader (value));
641          if (element.isScalar ()) {
642             IDataElement.Type type = element.getType ();
643             if (type == IDataElement.Type.NONE)
644                unsetValue (pos);
645             else if (type == IDataElement.Type.BOOLEAN)
646                setValue (pos, element.getBooleanValue ());
647             else if (type == IDataElement.Type.INTEGRAL)
648                setValue (pos, element.getIntegralValue ());
649             else if (type == IDataElement.Type.LITERAL)
650                setValue (pos, element.getLiteralValue ());
651             else
652                setValue (pos, element.getRealValue ());
653          } else
654             setValue (pos, element);
655       }
656    
657       /**
658        * {@inheritDoc}
659        */
660    
661       public void swapValues (long pos1, long pos2) {
662          if (pos1 < 1)
663             throw new IndexOutOfBoundsException (String.valueOf (pos1));
664          if (pos2 < 1)
665             throw new IndexOutOfBoundsException (String.valueOf (pos2));
666          Object temp = getDirect (pos2);
667          putDirect (pos2, getDirect (pos1));
668          putDirect (pos1, temp);
669       }
670    
671       /**
672        * {@inheritDoc}
673        */
674    
675       public void unsetValue (long pos) {
676          removeDirect (pos);
677       }
678    
679       /**
680        * Constructs a new element of the same type as this element.
681        */
682    
683       protected abstract EditableDataElement constructNew ();
684    
685       /**
686        * Gets the value at a position.  Boolean values are represented by Boolean.  Empty values are represented by null.  Integral values are
687        * represented by Integer.  Literal values are represented by String.  Multiple values are represented by DataElement.  Real values are
688        * represented by Double.  The position of the object must be valid.
689        *
690        * @param pos Position
691        */
692    
693       protected Object getChecked (long pos) {
694          if (pos < 1)
695             throw new IndexOutOfBoundsException (String.valueOf (pos));
696          return getDirect (pos);
697       }
698    
699       /**
700        * Gets the value at a position.  Boolean values are represented by Boolean.  Empty values are represented by null.  Integral values are
701        * represented by Integer.  Literal values are represented by String.  Multiple values are represented by DataElement.  Real values are
702        * represented by Double.  The position of the object may be anything.
703        *
704        * @param pos Position
705        */
706    
707       protected abstract Object getDirect (long pos);
708    
709       /**
710        * Sets the value at a position.  Boolean values are represented by Boolean.  Empty values are represented by null.  Integral values are
711        * represented by Integer.  Literal values are represented by String.  Multiple values are represented by DataElement.  Real values are
712        * represented by Double.  The position of the object must be valid.
713        *
714        * @param pos Position
715        * @param value Value
716        */
717    
718       protected void putChecked (long pos, Object value) {
719          if (pos < 1)
720             throw new IndexOutOfBoundsException (String.valueOf (pos));
721          putDirect (pos, value);
722       }
723    
724       /**
725        * Sets the value at a position.  Boolean values are represented by Boolean.  Empty values are represented by null.  Integral values are
726        * represented by Integer.  Literal values are represented by String.  Multiple values are represented by DataElement.  Real values are
727        * represented by Double.  The position of the object may be anything.
728        *
729        * @param pos Position
730        * @param value Value
731        */
732    
733       protected abstract void putDirect (long pos, Object value);
734    
735       /**
736        * Removes the value at a position.  The position of the object may be anything.
737        *
738        * @param pos Position
739        */
740    
741       protected abstract void removeDirect (long pos);
742    
743       /**
744        * Sets the known length of an element.  The length of the object may be anything.
745        *
746        * @param length Length of element
747        */
748    
749       protected abstract void setLengthDirect (long length);
750    }