001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.gvt.text;
020:
021: import java.text.AttributedCharacterIterator;
022: import java.text.AttributedString;
023: import java.text.CharacterIterator;
024: import java.text.StringCharacterIterator;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.Map;
029: import java.util.Set;
030:
031: /**
032: * GVTACIImpl
033: *
034: * Used to implement SVG <tspan> and <text>
035: * attributes. This implementation is designed for efficient support
036: * of per-character attributes (i.e. single character attribute spans).
037: * It supports an extended set of TextAttributes, via inner class
038: * GVTAttributedCharacterIterator.TextAttributes.
039: *
040: * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
041: * @version $Id: GVTACIImpl.java 476924 2006-11-19 21:13:26Z dvholten $
042: */
043:
044: public class GVTACIImpl implements GVTAttributedCharacterIterator {
045:
046: private String simpleString;
047: private Set allAttributes;
048: private ArrayList mapList;
049: private static int START_RUN = 2;
050: private static int END_RUN = 3;
051: private static int MID_RUN = 1;
052: private static int SINGLETON = 0;
053: private int[] charInRun;
054: private CharacterIterator iter = null;
055: private int currentIndex = -1;
056:
057: /**
058: * Constructs a new GVTAttributedCharacterIterator with no attributes
059: * and a null string.
060: */
061: public GVTACIImpl() {
062: simpleString = "";
063: buildAttributeTables();
064: }
065:
066: /**
067: * Constructs a GVTACIImpl whose contents are
068: * equivalent to those of aci.
069: * This constructor creates a new copy of the source data in <tt>aci</tt>.
070: */
071: public GVTACIImpl(AttributedCharacterIterator aci) {
072: buildAttributeTables(aci);
073: }
074:
075: /**
076: * Sets this iterator's contents to an unattributed copy of String s.
077: */
078: public void setString(String s) {
079: simpleString = s;
080: iter = new StringCharacterIterator(simpleString);
081: buildAttributeTables();
082: }
083:
084: /**
085: * Assigns this iterator's contents to be equivalent to AttributedString s.
086: */
087: public void setString(AttributedString s) {
088: iter = s.getIterator();
089: buildAttributeTables((AttributedCharacterIterator) iter);
090: }
091:
092: /**
093: * Sets values of a per-character attribute associated with the
094: * content string.
095: * Characters from <tt>beginIndex</tt> to <tt>endIndex</tt>
096: * (zero-offset) are assigned values for attribute key <tt>attr</tt>
097: * from the array <tt>attValues.</tt>
098: * If the length of attValues is less than character span
099: * <tt>(endIndex-beginIndex)</tt> the last value is duplicated;
100: * if attValues is longer than the character span
101: * the extra values are ignored.
102: * Note that if either beginIndex or endIndex are outside the bounds
103: * of the current character array they are clipped accordingly.
104: */
105: public void setAttributeArray(
106: GVTAttributedCharacterIterator.TextAttribute attr,
107: Object[] attValues, int beginIndex, int endIndex) {
108:
109: beginIndex = Math.max(beginIndex, 0);
110: endIndex = Math.min(endIndex, simpleString.length());
111: if (charInRun[beginIndex] == END_RUN) {
112: if (charInRun[beginIndex - 1] == MID_RUN) {
113: charInRun[beginIndex - 1] = END_RUN;
114: } else {
115: charInRun[beginIndex - 1] = SINGLETON;
116: }
117: }
118: if (charInRun[endIndex + 1] == END_RUN) {
119: charInRun[endIndex + 1] = SINGLETON;
120: } else if (charInRun[endIndex + 1] == MID_RUN) {
121: charInRun[endIndex + 1] = START_RUN;
122: }
123: for (int i = beginIndex; i <= endIndex; ++i) {
124: charInRun[i] = SINGLETON;
125: int n = Math.min(i, attValues.length - 1);
126: ((Map) mapList.get(i)).put(attr, attValues[n]);
127: }
128: }
129:
130: //From java.text.AttributedCharacterIterator
131:
132: /**
133: * Get the keys of all attributes defined on the iterator's text range.
134: */
135: public Set getAllAttributeKeys() {
136: return allAttributes;
137: }
138:
139: /**
140: * Get the value of the named attribute for the current
141: * character.
142: */
143: public Object getAttribute(
144: AttributedCharacterIterator.Attribute attribute) {
145: return getAttributes().get(attribute);
146: }
147:
148: /**
149: * Returns a map with the attributes defined on the current
150: * character.
151: */
152: public Map getAttributes() {
153: return (Map) mapList.get(currentIndex);
154: }
155:
156: /**
157: * Get the index of the first character following the
158: * run with respect to all attributes containing the current
159: * character.
160: */
161: public int getRunLimit() {
162: int ndx = currentIndex;
163: do {
164: ++ndx;
165: } while (charInRun[ndx] == MID_RUN);
166: return ndx;
167: }
168:
169: /**
170: * Get the index of the first character following the
171: * run with respect to the given attribute containing the current
172: * character.
173: */
174: public int getRunLimit(
175: AttributedCharacterIterator.Attribute attribute) {
176: int ndx = currentIndex;
177: Object value = getAttributes().get(attribute);
178:
179: //to avoid null pointer, treat null value as special case:-(
180: if (value == null) {
181: do {
182: ++ndx;
183: } while (((Map) mapList.get(ndx)).get(attribute) == null);
184: } else {
185: do {
186: ++ndx;
187: } while (value.equals(((Map) mapList.get(ndx))
188: .get(attribute)));
189: }
190: return ndx;
191: }
192:
193: /**
194: * Get the index of the first character following the
195: * run with respect to the given attributes containing the current
196: * character.
197: */
198: public int getRunLimit(Set attributes) {
199: int ndx = currentIndex;
200: do {
201: ++ndx;
202: } while (attributes.equals(mapList.get(ndx)));
203: return ndx;
204: }
205:
206: /**
207: * Get the index of the first character of the run with
208: * respect to all attributes containing the current character.
209: */
210: public int getRunStart() {
211: int ndx = currentIndex;
212: while (charInRun[ndx] == MID_RUN)
213: --ndx;
214: return ndx;
215: }
216:
217: /**
218: * Get the index of the first character of the run with
219: * respect to the given attribute containing the current character.
220: * @param attribute The attribute for whose appearance the first offset
221: * is requested.
222: */
223: public int getRunStart(
224: AttributedCharacterIterator.Attribute attribute) {
225: int ndx = currentIndex - 1;
226: Object value = getAttributes().get(attribute);
227:
228: //to avoid null pointer, treat null value as special case:-(
229: try {
230: if (value == null) {
231: while (((Map) mapList.get(ndx - 1)).get(attribute) == null)
232: --ndx;
233: } else {
234: while (value.equals(((Map) mapList.get(ndx - 1))
235: .get(attribute)))
236: --ndx;
237: }
238: } catch (IndexOutOfBoundsException e) {
239: }
240: return ndx;
241: }
242:
243: /**
244: * Get the index of the first character of the run with
245: * respect to the given attributes containing the current character.
246: * @param attributes the Set of attributes which begins at the returned
247: * index.
248: */
249: public int getRunStart(Set attributes) {
250: int ndx = currentIndex;
251: try {
252: while (attributes.equals(mapList.get(ndx - 1)))
253: --ndx;
254: } catch (IndexOutOfBoundsException e) {
255: }
256: return ndx;
257: }
258:
259: //From CharacterIterator
260:
261: /**
262: * Create a copy of this iterator
263: */
264: public Object clone() {
265: GVTAttributedCharacterIterator cloneACI = new GVTACIImpl(this );
266: return cloneACI;
267: }
268:
269: /**
270: * Get the character at the current position (as returned
271: * by getIndex()).
272: * <br><b>Specified by:</b> java.text.CharacterIterator.
273: */
274: public char current() {
275: return iter.current();
276: }
277:
278: /**
279: * Sets the position to getBeginIndex().
280: * @return the character at the start index of the text.
281: * <br><b>Specified by:</b> java.text.CharacterIterator.
282: */
283: public char first() {
284: return iter.first();
285: }
286:
287: /**
288: * Get the start index of the text.
289: * <br><b>Specified by:</b> java.text.CharacterIterator.
290: */
291: public int getBeginIndex() {
292: return iter.getBeginIndex();
293: }
294:
295: /**
296: * Get the end index of the text.
297: * <br><b>Specified by:</b> java.text.CharacterIterator.
298: */
299: public int getEndIndex() {
300: return iter.getEndIndex();
301: }
302:
303: /**
304: * Get the current index.
305: * <br><b>Specified by:</b> java.text.CharacterIterator.
306: */
307: public int getIndex() {
308: return iter.getIndex();
309: }
310:
311: /**
312: * Sets the position to getEndIndex()-1 (getEndIndex() if
313: * the text is empty) and returns the character at that position.
314: * <br><b>Specified by:</b> java.text.CharacterIterator.
315: */
316: public char last() {
317: return iter.last();
318: }
319:
320: /**
321: * Increments the iterator's index by one, returning the next character.
322: * @return the character at the new index.
323: * <br><b>Specified by:</b> java.text.CharacterIterator.
324: */
325: public char next() {
326: return iter.next();
327: }
328:
329: /**
330: * Decrements the iterator's index by one and returns
331: * the character at the new index.
332: * <br><b>Specified by:</b> java.text.CharacterIterator.
333: */
334: public char previous() {
335: return iter.previous();
336: }
337:
338: /**
339: * Sets the position to the specified position in the text.
340: * @param position The new (current) index into the text.
341: * @return the character at new index <em>position</em>.
342: * <br><b>Specified by:</b> java.text.CharacterIterator.
343: */
344: public char setIndex(int position) {
345: return iter.setIndex(position);
346: }
347:
348: //Private methods:
349:
350: private void buildAttributeTables() {
351: allAttributes = new HashSet();
352: mapList = new ArrayList(simpleString.length());
353: charInRun = new int[simpleString.length()];
354: for (int i = 0; i < charInRun.length; ++i) {
355: charInRun[i] = SINGLETON;
356: /*
357: * XXX TODO: loosen assumption, initially each character has its own
358: * attribute map.
359: */
360: mapList.set(i, new HashMap());
361: }
362: }
363:
364: private void buildAttributeTables(AttributedCharacterIterator aci) {
365: allAttributes = aci.getAllAttributeKeys();
366: int length = aci.getEndIndex() - aci.getBeginIndex();
367: mapList = new ArrayList(length);
368: charInRun = new int[length];
369: char c = aci.first();
370: char[] chars = new char[length];
371: for (int i = 0; i < length; ++i) {
372: chars[i] = c;
373: charInRun[i] = SINGLETON;
374: /*
375: * XXX TODO:loosen assumption, initially each character
376: * has its own attribute map.
377: */
378: mapList.set(i, new HashMap(aci.getAttributes()));
379: c = aci.next();
380: }
381: simpleString = new String(chars);
382: }
383:
384: //Inner classes:
385:
386: /**
387: * AttributeFilter which converts (extended) location attributes
388: * SVGAttributedCharacterIterator.TextAttribute.X, TextAttribute.Y,
389: * TextAttribute.ROTATE attributes to TextAttribute.TRANSFORM attributes.
390: */
391: public class TransformAttributeFilter implements
392: GVTAttributedCharacterIterator.AttributeFilter {
393:
394: /**
395: * Return a new AttributedCharacterIterator instance
396: * in which location attributes have been converted to
397: * TextAttribute.TRANSFORM attributes.
398: */
399: public AttributedCharacterIterator mutateAttributes(
400: AttributedCharacterIterator aci) {
401: //TODO:Implement this !!!
402: return aci;
403: }
404: }
405: }
|