001: //##header
002: /*
003: *******************************************************************************
004: * Copyright (C) 1996-2006, International Business Machines Corporation and *
005: * others. All Rights Reserved. *
006: *******************************************************************************
007: */
008: //#ifndef FOUNDATION
009: package com.ibm.icu.dev.test.util;
010:
011: import java.io.ByteArrayOutputStream;
012: import java.io.DataInput;
013: import java.io.DataOutput;
014: import java.io.Externalizable;
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.io.ObjectInput;
018: import java.io.ObjectInputStream;
019: import java.io.ObjectOutput;
020: import java.io.ObjectOutputStream;
021: import java.io.OutputStream;
022: import java.io.Serializable;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Comparator;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Iterator;
029: import java.util.LinkedHashMap;
030: import java.util.LinkedHashSet;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.Set;
034: import java.util.TreeSet;
035:
036: import com.ibm.icu.impl.Utility;
037: import com.ibm.icu.text.UTF16;
038: import com.ibm.icu.text.UnicodeSet;
039: import com.ibm.icu.text.UnicodeSetIterator;
040: import com.ibm.icu.util.Freezable;
041:
042: /**
043: * Class for mapping Unicode characters to values
044: * Much smaller storage than using HashMap, and much faster and more compact than
045: * a list of UnicodeSets.
046: * @author Davis
047: */
048:
049: public final class UnicodeMap implements Cloneable, Freezable,
050: Externalizable {
051: static final boolean ASSERTIONS = false;
052: static final long GROWTH_PERCENT = 200; // 100 is no growth!
053: static final long GROWTH_GAP = 10; // extra bump!
054:
055: private int length;
056: // two parallel arrays to save memory. Wish Java had structs.
057: private int[] transitions;
058: private Object[] values;
059:
060: private LinkedHashSet availableValues = new LinkedHashSet();
061: private transient boolean staleAvailableValues;
062:
063: private transient boolean errorOnReset;
064: private transient boolean locked;
065: private int lastIndex;
066:
067: {
068: clear();
069: }
070:
071: public UnicodeMap clear() {
072: if (locked)
073: throw new UnsupportedOperationException(
074: "Attempt to modify locked object");
075: length = 2;
076: transitions = new int[] { 0, 0x110000, 0, 0, 0, 0, 0, 0, 0, 0 };
077: values = new Object[10];
078:
079: availableValues.clear();
080: staleAvailableValues = false;
081:
082: errorOnReset = false;
083: lastIndex = 0;
084: return this ;
085: }
086:
087: /* Boilerplate */
088: public boolean equals(Object other) {
089: if (other == null)
090: return false;
091: try {
092: UnicodeMap that = (UnicodeMap) other;
093: if (length != that.length)
094: return false;
095: for (int i = 0; i < length - 1; ++i) {
096: if (transitions[i] != that.transitions[i])
097: return false;
098: if (!areEqual(values[i], that.values[i]))
099: return false;
100: }
101: return true;
102: } catch (ClassCastException e) {
103: return false;
104: }
105: }
106:
107: public int getHashCode(Object o) {
108: return o.hashCode();
109: //equator.getHashCode
110: }
111:
112: public static boolean areEqual(Object a, Object b) {
113: if (a == b)
114: return true;
115: if (a == null || b == null)
116: return false;
117: return a.equals(b);
118: }
119:
120: public int hashCode() {
121: int result = length;
122: // TODO might want to abbreviate this for speed.
123: for (int i = 0; i < length - 1; ++i) {
124: result = 37 * result + transitions[i];
125: result = 37 * result + getHashCode(values[i]);
126: }
127: return result;
128: }
129:
130: /**
131: * Standard clone. Warning, as with Collections, does not do deep clone.
132: */
133: public Object cloneAsThawed() {
134: UnicodeMap that = new UnicodeMap();
135: that.length = length;
136: that.transitions = (int[]) transitions.clone();
137: that.values = (Object[]) values.clone();
138: that.availableValues = new LinkedHashSet(availableValues);
139: that.locked = false;
140: return that;
141: }
142:
143: /* for internal consistency checking */
144:
145: void _checkInvariants() {
146: if (length < 2 || length > transitions.length
147: || transitions.length != values.length) {
148: throw new IllegalArgumentException(
149: "Invariant failed: Lengths bad");
150: }
151: for (int i = 1; i < length - 1; ++i) {
152: if (areEqual(values[i - 1], values[i])) {
153: throw new IllegalArgumentException(
154: "Invariant failed: values shared at " + "\t"
155: + Utility.hex(i - 1) + ": <"
156: + values[i - 1] + ">" + "\t"
157: + Utility.hex(i) + ": <" + values[i]
158: + ">");
159: }
160: }
161: if (transitions[0] != 0 || transitions[length - 1] != 0x110000) {
162: throw new IllegalArgumentException(
163: "Invariant failed: bounds set wrong");
164: }
165: for (int i = 1; i < length - 1; ++i) {
166: if (transitions[i - 1] >= transitions[i]) {
167: throw new IllegalArgumentException(
168: "Invariant failed: not monotonic" + "\t"
169: + Utility.hex(i - 1) + ": "
170: + transitions[i - 1] + "\t"
171: + Utility.hex(i) + ": "
172: + transitions[i]);
173: }
174: }
175: }
176:
177: /**
178: * Finds an index such that inversionList[i] <= codepoint < inversionList[i+1]
179: * Assumes that 0 <= codepoint <= 0x10FFFF
180: * @param codepoint
181: * @return the index
182: */
183: private int _findIndex(int c) {
184: int lo = 0;
185: int hi = length - 1;
186: int i = (lo + hi) >>> 1;
187: // invariant: c >= list[lo]
188: // invariant: c < list[hi]
189: while (i != lo) {
190: if (c < transitions[i]) {
191: hi = i;
192: } else {
193: lo = i;
194: }
195: i = (lo + hi) >>> 1;
196: }
197: if (ASSERTIONS)
198: _checkFind(c, lo);
199: return lo;
200: }
201:
202: private void _checkFind(int codepoint, int value) {
203: int other = __findIndex(codepoint);
204: if (other != value) {
205: throw new IllegalArgumentException(
206: "Invariant failed: binary search" + "\t"
207: + Utility.hex(codepoint) + ": " + value
208: + "\tshould be: " + other);
209: }
210: }
211:
212: private int __findIndex(int codepoint) {
213: // TODO use binary search
214: for (int i = length - 1; i > 0; --i) {
215: if (transitions[i] <= codepoint)
216: return i;
217: }
218: return 0;
219: }
220:
221: /*
222: * Try indexed lookup
223:
224: static final int SHIFT = 8;
225: int[] starts = new int[0x10FFFF>>SHIFT]; // lowest transition index where codepoint>>x can be found
226: boolean startsValid = false;
227: private int findIndex(int codepoint) {
228: if (!startsValid) {
229: int start = 0;
230: for (int i = 1; i < length; ++i) {
231:
232: }
233: }
234: for (int i = length-1; i > 0; --i) {
235: if (transitions[i] <= codepoint) return i;
236: }
237: return 0;
238: }
239: */
240:
241: /**
242: * Remove the items from index through index+count-1.
243: * Logically reduces the size of the internal arrays.
244: * @param index
245: * @param count
246: */
247: private void _removeAt(int index, int count) {
248: for (int i = index + count; i < length; ++i) {
249: transitions[i - count] = transitions[i];
250: values[i - count] = values[i];
251: }
252: length -= count;
253: }
254:
255: /**
256: * Add a gap from index to index+count-1.
257: * The values there are undefined, and must be set.
258: * Logically grows arrays to accomodate. Actual growth is limited
259: * @param index
260: * @param count
261: */
262: private void _insertGapAt(int index, int count) {
263: int newLength = length + count;
264: int[] oldtransitions = transitions;
265: Object[] oldvalues = values;
266: if (newLength > transitions.length) {
267: int allocation = (int) (GROWTH_GAP + (newLength * GROWTH_PERCENT) / 100);
268: transitions = new int[allocation];
269: values = new Object[allocation];
270: for (int i = 0; i < index; ++i) {
271: transitions[i] = oldtransitions[i];
272: values[i] = oldvalues[i];
273: }
274: }
275: for (int i = length - 1; i >= index; --i) {
276: transitions[i + count] = oldtransitions[i];
277: values[i + count] = oldvalues[i];
278: }
279: length = newLength;
280: }
281:
282: /**
283: * Associates code point with value. Removes any previous association.
284: * @param codepoint
285: * @param value
286: * @return this, for chaining
287: */
288: private UnicodeMap _put(int codepoint, Object value) {
289: // Warning: baseIndex is an invariant; must
290: // be defined such that transitions[baseIndex] < codepoint
291: // at end of this routine.
292: int baseIndex;
293: if (transitions[lastIndex] <= codepoint
294: && codepoint < transitions[lastIndex + 1]) {
295: baseIndex = lastIndex;
296: } else {
297: baseIndex = _findIndex(codepoint);
298: }
299: int limitIndex = baseIndex + 1;
300: // cases are (a) value is already set
301: if (areEqual(values[baseIndex], value))
302: return this ;
303: if (locked)
304: throw new UnsupportedOperationException(
305: "Attempt to modify locked object");
306: if (errorOnReset && values[baseIndex] != null) {
307: throw new IllegalArgumentException(
308: "Attempt to reset value for "
309: + Utility.hex(codepoint)
310: + " when that is disallowed. Old: "
311: + values[baseIndex] + "; New: " + value);
312: }
313:
314: // adjust the available values
315: staleAvailableValues = true;
316: availableValues.add(value); // add if not there already
317:
318: int baseCP = transitions[baseIndex];
319: int limitCP = transitions[limitIndex];
320: // we now start walking through the difference case,
321: // based on whether we are at the start or end of range
322: // and whether the range is a single character or multiple
323:
324: if (baseCP == codepoint) {
325: // CASE: At very start of range
326: boolean connectsWithPrevious = baseIndex != 0
327: && areEqual(value, values[baseIndex - 1]);
328:
329: if (limitCP == codepoint + 1) {
330: // CASE: Single codepoint range
331: boolean connectsWithFollowing = baseIndex < length - 1
332: && areEqual(value, values[limitIndex]);
333:
334: if (connectsWithPrevious) {
335: // A1a connects with previous & following, so remove index
336: if (connectsWithFollowing) {
337: _removeAt(baseIndex, 2);
338: } else {
339: _removeAt(baseIndex, 1); // extend previous
340: }
341: --baseIndex; // fix up
342: } else if (connectsWithFollowing) {
343: _removeAt(baseIndex, 1); // extend following backwards
344: transitions[baseIndex] = codepoint;
345: } else {
346: // doesn't connect on either side, just reset
347: values[baseIndex] = value;
348: }
349: } else if (connectsWithPrevious) {
350: // A.1: start of multi codepoint range
351: // if connects
352: ++transitions[baseIndex]; // extend previous
353: } else {
354: // otherwise insert new transition
355: transitions[baseIndex] = codepoint + 1; // fix following range
356: _insertGapAt(baseIndex, 1);
357: values[baseIndex] = value;
358: transitions[baseIndex] = codepoint;
359: }
360: } else if (limitCP == codepoint + 1) {
361: // CASE: at end of range
362: // if connects, just back up range
363: boolean connectsWithFollowing = baseIndex < length - 1
364: && areEqual(value, values[limitIndex]);
365:
366: if (connectsWithFollowing) {
367: --transitions[limitIndex];
368: return this ;
369: } else {
370: _insertGapAt(limitIndex, 1);
371: transitions[limitIndex] = codepoint;
372: values[limitIndex] = value;
373: }
374: } else {
375: // CASE: in middle of range
376: // insert gap, then set the new range
377: _insertGapAt(++baseIndex, 2);
378: transitions[baseIndex] = codepoint;
379: values[baseIndex] = value;
380: transitions[baseIndex + 1] = codepoint + 1;
381: values[baseIndex + 1] = values[baseIndex - 1]; // copy lower range values
382: }
383: lastIndex = baseIndex; // store for next time
384: return this ;
385: }
386:
387: private UnicodeMap _putAll(int startCodePoint, int endCodePoint,
388: Object value) {
389: for (int i = startCodePoint; i <= endCodePoint; ++i) {
390: _put(i, value);
391: }
392: return this ;
393: }
394:
395: /**
396: * Sets the codepoint value.
397: * @param codepoint
398: * @param value
399: * @return this (for chaining)
400: */
401: public UnicodeMap put(int codepoint, Object value) {
402: if (codepoint < 0 || codepoint > 0x10FFFF) {
403: throw new IllegalArgumentException(
404: "Codepoint out of range: " + codepoint);
405: }
406: _put(codepoint, value);
407: if (ASSERTIONS)
408: _checkInvariants();
409: return this ;
410: }
411:
412: /**
413: * Adds bunch o' codepoints; otherwise like put.
414: * @param codepoints
415: * @param value
416: * @return this (for chaining)
417: */
418: public UnicodeMap putAll(UnicodeSet codepoints, Object value) {
419: // TODO optimize
420: UnicodeSetIterator it = new UnicodeSetIterator(codepoints);
421: while (it.nextRange()) {
422: _putAll(it.codepoint, it.codepointEnd, value);
423: }
424: return this ;
425: }
426:
427: /**
428: * Adds bunch o' codepoints; otherwise like add.
429: * @param startCodePoint
430: * @param endCodePoint
431: * @param value
432: * @return this (for chaining)
433: */
434: public UnicodeMap putAll(int startCodePoint, int endCodePoint,
435: Object value) {
436: if (startCodePoint < 0 || endCodePoint > 0x10FFFF) {
437: throw new IllegalArgumentException(
438: "Codepoint out of range: "
439: + Utility.hex(startCodePoint) + ".."
440: + Utility.hex(endCodePoint));
441: }
442: // TODO optimize
443: for (int i = startCodePoint; i <= endCodePoint; ++i) {
444: _put(i, value);
445: }
446: return this ;
447: }
448:
449: /**
450: * Add all the (main) values from a Unicode property
451: * @param prop the property to add to the map
452: * @return this (for chaining)
453: */
454: public UnicodeMap putAll(UnicodeProperty prop) {
455: // TODO optimize
456: for (int i = 0; i <= 0x10FFFF; ++i) {
457: _put(i, prop.getValue(i));
458: }
459: return this ;
460: }
461:
462: /**
463: * Add all the (main) values from a Unicode property
464: * @param prop the property to add to the map
465: * @return this (for chaining)
466: */
467: public UnicodeMap putAll(UnicodeMap prop) {
468: // TODO optimize
469: for (int i = 0; i <= 0x10FFFF; ++i) {
470: _put(i, prop.getValue(i));
471: }
472: return this ;
473: }
474:
475: /**
476: * Set the currently unmapped Unicode code points to the given value.
477: * @param value the value to set
478: * @return this (for chaining)
479: */
480: public UnicodeMap setMissing(Object value) {
481: // fast path, if value not yet present
482: if (!getAvailableValues().contains(value)) {
483: staleAvailableValues = true;
484: availableValues.add(value);
485: for (int i = 0; i < length; ++i) {
486: if (values[i] == null)
487: values[i] = value;
488: }
489: return this ;
490: } else {
491: return putAll(getSet(null), value);
492: }
493: }
494:
495: /**
496: * Returns the set associated with a given value. Deposits into
497: * result if it is not null. Remember to clear if you just want
498: * the new values.
499: * @param value
500: * @param result
501: * @return result
502: */
503: public UnicodeSet getSet(Object value, UnicodeSet result) {
504: if (result == null)
505: result = new UnicodeSet();
506: for (int i = 0; i < length - 1; ++i) {
507: if (areEqual(value, values[i])) {
508: result.add(transitions[i], transitions[i + 1] - 1);
509: }
510: }
511: return result;
512: }
513:
514: public UnicodeSet getSet(Object value) {
515: return getSet(value, null);
516: }
517:
518: public UnicodeSet keySet() {
519: return getSet(null, null).complement();
520: }
521:
522: /**
523: * Returns the list of possible values. Deposits each non-null value into
524: * result. Creates result if it is null. Remember to clear result if
525: * you are not appending to existing collection.
526: * @param result
527: * @return result
528: */
529: public Collection getAvailableValues(Collection result) {
530: if (staleAvailableValues) {
531: // collect all the current values
532: // retain them in the availableValues
533: Set temp = new HashSet();
534: for (int i = 0; i < length - 1; ++i) {
535: if (values[i] != null)
536: temp.add(values[i]);
537: }
538: availableValues.retainAll(temp);
539: staleAvailableValues = false;
540: }
541: if (result == null)
542: result = new ArrayList(availableValues.size());
543: result.addAll(availableValues);
544: return result;
545: }
546:
547: /**
548: * Convenience method
549: */
550: public Collection getAvailableValues() {
551: return getAvailableValues(null);
552: }
553:
554: /**
555: * Gets the value associated with a given code point.
556: * Returns null, if there is no such value.
557: * @param codepoint
558: * @return the value
559: */
560: public Object getValue(int codepoint) {
561: if (codepoint < 0 || codepoint > 0x10FFFF) {
562: throw new IllegalArgumentException(
563: "Codepoint out of range: " + codepoint);
564: }
565: return values[_findIndex(codepoint)];
566: }
567:
568: /**
569: * Change a new string from the source string according to the mappings. For each code point cp, if getValue(cp) is null, append the character, otherwise append getValue(cp).toString()
570: * @param source
571: * @return
572: */
573: public String fold(String source) {
574: StringBuffer result = new StringBuffer();
575: int cp;
576: for (int i = 0; i < source.length(); i += UTF16
577: .getCharCount(cp)) {
578: cp = UTF16.charAt(source, i);
579: Object mResult = getValue(cp);
580: if (mResult != null) {
581: result.append(mResult);
582: } else {
583: UTF16.append(result, cp);
584: }
585: }
586: return result.toString();
587: }
588:
589: public interface Composer {
590: Object compose(int codePoint, Object a, Object b);
591: }
592:
593: public UnicodeMap composeWith(UnicodeMap other, Composer composer) {
594: for (int i = 0; i <= 0x10FFFF; ++i) {
595: Object v1 = getValue(i);
596: Object v2 = other.getValue(i);
597: Object v3 = composer.compose(i, v1, v2);
598: if (v1 != v3 && (v1 == null || !v1.equals(v3)))
599: put(i, v3);
600: }
601: return this ;
602: }
603:
604: public UnicodeMap composeWith(UnicodeSet set, Object value,
605: Composer composer) {
606: for (UnicodeSetIterator it = new UnicodeSetIterator(set); it
607: .next();) {
608: int i = it.codepoint;
609: Object v1 = getValue(i);
610: Object v3 = composer.compose(i, v1, value);
611: if (v1 != v3 && (v1 == null || !v1.equals(v3)))
612: put(i, v3);
613: }
614: return this ;
615: }
616:
617: /**
618: * Follow the style used by UnicodeSetIterator
619: */
620: public static class MapIterator {
621: public int codepoint;
622: public int codepointEnd;
623: public Object value;
624:
625: private UnicodeMap map;
626: private int index;
627: private int startRange;
628: private int endRange;
629: private Object lastValue;
630:
631: public MapIterator(UnicodeMap map) {
632: reset(map);
633: }
634:
635: // note: length of 2 means {0, 110000}. Only want to index up to 0!
636: public boolean nextRange() {
637: if (index < 0 || index >= map.length - 1)
638: return false;
639: value = map.values[index];
640: codepoint = startRange = map.transitions[index++];
641: codepointEnd = endRange = map.transitions[index] - 1; // -1 to make limit into end
642: return true;
643: }
644:
645: public boolean next() {
646: if (startRange > endRange) {
647: //System.out.println("***" + Utility.hex(startRange) + ".." + Utility.hex(endRange));
648: if (!nextRange())
649: return false;
650: // index now points AFTER the start of the range
651: lastValue = map.values[index - 1];
652: //System.out.println("***" + Utility.hex(codepoint) + ".." + Utility.hex(codepointEnd) + " => " + lastValue);
653: }
654: value = lastValue;
655: codepoint = codepointEnd = startRange++; // set to first, and iterate
656: return true;
657: }
658:
659: public MapIterator reset() {
660: index = 0;
661: startRange = 0;
662: endRange = -1;
663: return this ;
664: }
665:
666: public MapIterator reset(UnicodeMap map) {
667: this .map = map;
668: return reset();
669: }
670: }
671:
672: public String toString() {
673: return toString(null);
674: }
675:
676: public String toString(Comparator collected) {
677: StringBuffer result = new StringBuffer();
678: if (collected == null) {
679: for (int i = 0; i < length - 1; ++i) {
680: Object value = values[i];
681: if (value == null)
682: continue;
683: int start = transitions[i];
684: int end = transitions[i + 1] - 1;
685: result.append(Utility.hex(start));
686: if (start != end)
687: result.append("..").append(Utility.hex(end));
688: result.append("\t=> ").append(
689: values[i] == null ? "null" : values[i]
690: .toString()).append("\r\n");
691: }
692: } else {
693: Set set = (Set) getAvailableValues(new TreeSet(collected));
694: for (Iterator it = set.iterator(); it.hasNext();) {
695: Object value = it.next();
696: UnicodeSet s = getSet(value);
697: result.append(value).append("\t=> ").append(
698: s.toPattern(true)).append("\r\n");
699: }
700: }
701: return result.toString();
702: }
703:
704: /**
705: * @return Returns the errorOnReset.
706: */
707: public boolean getErrorOnReset() {
708: return errorOnReset;
709: }
710:
711: /**
712: * @param errorOnReset The errorOnReset to set.
713: */
714: public void setErrorOnReset(boolean errorOnReset) {
715: this .errorOnReset = errorOnReset;
716: }
717:
718: /* (non-Javadoc)
719: * @see com.ibm.icu.dev.test.util.Lockable#isLocked()
720: */
721: public boolean isFrozen() {
722: // TODO Auto-generated method stub
723: return locked;
724: }
725:
726: /* (non-Javadoc)
727: * @see com.ibm.icu.dev.test.util.Lockable#lock()
728: */
729: public Object freeze() {
730: locked = true;
731: return this ;
732: }
733:
734: static final boolean DEBUG_WRITE = false;
735:
736: // TODO Fix to serialize more than just strings.
737: // Only if all the items are strings will we do the following compression
738: // Otherwise we'll just use Java Serialization, bulky as it is
739: public void writeExternal(ObjectOutput out1) throws IOException {
740: DataOutputCompressor sc = new DataOutputCompressor(out1);
741: // if all objects are strings
742: Collection availableValues = getAvailableValues();
743: boolean allStrings = allAreString(availableValues);
744: sc.writeBoolean(allStrings);
745: Map object_index = new LinkedHashMap();
746: if (allAreString(availableValues)) {
747: sc.writeStringSet(new TreeSet(availableValues),
748: object_index);
749: } else {
750: sc.writeCollection(availableValues, object_index);
751: }
752: sc.writeUInt(length);
753: int lastTransition = -1;
754: int lastValueNumber = 0;
755: if (DEBUG_WRITE)
756: System.out.println("Trans count: " + length);
757: for (int i = 0; i < length; ++i) {
758: int valueNumber = ((Integer) object_index.get(values[i]))
759: .intValue();
760: if (DEBUG_WRITE)
761: System.out.println("Trans: " + transitions[i] + ",\t"
762: + valueNumber);
763:
764: int deltaTransition = transitions[i] - lastTransition;
765: lastTransition = transitions[i];
766: int deltaValueNumber = valueNumber - lastValueNumber;
767: lastValueNumber = valueNumber;
768:
769: deltaValueNumber <<= 1; // make room for one bit
770: boolean canCombine = deltaTransition == 1;
771: if (canCombine)
772: deltaValueNumber |= 1;
773: sc.writeInt(deltaValueNumber);
774: if (DEBUG_WRITE)
775: System.out.println("deltaValueNumber: "
776: + deltaValueNumber);
777: if (!canCombine) {
778: sc.writeUInt(deltaTransition);
779: if (DEBUG_WRITE)
780: System.out.println("deltaTransition: "
781: + deltaTransition);
782: }
783: }
784: sc.flush();
785: }
786:
787: /**
788: *
789: */
790: private boolean allAreString(Collection availableValues2) {
791: //if (true) return false;
792: for (Iterator it = availableValues2.iterator(); it.hasNext();) {
793: if (!(it.next() instanceof String))
794: return false;
795: }
796: return true;
797: }
798:
799: public void readExternal(ObjectInput in1) throws IOException,
800: ClassNotFoundException {
801: DataInputCompressor sc = new DataInputCompressor(in1);
802: boolean allStrings = sc.readBoolean();
803: Object[] valuesList;
804: availableValues = new LinkedHashSet();
805: if (allStrings) {
806: valuesList = sc.readStringSet(availableValues);
807: } else {
808: valuesList = sc.readCollection(availableValues);
809: }
810: length = sc.readUInt();
811: transitions = new int[length];
812: if (DEBUG_WRITE)
813: System.out.println("Trans count: " + length);
814: values = new Object[length];
815: int currentTransition = -1;
816: int currentValue = 0;
817: int deltaTransition;
818: for (int i = 0; i < length; ++i) {
819: int temp = sc.readInt();
820: if (DEBUG_WRITE)
821: System.out.println("deltaValueNumber: " + temp);
822: boolean combined = (temp & 1) != 0;
823: temp >>= 1;
824: values[i] = valuesList[currentValue += temp];
825: if (!combined) {
826: deltaTransition = sc.readUInt();
827: if (DEBUG_WRITE)
828: System.out.println("deltaTransition: "
829: + deltaTransition);
830: } else {
831: deltaTransition = 1;
832: }
833: transitions[i] = currentTransition += deltaTransition; // delta value
834: if (DEBUG_WRITE)
835: System.out.println("Trans: " + transitions[i] + ",\t"
836: + currentValue);
837: }
838: }
839:
840: /**
841: *
842: */
843: static int findCommon(String last, String s) {
844: int minLen = Math.min(last.length(), s.length());
845: for (int i = 0; i < minLen; ++i) {
846: if (last.charAt(i) != s.charAt(i))
847: return i;
848: }
849: return minLen;
850: }
851:
852: // /**
853: // * @param sc
854: // * @throws IOException
855: // *
856: // */
857: // private void showSize(String title, ObjectOutput out, StreamCompressor sc) throws IOException {
858: // sc.showSize(this, title, out);
859: // }
860: // //public void readObject(ObjectInputStream in) throws IOException {
861: // public static class StreamCompressor {
862: // transient byte[] buffer = new byte[1];
863: // transient StringBuffer stringBuffer = new StringBuffer();
864: //
865: // transient byte[] readWriteBuffer = new byte[8];
866: // int position = 0;
867: // DataOutput out;
868: // DataInput in;
869: //
870: // /**
871: // * Format is:
872: // * @throws IOException
873: // */
874: // public void writeInt(int i) throws IOException {
875: // while (true) {
876: // if (position == readWriteBuffer.length) {
877: // out.write(readWriteBuffer);
878: // position = 0;
879: // }
880: // if ((i & ~0x7F) == 0) {
881: // readWriteBuffer[position++] = (byte)i;
882: // break;
883: // }
884: // readWriteBuffer[position++] = (byte)(0x80 | i);
885: // i >>>= 7;
886: // }
887: // }
888: // /**
889: // * @throws IOException
890: // *
891: // */
892: // public int readNInt(ObjectInput in) throws IOException {
893: // int result = readInt(in);
894: // boolean negative = (result & 1) != 0;
895: // result >>>= 1;
896: // if (negative) result = ~result;
897: // return result;
898: // }
899: // /**
900: // * @throws IOException
901: // *
902: // */
903: // public void writeNInt(int input) throws IOException {
904: // int flag = 0;
905: // if (input < 0) {
906: // input = ~input;
907: // flag = 1;
908: // }
909: // input = (input << 1) | flag;
910: // writeInt(out, input);
911: // }
912: // /**
913: // * @throws IOException
914: // *
915: // */
916: // public void flush() throws IOException {
917: // out.write(readWriteBuffer);
918: // position = 0;
919: // }
920: //
921: // int readPosition = readWriteBuffer.length;
922: //
923: // public int readInt(ObjectInput in) throws IOException {
924: // int result = 0;
925: // int offset = 0;
926: // while (true) {
927: // if (readPosition == readWriteBuffer.length) {
928: // in.read(readWriteBuffer);
929: // readPosition = 0;
930: // }
931: // //in.read(buffer);
932: // int input = readWriteBuffer[readPosition++]; // buffer[0];
933: // result |= (input & 0x7F) << offset;
934: // if ((input & 0x80) == 0) {
935: // return result;
936: // }
937: // offset += 7;
938: // }
939: // }
940: //
941: // /**
942: // * @throws IOException
943: // *
944: // */
945: // public void writeString(String s) throws IOException {
946: // writeInt(UTF16.countCodePoint(s));
947: // writeCodePoints(s);
948: // }
949: // /**
950: // *
951: // */
952: // private void writeCodePoints(String s) throws IOException {
953: // int cp = 0;
954: // for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {
955: // cp = UTF16.charAt(s, i);
956: // writeInt(cp);
957: // }
958: // }
959: // /**
960: // * @throws IOException
961: // *
962: // */
963: // public String readString() throws IOException {
964: // int len = readInt(in);
965: // return readCodePoints(in, len);
966: // }
967: // /**
968: // *
969: // */
970: // private String readCodePoints(int len) throws IOException {
971: // stringBuffer.setLength(0);
972: // for (int i = 0; i < len; ++i) {
973: // int cp = readInt(in);
974: // UTF16.append(stringBuffer, cp);
975: // }
976: // return stringBuffer.toString();
977: // }
978: // /**
979: // * @param this
980: // * @throws IOException
981: // *
982: // */
983: // private void showSize(UnicodeMap map, String title, ObjectOutput out) throws IOException {
984: // out.flush();
985: // System.out.println(title + ": " + (map.debugOut.size() + position));
986: // }
987: // }
988: }
989: //#endif
|