You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by ge...@apache.org on 2005/12/01 07:04:00 UTC

svn commit: r350181 [135/198] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core: ./ depends/ depends/files/ depends/jars/ depends/libs/ depends/libs/linux.IA32/ depends/libs/win.IA32/ depends/oss/ depends/oss/linux.IA32/ depends/oss/win....

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/AttributedString.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/AttributedString.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/AttributedString.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/AttributedString.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,590 @@
+/* Copyright 1998, 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.ibm.oti.util.Msg;
+
+/**
+ * AttributedString
+ */
+public class AttributedString {
+
+	String text;
+
+	Map attributeMap;
+
+	static class Range {
+		int start, end;
+
+		Object value;
+
+		Range(int s, int e, Object v) {
+			start = s;
+			end = e;
+			value = v;
+		}
+	}
+
+	static class AttributedIterator implements AttributedCharacterIterator {
+
+		private int begin, end, offset;
+
+		private AttributedString attrString;
+
+		private HashSet attributesAllowed;
+
+		AttributedIterator(AttributedString attrString) {
+			this.attrString = attrString;
+			begin = 0;
+			end = attrString.text.length();
+			offset = 0;
+		}
+
+		AttributedIterator(AttributedString attrString,
+				AttributedCharacterIterator.Attribute[] attributes, int begin,
+				int end) {
+			if (begin < 0 || end > attrString.text.length() || begin > end)
+				throw new IllegalArgumentException();
+			this.begin = begin;
+			this.end = end;
+			offset = begin;
+			this.attrString = attrString;
+			if (attributes != null) {
+				HashSet set = new HashSet((attributes.length * 4 / 3) + 1);
+				for (int i = attributes.length; --i >= 0;)
+					set.add(attributes[i]);
+				attributesAllowed = set;
+			}
+		}
+
+		/**
+		 * Answers a new StringCharacterIterator with the same source String,
+		 * begin, end, and current index as this StringCharacterIterator.
+		 * 
+		 * @return a shallow copy of this StringCharacterIterator
+		 * 
+		 * @see java.lang.Cloneable
+		 */
+		public Object clone() {
+			try {
+				AttributedIterator clone = (AttributedIterator) super.clone();
+				if (attributesAllowed != null)
+					clone.attributesAllowed = (HashSet) attributesAllowed
+							.clone();
+				return clone;
+			} catch (CloneNotSupportedException e) {
+				return null;
+			}
+		}
+
+		/**
+		 * Answers the character at the current index in the source String.
+		 * 
+		 * @return the current character, or DONE if the current index is past
+		 *         the end
+		 */
+		public char current() {
+			if (offset == end)
+				return DONE;
+			return attrString.text.charAt(offset);
+		}
+
+		/**
+		 * Sets the current position to the begin index and answers the
+		 * character at the begin index.
+		 * 
+		 * @return the character at the begin index
+		 */
+		public char first() {
+			if (begin == end)
+				return DONE;
+			offset = begin;
+			return attrString.text.charAt(offset);
+		}
+
+		/**
+		 * Answers the begin index in the source String.
+		 * 
+		 * @return the index of the first character to iterate
+		 */
+		public int getBeginIndex() {
+			return begin;
+		}
+
+		/**
+		 * Answers the end index in the source String.
+		 * 
+		 * @return the index one past the last character to iterate
+		 */
+		public int getEndIndex() {
+			return end;
+		}
+
+		/**
+		 * Answers the current index in the source String.
+		 * 
+		 * @return the current index
+		 */
+		public int getIndex() {
+			return offset;
+		}
+
+		private boolean inRange(Range range) {
+			if (!(range.value instanceof Annotation))
+				return true;
+			return range.start >= begin && range.start < end
+					&& range.end > begin && range.end <= end;
+		}
+
+		private boolean inRange(ArrayList ranges) {
+			Iterator it = ranges.iterator();
+			while (it.hasNext()) {
+				Range range = (Range) it.next();
+				if (range.start >= begin && range.start < end) {
+					return !(range.value instanceof Annotation)
+							|| (range.end > begin && range.end <= end);
+				} else if (range.end > begin && range.end <= end) {
+					return !(range.value instanceof Annotation)
+							|| (range.start >= begin && range.start < end);
+				}
+			}
+			return false;
+		}
+
+		public Set getAllAttributeKeys() {
+			if (begin == 0 && end == attrString.text.length()
+					&& attributesAllowed == null)
+				return attrString.attributeMap.keySet();
+
+			HashSet result = new HashSet(
+					(attrString.attributeMap.size() * 4 / 3) + 1);
+			Iterator it = attrString.attributeMap.entrySet().iterator();
+			while (it.hasNext()) {
+				Map.Entry entry = (Map.Entry) it.next();
+				if (attributesAllowed == null
+						|| attributesAllowed.contains(entry.getKey())) {
+					ArrayList ranges = (ArrayList) entry.getValue();
+					if (inRange(ranges))
+						result.add(entry.getKey());
+				}
+			}
+			return result;
+		}
+
+		private Object currentValue(ArrayList ranges) {
+			Iterator it = ranges.iterator();
+			while (it.hasNext()) {
+				Range range = (Range) it.next();
+				if (offset >= range.start && offset < range.end)
+					return inRange(range) ? range.value : null;
+			}
+			return null;
+		}
+
+		public Object getAttribute(
+				AttributedCharacterIterator.Attribute attribute) {
+			if (attributesAllowed != null
+					&& !attributesAllowed.contains(attribute))
+				return null;
+			ArrayList ranges = (ArrayList) attrString.attributeMap
+					.get(attribute);
+			if (ranges == null)
+				return null;
+			return currentValue(ranges);
+		}
+
+		public Map getAttributes() {
+			HashMap result = new HashMap(
+					(attrString.attributeMap.size() * 4 / 3) + 1);
+			Iterator it = attrString.attributeMap.entrySet().iterator();
+			while (it.hasNext()) {
+				Map.Entry entry = (Map.Entry) it.next();
+				if (attributesAllowed == null
+						|| attributesAllowed.contains(entry.getKey())) {
+					Object value = currentValue((ArrayList) entry.getValue());
+					if (value != null)
+						result.put(entry.getKey(), value);
+				}
+			}
+			return result;
+		}
+
+		public int getRunLimit() {
+			return getRunLimit(getAllAttributeKeys());
+		}
+
+		private int runLimit(ArrayList ranges) {
+			int result = end;
+			ListIterator it = ranges.listIterator(ranges.size());
+			while (it.hasPrevious()) {
+				Range range = (Range) it.previous();
+				if (range.end <= begin)
+					break;
+				if (offset >= range.start && offset < range.end) {
+					return inRange(range) ? range.end : result;
+				} else if (offset >= range.end)
+					break;
+				result = range.start;
+			}
+			return result;
+		}
+
+		public int getRunLimit(AttributedCharacterIterator.Attribute attribute) {
+			if (attributesAllowed != null
+					&& !attributesAllowed.contains(attribute))
+				return end;
+			ArrayList ranges = (ArrayList) attrString.attributeMap
+					.get(attribute);
+			if (ranges == null)
+				return end;
+			return runLimit(ranges);
+		}
+
+		public int getRunLimit(Set attributes) {
+			int limit = end;
+			Iterator it = attributes.iterator();
+			while (it.hasNext()) {
+				AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it
+						.next();
+				int newLimit = getRunLimit(attribute);
+				if (newLimit < limit)
+					limit = newLimit;
+			}
+			return limit;
+		}
+
+		public int getRunStart() {
+			return getRunStart(getAllAttributeKeys());
+		}
+
+		private int runStart(ArrayList ranges) {
+			int result = begin;
+			Iterator it = ranges.iterator();
+			while (it.hasNext()) {
+				Range range = (Range) it.next();
+				if (range.start >= end)
+					break;
+				if (offset >= range.start && offset < range.end) {
+					return inRange(range) ? range.start : result;
+				} else if (offset < range.start)
+					break;
+				result = range.end;
+			}
+			return result;
+		}
+
+		public int getRunStart(AttributedCharacterIterator.Attribute attribute) {
+			if (attributesAllowed != null
+					&& !attributesAllowed.contains(attribute))
+				return begin;
+			ArrayList ranges = (ArrayList) attrString.attributeMap
+					.get(attribute);
+			if (ranges == null)
+				return begin;
+			return runStart(ranges);
+		}
+
+		public int getRunStart(Set attributes) {
+			int start = begin;
+			Iterator it = attributes.iterator();
+			while (it.hasNext()) {
+				AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it
+						.next();
+				int newStart = getRunStart(attribute);
+				if (newStart > start)
+					start = newStart;
+			}
+			return start;
+		}
+
+		/**
+		 * Sets the current position to the end index - 1 and answers the
+		 * character at the current position.
+		 * 
+		 * @return the character before the end index
+		 */
+		public char last() {
+			if (begin == end)
+				return DONE;
+			offset = end - 1;
+			return attrString.text.charAt(offset);
+		}
+
+		/**
+		 * Increments the current index and returns the character at the new
+		 * index.
+		 * 
+		 * @return the character at the next index, or DONE if the next index is
+		 *         past the end
+		 */
+		public char next() {
+			if (offset >= (end - 1)) {
+				offset = end;
+				return DONE;
+			}
+			return attrString.text.charAt(++offset);
+		}
+
+		/**
+		 * Decrements the current index and returns the character at the new
+		 * index.
+		 * 
+		 * @return the character at the previous index, or DONE if the previous
+		 *         index is past the beginning
+		 */
+		public char previous() {
+			if (offset == begin)
+				return DONE;
+			return attrString.text.charAt(--offset);
+		}
+
+		/**
+		 * Sets the current index in the source String.
+		 * 
+		 * @return the character at the new index, or DONE if the index is past
+		 *         the end
+		 * 
+		 * @exception IllegalArgumentException
+		 *                when the new index is less than the begin index or
+		 *                greater than the end index
+		 */
+		public char setIndex(int location) {
+			if (location < begin || location > end)
+				throw new IllegalArgumentException();
+			offset = location;
+			if (offset == end)
+				return DONE;
+			return attrString.text.charAt(offset);
+		}
+	}
+
+	public AttributedString(AttributedCharacterIterator iterator) {
+		StringBuffer buffer = new StringBuffer();
+		while (iterator.current() != CharacterIterator.DONE) {
+			buffer.append(iterator.current());
+			iterator.next();
+		}
+		text = buffer.toString();
+		Set attributes = iterator.getAllAttributeKeys();
+		attributeMap = new HashMap((attributes.size() * 4 / 3) + 1);
+
+		Iterator it = attributes.iterator();
+		while (it.hasNext()) {
+			AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it
+					.next();
+			iterator.setIndex(0);
+			while (iterator.current() != CharacterIterator.DONE) {
+				int start = iterator.getRunStart(attribute);
+				int limit = iterator.getRunLimit(attribute);
+				Object value = iterator.getAttribute(attribute);
+				if (value != null)
+					addAttribute(attribute, value, start, limit);
+				iterator.setIndex(limit);
+			}
+		}
+	}
+
+	private AttributedString(AttributedCharacterIterator iterator, int start,
+			int end, Set attributes) {
+		if (start < iterator.getBeginIndex() || end > iterator.getEndIndex()
+				|| start > end)
+			throw new IllegalArgumentException();
+
+		StringBuffer buffer = new StringBuffer();
+		iterator.setIndex(start);
+		while (iterator.getIndex() < end) {
+			buffer.append(iterator.current());
+			iterator.next();
+		}
+		text = buffer.toString();
+		attributeMap = new HashMap((attributes.size() * 4 / 3) + 1);
+
+		Iterator it = attributes.iterator();
+		while (it.hasNext()) {
+			AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it
+					.next();
+			iterator.setIndex(start);
+			while (iterator.getIndex() < end) {
+				Object value = iterator.getAttribute(attribute);
+				int runStart = iterator.getRunStart(attribute);
+				int limit = iterator.getRunLimit(attribute);
+				if ((value instanceof Annotation && runStart >= start && limit <= end)
+						|| (value != null && !(value instanceof Annotation))) {
+					addAttribute(attribute, value, (runStart < start ? start
+							: runStart)
+							- start, (limit > end ? end : limit) - start);
+				}
+				iterator.setIndex(limit);
+			}
+		}
+	}
+
+	public AttributedString(AttributedCharacterIterator iterator, int start,
+			int end) {
+		this(iterator, start, end, iterator.getAllAttributeKeys());
+	}
+
+	public AttributedString(AttributedCharacterIterator iterator, int start,
+			int end, AttributedCharacterIterator.Attribute[] attributes) {
+		this(iterator, start, end, new HashSet(Arrays.asList(attributes)));
+	}
+
+	public AttributedString(String value) {
+		if (value == null)
+			throw new NullPointerException();
+		text = value;
+		attributeMap = new HashMap(11);
+	}
+
+	public AttributedString(String value, Map attributes) {
+		if (value == null)
+			throw new NullPointerException();
+		if (value.length() == 0 && !attributes.isEmpty())
+			throw new IllegalArgumentException(Msg.getString("K000e"));
+		text = value;
+		attributeMap = new HashMap((attributes.size() * 4 / 3) + 1);
+		Iterator it = attributes.entrySet().iterator();
+		while (it.hasNext()) {
+			Map.Entry entry = (Map.Entry) it.next();
+			ArrayList ranges = new ArrayList(1);
+			ranges.add(new Range(0, text.length(), entry.getValue()));
+			attributeMap.put(entry.getKey(), ranges);
+		}
+	}
+
+	public void addAttribute(AttributedCharacterIterator.Attribute attribute,
+			Object value) {
+		if (text.length() == 0)
+			throw new IllegalArgumentException();
+
+		ArrayList ranges = (ArrayList) attributeMap.get(attribute);
+		if (ranges == null) {
+			ranges = new ArrayList(1);
+			attributeMap.put(attribute, ranges);
+		} else {
+			ranges.clear();
+		}
+		ranges.add(new Range(0, text.length(), value));
+	}
+
+	public void addAttribute(AttributedCharacterIterator.Attribute attribute,
+			Object value, int start, int end) {
+		if (start < 0 || end > text.length() || start >= end)
+			throw new IllegalArgumentException();
+
+		ArrayList ranges = (ArrayList) attributeMap.get(attribute);
+		if (ranges == null) {
+			ranges = new ArrayList(1);
+			ranges.add(new Range(start, end, value));
+			attributeMap.put(attribute, ranges);
+			return;
+		}
+		ListIterator it = ranges.listIterator();
+		while (it.hasNext()) {
+			Range range = (Range) it.next();
+			if (end <= range.start) {
+				it.previous();
+				break;
+			} else if (start < range.end
+					|| (start == range.end && (value == null ? range.value == null
+							: value.equals(range.value)))) {
+				Range r1 = null, r3;
+				it.remove();
+				r1 = new Range(range.start, start, range.value);
+				r3 = new Range(end, range.end, range.value);
+
+				while (end > range.end && it.hasNext()) {
+					range = (Range) it.next();
+					if (end <= range.end) {
+						if (end > range.start
+								|| (end == range.start && (value == null ? range.value == null
+										: value.equals(range.value)))) {
+							it.remove();
+							r3 = new Range(end, range.end, range.value);
+							break;
+						}
+					} else
+						it.remove();
+				}
+
+				if (value == null ? r1.value == null : value.equals(r1.value)) {
+					if (value == null ? r3.value == null : value
+							.equals(r3.value)) {
+						it.add(new Range(r1.start < start ? r1.start : start,
+								r3.end > end ? r3.end : end, r1.value));
+					} else {
+						it.add(new Range(r1.start < start ? r1.start : start,
+								end, r1.value));
+						if (r3.start < r3.end)
+							it.add(r3);
+					}
+				} else {
+					if (value == null ? r3.value == null : value
+							.equals(r3.value)) {
+						if (r1.start < r1.end)
+							it.add(r1);
+						it.add(new Range(start, r3.end > end ? r3.end : end,
+								r3.value));
+					} else {
+						if (r1.start < r1.end)
+							it.add(r1);
+						it.add(new Range(start, end, value));
+						if (r3.start < r3.end)
+							it.add(r3);
+					}
+				}
+				return;
+			}
+		}
+		it.add(new Range(start, end, value));
+	}
+
+	public void addAttributes(Map attributes, int start, int end) {
+		Iterator it = attributes.entrySet().iterator();
+		while (it.hasNext()) {
+			Map.Entry entry = (Map.Entry) it.next();
+			addAttribute(
+					(AttributedCharacterIterator.Attribute) entry.getKey(),
+					entry.getValue(), start, end);
+		}
+	}
+
+	public AttributedCharacterIterator getIterator() {
+		return new AttributedIterator(this);
+	}
+
+	public AttributedCharacterIterator getIterator(
+			AttributedCharacterIterator.Attribute[] attributes) {
+		return new AttributedIterator(this, attributes, 0, text.length());
+	}
+
+	public AttributedCharacterIterator getIterator(
+			AttributedCharacterIterator.Attribute[] attributes, int start,
+			int end) {
+		return new AttributedIterator(this, attributes, start, end);
+	}
+
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Bidi.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Bidi.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Bidi.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Bidi.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,463 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+import java.util.Arrays;
+import java.util.LinkedList;
+
+import com.ibm.text.BidiRun;
+import com.ibm.text.BidiWrapper;
+
+/**
+ * Bidi is the class providing the bidirectional algorithm. The algorithm is
+ * defined in the Unicode Standard Annex #9, version 13, also described in The
+ * Unicode Standard, Version 4.0 .
+ * 
+ * Use a Bidi object to get the infomation on the position reordering of a
+ * bidirectional text, such as Arabic or Hebrew. The natural display ordering of
+ * horizontal text in these languages is from right to left, while they order
+ * numbers from left to right.
+ * 
+ * If the text contains multiple runs, the information of each run can be
+ * obtained from the run index. The level of any particular run indicates the
+ * direction of the text as well as the nesting level. Left-to-right runs have
+ * even levels while right-to-left runs have odd levels.
+ * 
+ */
+public final class Bidi {
+	/**
+	 * Constant that indicates the default base level. If there is no strong
+	 * character, then set the paragraph level to 0 (left-to-right).
+	 */
+	public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2;
+
+	/**
+	 * Constant that indicates the default base level. If there is no strong
+	 * character, then set the paragraph level to 1 (right-to-left).
+	 */
+	public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1;
+
+	/**
+	 * Constant that specifies the default base level as 0 (left-to-right).
+	 */
+	public static final int DIRECTION_LEFT_TO_RIGHT = 0;
+
+	/**
+	 * Constant that specifies the default base level as 1 (right-to-left).
+	 */
+	public static final int DIRECTION_RIGHT_TO_LEFT = 1;
+
+	/**
+	 * Create a Bidi object from the AttributedCharacterIterator of a paragraph
+	 * text.
+	 * 
+	 * The RUN_DIRECTION attribute determines the base direction of the
+	 * bidirectional text. If it's not specified explicitly, the algorithm uses
+	 * DIRECTION_DEFAULT_LEFT_TO_RIGHT by default.
+	 * 
+	 * The BIDI_EMBEDDING attribute specifies the level of embedding for each
+	 * character. Values between -1 and -62 denote overrides at the level's
+	 * absolute value, values from 1 to 62 indicate embeddings, and the
+	 * 0 value indicates the level is calculated by the algorithm automatically.
+	 * For the character with no BIDI_EMBEDDING attribute or with a improper
+	 * attribute value, such as a null value, the algorithm treats its embedding
+	 * level as 0.
+	 * 
+	 * The NUMERIC_SHAPING attribute specifies the instance of NumericShaper
+	 * used to convert European digits to other decimal digits before performing
+	 * the bidi algorithm.
+	 * 
+	 * @param paragraph
+	 * 
+	 * @see TextAttribute.BIDI_EMBEDDING
+	 * @see TextAttribute.NUMERIC_SHAPING
+	 * @see TextAttribute.RUN_DIRECTION
+	 */
+	public Bidi(AttributedCharacterIterator paragraph) {
+		/*
+		 * TODO: dependency on java.awt.font.TextAttribute and
+		 * java.awt.font.NumericShaper which is not implemented yet.
+		 */
+	}
+
+	/**
+	 * Create a Bidi object.
+	 * 
+	 * @param text
+	 *            the char array of the paragraph text.
+	 * @param textStart
+	 *            the start offset of the text array to perform the algorithm.
+	 * @param embeddings
+	 *            the embedding level array of the paragraph text, specifying
+	 *            the embedding level infomation for each character. Values
+	 *            between -1 and -62 denote overrides at the level's absolute
+	 *            value, values from 1 to 62 indicate embeddings, and the 0
+	 *            value indicates the level is calculated by the algorithm
+	 *            automatically.
+	 * @param embStart
+	 *            the start offset of the embeddings array to perform the
+	 *            algorithm.
+	 * @param paragraphLength
+	 *            the length of the text to perform the algorithm. It must be
+	 *            text.length >= textStart + paragraphLength, and
+	 *            embeddings.length >= embStart + paragraphLength.
+	 * @param flags
+	 *            indicates the base direction of the bidirectional text. It is
+	 *            expected that this will be one of the direction constant
+	 *            values defined in this class. An unknown value is treated as
+	 *            DIRECTION_DEFAULT_LEFT_TO_RIGHT.
+	 * 
+	 * @see #DIRECTION_LEFT_TO_RIGHT
+	 * @see #DIRECTION_RIGHT_TO_LEFT
+	 * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
+	 * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
+	 */
+	public Bidi(char[] text, int textStart, byte[] embeddings, int embStart,
+			int paragraphLength, int flags) {
+		long pBidi = createUBiDi(text, textStart, embeddings, embStart,
+				paragraphLength, flags);
+		readBidiInfo(pBidi);
+		BidiWrapper.ubidi_close(pBidi);
+	}
+
+	/**
+	 * Create a Bidi object.
+	 * 
+	 * @param paragraph
+	 *            the String containing the paragraph text to perform the
+	 *            algorithm.
+	 * @param flags
+	 *            indicates the base direction of the bidirectional text. It is
+	 *            expected that this will be one of the direction constant
+	 *            values defined in this class. An unknown value is treated as
+	 *            DIRECTION_DEFAULT_LEFT_TO_RIGHT.
+	 * 
+	 * @see #DIRECTION_LEFT_TO_RIGHT
+	 * @see #DIRECTION_RIGHT_TO_LEFT
+	 * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT
+	 * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT
+	 */
+	public Bidi(String paragraph, int flags) {
+		this((paragraph == null ? null : paragraph.toCharArray()), 0, null, 0,
+				(paragraph == null ? 0 : paragraph.length()), flags);
+	}
+
+	// create the native UBiDi struct, need to be closed with ubidi_close().
+	private static long createUBiDi(char[] text, int textStart,
+			byte[] embeddings, int embStart, int paragraphLength, int flags) {
+		char[] realText = null;
+
+		byte[] realEmbeddings = null;
+
+		if (text == null || text.length < textStart + paragraphLength) {
+			throw new IllegalArgumentException();
+		}
+		realText = new char[paragraphLength];
+		System.arraycopy(text, textStart, realText, 0, paragraphLength);
+
+		if (embeddings != null) {
+			if (embeddings.length < embStart + paragraphLength) {
+				throw new IllegalArgumentException();
+			}
+			Bidi temp = new Bidi(text, textStart, null, 0, paragraphLength,
+					flags);
+			realEmbeddings = new byte[paragraphLength];
+			System.arraycopy(temp.offsetLevel, 0, realEmbeddings, 0,
+					paragraphLength);
+			for (int i = 0; i < paragraphLength; i++) {
+				byte e = embeddings[i];
+				if (e < 0) {
+					realEmbeddings[i] = (byte) (BidiWrapper.UBIDI_LEVEL_OVERRIDE - e);
+				} else if (e > 0) {
+					realEmbeddings[i] = e;
+				} else {
+					realEmbeddings[i] |= (byte) BidiWrapper.UBIDI_LEVEL_OVERRIDE;
+				}
+			}
+		}
+
+		if (flags > 1 || flags < -2) {
+			flags = 0;
+		}
+
+		long bidi = BidiWrapper.ubidi_open();
+		BidiWrapper.ubidi_setPara(bidi, realText, paragraphLength,
+				(byte) flags, realEmbeddings);
+		return bidi;
+	}
+
+	// private constructor, used by createLineBidi()
+	private Bidi(long pBidi) {
+		readBidiInfo(pBidi);
+	}
+
+	// read info from the native UBiDi struct
+	private void readBidiInfo(long pBidi) {
+
+		length = BidiWrapper.ubidi_getLength(pBidi);
+
+		offsetLevel = (length == 0) ? null : BidiWrapper.ubidi_getLevels(pBidi);
+
+		baseLevel = BidiWrapper.ubidi_getParaLevel(pBidi);
+
+		int runCount = BidiWrapper.ubidi_countRuns(pBidi);
+		if (runCount == 0) {
+			runCount = 1;
+			runs = new BidiRun[runCount];
+			runs[0] = new BidiRun(0, 0, baseLevel);
+		} else if (runCount < 0) {
+			runCount = 0;
+			runs = null;
+		} else {
+			runs = new BidiRun[runCount];
+			for (int i = 0; i < runs.length; i++) {
+				runs[i] = BidiWrapper.ubidi_getRun(pBidi, i);
+			}
+		}
+
+		direction = BidiWrapper.ubidi_getDirection(pBidi);
+	}
+
+	private int baseLevel;
+
+	private int length;
+
+	private byte[] offsetLevel;
+
+	private BidiRun[] runs;
+
+	private int direction;
+
+	/**
+	 * Return whether the base level is from left to right.
+	 * 
+	 * @return true if the base level is from left to right.
+	 */
+	public boolean baseIsLeftToRight() {
+		return baseLevel % 2 == 0 ? true : false;
+	}
+
+	/**
+	 * Create a new Bidi object containing the infomation of one line from this
+	 * object.
+	 * 
+	 * @param lineStart
+	 *            the start offset of the line.
+	 * @param lineLimit
+	 *            the limit of the line.
+	 * @return the new line Bidi object. In this new object, the indices will
+	 *         range from 0 to (limit - start - 1).
+	 */
+	public Bidi createLineBidi(int lineStart, int lineLimit) {
+		char[] text = new char[this.length];
+		Arrays.fill(text, 'a');
+		byte[] embeddings = new byte[this.length];
+		for (int i = 0; i < embeddings.length; i++) {
+			embeddings[i] = (byte) -this.offsetLevel[i];
+		}
+
+		int dir = this.baseIsLeftToRight() ? Bidi.DIRECTION_LEFT_TO_RIGHT
+				: Bidi.DIRECTION_RIGHT_TO_LEFT;
+
+		long parent = createUBiDi(text, 0, embeddings, 0, this.length, dir);
+
+		long line = BidiWrapper.ubidi_setLine(parent, lineStart, lineLimit);
+		Bidi result = new Bidi(line);
+		BidiWrapper.ubidi_close(line);
+		BidiWrapper.ubidi_close(parent);
+		return result;
+	}
+
+	/**
+	 * Return the base level.
+	 * 
+	 * @return the int value of the base level.
+	 */
+	public int getBaseLevel() {
+		return baseLevel;
+	}
+
+	/**
+	 * Return the length of the text in the Bidi object.
+	 * 
+	 * @return the int value of the length.
+	 */
+	public int getLength() {
+		return length;
+	}
+
+	/**
+	 * Return the level of a specified character.
+	 * 
+	 * @param offset
+	 *            the offset of the character.
+	 * @return the int value of the evel.
+	 */
+	public int getLevelAt(int offset) {
+		try {
+			return offsetLevel[offset] & ~BidiWrapper.UBIDI_LEVEL_OVERRIDE;
+		} catch (RuntimeException e) {
+			return baseLevel;
+		}
+	}
+
+	/**
+	 * Return the number of runs in the bidirectional text.
+	 * 
+	 * @return the int value of runs, at least 1.
+	 */
+	public int getRunCount() {
+		return runs.length;
+	}
+
+	/**
+	 * Return the level of a specified run.
+	 * 
+	 * @param run
+	 *            the index of the run.
+	 * @return the level of the run.
+	 */
+	public int getRunLevel(int run) {
+		return runs[run].getLevel();
+	}
+
+	/**
+	 * Return the limit offset of a specified run.
+	 * 
+	 * @param run
+	 *            the index of the run.
+	 * @return the limit offset of the run.
+	 */
+	public int getRunLimit(int run) {
+		return runs[run].getLimit();
+	}
+
+	/**
+	 * Return the start offset of a specified run.
+	 * 
+	 * @param run
+	 *            the index of the run.
+	 * @return the start offset of the run.
+	 */
+	public int getRunStart(int run) {
+		return runs[run].getStart();
+	}
+
+	/**
+	 * Return whether the text is from left to right, that is, both the base
+	 * direction and the text direction is from left to right.
+	 * 
+	 * @return true if the text is from left to right.
+	 */
+	public boolean isLeftToRight() {
+		return direction == BidiWrapper.UBiDiDirection_UBIDI_LTR;
+	}
+
+	/**
+	 * Return whether the text direction is mixed.
+	 * 
+	 * @return true if the text direction is mixed.
+	 */
+	public boolean isMixed() {
+		return direction == BidiWrapper.UBiDiDirection_UBIDI_MIXED;
+	}
+
+	/**
+	 * Return whether the text is from right to left, that is, both the base
+	 * direction and the text direction is from right to left.
+	 * 
+	 * @return true if the text is from right to left.
+	 */
+	public boolean isRightToLeft() {
+		return direction == BidiWrapper.UBiDiDirection_UBIDI_RTL;
+	}
+
+	/**
+	 * Reorder a range of objects according to their spefied levels. This is a
+	 * convenience function that does not use a Bidi object. The range of
+	 * objects at index from objectStart to objectStart + count will be
+	 * reordered according to the range of levels at index from levelStart to
+	 * levelStart + count.
+	 * 
+	 * @param levels
+	 *            the level array, which is already determined.
+	 * @param levelStart
+	 *            the start offset of the range of the levels.
+	 * @param objects
+	 *            the object array to reoeder.
+	 * @param objectStart
+	 *            the start offset of the range of objects.
+	 * @param count
+	 *            the count of the range of objects to reorder.
+	 */
+	public static void reorderVisually(byte[] levels, int levelStart,
+			Object[] objects, int objectStart, int count) {
+		try {
+			byte[] realLevels = new byte[count];
+			System.arraycopy(levels, levelStart, realLevels, 0, count);
+
+			int[] indices = BidiWrapper.ubidi_reorderVisual(realLevels, count);
+
+			LinkedList result = new LinkedList();
+			for (int i = 0; i < count; i++) {
+				result.addLast(objects[objectStart + indices[i]]);
+			}
+
+			System.arraycopy(result.toArray(), 0, objects, objectStart, count);
+		} catch (ArrayIndexOutOfBoundsException e) {
+			throw new IllegalArgumentException();
+		}
+	}
+
+	/**
+	 * Return whether a range of characters of a text requires a Bidi object to
+	 * display properly.
+	 * 
+	 * @param text
+	 *            the char array of the text.
+	 * @param start
+	 *            the start offset of the range of characters.
+	 * @param limit
+	 *            the limit offset of the range of characters.
+	 * @return true if the range of characters requires a Bidi object.
+	 */
+	public static boolean requiresBidi(char[] text, int start, int limit) {
+		if (limit < 0 || start >= limit) {
+			return false;
+		} else if (start < 0 || start > text.length || limit > text.length) {
+			throw new ArrayIndexOutOfBoundsException();
+		}
+
+		Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0);
+
+		if (bidi.isLeftToRight()) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Return the internal message of the Bidi object, used in debugging.
+	 * 
+	 * @return a string containing the internal messsage.
+	 */
+	public String toString() {
+		// simply return nothing
+		return "";
+	}
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/BreakIterator.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/BreakIterator.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/BreakIterator.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/BreakIterator.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,365 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+import java.util.Locale;
+
+/**
+ * This class is used to locate the boundaries of text. Instance of this class
+ * can be got by some factory methods:
+ * <ul>
+ * <li>
+ * <code>getCharacterIntance()<code> returns a BreakIterator that iterate the 
+ * logical characters without worrying about how the character is stored. For 
+ * example, some character may be stored in more than one Unicode code point 
+ * according to Unicode specification, this character can handle the logical 
+ * characters with multi code points.</li>
+ * <li>
+ * <code>getWordIntance()<code> returns a <code>BreakIterator</code> that 
+ * iterate the word-breaks. The beginning and end of each word(including numbers) 
+ * is treated as boundary position. Whitespace and punctuation are kept separate 
+ * from real words.</li>
+ * <li>
+ * <code>getSentenceInstance()</code> returns a BreakIterator that iterate the 
+ * sentence-breaks.</li>
+ * <li><code>getLineInstance()</code> retuens a BreakIterator that iterate the 
+ * line-breaks which can be used to wrap lines. This iterator can handle whitespaces, 
+ * hyphens and punctuations.  
+ * </ul>
+ * 
+ * <code>BreakIterator</code> uses <code>CharacterIterator</code> to perform the 
+ * analysis, so that any storage which provides <code>CharacterIterator</code> 
+ * interface.
+ * 
+ * @see CharacterIterator
+ */
+public abstract class BreakIterator implements Cloneable {
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * constantants
+	 * -----------------------------------------------------------------------
+	 */
+	/**
+	 * This constant is returned by iterate methods like previous() or next() if
+	 * they have returned all valid boundaries.
+	 */
+	public static final int DONE = -1;
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * variables
+	 * -----------------------------------------------------------------------
+	 */
+	// the wrapped icu implementation
+	com.ibm.icu.text.BreakIterator wrapped;
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * constructors
+	 * -----------------------------------------------------------------------
+	 */
+	/**
+	 * Default constructor, just for invocation by subclass.
+	 */
+	protected BreakIterator() {
+		super();
+	}
+
+	/*
+	 * wrapping constructor
+	 */
+	BreakIterator(com.ibm.icu.text.BreakIterator iterator) {
+		wrapped = iterator;
+	}
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * methods
+	 * -----------------------------------------------------------------------
+	 */
+	/**
+	 * Return all supported locales.
+	 * 
+	 * @return all supported locales
+	 */
+	public static Locale[] getAvailableLocales() {
+		return com.ibm.icu.text.BreakIterator.getAvailableLocales();
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate characters using
+	 * default locale.
+	 * 
+	 * @return a new instance of BreakIterator used to iterate characters using
+	 *         default locale.
+	 */
+	public static BreakIterator getCharacterInstance() {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getCharacterInstance());
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate characters using
+	 * given locale.
+	 * 
+	 * @param where
+	 *            the given locale
+	 * @return a new instance of BreakIterator used to iterate characters using
+	 *         given locale.
+	 */
+	public static BreakIterator getCharacterInstance(Locale where) {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getCharacterInstance(where));
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate line-breaks using
+	 * default locale.
+	 * 
+	 * @return a new instance of BreakIterator used to iterate line-breaks using
+	 *         default locale.
+	 */
+	public static BreakIterator getLineInstance() {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getLineInstance());
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate line-breaks using
+	 * given locale.
+	 * 
+	 * @param where
+	 *            the given locale
+	 * @return a new instance of BreakIterator used to iterate line-breaks using
+	 *         given locale.
+	 */
+	public static BreakIterator getLineInstance(Locale where) {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getLineInstance(where));
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate sentense-breaks
+	 * using default locale.
+	 * 
+	 * @return a new instance of BreakIterator used to iterate sentense-breaks
+	 *         using default locale.
+	 */
+	public static BreakIterator getSentenceInstance() {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getSentenceInstance());
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate sentense-breaks
+	 * using given locale.
+	 * 
+	 * @param where
+	 *            the given locale
+	 * @return a new instance of BreakIterator used to iterate sentense-breaks
+	 *         using given locale.
+	 */
+	public static BreakIterator getSentenceInstance(Locale where) {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getSentenceInstance(where));
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate word-breaks using
+	 * default locale.
+	 * 
+	 * @return a new instance of BreakIterator used to iterate word-breaks using
+	 *         default locale.
+	 */
+	public static BreakIterator getWordInstance() {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getWordInstance());
+	}
+
+	/**
+	 * Return a new instance of BreakIterator used to iterate word-breaks using
+	 * given locale.
+	 * 
+	 * @param where
+	 *            the given locale
+	 * @return a new instance of BreakIterator used to iterate word-breaks using
+	 *         given locale.
+	 */
+	public static BreakIterator getWordInstance(Locale where) {
+		return new RuleBasedBreakIterator(com.ibm.icu.text.BreakIterator
+				.getSentenceInstance(where));
+	}
+
+	/**
+	 * Return true if the given offset is a boundary position. If this method
+	 * returns true, the current iteration position is set to the given
+	 * position; if the function returns false, the current iteration position
+	 * is set as though following() had been called.
+	 * 
+	 * @param offset
+	 *            the given offset to check
+	 * @return true if the given offset is a boudary postion
+	 */
+	public boolean isBoundary(int offset) {
+		return wrapped.isBoundary(offset);
+	}
+
+	/**
+	 * Return the postion of last boundary precede the given offset, and set
+	 * current postion to returned value, or <code>DONE</code> if the given
+	 * offset specifies the starting position.
+	 * <p>
+	 * <code>IllegalArgumentException</code> will be thrown if given offset is
+	 * invalid.
+	 * </p>
+	 * 
+	 * @param offset
+	 *            the given start position to be searched for
+	 * @return the postion of last boundary precede the given offset
+	 */
+	public int preceding(int offset) {
+		return wrapped.preceding(offset);
+	}
+
+	/**
+	 * Set the new text string to be analyzed, the current position will be
+	 * reset to beginning of this new string, and the old string will lost.
+	 * 
+	 * @param newText
+	 *            the new text string to be analyzed
+	 */
+	public void setText(String newText) {
+		wrapped.setText(newText);
+	}
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * abstract methods
+	 * -----------------------------------------------------------------------
+	 */
+	/**
+	 * Return this iterator's current position.
+	 * 
+	 * @return this iterator's current position
+	 */
+	public abstract int current();
+
+	/**
+	 * Set this iterator's current position to the first boundary, and return
+	 * this postion.
+	 * 
+	 * @return the position of first boundary
+	 */
+	public abstract int first();
+
+	/**
+	 * Set the position of the first boundary following the given offset, and
+	 * return this position. If there is no boundary after the given offset,
+	 * return DONE.
+	 * <p>
+	 * <code>IllegalArgumentException</code> will be thrown if given offset is
+	 * invalid.
+	 * </p>
+	 * 
+	 * @param offset
+	 *            the given position to be searched for
+	 * @return the position of the first boundary following the given offset
+	 */
+	public abstract int following(int offset);
+
+	/**
+	 * Return a <code>CharacterIterator</code> which represents the text being
+	 * analyzed. Please note that the returned value is probablly the internal
+	 * iterator used by this object, so that if the invoker want to modify the
+	 * status of the returned iterator, a clone operation at first is
+	 * recommended.
+	 * 
+	 * @return a <code>CharacterIterator</code> which represents the text
+	 *         being analyzed.
+	 */
+	public abstract CharacterIterator getText();
+
+	/**
+	 * Set this iterator's current position to the last boundary, and return
+	 * this postion.
+	 * 
+	 * @return the position of last boundary
+	 */
+	public abstract int last();
+
+	/**
+	 * Set this iterator's current position to the next boundary after current
+	 * position, and return this postion. Return <code>DONE</code> if no
+	 * boundary found after current position.
+	 * 
+	 * @return the position of last boundary
+	 */
+	public abstract int next();
+
+	/**
+	 * Set this iterator's current position to the next boundary after the given
+	 * position, and return this postion. Return <code>DONE</code> if no
+	 * boundary found after the given position.
+	 * 
+	 * @param n
+	 *            the given position.
+	 * @return the position of last boundary
+	 */
+	public abstract int next(int n);
+
+	/**
+	 * Set this iterator's current position to the previous boundary before
+	 * current position, and return this postion. Return <code>DONE</code> if
+	 * no boundary found before current position.
+	 * 
+	 * @return the position of last boundary
+	 */
+	public abstract int previous();
+
+	/**
+	 * Set new text to be analyzed by given <code>CharacterIterator</code>.
+	 * The position will be reset to the beginning of the new text, and other
+	 * status of this iterator will be kept.
+	 * 
+	 * @param newText
+	 *            the given <code>CharacterIterator</code> refer to the text
+	 *            to be analyzed
+	 */
+	public abstract void setText(CharacterIterator newText);
+
+	/*
+	 * -----------------------------------------------------------------------
+	 * methods override Object
+	 * -----------------------------------------------------------------------
+	 */
+	/**
+	 * Create copy of this iterator, all status including current position is
+	 * kept.
+	 * 
+	 * @return copy of this iterator
+	 */
+	public Object clone() {
+		try {
+			BreakIterator cloned = (BreakIterator) super.clone();
+			cloned.wrapped = (com.ibm.icu.text.BreakIterator) wrapped.clone();
+			return cloned;
+		} catch (CloneNotSupportedException e) {
+			throw new InternalError(e.getMessage());
+		}
+	}
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CharacterIterator.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CharacterIterator.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CharacterIterator.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CharacterIterator.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,112 @@
+/* Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+/**
+ * CharacterIterator is used to sequence over a group of characters. The
+ * iteration starts at the begin index in the group of character and continues
+ * to one index before the end index.
+ */
+public interface CharacterIterator extends Cloneable {
+
+	/**
+	 * A constant which indicates there is no character.
+	 */
+	public static char DONE = '\uffff';
+
+	/**
+	 * Answers a new CharacterIterator with the same properties.
+	 * 
+	 * @return a shallow copy of this CharacterIterator
+	 * 
+	 * @see java.lang.Cloneable
+	 */
+	public Object clone();
+
+	/**
+	 * Answers the character at the current index.
+	 * 
+	 * @return the current character, or DONE if the current index is past the
+	 *         end
+	 */
+	public char current();
+
+	/**
+	 * Sets the current position to the begin index and answers the character at
+	 * the begin index.
+	 * 
+	 * @return the character at the begin index
+	 */
+	public char first();
+
+	/**
+	 * Answers the begin index.
+	 * 
+	 * @return the index of the first character to iterate
+	 */
+	public int getBeginIndex();
+
+	/**
+	 * Answers the end index.
+	 * 
+	 * @return the index one past the last character to iterate
+	 */
+	public int getEndIndex();
+
+	/**
+	 * Answers the current index.
+	 * 
+	 * @return the current index
+	 */
+	public int getIndex();
+
+	/**
+	 * Sets the current position to the end index - 1 and answers the character
+	 * at the current position.
+	 * 
+	 * @return the character before the end index
+	 */
+	public char last();
+
+	/**
+	 * Increments the current index and returns the character at the new index.
+	 * 
+	 * @return the character at the next index, or DONE if the next index is
+	 *         past the end
+	 */
+	public char next();
+
+	/**
+	 * Decrements the current index and returns the character at the new index.
+	 * 
+	 * @return the character at the previous index, or DONE if the previous
+	 *         index is past the beginning
+	 */
+	public char previous();
+
+	/**
+	 * Sets the current index.
+	 * 
+	 * @return the character at the new index, or DONE if the index is past the
+	 *         end
+	 * 
+	 * @exception IllegalArgumentException
+	 *                when the new index is less than the begin index or greater
+	 *                than the end index
+	 */
+	public char setIndex(int location);
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/ChoiceFormat.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/ChoiceFormat.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/ChoiceFormat.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/ChoiceFormat.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,389 @@
+/* Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+import java.util.Arrays;
+import java.util.Vector;
+
+/**
+ * ChoiceFormat is used to assocate strings with ranges of double values. The
+ * strings and ranges are either specified using arrays or with a pattern which
+ * is parsed to determine the Strings and ranges.
+ */
+
+public class ChoiceFormat extends NumberFormat {
+
+	static final long serialVersionUID = 1795184449645032964L;
+
+	private double[] choiceLimits;
+
+	private String[] choiceFormats;
+
+	/**
+	 * Constructs a new ChoiceFormat with the specified ranges and associated
+	 * strings.
+	 * 
+	 * @param limits
+	 *            an array of double, the ranges are greater or equal to the
+	 *            value in lower index up to less than the value in the next
+	 *            higher index. The bounds of the lowest and highest indexes are
+	 *            negative and positive infinity.
+	 * @param formats
+	 *            the strings associated with the ranges. The lower bound of the
+	 *            associated range is at the same index as the string.
+	 */
+	public ChoiceFormat(double[] limits, String[] formats) {
+		setChoices(limits, formats);
+	}
+
+	/**
+	 * Constructs a new ChoiceFormat with the strings and ranges parsed from the
+	 * specified pattern.
+	 * 
+	 * @param template
+	 *            the pattern of strings and ranges
+	 * 
+	 * @exception IllegalArgumentException
+	 *                then an error occurs parsing the pattern
+	 */
+	public ChoiceFormat(String template) {
+		applyPattern(template);
+	}
+
+	/**
+	 * Parses the pattern to determine new strings and ranges for this
+	 * ChoiceFormat.
+	 * 
+	 * @param template
+	 *            the pattern of strings and ranges
+	 * 
+	 * @exception IllegalArgumentException
+	 *                then an error occurs parsing the pattern
+	 */
+	public void applyPattern(String template) {
+		boolean first = true;
+		double[] limits = new double[5];
+		Vector formats = new Vector();
+		int length = template.length(), limitCount = 0, index = 0;
+		StringBuffer buffer = new StringBuffer();
+		NumberFormat format = NumberFormat.getInstance();
+		ParsePosition position = new ParsePosition(0);
+		while (true) {
+			index = skipWhitespace(template, index);
+			if (index >= length) {
+				if (limitCount == limits.length)
+					choiceLimits = limits;
+				else {
+					choiceLimits = new double[limitCount];
+					System.arraycopy(limits, 0, choiceLimits, 0, limitCount);
+				}
+				choiceFormats = new String[formats.size()];
+				for (int i = 0; i < formats.size(); i++)
+					choiceFormats[i] = (String) formats.elementAt(i);
+				return;
+			}
+
+			position.setIndex(index);
+			Number value = format.parse(template, position);
+			index = skipWhitespace(template, position.getIndex());
+			if (position.getErrorIndex() != -1 || index >= length)
+				throw new IllegalArgumentException();
+			char ch = template.charAt(index++);
+			if (limitCount == limits.length) {
+				double[] newLimits = new double[limitCount * 2];
+				System.arraycopy(limits, 0, newLimits, 0, limitCount);
+				limits = newLimits;
+			}
+			double next;
+			switch (ch) {
+			case '#':
+			case '\u2264':
+				next = value.doubleValue();
+				break;
+			case '<':
+				if (first)
+					throw new IllegalArgumentException();
+				next = nextDouble(value.doubleValue());
+				break;
+			default:
+				throw new IllegalArgumentException();
+			}
+			first = false;
+			if (limitCount > 0 && next <= limits[limitCount - 1])
+				throw new IllegalArgumentException();
+			buffer.setLength(0);
+			position.setIndex(index);
+			upTo(template, position, buffer, '|');
+			index = position.getIndex();
+			limits[limitCount++] = next;
+			formats.addElement(buffer.toString());
+		}
+	}
+
+	/**
+	 * Answers a new instance of ChoiceFormat with the same ranges and strings
+	 * as this ChoiceFormat.
+	 * 
+	 * @return a shallow copy of this ChoiceFormat
+	 * 
+	 * @see java.lang.Cloneable
+	 */
+	public Object clone() {
+		ChoiceFormat clone = (ChoiceFormat) super.clone();
+		clone.choiceLimits = (double[]) choiceLimits.clone();
+		clone.choiceFormats = (String[]) choiceFormats.clone();
+		return clone;
+	}
+
+	/**
+	 * Compares the specified object to this ChoiceFormat and answer if they are
+	 * equal. The object must be an instance of ChoiceFormat and have the same
+	 * limits and formats.
+	 * 
+	 * @param object
+	 *            the object to compare with this object
+	 * @return true if the specified object is equal to this ChoiceFormat, false
+	 *         otherwise
+	 * 
+	 * @see #hashCode
+	 */
+	public boolean equals(Object object) {
+		if (this == object)
+			return true;
+		if (!(object instanceof ChoiceFormat))
+			return false;
+		ChoiceFormat choice = (ChoiceFormat) object;
+		return Arrays.equals(choiceLimits, choice.choiceLimits)
+				&& Arrays.equals(choiceFormats, choice.choiceFormats);
+	}
+
+	/**
+	 * Appends to the specified StringBuffer the string associated with the
+	 * range in which the specified double value fits.
+	 * 
+	 * @param value
+	 *            the double to format
+	 * @param buffer
+	 *            the StringBuffer
+	 * @param field
+	 *            a FieldPosition which is ignored
+	 * @return the StringBuffer parameter <code>buffer</code>
+	 */
+	public StringBuffer format(double value, StringBuffer buffer,
+			FieldPosition field) {
+		if (Double.isNaN(value)
+				|| (choiceLimits.length > 1 && value < choiceLimits[1]))
+			return buffer.append(choiceFormats[0]);
+		for (int i = 2; i < choiceLimits.length; i++)
+			if (value >= choiceLimits[i - 1] && value < choiceLimits[i])
+				return buffer.append(choiceFormats[i - 1]);
+		return buffer.append(choiceFormats[choiceFormats.length - 1]);
+	}
+
+	/**
+	 * Appends to the specified StringBuffer the string associated with the
+	 * range in which the specified long value fits.
+	 * 
+	 * @param value
+	 *            the long to format
+	 * @param buffer
+	 *            the StringBuffer
+	 * @param field
+	 *            a FieldPosition which is ignored
+	 * @return the StringBuffer parameter <code>buffer</code>
+	 */
+	public StringBuffer format(long value, StringBuffer buffer,
+			FieldPosition field) {
+		return format((double) value, buffer, field);
+	}
+
+	/**
+	 * Answers the Strings associated with the ranges of this ChoiceFormat.
+	 * 
+	 * @return an array of String
+	 */
+	public Object[] getFormats() {
+		return choiceFormats;
+	}
+
+	/**
+	 * Answers the ranges of this ChoiceFormat.
+	 * 
+	 * @return an array of double, the ranges are greater or equal to the value
+	 *         in lower index up to less than the value in the next higher
+	 *         index. The bounds of the lowest and highest indexes are negative
+	 *         and positive infinity.
+	 */
+	public double[] getLimits() {
+		return choiceLimits;
+	}
+
+	/**
+	 * Answers an integer hash code for the receiver. Objects which are equal
+	 * answer the same value for this method.
+	 * 
+	 * @return the receiver's hash
+	 * 
+	 * @see #equals
+	 */
+	public int hashCode() {
+		int hashCode = 0;
+		for (int i = 0; i < choiceLimits.length; i++) {
+			long v = Double.doubleToLongBits(choiceLimits[i]);
+			hashCode += (int) (v ^ (v >>> 32)) + choiceFormats[i].hashCode();
+		}
+		return hashCode;
+	}
+
+	/**
+	 * Answers the double value which is closest to the specified double but
+	 * larger.
+	 * 
+	 * @param value
+	 *            a double value
+	 * @return the next larger double value
+	 */
+	public static final double nextDouble(double value) {
+		if (value == Double.POSITIVE_INFINITY)
+			return value;
+		long bits;
+		// Handle -0.0
+		if (value == 0)
+			bits = 0;
+		else
+			bits = Double.doubleToLongBits(value);
+		return Double.longBitsToDouble(value < 0 ? bits - 1 : bits + 1);
+	}
+
+	/**
+	 * Answers the double value which is closest to the specified double but
+	 * either larger or smaller as specified.
+	 * 
+	 * @param value
+	 *            a double value
+	 * @param increment
+	 *            true to get a larger value, false to get a smaller value
+	 * @return the next larger or smaller double value
+	 */
+	public static double nextDouble(double value, boolean increment) {
+		return increment ? nextDouble(value) : previousDouble(value);
+	}
+
+	/**
+	 * Parse a Double from the specified String starting at the index specified
+	 * by the ParsePosition. The String is compared to the strings of this
+	 * ChoiceFormat and if a match occurs, the answer is the lower bound of the
+	 * corresponding range. If the string is successfully parsed, the index of
+	 * the ParsePosition is updated to the index following the parsed text.
+	 * 
+	 * @param string
+	 *            the String to parse
+	 * @param position
+	 *            the ParsePosition, updated on return with the index following
+	 *            the parsed text, or on error the index is unchanged and the
+	 *            error index is set to the index where the error occurred
+	 * @return a Double resulting from the parse, or Double.NaN if there is an
+	 *         error
+	 */
+	public Number parse(String string, ParsePosition position) {
+		int offset = position.getIndex();
+		for (int i = 0; i < choiceFormats.length; i++) {
+			if (string.startsWith(choiceFormats[i], offset)) {
+				position.setIndex(offset + choiceFormats[i].length());
+				return new Double(choiceLimits[i]);
+			}
+		}
+		position.setErrorIndex(offset);
+		return new Double(Double.NaN);
+	}
+
+	/**
+	 * Answers the double value which is closest to the specified double but
+	 * smaller.
+	 * 
+	 * @param value
+	 *            a double value
+	 * @return the next smaller double value
+	 */
+	public static final double previousDouble(double value) {
+		if (value == Double.NEGATIVE_INFINITY)
+			return value;
+		long bits;
+		// Handle 0.0
+		if (value == 0)
+			bits = 0x8000000000000000L;
+		else
+			bits = Double.doubleToLongBits(value);
+		return Double.longBitsToDouble(value <= 0 ? bits + 1 : bits - 1);
+	}
+
+	/**
+	 * Sets the ranges and associated strings of this ChoiceFormat.
+	 * 
+	 * @param limits
+	 *            an array of double, the ranges are greater or equal to the
+	 *            value in lower index up to less than the value in the next
+	 *            higher index. The bounds of the lowest and highest indexes are
+	 *            negative and positive infinity.
+	 * @param formats
+	 *            the strings associated with the ranges. The lower bound of the
+	 *            range is at the same index as the string.
+	 */
+	public void setChoices(double[] limits, String[] formats) {
+		if (limits.length != formats.length)
+			throw new IllegalArgumentException();
+		choiceLimits = limits;
+		choiceFormats = formats;
+	}
+
+	private int skipWhitespace(String string, int index) {
+		int length = string.length();
+		while (index < length && Character.isWhitespace(string.charAt(index)))
+			index++;
+		return index;
+	}
+
+	/**
+	 * Answers the pattern of this ChoiceFormat which specified the ranges and
+	 * their associated strings.
+	 * 
+	 * @return the pattern
+	 */
+	public String toPattern() {
+		StringBuffer buffer = new StringBuffer();
+		for (int i = 0; i < choiceLimits.length; i++) {
+			String previous = String.valueOf(previousDouble(choiceLimits[i]));
+			String limit = String.valueOf(choiceLimits[i]);
+			if (previous.length() < limit.length()) {
+				buffer.append(previous);
+				buffer.append('<');
+			} else {
+				buffer.append(limit);
+				buffer.append('#');
+			}
+			boolean quote = (choiceFormats[i].indexOf('|') != -1);
+			if (quote)
+				buffer.append('\'');
+			buffer.append(choiceFormats[i]);
+			if (quote)
+				buffer.append('\'');
+			buffer.append('|');
+		}
+		buffer.setLength(buffer.length() - 1);
+		return buffer.toString();
+	}
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationElementIterator.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationElementIterator.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationElementIterator.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationElementIterator.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,246 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+/**
+ * <p>
+ * <code>CollationElementIterator</code> is created by a
+ * <code>RuleBasedCollator</code> to iterate through a string. The return result
+ * of each iteration is a 32-bit collation element that defines the ordering
+ * priority of the next character or sequence of characters in the source
+ * string.
+ * </p>
+ * <p>
+ * For illustration, consider the following in Spanish:
+ * </p>
+ * 
+ * <p>
+ * <code>
+ * "ca" -> the first collation element is collation_element('c') and second
+ * collation element is collation_element('a').
+ * </code>
+ * </p>
+ * 
+ * <p>
+ * <code>
+ * Since "ch" in Spanish sorts as one entity, the below example returns one
+ * collation element for the two characters 'c' and 'h'
+ * </code>
+ * </p>
+ * 
+ * <p>
+ * <code>
+ * "cha" -> the first collation element is collation_element('ch') and second
+ * collation element is collation_element('a').
+ * </code>
+ * </p>
+ * <p>
+ * And in German,
+ * </p>
+ * 
+ * <p>
+ * <code>
+ * Since the character '�' is a composed character of 'a' and 'e', the iterator
+ * returns two collation elements for the single character '�'
+ * </code>
+ * </p>
+ * <p>
+ * <code>
+ * "�b" -> the first
+ * collation element is collation_element('a'), the second collation element is
+ * collation_element('e'), and the third collation element is
+ * collation_element('b').
+ * </code>
+ * </p>
+ * 
+ */
+public final class CollationElementIterator {
+
+	/**
+	 * This constant is returned by the iterator in the methods
+	 * <code>next()</code> and <code>previous()</code> when the end or the
+	 * beginning of the source string has been reached, and there are no more
+	 * valid collation elements to return.
+	 */
+	public static final int NULLORDER = -1;
+
+	private com.ibm.icu.text.CollationElementIterator icuIterator;
+
+	CollationElementIterator(com.ibm.icu.text.CollationElementIterator iterator) {
+		this.icuIterator = iterator;
+	}
+
+	/**
+	 * Obtains the maximum length of any expansion sequence that ends with the
+	 * specified collation element. If there is no expansion with this collation
+	 * element as the last element, returns <code>1</code>.
+	 * 
+	 * @param order
+	 *            a collation element that has been previously obtained from
+	 *            a call to either the {@link #next()} or {@link #previous()} 
+	 *            method. 
+	 * @return the maximum length of any expansion sequence ending with the
+	 *         specified collation element.
+	 */
+	public int getMaxExpansion(int order) {
+		return this.icuIterator.getMaxExpansion(order);
+	}
+
+	/**
+	 * Obtains the character offset in the source string corresponding to the
+	 * next collation element. This value could be any of: <ui>
+	 * <li>The index of the first character in the source string that matches
+	 * the value of the next collation element. (This means that if
+	 * setOffset(offset) sets the index in the middle of a contraction,
+	 * getOffset() returns the index of the first character in the contraction,
+	 * which may not be equal to the original offset that was set. Hence calling
+	 * getOffset() immediately after setOffset(offset) does not guarantee that
+	 * the original offset set will be returned.)</li>
+	 * <li>If normalization is on, the index of the immediate subsequent
+	 * character, or composite character with the first character, having a
+	 * combining class of 0.</li>
+	 * <li>The length of the source string, if iteration has reached the end.
+	 * </li>
+	 * <ui>
+	 * 
+	 * @return The position of the collation element in the source string that
+	 * will be returned in the next invocation of the {@link #next()} method.
+	 */
+	public int getOffset() {
+		return this.icuIterator.getOffset();
+	}
+
+	/**
+	 * Obtains the next collation element in the source string.
+	 * 
+	 * @return the next collation element or <code>NULLORDER</code> if the end
+	 *         of the iteration has been reached.
+	 */
+	public int next() {
+		return this.icuIterator.next();
+	}
+
+	/**
+	 * Obtains the previous collation element in the source string.
+	 * 
+	 * @return the previous collation element, or <code>NULLORDER</code> when
+	 *         the start of the iteration has been reached.
+	 */
+	public int previous() {
+		return this.icuIterator.previous();
+	}
+
+	/**
+	 * Obtains the primary order of the specified collation element, i.e. the
+	 * first 16 bits. This value is unsigned.
+	 * 
+	 * @param order
+	 * @return the element's 16 bits primary order.
+	 */
+	public static final int primaryOrder(int order) {
+		return com.ibm.icu.text.CollationElementIterator.primaryOrder(order);
+	}
+
+	/**
+	 * Repositions the cursor to point at the first element of the current
+	 * string. The next call to <code>next()</code> or <code>previous()</code>
+	 * will return the first and last collation element in the string,
+	 * respectively.
+	 * <p>
+	 * If the <code>RuleBasedCollator</code> used by this iterator has had its
+	 * attributes changed, calling <code>reset()</code> will reinitialize the
+	 * iterator to use the new attributes.
+	 * </p>
+	 */
+	public void reset() {
+		this.icuIterator.reset();
+	}
+
+	/**
+	 * Obtains the secondary order of the specified collation element, i.e. the
+	 * 16th to 23th bits, inclusive. This value is unsigned.
+	 * 
+	 * @param order
+	 * @return the 8 bit secondary order of the element
+	 */
+	public static final short secondaryOrder(int order) {
+		return (short) com.ibm.icu.text.CollationElementIterator
+				.secondaryOrder(order);
+	}
+
+	/**
+	 * Points the iterator at the collation element associated with the
+	 * character in the source string which is found at the supplied offset.
+	 * After this call completes, an invocation of the {@link #next()} method
+	 * will return this collation element.
+	 * <p>
+	 * If <code>newOffset</code> corresponds to a character which is part of a
+	 * sequence that maps to a single collation element the iterator is adjusted
+	 * to the start of that sequence. As a result of this, any subsequent call
+	 * made to <code>getOffset()</code> may not return the same value set by
+	 * this method.
+	 * </p>
+	 * <p>
+	 * If the decomposition mode is on, and offset is in the middle of a
+	 * decomposible range of source text, the iterator may not return a correct
+	 * result for the next forwards or backwards iteration. The user must ensure
+	 * that the offset is not in the middle of a decomposible range.
+	 * </p>
+	 * 
+	 * @param newOffset
+	 *            the character offset into the original source string to set.
+	 *            Note that this is not an offset into the corresponding
+	 *            sequence of collation elements.
+	 */
+	public void setOffset(int newOffset) {
+		this.icuIterator.setOffset(newOffset);
+	}
+
+	/**
+	 * Sets a new source string interator for iteration, and reset the offset to
+	 * the beginning of the text.
+	 * 
+	 * @param source
+	 *            the new source string iterator for iteration.
+	 */
+	public void setText(CharacterIterator source) {
+		this.icuIterator.setText(source);
+	}
+
+	/**
+	 * Sets a new source string for iteration, and reset the offset to the
+	 * beginning of the text.
+	 * 
+	 * @param source
+	 *            the new source string for iteration
+	 */
+	public void setText(String source) {
+		this.icuIterator.setText(source);
+	}
+
+	/**
+	 * Obtains the tertiary order of the specified collation element, i.e. the
+	 * last 8 bits. This value is unsigned.
+	 * 
+	 * @param order
+	 * @return the 8 bits tertiary order of the element
+	 */
+	public static final short tertiaryOrder(int order) {
+		return (short) com.ibm.icu.text.CollationElementIterator
+				.tertiaryOrder(order);
+	}
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationKey.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationKey.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationKey.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/CollationKey.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,116 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+/**
+ * CollationKey represents the collation order of a particular String for a
+ * specific Collator. CollationKeys can be compared to determine the relative
+ * ordering of their source Strings. This is useful when the Strings must be
+ * compared multiple times, as in sorting.
+ */
+public final class CollationKey implements Comparable {
+
+	private String source;
+
+	private com.ibm.icu.text.CollationKey icuKey;
+
+	CollationKey(String source, com.ibm.icu.text.CollationKey key) {
+		this.source = source;
+		this.icuKey = key;
+	}
+
+	/**
+	 * Compare the receiver to the specified CollationKey to determine the
+	 * relative ordering.
+	 * 
+	 * @param value
+	 *            a CollationKey
+	 * @return an int < 0 if this CollationKey is less than the specified
+	 *         CollationKey, 0 if they are equal, and > 0 if this CollationKey
+	 *         is greater
+	 */
+	public int compareTo(CollationKey value) {
+		return icuKey.compareTo(value.icuKey);
+	}
+
+	/**
+	 * Compare this CollationKey to the specified Object to determine the
+	 * relative ordering.
+	 * 
+	 * @param object
+	 *            an Object
+	 * @return an int < 0 if this CollationKey is less than the specified
+	 *         CollationKey, 0 if they are equal, and > 0 if this CollationKey
+	 *         is greater
+	 * 
+	 * @exception ClassCastException
+	 *                when object is not a CollationKey
+	 */
+	public int compareTo(Object object) {
+		return icuKey.compareTo(((CollationKey) object).icuKey);
+	}
+
+	/**
+	 * Compares the specified object to this CollationKey and answer if they are
+	 * equal. The object must be an instance of CollationKey and have the same
+	 * source string and collation key. The instances of CollationKey must have
+	 * been created by the same Collator.
+	 * 
+	 * @param object
+	 *            the object to compare with this object
+	 * @return true if the specified object is equal to this CollationKey, false
+	 *         otherwise
+	 * 
+	 * @see #hashCode
+	 */
+	public boolean equals(Object object) {
+		if (!(object instanceof CollationKey))
+			return false;
+		CollationKey collationKey = (CollationKey) object;
+		return icuKey.equals(collationKey.icuKey);
+	}
+
+	/**
+	 * Answer the String from which this CollationKey was created.
+	 * 
+	 * @return a String
+	 */
+	public String getSourceString() {
+		return this.source;
+	}
+
+	/**
+	 * Answers an integer hash code for the receiver. Objects which are equal
+	 * answer the same value for this method.
+	 * 
+	 * @return the receiver's hash
+	 * 
+	 * @see #equals
+	 */
+	public int hashCode() {
+		return icuKey.hashCode();
+	}
+
+	/**
+	 * Answer the collation key as a byte array.
+	 * 
+	 * @return an array of bytes
+	 */
+	public byte[] toByteArray() {
+		return icuKey.toByteArray();
+	}
+}

Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Collator.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Collator.java?rev=350181&view=auto
==============================================================================
--- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Collator.java (added)
+++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/text/src/java/text/Collator.java Wed Nov 30 21:29:27 2005
@@ -0,0 +1,358 @@
+/* Copyright 2005 The Apache Software Foundation or its licensors, as applicable
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.text;
+
+
+import java.security.AccessController;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.Vector;
+
+import com.ibm.oti.util.PriviAction;
+
+/**
+ * Collator is an abstract class which is the root of classes which provide
+ * Locale specific String comparison to determine their ordering with respect to
+ * each other.
+ */
+public abstract class Collator implements Comparator, Cloneable {
+
+	static final int EQUAL = 0;
+
+	static final int GREATER = 1;
+
+	static final int LESS = -1;
+
+	/**
+	 * Constant used to specify the decomposition rule.
+	 */
+	public static final int NO_DECOMPOSITION = 0;
+
+	/**
+	 * Constant used to specify the decomposition rule.
+	 */
+	public static final int CANONICAL_DECOMPOSITION = 1;
+
+	/**
+	 * Constant used to specify the decomposition rule.
+	 */
+	public static final int FULL_DECOMPOSITION = 2;
+
+	/**
+	 * Constant used to specify the collation strength.
+	 */
+	public static final int PRIMARY = 0;
+
+	/**
+	 * Constant used to specify the collation strength.
+	 */
+	public static final int SECONDARY = 1;
+
+	/**
+	 * Constant used to specify the collation strength.
+	 */
+	public static final int TERTIARY = 2;
+
+	/**
+	 * Constant used to specify the collation strength.
+	 */
+	public static final int IDENTICAL = 3;
+
+	private static int CACHE_SIZE;
+
+	private static Vector cache = new Vector(CACHE_SIZE);
+
+	// Wrapper class of ICU4J Collator
+	com.ibm.icu.text.Collator icuColl;
+
+	static {
+		// CACHE_SIZE includes key and value, so needs to be double
+		String cacheSize = (String) AccessController
+				.doPrivileged(new PriviAction("collator.cache")); //$NON-NLS-1$
+		if (cacheSize != null) {
+			try {
+				CACHE_SIZE = Integer.parseInt(cacheSize);
+			} catch (NumberFormatException e) {
+				CACHE_SIZE = 6;
+			}
+		} else
+			CACHE_SIZE = 6;
+	}
+
+	Collator(com.ibm.icu.text.Collator wrapper) {
+		this.icuColl = wrapper;
+	}
+
+	/**
+	 * Constructs a new instance of this Collator.
+	 */
+	protected Collator() {
+		super();
+	}
+
+	/**
+	 * Answers a new Collator with the same decomposition rule and strength
+	 * value as this Collator.
+	 * 
+	 * @return a shallow copy of this Collator
+	 * @see java.lang.Cloneable
+	 */
+	public Object clone() {
+		try {
+			Collator clone = (Collator) super.clone();
+			clone.icuColl = (com.ibm.icu.text.Collator) this.icuColl.clone();
+			return clone;
+		} catch (CloneNotSupportedException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Compares the two objects to determine their relative ordering. The
+	 * objects must be Strings.
+	 * 
+	 * @param object1
+	 *            the first String to compare
+	 * @param object2
+	 *            the second String to compare
+	 * @return an int < 0 if object1 is less than object2, 0 if they are equal,
+	 *         and > 0 if object1 is greater than object2
+	 * 
+	 * @exception ClassCastException
+	 *                when the objects are not Strings
+	 */
+	public int compare(Object object1, Object object2) {
+		return compare((String) object1, (String) object2);
+	}
+
+	/**
+	 * Compares the two Strings to determine their relative ordering.
+	 * 
+	 * @param string1
+	 *            the first String to compare
+	 * @param string2
+	 *            the second String to compare
+	 * @return an int < 0 if string1 is less than string2, 0 if they are equal,
+	 *         and > 0 if string1 is greater than string2
+	 */
+	public abstract int compare(String string1, String string2);
+
+	/**
+	 * Compares the specified object to this Collator and answer if they are
+	 * equal. The object must be an instance of Collator and have the same
+	 * strength and decomposition values.
+	 * 
+	 * @param object
+	 *            the object to compare with this object
+	 * @return true if the specified object is equal to this Collator, false
+	 *         otherwise
+	 * 
+	 * @see #hashCode
+	 */
+	public boolean equals(Object object) {
+		if (!(object instanceof Collator))
+			return false;
+		Collator collator = (Collator) object;
+		return this.icuColl == null ? collator.icuColl == null : this.icuColl
+				.equals(collator.icuColl);
+	}
+
+	/**
+	 * Compares the two Strings using the collation rules to determine if they
+	 * are equal.
+	 * 
+	 * @param string1
+	 *            the first String to compare
+	 * @param string2
+	 *            the second String to compare
+	 * @return true if the strings are equal using the collation rules, false
+	 *         otherwise
+	 */
+	public boolean equals(String string1, String string2) {
+		return compare(string1, string2) == 0;
+	}
+
+	/**
+	 * Gets the list of installed Locales which support Collator.
+	 * 
+	 * @return an array of Locale
+	 */
+	public static Locale[] getAvailableLocales() {
+		return com.ibm.icu.text.Collator.getAvailableLocales();
+	}
+
+	/**
+	 * Answers a CollationKey for the specified String for this Collator with
+	 * the current decomposition rule and stength value.
+	 * 
+	 * @param string
+	 *            the collation key.
+	 * @return a CollationKey
+	 */
+	public abstract CollationKey getCollationKey(String string);
+
+	/**
+	 * Answers the decomposition rule for this Collator.
+	 * 
+	 * @return the decomposition rule, either NO_DECOMPOSITION,
+	 *         CANONICAL_DECOMPOSITION or FULL_DECOMPOSITION
+	 */
+	public int getDecomposition() {
+		return decompositionMode_ICU_Java(this.icuColl.getDecomposition());
+	}
+
+	/**
+	 * Answers a Collator instance which is appropriate for the default Locale.
+	 * 
+	 * @return a Collator
+	 */
+	public static Collator getInstance() {
+		return getInstance(Locale.getDefault());
+	}
+
+	/**
+	 * Answers a Collator instance which is appropriate for the specified
+	 * Locale.
+	 * 
+	 * @param locale
+	 *            the Locale
+	 * @return a Collator
+	 */
+	public static Collator getInstance(Locale locale) {
+		String key = locale.toString();
+		for (int i = cache.size() - 1; i >= 0; i -= 2) {
+			if (cache.elementAt(i).equals(key))
+				return (Collator) ((Collator) cache.elementAt(i - 1)).clone();
+		}
+
+		return new RuleBasedCollator(com.ibm.icu.text.Collator
+				.getInstance(locale));
+	}
+
+	/**
+	 * Answers the strength value for this Collator.
+	 * 
+	 * @return the strength value, either PRIMARY, SECONDARY, TERTIARY, or
+	 *         IDENTICAL
+	 */
+	public int getStrength() {
+		return strength_ICU_Java(this.icuColl.getStrength());
+	}
+
+	/**
+	 * Answers an integer hash code for the receiver. Objects which are equal
+	 * answer the same value for this method.
+	 * 
+	 * @return the receiver's hash
+	 * 
+	 * @see #equals(Object)
+	 * @see #equals(String, String)
+	 */
+	public abstract int hashCode();
+
+	/**
+	 * Sets the decomposition rule for this Collator.
+	 * 
+	 * @param value
+	 *            the decomposition rule, either NO_DECOMPOSITION,
+	 *            CANONICAL_DECOMPOSITION or FULL_DECOMPOSITION
+	 * 
+	 * @exception IllegalArgumentException
+	 *                when the decomposition rule is not valid
+	 */
+	public void setDecomposition(int value) {
+		this.icuColl.setDecomposition(decompositionMode_Java_ICU(value));
+	}
+
+	/**
+	 * Sets the strength value for this Collator.
+	 * 
+	 * @param value
+	 *            the strength value, either PRIMARY, SECONDARY, TERTIARY, or
+	 *            IDENTICAL
+	 * 
+	 * @exception IllegalArgumentException
+	 *                when the strength value is not valid
+	 */
+	public void setStrength(int value) {
+		this.icuColl.setStrength(strength_Java_ICU(value));
+	}
+
+	private int decompositionMode_Java_ICU(int mode) {
+		int icuDecomp = mode;
+		switch (mode) {
+		case Collator.CANONICAL_DECOMPOSITION:
+			icuDecomp = com.ibm.icu.text.Collator.CANONICAL_DECOMPOSITION;
+			break;
+		case Collator.NO_DECOMPOSITION:
+			icuDecomp = com.ibm.icu.text.Collator.NO_DECOMPOSITION;
+			break;
+		}
+		return icuDecomp;
+	}
+
+	private int decompositionMode_ICU_Java(int mode) {
+		int javaMode = mode;
+		switch (mode) {
+		case com.ibm.icu.text.Collator.NO_DECOMPOSITION:
+			javaMode = Collator.NO_DECOMPOSITION;
+			break;
+		case com.ibm.icu.text.Collator.CANONICAL_DECOMPOSITION:
+			javaMode = Collator.CANONICAL_DECOMPOSITION;
+			break;
+		}
+		return javaMode;
+	}
+
+	private int strength_Java_ICU(int value) {
+		int icuValue = value;
+		switch (value) {
+		case Collator.PRIMARY:
+			icuValue = com.ibm.icu.text.Collator.PRIMARY;
+			break;
+		case Collator.SECONDARY:
+			icuValue = com.ibm.icu.text.Collator.SECONDARY;
+			break;
+		case Collator.TERTIARY:
+			icuValue = com.ibm.icu.text.Collator.TERTIARY;
+			break;
+		case Collator.IDENTICAL:
+			icuValue = com.ibm.icu.text.Collator.IDENTICAL;
+			break;
+		}
+		return icuValue;
+
+	}
+
+	private int strength_ICU_Java(int value) {
+		int javaValue = value;
+		switch (value) {
+		case com.ibm.icu.text.Collator.PRIMARY:
+			javaValue = Collator.PRIMARY;
+			break;
+		case com.ibm.icu.text.Collator.SECONDARY:
+			javaValue = Collator.SECONDARY;
+			break;
+		case com.ibm.icu.text.Collator.TERTIARY:
+			javaValue = Collator.TERTIARY;
+			break;
+		case com.ibm.icu.text.Collator.IDENTICAL:
+			javaValue = Collator.IDENTICAL;
+			break;
+		}
+		return javaValue;
+	}
+}