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 }