1 /* FormatCharacter.java -- Implementation of AttributedCharacterIterator for
2    formatters.
3    Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 package java.text;
39 
40 import java.util.Set;
41 import java.util.HashSet;
42 import java.util.Map;
43 import java.util.HashMap;
44 import java.util.Vector;
45 
46 
47 /**
48  * This class should not be put public and it is only intended to the
49  * classes of the java.text package. Its aim is to build a segmented
50  * character iterator by appending strings and adding attributes to
51  * portions of strings. The code intends to do some optimization
52  * concerning memory consumption and attribute access but at the
53  * end it is only an AttributedCharacterIterator.
54  *
55  * @author Guilhem Lavaux <guilhem@kaffe.org>
56  * @date November 22, 2003
57  */
58 class FormatCharacterIterator implements AttributedCharacterIterator
59 {
60   private String formattedString;
61   private int charIndex;
62   private int attributeIndex;
63   private int[] ranges;
64   private HashMap[] attributes;
65 
66   /**
67    * This constructor builds an empty iterated strings. The attributes
68    * are empty and so is the string. However you may append strings
69    * and attributes to this iterator.
70    */
FormatCharacterIterator()71   FormatCharacterIterator()
72   {
73     formattedString = "";
74     ranges = new int[0];
75     attributes = new HashMap[0];
76   }
77 
78   /**
79    * This constructor take a string <code>s</code>, a set of ranges
80    * and the corresponding attributes. This is used to build an iterator.
81    * The array <code>ranges</code> should be formatted as follow:
82    * each element of <code>ranges</code> specifies the index in the string
83    * until which the corresponding map of attributes at the same position
84    * is applied. For example, if you have:
85    * <pre>
86    *   s = "hello";
87    *   ranges = new int[] { 2, 6 };
88    *   attributes = new HashMap[2];
89    * </pre>
90    * <code>"he"</code> will have the attributes <code>attributes[0]</code>,
91    * <code>"llo"</code> the <code>attributes[1]</code>.
92    */
FormatCharacterIterator(String s, int[] ranges, HashMap[] attributes)93   FormatCharacterIterator (String s, int[] ranges, HashMap[] attributes)
94   {
95     formattedString = s;
96     this.ranges = ranges;
97     this.attributes = attributes;
98   }
99 
100   /*
101    * The following methods are inherited from AttributedCharacterIterator,
102    * and thus are already documented.
103    */
104 
getAllAttributeKeys()105   public Set getAllAttributeKeys()
106   {
107     if (attributes != null && attributes[attributeIndex] != null)
108       return attributes[attributeIndex].keySet();
109     else
110       return new HashSet();
111   }
112 
getAttributes()113   public Map getAttributes()
114   {
115     if (attributes != null && attributes[attributeIndex] != null)
116       return attributes[attributeIndex];
117     else
118       return new HashMap();
119   }
120 
getAttribute(AttributedCharacterIterator.Attribute attrib)121   public Object getAttribute (AttributedCharacterIterator.Attribute attrib)
122   {
123     if (attributes != null && attributes[attributeIndex] != null)
124       return attributes[attributeIndex].get (attrib);
125     else
126       return null;
127   }
128 
getRunLimit(Set reqAttrs)129   public int getRunLimit(Set reqAttrs)
130   {
131     if (attributes == null)
132       return formattedString.length();
133 
134     int currentAttrIndex = attributeIndex;
135     Set newKeys;
136 
137     do
138       {
139 	currentAttrIndex++;
140 	if (currentAttrIndex == attributes.length)
141 	  return formattedString.length();
142 	if (attributes[currentAttrIndex] == null)
143 	  break;
144 	newKeys = attributes[currentAttrIndex].keySet();
145       }
146     while (newKeys.containsAll (reqAttrs));
147 
148     return ranges[currentAttrIndex-1];
149   }
150 
getRunLimit(AttributedCharacterIterator.Attribute attribute)151   public int getRunLimit (AttributedCharacterIterator.Attribute attribute)
152   {
153     Set s = new HashSet();
154 
155     s.add (attribute);
156     return getRunLimit (s);
157   }
158 
getRunLimit()159   public int getRunLimit()
160   {
161     if (attributes == null)
162       return formattedString.length();
163     if (attributes[attributeIndex] == null)
164       {
165 	for (int i=attributeIndex+1;i<attributes.length;i++)
166 	  if (attributes[i] != null)
167 	    return ranges[i-1];
168 	return formattedString.length();
169       }
170 
171     return getRunLimit (attributes[attributeIndex].keySet());
172   }
173 
getRunStart(Set reqAttrs)174   public int getRunStart (Set reqAttrs)
175   {
176     if (attributes == null)
177       return formattedString.length();
178 
179     int currentAttrIndex = attributeIndex;
180     Set newKeys = null;
181 
182     do
183       {
184 	if (currentAttrIndex == 0)
185 	  return 0;
186 
187 	currentAttrIndex--;
188 	if (attributes[currentAttrIndex] == null)
189 	  break;
190 	newKeys = attributes[currentAttrIndex].keySet();
191       }
192     while (newKeys.containsAll (reqAttrs));
193 
194     return (currentAttrIndex > 0) ? ranges[currentAttrIndex-1] : 0;
195   }
196 
getRunStart()197   public int getRunStart()
198   {
199     if (attributes == null)
200       return 0;
201 
202     if (attributes[attributeIndex] == null)
203       {
204 	for (int i=attributeIndex;i>0;i--)
205 	  if (attributes[i] != null)
206 	    return ranges[attributeIndex-1];
207 	return 0;
208       }
209 
210     return getRunStart (attributes[attributeIndex].keySet());
211   }
212 
getRunStart(AttributedCharacterIterator.Attribute attribute)213   public int getRunStart (AttributedCharacterIterator.Attribute attribute)
214   {
215     Set s = new HashSet();
216 
217     s.add (attribute);
218     return getRunStart (s);
219   }
220 
clone()221   public Object clone()
222   {
223     return new FormatCharacterIterator (formattedString, ranges, attributes);
224   }
225 
226   /*
227    * The following methods are inherited from CharacterIterator and thus
228    * are already documented.
229    */
230 
current()231   public char current()
232   {
233     return formattedString.charAt (charIndex);
234   }
235 
first()236   public char first()
237   {
238     charIndex = 0;
239     attributeIndex = 0;
240     return formattedString.charAt (0);
241   }
242 
getBeginIndex()243   public int getBeginIndex()
244   {
245     return 0;
246   }
247 
getEndIndex()248   public int getEndIndex()
249   {
250     return formattedString.length();
251   }
252 
getIndex()253   public int getIndex()
254   {
255     return charIndex;
256   }
257 
last()258   public char last()
259   {
260     charIndex = formattedString.length()-1;
261     if (attributes != null)
262       attributeIndex = attributes.length-1;
263     return formattedString.charAt (charIndex);
264   }
265 
next()266   public char next()
267   {
268     charIndex++;
269     if (charIndex >= formattedString.length())
270       {
271 	charIndex = getEndIndex();
272 	return DONE;
273       }
274     if (attributes != null)
275       {
276 	if (charIndex >= ranges[attributeIndex])
277 	  attributeIndex++;
278       }
279     return formattedString.charAt (charIndex);
280   }
281 
previous()282   public char previous()
283   {
284     charIndex--;
285     if (charIndex < 0)
286       {
287 	charIndex = 0;
288 	return DONE;
289       }
290 
291     if (attributes != null)
292       {
293 	if (charIndex < ranges[attributeIndex])
294 	  attributeIndex--;
295       }
296     return formattedString.charAt (charIndex);
297   }
298 
setIndex(int position)299   public char setIndex (int position)
300   {
301     if (position < 0 || position > formattedString.length())
302       throw new IllegalArgumentException ("position is out of range");
303 
304     charIndex = position;
305     if (attributes != null)
306       {
307 	for (attributeIndex=0;attributeIndex<attributes.length;
308 	     attributeIndex++)
309 	  if (ranges[attributeIndex] > charIndex)
310 	    break;
311 	attributeIndex--;
312       }
313     if (charIndex == formattedString.length())
314       return DONE;
315     else
316       return formattedString.charAt (charIndex);
317   }
318 
319   /**
320    * This method merge the specified attributes and ranges with the
321    * internal tables. This method is in charge of the optimization
322    * of tables. Two following sets of attributes are never the same.
323    *
324    * @see #FormatCharacterIterator()
325    *
326    * @param attributes the new array attributes to apply to the string.
327    */
mergeAttributes(HashMap[] attributes, int[] ranges)328   protected void mergeAttributes (HashMap[] attributes, int[] ranges)
329   {
330     Vector new_ranges = new Vector();
331     Vector new_attributes = new Vector();
332     int i = 0, j = 0;
333 
334     while (i < this.ranges.length && j < ranges.length)
335       {
336 	if (this.attributes[i] != null)
337 	  {
338 	    new_attributes.add (this.attributes[i]);
339 	    if (attributes[j] != null)
340 	      this.attributes[i].putAll (attributes[j]);
341 	  }
342 	else
343 	  {
344 	    new_attributes.add (attributes[j]);
345 	  }
346 	if (this.ranges[i] == ranges[j])
347 	  {
348 	    new_ranges.add (new Integer (ranges[j]));
349 	    i++;
350 	    j++;
351 	  }
352 	else if (this.ranges[i] < ranges[j])
353 	  {
354 	    new_ranges.add (new Integer (this.ranges[i]));
355 	    i++;
356 	  }
357 	else
358 	  {
359 	    new_ranges.add (new Integer (ranges[j]));
360 	    j++;
361 	  }
362      }
363 
364     if (i != this.ranges.length)
365       {
366 	for (;i<this.ranges.length;i++)
367 	  {
368 	    new_attributes.add (this.attributes[i]);
369 	    new_ranges.add (new Integer (this.ranges[i]));
370 	  }
371       }
372     if (j != ranges.length)
373       {
374 	for (;j<ranges.length;j++)
375 	  {
376 	    new_attributes.add (attributes[j]);
377 	    new_ranges.add (new Integer (ranges[j]));
378 	  }
379       }
380 
381     this.attributes = new HashMap[new_attributes.size()];
382     this.ranges = new int[new_ranges.size()];
383     System.arraycopy (new_attributes.toArray(), 0, this.attributes,
384 		      0, this.attributes.length);
385 
386     for (i=0;i<new_ranges.size();i++)
387       {
388 	this.ranges[i] = ((Integer)new_ranges.elementAt (i)).intValue();
389       }
390 
391   }
392 
393   /**
394    * This method appends to the internal attributed string the attributed
395    * string contained in the specified iterator.
396    *
397    * @param iterator the iterator which contains the attributed string to
398    * append to this iterator.
399    */
append(AttributedCharacterIterator iterator)400   protected void append (AttributedCharacterIterator iterator)
401   {
402     char c = iterator.first();
403     Vector more_ranges = new Vector();
404     Vector more_attributes = new Vector();
405 
406     do
407       {
408 	formattedString = formattedString + String.valueOf(c);
409 	// TODO: Reduce the size of the output array.
410 	more_attributes.add (iterator.getAttributes());
411 	more_ranges.add (new Integer (formattedString.length()));
412 	// END TOOD
413 	c = iterator.next();
414       }
415     while (c != DONE);
416 
417     HashMap[] new_attributes = new HashMap[attributes.length
418 					   + more_attributes.size()];
419     int[] new_ranges = new int[ranges.length + more_ranges.size()];
420 
421     System.arraycopy (attributes, 0, new_attributes, 0, attributes.length);
422     System.arraycopy (more_attributes.toArray(), 0, new_attributes,
423 		      attributes.length, more_attributes.size());
424 
425     System.arraycopy (ranges, 0, new_ranges, 0, ranges.length);
426     Object[] new_ranges_array = more_ranges.toArray();
427     for (int i=0;i<more_ranges.size();i++)
428       new_ranges[i+ranges.length] = ((Integer)new_ranges_array[i]).intValue();
429 
430     attributes = new_attributes;
431     ranges = new_ranges;
432   }
433 
434   /**
435    * This method appends an attributed string which attributes are specified
436    * directly in the calling parameters.
437    *
438    * @param text The string to append.
439    * @param local_attributes The attributes to put on this string in the
440    * iterator. If it is <code>null</code> the string will simply have no
441    * attributes.
442    */
append(String text, HashMap local_attributes)443   protected void append (String text, HashMap local_attributes)
444   {
445     int[] new_ranges = new int[ranges.length+1];
446     HashMap[] new_attributes = new HashMap[attributes.length+1];
447 
448     formattedString += text;
449     System.arraycopy (attributes, 0, new_attributes, 0, attributes.length);
450     System.arraycopy (ranges, 0, new_ranges, 0, ranges.length);
451     new_ranges[ranges.length] = formattedString.length();
452     new_attributes[attributes.length] = local_attributes;
453 
454     ranges = new_ranges;
455     attributes = new_attributes;
456   }
457 
458   /**
459    * This method appends a string without attributes. It is completely
460    * equivalent to call {@link #append(String,HashMap)} with local_attributes
461    * equal to <code>null</code>.
462    *
463    * @param text The string to append to the iterator.
464    */
append(String text)465   protected void append (String text)
466   {
467     append (text, null);
468   }
469 }
470