001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017:
018: package java.text;
019:
020: import java.text.AttributedCharacterIterator.Attribute;
021: import java.util.ArrayList;
022: import java.util.Arrays;
023: import java.util.HashMap;
024: import java.util.HashSet;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.ListIterator;
028: import java.util.Map;
029: import java.util.Set;
030:
031: import org.apache.harmony.text.internal.nls.Messages;
032:
033: /**
034: * AttributedString
035: */
036: public class AttributedString {
037:
038: String text;
039:
040: Map<AttributedCharacterIterator.Attribute, List<Range>> attributeMap;
041:
042: static class Range {
043: int start;
044:
045: int end;
046:
047: Object value;
048:
049: Range(int s, int e, Object v) {
050: start = s;
051: end = e;
052: value = v;
053: }
054: }
055:
056: static class AttributedIterator implements
057: AttributedCharacterIterator {
058:
059: private int begin, end, offset;
060:
061: private AttributedString attrString;
062:
063: private HashSet<Attribute> attributesAllowed;
064:
065: AttributedIterator(AttributedString attrString) {
066: this .attrString = attrString;
067: begin = 0;
068: end = attrString.text.length();
069: offset = 0;
070: }
071:
072: AttributedIterator(AttributedString attrString,
073: AttributedCharacterIterator.Attribute[] attributes,
074: int begin, int end) {
075: if (begin < 0 || end > attrString.text.length()
076: || begin > end) {
077: throw new IllegalArgumentException();
078: }
079: this .begin = begin;
080: this .end = end;
081: offset = begin;
082: this .attrString = attrString;
083: if (attributes != null) {
084: HashSet<Attribute> set = new HashSet<Attribute>(
085: (attributes.length * 4 / 3) + 1);
086: for (int i = attributes.length; --i >= 0;) {
087: set.add(attributes[i]);
088: }
089: attributesAllowed = set;
090: }
091: }
092:
093: /**
094: * Answers a new StringCharacterIterator with the same source String,
095: * begin, end, and current index as this StringCharacterIterator.
096: *
097: * @return a shallow copy of this StringCharacterIterator
098: *
099: * @see java.lang.Cloneable
100: */
101: @Override
102: @SuppressWarnings("unchecked")
103: public Object clone() {
104: try {
105: AttributedIterator clone = (AttributedIterator) super
106: .clone();
107: if (attributesAllowed != null) {
108: clone.attributesAllowed = (HashSet<Attribute>) attributesAllowed
109: .clone();
110: }
111: return clone;
112: } catch (CloneNotSupportedException e) {
113: return null;
114: }
115: }
116:
117: /**
118: * Answers the character at the current index in the source String.
119: *
120: * @return the current character, or DONE if the current index is past
121: * the end
122: */
123: public char current() {
124: if (offset == end) {
125: return DONE;
126: }
127: return attrString.text.charAt(offset);
128: }
129:
130: /**
131: * Sets the current position to the begin index and answers the
132: * character at the begin index.
133: *
134: * @return the character at the begin index
135: */
136: public char first() {
137: if (begin == end) {
138: return DONE;
139: }
140: offset = begin;
141: return attrString.text.charAt(offset);
142: }
143:
144: /**
145: * Answers the begin index in the source String.
146: *
147: * @return the index of the first character to iterate
148: */
149: public int getBeginIndex() {
150: return begin;
151: }
152:
153: /**
154: * Answers the end index in the source String.
155: *
156: * @return the index one past the last character to iterate
157: */
158: public int getEndIndex() {
159: return end;
160: }
161:
162: /**
163: * Answers the current index in the source String.
164: *
165: * @return the current index
166: */
167: public int getIndex() {
168: return offset;
169: }
170:
171: private boolean inRange(Range range) {
172: if (!(range.value instanceof Annotation)) {
173: return true;
174: }
175: return range.start >= begin && range.start < end
176: && range.end > begin && range.end <= end;
177: }
178:
179: private boolean inRange(List<Range> ranges) {
180: Iterator<Range> it = ranges.iterator();
181: while (it.hasNext()) {
182: Range range = it.next();
183: if (range.start >= begin && range.start < end) {
184: return !(range.value instanceof Annotation)
185: || (range.end > begin && range.end <= end);
186: } else if (range.end > begin && range.end <= end) {
187: return !(range.value instanceof Annotation)
188: || (range.start >= begin && range.start < end);
189: }
190: }
191: return false;
192: }
193:
194: public Set<AttributedIterator.Attribute> getAllAttributeKeys() {
195: if (begin == 0 && end == attrString.text.length()
196: && attributesAllowed == null) {
197: return attrString.attributeMap.keySet();
198: }
199:
200: Set<AttributedIterator.Attribute> result = new HashSet<Attribute>(
201: (attrString.attributeMap.size() * 4 / 3) + 1);
202: Iterator<Map.Entry<Attribute, List<Range>>> it = attrString.attributeMap
203: .entrySet().iterator();
204: while (it.hasNext()) {
205: Map.Entry<Attribute, List<Range>> entry = it.next();
206: if (attributesAllowed == null
207: || attributesAllowed.contains(entry.getKey())) {
208: List<Range> ranges = entry.getValue();
209: if (inRange(ranges)) {
210: result.add(entry.getKey());
211: }
212: }
213: }
214: return result;
215: }
216:
217: private Object currentValue(List<Range> ranges) {
218: Iterator<Range> it = ranges.iterator();
219: while (it.hasNext()) {
220: Range range = it.next();
221: if (offset >= range.start && offset < range.end) {
222: return inRange(range) ? range.value : null;
223: }
224: }
225: return null;
226: }
227:
228: public Object getAttribute(
229: AttributedCharacterIterator.Attribute attribute) {
230: if (attributesAllowed != null
231: && !attributesAllowed.contains(attribute)) {
232: return null;
233: }
234: ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap
235: .get(attribute);
236: if (ranges == null) {
237: return null;
238: }
239: return currentValue(ranges);
240: }
241:
242: public Map<Attribute, Object> getAttributes() {
243: Map<Attribute, Object> result = new HashMap<Attribute, Object>(
244: (attrString.attributeMap.size() * 4 / 3) + 1);
245: Iterator<Map.Entry<Attribute, List<Range>>> it = attrString.attributeMap
246: .entrySet().iterator();
247: while (it.hasNext()) {
248: Map.Entry<Attribute, List<Range>> entry = it.next();
249: if (attributesAllowed == null
250: || attributesAllowed.contains(entry.getKey())) {
251: Object value = currentValue(entry.getValue());
252: if (value != null) {
253: result.put(entry.getKey(), value);
254: }
255: }
256: }
257: return result;
258: }
259:
260: public int getRunLimit() {
261: return getRunLimit(getAllAttributeKeys());
262: }
263:
264: private int runLimit(List<Range> ranges) {
265: int result = end;
266: ListIterator<Range> it = ranges.listIterator(ranges.size());
267: while (it.hasPrevious()) {
268: Range range = it.previous();
269: if (range.end <= begin) {
270: break;
271: }
272: if (offset >= range.start && offset < range.end) {
273: return inRange(range) ? range.end : result;
274: } else if (offset >= range.end) {
275: break;
276: }
277: result = range.start;
278: }
279: return result;
280: }
281:
282: public int getRunLimit(
283: AttributedCharacterIterator.Attribute attribute) {
284: if (attributesAllowed != null
285: && !attributesAllowed.contains(attribute)) {
286: return end;
287: }
288: ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap
289: .get(attribute);
290: if (ranges == null) {
291: return end;
292: }
293: return runLimit(ranges);
294: }
295:
296: public int getRunLimit(Set<? extends Attribute> attributes) {
297: int limit = end;
298: Iterator<? extends Attribute> it = attributes.iterator();
299: while (it.hasNext()) {
300: AttributedCharacterIterator.Attribute attribute = it
301: .next();
302: int newLimit = getRunLimit(attribute);
303: if (newLimit < limit) {
304: limit = newLimit;
305: }
306: }
307: return limit;
308: }
309:
310: public int getRunStart() {
311: return getRunStart(getAllAttributeKeys());
312: }
313:
314: private int runStart(List<Range> ranges) {
315: int result = begin;
316: Iterator<Range> it = ranges.iterator();
317: while (it.hasNext()) {
318: Range range = it.next();
319: if (range.start >= end) {
320: break;
321: }
322: if (offset >= range.start && offset < range.end) {
323: return inRange(range) ? range.start : result;
324: } else if (offset < range.start) {
325: break;
326: }
327: result = range.end;
328: }
329: return result;
330: }
331:
332: public int getRunStart(
333: AttributedCharacterIterator.Attribute attribute) {
334: if (attributesAllowed != null
335: && !attributesAllowed.contains(attribute)) {
336: return begin;
337: }
338: ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap
339: .get(attribute);
340: if (ranges == null) {
341: return begin;
342: }
343: return runStart(ranges);
344: }
345:
346: public int getRunStart(Set<? extends Attribute> attributes) {
347: int start = begin;
348: Iterator<? extends Attribute> it = attributes.iterator();
349: while (it.hasNext()) {
350: AttributedCharacterIterator.Attribute attribute = it
351: .next();
352: int newStart = getRunStart(attribute);
353: if (newStart > start) {
354: start = newStart;
355: }
356: }
357: return start;
358: }
359:
360: /**
361: * Sets the current position to the end index - 1 and answers the
362: * character at the current position.
363: *
364: * @return the character before the end index
365: */
366: public char last() {
367: if (begin == end) {
368: return DONE;
369: }
370: offset = end - 1;
371: return attrString.text.charAt(offset);
372: }
373:
374: /**
375: * Increments the current index and returns the character at the new
376: * index.
377: *
378: * @return the character at the next index, or DONE if the next index is
379: * past the end
380: */
381: public char next() {
382: if (offset >= (end - 1)) {
383: offset = end;
384: return DONE;
385: }
386: return attrString.text.charAt(++offset);
387: }
388:
389: /**
390: * Decrements the current index and returns the character at the new
391: * index.
392: *
393: * @return the character at the previous index, or DONE if the previous
394: * index is past the beginning
395: */
396: public char previous() {
397: if (offset == begin) {
398: return DONE;
399: }
400: return attrString.text.charAt(--offset);
401: }
402:
403: /**
404: * Sets the current index in the source String.
405: *
406: * @return the character at the new index, or DONE if the index is past
407: * the end
408: *
409: * @exception IllegalArgumentException
410: * when the new index is less than the begin index or
411: * greater than the end index
412: */
413: public char setIndex(int location) {
414: if (location < begin || location > end) {
415: throw new IllegalArgumentException();
416: }
417: offset = location;
418: if (offset == end) {
419: return DONE;
420: }
421: return attrString.text.charAt(offset);
422: }
423: }
424:
425: public AttributedString(AttributedCharacterIterator iterator) {
426: if (iterator.getBeginIndex() > iterator.getEndIndex()) {
427: // text.0A=Invalid substring range
428: throw new IllegalArgumentException(Messages
429: .getString("text.0A")); //$NON-NLS-1$
430: }
431: StringBuffer buffer = new StringBuffer();
432: for (int i = iterator.getBeginIndex(); i < iterator
433: .getEndIndex(); i++) {
434: buffer.append(iterator.current());
435: iterator.next();
436: }
437: text = buffer.toString();
438: Set<AttributedCharacterIterator.Attribute> attributes = iterator
439: .getAllAttributeKeys();
440: if (attributes == null) {
441: return;
442: }
443: attributeMap = new HashMap<Attribute, List<Range>>((attributes
444: .size() * 4 / 3) + 1);
445:
446: Iterator<Attribute> it = attributes.iterator();
447: while (it.hasNext()) {
448: AttributedCharacterIterator.Attribute attribute = it.next();
449: iterator.setIndex(0);
450: while (iterator.current() != CharacterIterator.DONE) {
451: int start = iterator.getRunStart(attribute);
452: int limit = iterator.getRunLimit(attribute);
453: Object value = iterator.getAttribute(attribute);
454: if (value != null) {
455: addAttribute(attribute, value, start, limit);
456: }
457: iterator.setIndex(limit);
458: }
459: }
460: }
461:
462: private AttributedString(AttributedCharacterIterator iterator,
463: int start, int end, Set<Attribute> attributes) {
464: if (start < iterator.getBeginIndex()
465: || end > iterator.getEndIndex() || start > end) {
466: throw new IllegalArgumentException();
467: }
468:
469: if (attributes == null) {
470: return;
471: }
472:
473: StringBuffer buffer = new StringBuffer();
474: iterator.setIndex(start);
475: while (iterator.getIndex() < end) {
476: buffer.append(iterator.current());
477: iterator.next();
478: }
479: text = buffer.toString();
480: attributeMap = new HashMap<Attribute, List<Range>>((attributes
481: .size() * 4 / 3) + 1);
482:
483: Iterator<Attribute> it = attributes.iterator();
484: while (it.hasNext()) {
485: AttributedCharacterIterator.Attribute attribute = it.next();
486: iterator.setIndex(start);
487: while (iterator.getIndex() < end) {
488: Object value = iterator.getAttribute(attribute);
489: int runStart = iterator.getRunStart(attribute);
490: int limit = iterator.getRunLimit(attribute);
491: if ((value instanceof Annotation && runStart >= start && limit <= end)
492: || (value != null && !(value instanceof Annotation))) {
493: addAttribute(attribute, value,
494: (runStart < start ? start : runStart)
495: - start,
496: (limit > end ? end : limit) - start);
497: }
498: iterator.setIndex(limit);
499: }
500: }
501: }
502:
503: public AttributedString(AttributedCharacterIterator iterator,
504: int start, int end) {
505: this (iterator, start, end, iterator.getAllAttributeKeys());
506: }
507:
508: public AttributedString(AttributedCharacterIterator iterator,
509: int start, int end,
510: AttributedCharacterIterator.Attribute[] attributes) {
511: this (iterator, start, end, new HashSet<Attribute>(Arrays
512: .asList(attributes)));
513: }
514:
515: public AttributedString(String value) {
516: if (value == null) {
517: throw new NullPointerException();
518: }
519: text = value;
520: attributeMap = new HashMap<Attribute, List<Range>>(11);
521: }
522:
523: public AttributedString(
524: String value,
525: Map<? extends AttributedCharacterIterator.Attribute, ?> attributes) {
526: if (value == null) {
527: throw new NullPointerException();
528: }
529: if (value.length() == 0 && !attributes.isEmpty()) {
530: // text.0B=Cannot add attributes to empty string
531: throw new IllegalArgumentException(Messages
532: .getString("text.0B")); //$NON-NLS-1$
533: }
534: text = value;
535: attributeMap = new HashMap<Attribute, List<Range>>((attributes
536: .size() * 4 / 3) + 1);
537: Iterator<?> it = attributes.entrySet().iterator();
538: while (it.hasNext()) {
539: Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next();
540: ArrayList<Range> ranges = new ArrayList<Range>(1);
541: ranges.add(new Range(0, text.length(), entry.getValue()));
542: attributeMap.put(
543: (AttributedCharacterIterator.Attribute) entry
544: .getKey(), ranges);
545: }
546: }
547:
548: public void addAttribute(
549: AttributedCharacterIterator.Attribute attribute,
550: Object value) {
551: if (null == attribute) {
552: throw new NullPointerException();
553: }
554: if (text.length() == 0) {
555: throw new IllegalArgumentException();
556: }
557:
558: List<Range> ranges = attributeMap.get(attribute);
559: if (ranges == null) {
560: ranges = new ArrayList<Range>(1);
561: attributeMap.put(attribute, ranges);
562: } else {
563: ranges.clear();
564: }
565: ranges.add(new Range(0, text.length(), value));
566: }
567:
568: public void addAttribute(
569: AttributedCharacterIterator.Attribute attribute,
570: Object value, int start, int end) {
571: if (null == attribute) {
572: throw new NullPointerException();
573: }
574: if (start < 0 || end > text.length() || start >= end) {
575: throw new IllegalArgumentException();
576: }
577:
578: if (value == null) {
579: return;
580: }
581:
582: List<Range> ranges = attributeMap.get(attribute);
583: if (ranges == null) {
584: ranges = new ArrayList<Range>(1);
585: ranges.add(new Range(start, end, value));
586: attributeMap.put(attribute, ranges);
587: return;
588: }
589: ListIterator<Range> it = ranges.listIterator();
590: while (it.hasNext()) {
591: Range range = it.next();
592: if (end <= range.start) {
593: it.previous();
594: break;
595: } else if (start < range.end
596: || (start == range.end && value.equals(range.value))) {
597: Range r1 = null, r3;
598: it.remove();
599: r1 = new Range(range.start, start, range.value);
600: r3 = new Range(end, range.end, range.value);
601:
602: while (end > range.end && it.hasNext()) {
603: range = it.next();
604: if (end <= range.end) {
605: if (end > range.start
606: || (end == range.start && value
607: .equals(range.value))) {
608: it.remove();
609: r3 = new Range(end, range.end, range.value);
610: break;
611: }
612: } else {
613: it.remove();
614: }
615: }
616:
617: if (value.equals(r1.value)) {
618: if (value.equals(r3.value)) {
619: it.add(new Range(r1.start < start ? r1.start
620: : start, r3.end > end ? r3.end : end,
621: r1.value));
622: } else {
623: it.add(new Range(r1.start < start ? r1.start
624: : start, end, r1.value));
625: if (r3.start < r3.end) {
626: it.add(r3);
627: }
628: }
629: } else {
630: if (value.equals(r3.value)) {
631: if (r1.start < r1.end) {
632: it.add(r1);
633: }
634: it.add(new Range(start, r3.end > end ? r3.end
635: : end, r3.value));
636: } else {
637: if (r1.start < r1.end) {
638: it.add(r1);
639: }
640: it.add(new Range(start, end, value));
641: if (r3.start < r3.end) {
642: it.add(r3);
643: }
644: }
645: }
646: return;
647: }
648: }
649: it.add(new Range(start, end, value));
650: }
651:
652: public void addAttributes(
653: Map<? extends AttributedCharacterIterator.Attribute, ?> attributes,
654: int start, int end) {
655: Iterator<?> it = attributes.entrySet().iterator();
656: while (it.hasNext()) {
657: Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next();
658: addAttribute((AttributedCharacterIterator.Attribute) entry
659: .getKey(), entry.getValue(), start, end);
660: }
661: }
662:
663: public AttributedCharacterIterator getIterator() {
664: return new AttributedIterator(this );
665: }
666:
667: public AttributedCharacterIterator getIterator(
668: AttributedCharacterIterator.Attribute[] attributes) {
669: return new AttributedIterator(this , attributes, 0, text
670: .length());
671: }
672:
673: public AttributedCharacterIterator getIterator(
674: AttributedCharacterIterator.Attribute[] attributes,
675: int start, int end) {
676: return new AttributedIterator(this, attributes, start, end);
677: }
678: }
|