KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > font > StyledParagraph


1 /*
2  * @(#)StyledParagraph.java 1.8 03/08/18
3  * (C) Copyright IBM Corp. 1999, All rights reserved.
4  */

5 package java.awt.font;
6
7 import java.awt.Font JavaDoc;
8 import java.awt.Toolkit JavaDoc;
9 import java.awt.im.InputMethodHighlight JavaDoc;
10 import java.text.Annotation JavaDoc;
11 import java.text.AttributedCharacterIterator JavaDoc;
12 import java.util.Vector JavaDoc;
13 import java.util.Hashtable JavaDoc;
14 import java.util.Map JavaDoc;
15 import sun.font.Decoration;
16 import sun.font.FontResolver;
17 import sun.text.CodePointIterator;
18
19 /**
20  * This class stores Font, GraphicAttribute, and Decoration intervals
21  * on a paragraph of styled text.
22  * <p>
23  * Currently, this class is optimized for a small number of intervals
24  * (preferrably 1).
25  */

26 final class StyledParagraph {
27     
28     // the length of the paragraph
29
private int length;
30     
31     // If there is a single Decoration for the whole paragraph, it
32
// is stored here. Otherwise this field is ignored.
33

34     private Decoration decoration;
35
36     // If there is a single Font or GraphicAttribute for the whole
37
// paragraph, it is stored here. Otherwise this field is ignored.
38
private Object JavaDoc font;
39     
40     // If there are multiple Decorations in the paragraph, they are
41
// stored in this Vector, in order. Otherwise this vector and
42
// the decorationStarts array are null.
43
private Vector JavaDoc decorations;
44     // If there are multiple Decorations in the paragraph,
45
// decorationStarts[i] contains the index where decoration i
46
// starts. For convenience, there is an extra entry at the
47
// end of this array with the length of the paragraph.
48
int[] decorationStarts;
49     
50     // If there are multiple Fonts/GraphicAttributes in the paragraph,
51
// they are
52
// stored in this Vector, in order. Otherwise this vector and
53
// the fontStarts array are null.
54
private Vector JavaDoc fonts;
55     // If there are multiple Fonts/GraphicAttributes in the paragraph,
56
// fontStarts[i] contains the index where decoration i
57
// starts. For convenience, there is an extra entry at the
58
// end of this array with the length of the paragraph.
59
int[] fontStarts;
60     
61     private static int INITIAL_SIZE = 8;
62     
63     /**
64      * Create a new StyledParagraph over the given styled text.
65      * @param aci an iterator over the text
66      * @param chars the characters extracted from aci
67      */

68     public StyledParagraph(AttributedCharacterIterator JavaDoc aci,
69                            char[] chars) {
70         
71         int start = aci.getBeginIndex();
72         int end = aci.getEndIndex();
73         length = end - start;
74
75         int index = start;
76         aci.first();
77         
78         do {
79             final int nextRunStart = aci.getRunLimit();
80             final int localIndex = index-start;
81             
82             Map JavaDoc attributes = aci.getAttributes();
83             attributes = addInputMethodAttrs(attributes);
84             Decoration d = Decoration.getDecoration(attributes);
85             addDecoration(d, localIndex);
86             
87             Object JavaDoc f = getGraphicOrFont(attributes);
88             if (f == null) {
89                 addFonts(chars, attributes, localIndex, nextRunStart-start);
90             }
91             else {
92                 addFont(f, localIndex);
93             }
94             
95             aci.setIndex(nextRunStart);
96             index = nextRunStart;
97             
98         } while (index < end);
99         
100         // Add extra entries to starts arrays with the length
101
// of the paragraph. 'this' is used as a dummy value
102
// in the Vector.
103
if (decorations != null) {
104             decorationStarts = addToVector(this, length, decorations, decorationStarts);
105         }
106         if (fonts != null) {
107             fontStarts = addToVector(this, length, fonts, fontStarts);
108         }
109     }
110     
111     /**
112      * Adjust indices in starts to reflect an insertion after pos.
113      * Any index in starts greater than pos will be increased by 1.
114      */

115     private static void insertInto(int pos, int[] starts, int numStarts) {
116         
117         while (starts[--numStarts] > pos) {
118             starts[numStarts] += 1;
119         }
120     }
121     
122     /**
123      * Return a StyledParagraph reflecting the insertion of a single character
124      * into the text. This method will attempt to reuse the given paragraph,
125      * but may create a new paragraph.
126      * @param aci an iterator over the text. The text should be the same as the
127      * text used to create (or most recently update) oldParagraph, with
128      * the exception of inserting a single character at insertPos.
129      * @param chars the characters in aci
130      * @param insertPos the index of the new character in aci
131      * @param oldParagraph a StyledParagraph for the text in aci before the
132      * insertion
133      */

134     public static StyledParagraph JavaDoc insertChar(AttributedCharacterIterator JavaDoc aci,
135                                              char[] chars,
136                                              int insertPos,
137                                              StyledParagraph JavaDoc oldParagraph) {
138         
139         // If the styles at insertPos match those at insertPos-1,
140
// oldParagraph will be reused. Otherwise we create a new
141
// paragraph.
142

143         char ch = aci.setIndex(insertPos);
144         int relativePos = Math.max(insertPos - aci.getBeginIndex() - 1, 0);
145         
146         Map JavaDoc attributes = addInputMethodAttrs(aci.getAttributes());
147         Decoration d = Decoration.getDecoration(attributes);
148         if (!oldParagraph.getDecorationAt(relativePos).equals(d)) {
149             return new StyledParagraph JavaDoc(aci, chars);
150         }
151         Object JavaDoc f = getGraphicOrFont(attributes);
152         if (f == null) {
153             FontResolver resolver = FontResolver.getInstance();
154             int fontIndex = resolver.getFontIndex(ch);
155             f = resolver.getFont(fontIndex, attributes);
156         }
157         if (!oldParagraph.getFontOrGraphicAt(relativePos).equals(f)) {
158             return new StyledParagraph JavaDoc(aci, chars);
159         }
160         
161         // insert into existing paragraph
162
oldParagraph.length += 1;
163         if (oldParagraph.decorations != null) {
164             insertInto(relativePos,
165                        oldParagraph.decorationStarts,
166                        oldParagraph.decorations.size());
167         }
168         if (oldParagraph.fonts != null) {
169             insertInto(relativePos,
170                        oldParagraph.fontStarts,
171                        oldParagraph.fonts.size());
172         }
173         return oldParagraph;
174     }
175     
176     /**
177      * Adjust indices in starts to reflect a deletion after deleteAt.
178      * Any index in starts greater than deleteAt will be increased by 1.
179      * It is the caller's responsibility to make sure that no 0-length
180      * runs result.
181      */

182     private static void deleteFrom(int deleteAt, int[] starts, int numStarts) {
183         
184         while (starts[--numStarts] > deleteAt) {
185             starts[numStarts] -= 1;
186         }
187     }
188     
189     /**
190      * Return a StyledParagraph reflecting the insertion of a single character
191      * into the text. This method will attempt to reuse the given paragraph,
192      * but may create a new paragraph.
193      * @param aci an iterator over the text. The text should be the same as the
194      * text used to create (or most recently update) oldParagraph, with
195      * the exception of deleting a single character at deletePos.
196      * @param chars the characters in aci
197      * @param deletePos the index where a character was removed
198      * @param oldParagraph a StyledParagraph for the text in aci before the
199      * insertion
200      */

201     public static StyledParagraph JavaDoc deleteChar(AttributedCharacterIterator JavaDoc aci,
202                                              char[] chars,
203                                              int deletePos,
204                                              StyledParagraph JavaDoc oldParagraph) {
205     
206         // We will reuse oldParagraph unless there was a length-1 run
207
// at deletePos. We could do more work and check the individual
208
// Font and Decoration runs, but we don't right now...
209
deletePos -= aci.getBeginIndex();
210         
211         if (oldParagraph.decorations == null && oldParagraph.fonts == null) {
212             oldParagraph.length -= 1;
213             return oldParagraph;
214         }
215         
216         if (oldParagraph.getRunLimit(deletePos) == deletePos+1) {
217             if (deletePos == 0 || oldParagraph.getRunLimit(deletePos-1) == deletePos) {
218                 return new StyledParagraph JavaDoc(aci, chars);
219             }
220         }
221         
222         oldParagraph.length -= 1;
223         if (oldParagraph.decorations != null) {
224             deleteFrom(deletePos,
225                        oldParagraph.decorationStarts,
226                        oldParagraph.decorations.size());
227         }
228         if (oldParagraph.fonts != null) {
229             deleteFrom(deletePos,
230                        oldParagraph.fontStarts,
231                        oldParagraph.fonts.size());
232         }
233         return oldParagraph;
234     }
235     
236     /**
237      * Return the index at which there is a different Font, GraphicAttribute, or
238      * Dcoration than at the given index.
239      * @param index a valid index in the paragraph
240      * @return the first index where there is a change in attributes from
241      * those at index
242      */

243     public int getRunLimit(int index) {
244         
245         if (index < 0 || index >= length) {
246             throw new IllegalArgumentException JavaDoc("index out of range");
247         }
248         int limit1 = length;
249         if (decorations != null) {
250             int run = findRunContaining(index, decorationStarts);
251             limit1 = decorationStarts[run+1];
252         }
253         int limit2 = length;
254         if (fonts != null) {
255             int run = findRunContaining(index, fontStarts);
256             limit2 = fontStarts[run+1];
257         }
258         return Math.min(limit1, limit2);
259     }
260     
261     /**
262      * Return the Decoration in effect at the given index.
263      * @param index a valid index in the paragraph
264      * @return the Decoration at index.
265      */

266     public Decoration getDecorationAt(int index) {
267         
268         if (index < 0 || index >= length) {
269             throw new IllegalArgumentException JavaDoc("index out of range");
270         }
271         if (decorations == null) {
272             return decoration;
273         }
274         int run = findRunContaining(index, decorationStarts);
275         return (Decoration) decorations.elementAt(run);
276     }
277     
278     /**
279      * Return the Font or GraphicAttribute in effect at the given index.
280      * The client must test the type of the return value to determine what
281      * it is.
282      * @param index a valid index in the paragraph
283      * @return the Font or GraphicAttribute at index.
284      */

285     public Object JavaDoc getFontOrGraphicAt(int index) {
286         
287         if (index < 0 || index >= length) {
288             throw new IllegalArgumentException JavaDoc("index out of range");
289         }
290         if (fonts == null) {
291             return font;
292         }
293         int run = findRunContaining(index, fontStarts);
294         return fonts.elementAt(run);
295     }
296     
297     /**
298      * Return i such that starts[i] <= index < starts[i+1]. starts
299      * must be in increasing order, with at least one element greater
300      * than index.
301      */

302     private static int findRunContaining(int index, int[] starts) {
303         
304         for (int i=1; true; i++) {
305             if (starts[i] > index) {
306                 return i-1;
307             }
308         }
309     }
310     
311     /**
312      * Append the given Object to the given Vector. Add
313      * the given index to the given starts array. If the
314      * starts array does not have room for the index, a
315      * new array is created and returned.
316      */

317     private static int[] addToVector(Object JavaDoc obj,
318                                      int index,
319                                      Vector JavaDoc v,
320                                      int[] starts) {
321         
322         if (!v.lastElement().equals(obj)) {
323             v.addElement(obj);
324             int count = v.size();
325             if (starts.length == count) {
326                 int[] temp = new int[starts.length*2];
327                 System.arraycopy(starts, 0, temp, 0, starts.length);
328                 starts = temp;
329             }
330             starts[count-1] = index;
331         }
332         return starts;
333     }
334     
335     /**
336      * Add a new Decoration run with the given Decoration at the
337      * given index.
338      */

339     private void addDecoration(Decoration d, int index) {
340         
341         if (decorations != null) {
342             decorationStarts = addToVector(d,
343                                            index,
344                                            decorations,
345                                            decorationStarts);
346         }
347         else if (decoration == null) {
348             decoration = d;
349         }
350         else {
351             if (!decoration.equals(d)) {
352                 decorations = new Vector JavaDoc(INITIAL_SIZE);
353                 decorations.addElement(decoration);
354                 decorations.addElement(d);
355                 decorationStarts = new int[INITIAL_SIZE];
356                 decorationStarts[0] = 0;
357                 decorationStarts[1] = index;
358             }
359         }
360     }
361     
362     /**
363      * Add a new Font/GraphicAttribute run with the given object at the
364      * given index.
365      */

366     private void addFont(Object JavaDoc f, int index) {
367         
368         if (fonts != null) {
369             fontStarts = addToVector(f, index, fonts, fontStarts);
370         }
371         else if (font == null) {
372             font = f;
373         }
374         else {
375             if (!font.equals(f)) {
376                 fonts = new Vector JavaDoc(INITIAL_SIZE);
377                 fonts.addElement(font);
378                 fonts.addElement(f);
379                 fontStarts = new int[INITIAL_SIZE];
380                 fontStarts[0] = 0;
381                 fontStarts[1] = index;
382             }
383         }
384     }
385     
386     /**
387      * Resolve the given chars into Fonts using FontResolver, then add
388      * font runs for each.
389      */

390     private void addFonts(char[] chars, Map JavaDoc attributes, int start, int limit) {
391     
392         FontResolver resolver = FontResolver.getInstance();
393     CodePointIterator iter = CodePointIterator.create(chars, start, limit);
394     for (int runStart = iter.charIndex(); runStart < limit; runStart = iter.charIndex()) {
395         int fontIndex = resolver.nextFontRunIndex(iter);
396             addFont(resolver.getFont(fontIndex, attributes), runStart);
397         }
398     }
399     
400     /**
401      * Return a Map with entries from oldStyles, as well as input
402      * method entries, if any.
403      */

404     static Map JavaDoc addInputMethodAttrs(Map JavaDoc oldStyles) {
405
406         Object JavaDoc value = oldStyles.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
407
408         try {
409             if (value != null) {
410                 if (value instanceof Annotation JavaDoc) {
411                     value = ((Annotation JavaDoc)value).getValue();
412                 }
413
414                 InputMethodHighlight JavaDoc hl;
415                 hl = (InputMethodHighlight JavaDoc) value;
416
417                 Map JavaDoc imStyles = null;
418                 try {
419                     imStyles = hl.getStyle();
420                 } catch (NoSuchMethodError JavaDoc e) {
421                 }
422                 
423                 if (imStyles == null) {
424                     Toolkit JavaDoc tk = Toolkit.getDefaultToolkit();
425                     imStyles = tk.mapInputMethodHighlight(hl);
426                 }
427
428                 if (imStyles != null) {
429                     Hashtable JavaDoc newStyles = new Hashtable JavaDoc(5, (float)0.9);
430                     newStyles.putAll(oldStyles);
431
432                     newStyles.putAll(imStyles);
433
434                     return newStyles;
435                 }
436             }
437         }
438         catch(ClassCastException JavaDoc e) {
439         }
440
441         return oldStyles;
442     }
443     
444     /**
445      * Extract a GraphicAttribute or Font from the given attributes.
446      * If attributes does not contain a GraphicAttribute, Font, or
447      * Font family entry this method returns null.
448      */

449     private static Object JavaDoc getGraphicOrFont(Map JavaDoc attributes) {
450         
451         Object JavaDoc value = attributes.get(TextAttribute.CHAR_REPLACEMENT);
452         if (value != null) {
453             return value;
454         }
455         value = attributes.get(TextAttribute.FONT);
456         if (value != null) {
457             return value;
458         }
459         
460         if (attributes.get(TextAttribute.FAMILY) != null) {
461             return Font.getFont(attributes);
462         }
463         else {
464             return null;
465         }
466     }
467 }
468
Popular Tags