001    /* AttributedString.java -- Models text with attributes
002       Copyright (C) 1998, 1999, 2004, 2005, 2006, Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010     
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.text;
040    
041    import java.util.ArrayList;
042    import java.util.Arrays;
043    import java.util.HashMap;
044    import java.util.Hashtable;
045    import java.util.Iterator;
046    import java.util.Map;
047    import java.util.Set;
048    
049    /**
050     * This class models a <code>String</code> with attributes over various
051     * subranges of the string.  It allows applications to access this 
052     * information via the <code>AttributedCharacterIterator</code> interface.
053     * 
054     * @since 1.2
055     *
056     * @author Aaron M. Renn (arenn@urbanophile.com)
057     * @since 1.2
058     */
059    public class AttributedString
060    {
061    
062      /** 
063       * The attributes and ranges of text over which those attributes apply. 
064       */
065      final class AttributeRange
066      {
067    
068        /** A Map of the attributes */
069        Map attribs;
070    
071        /** The beginning index of the attributes */
072        int beginIndex;
073    
074        /** The ending index of the attributes */
075        int endIndex;
076    
077        /**
078         * Creates a new attribute range.
079         * 
080         * @param attribs  the attributes.
081         * @param beginIndex  the start index.
082         * @param endIndex  the end index.
083         */
084        AttributeRange(Map attribs, int beginIndex, int endIndex) 
085        {
086          this.attribs = attribs;
087          this.beginIndex = beginIndex;
088          this.endIndex = endIndex;
089        }
090    
091      } // Inner class AttributeRange
092    
093      /** The string we are representing. */
094      private StringCharacterIterator sci;
095    
096      /** The attribute information */
097      private AttributeRange[] attribs;
098    
099      /**
100       * Creates a new instance of <code>AttributedString</code>
101       * that represents the specified <code>String</code> with no attributes.
102       *
103       * @param str The <code>String</code> to be attributed (<code>null</code> not
104       *            permitted).
105       * 
106       * @throws NullPointerException if <code>str</code> is <code>null</code>.
107       */
108      public AttributedString(String str)
109      {
110        sci = new StringCharacterIterator(str);
111        attribs = new AttributeRange[0];
112      }
113    
114      /**
115       * Creates a new instance of <code>AttributedString</code>
116       * that represents that specified <code>String</code> with the specified
117       * attributes over the entire length of the <code>String</code>.
118       *
119       * @param str The <code>String</code> to be attributed.
120       * @param attributes The attribute list.
121       */
122      public AttributedString(String str,
123                              Map<? extends AttributedCharacterIterator.Attribute, ?> attributes)
124      {
125        this(str);
126    
127        attribs = new AttributeRange[1];
128        attribs[0] = new AttributeRange(attributes, 0, str.length());
129      }
130    
131      /**
132       * Initializes a new instance of <code>AttributedString</code>
133       * that will use the text and attribute information from the specified
134       * <code>AttributedCharacterIterator</code>.
135       *
136       * @param aci The <code>AttributedCharacterIterator</code> containing the 
137       *            text and attribute information (<code>null</code> not 
138       *            permitted).
139       * 
140       * @throws NullPointerException if <code>aci</code> is <code>null</code>.
141       */
142      public AttributedString(AttributedCharacterIterator aci)
143      {
144        this(aci, aci.getBeginIndex(), aci.getEndIndex(), null);
145      }
146    
147      /**
148       * Initializes a new instance of <code>AttributedString</code>
149       * that will use the text and attribute information from the specified
150       * subrange of the specified <code>AttributedCharacterIterator</code>.
151       *
152       * @param aci The <code>AttributedCharacterIterator</code> containing the 
153       *            text and attribute information.
154       * @param beginIndex The beginning index of the text subrange.
155       * @param endIndex The ending index of the text subrange.
156       */
157      public AttributedString(AttributedCharacterIterator aci, int beginIndex,
158                              int endIndex)
159      {
160        this(aci, beginIndex, endIndex, null);
161      }
162    
163      /**
164       * Initializes a new instance of <code>AttributedString</code>
165       * that will use the text and attribute information from the specified
166       * subrange of the specified <code>AttributedCharacterIterator</code>.
167       * Only attributes from the source iterator that are present in the
168       * specified array of attributes will be included in the attribute list
169       * for this object.
170       *
171       * @param aci The <code>AttributedCharacterIterator</code> containing the 
172       *            text and attribute information.
173       * @param begin The beginning index of the text subrange.
174       * @param end The ending index of the text subrange.
175       * @param attributes A list of attributes to include from the iterator, or 
176       *                   <code>null</code> to include all attributes.
177       */
178      public AttributedString(AttributedCharacterIterator aci, int begin, int end, 
179                              AttributedCharacterIterator.Attribute[] attributes)
180      {
181        // Validate some arguments
182        if ((begin < 0) || (end < begin) || end > aci.getEndIndex())
183          throw new IllegalArgumentException("Bad index values");
184    
185        StringBuffer sb = new StringBuffer("");
186    
187        // Get the valid attribute list
188        Set allAttribs = aci.getAllAttributeKeys();
189        if (attributes != null)
190          allAttribs.retainAll(Arrays.asList(attributes));
191    
192        // Loop through and extract the attributes
193        char c = aci.setIndex(begin);
194    
195        ArrayList accum = new ArrayList();
196        do
197          { 
198            sb.append(c);
199    
200            Iterator iter = allAttribs.iterator();
201            while(iter.hasNext())
202              {
203                Object obj = iter.next();
204    
205                // What should we do if this is not true?
206                if (!(obj instanceof AttributedCharacterIterator.Attribute))
207                  continue;
208    
209                AttributedCharacterIterator.Attribute attrib = 
210                  (AttributedCharacterIterator.Attribute)obj;
211    
212                // Make sure the attribute is defined.
213                Object attribObj = aci.getAttribute(attrib);
214                if (attribObj == null)
215                  continue;
216                int rl = aci.getRunLimit(attrib);
217                if (rl > end)
218                  rl = end;
219                rl -= begin;
220    
221                // Check to see if we already processed this one
222                int rs = aci.getRunStart(attrib);
223                if ((rs < aci.getIndex()) && (aci.getIndex() != begin))
224                  continue;
225    
226                // If the attribute run starts before the beginning index, we
227                // need to junk it if it is an Annotation.
228                rs -= begin;
229                if (rs < 0)
230                  {
231                    if (attribObj instanceof Annotation)
232                       continue;
233    
234                    rs = 0;
235                  }
236    
237                // Create a map object.  Yes this will only contain one attribute
238                Map newMap = new Hashtable();
239                newMap.put(attrib, attribObj);
240    
241                // Add it to the attribute list.
242                accum.add(new AttributeRange(newMap, rs, rl));
243              }
244    
245            c = aci.next();
246          }
247        while( aci.getIndex() < end );
248    
249        attribs = new AttributeRange[accum.size()];
250        attribs = (AttributeRange[]) accum.toArray(attribs);
251    
252        sci = new StringCharacterIterator(sb.toString());
253      }
254    
255      /**
256       * Adds a new attribute that will cover the entire string.
257       *
258       * @param attrib The attribute to add.
259       * @param value The value of the attribute.
260       */
261      public void addAttribute(AttributedCharacterIterator.Attribute attrib, 
262              Object value)
263      {
264        addAttribute(attrib, value, 0, sci.getEndIndex());
265      }
266    
267      /**
268       * Adds a new attribute that will cover the specified subrange
269       * of the string.
270       *
271       * @param attrib The attribute to add.
272       * @param value The value of the attribute, which may be <code>null</code>.
273       * @param begin The beginning index of the subrange.
274       * @param end The ending index of the subrange.
275       *
276       * @exception IllegalArgumentException If attribute is <code>null</code> or 
277       *            the subrange is not valid.
278       */
279      public void addAttribute(AttributedCharacterIterator.Attribute attrib, 
280              Object value, int begin, int end)
281      {
282        if (attrib == null)
283          throw new IllegalArgumentException("null attribute");
284        if (end <= begin)
285          throw new IllegalArgumentException("Requires end > begin");
286        HashMap hm = new HashMap();
287        hm.put(attrib, value);
288    
289        addAttributes(hm, begin, end);
290      }
291    
292      /**
293       * Adds all of the attributes in the specified list to the
294       * specified subrange of the string.
295       *
296       * @param attributes The list of attributes.
297       * @param beginIndex The beginning index.
298       * @param endIndex The ending index
299       *
300       * @throws NullPointerException if <code>attributes</code> is 
301       *         <code>null</code>.
302       * @throws IllegalArgumentException if the subrange is not valid.
303       */
304      public void addAttributes(Map<? extends AttributedCharacterIterator.Attribute, ?> attributes,
305                                int beginIndex, int endIndex)
306      {
307        if (attributes == null)
308          throw new NullPointerException("null attribute");
309    
310        if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
311            (endIndex <= beginIndex))
312          throw new IllegalArgumentException("bad range");
313    
314        AttributeRange[] new_list = new AttributeRange[attribs.length + 1];
315        System.arraycopy(attribs, 0, new_list, 0, attribs.length);
316        attribs = new_list;
317        attribs[attribs.length - 1] = new AttributeRange(attributes, beginIndex, 
318                                                         endIndex);
319      } 
320    
321      /**
322       * Returns an <code>AttributedCharacterIterator</code> that 
323       * will iterate over the entire string.
324       *
325       * @return An <code>AttributedCharacterIterator</code> for the entire string.
326       */
327      public AttributedCharacterIterator getIterator()
328      {
329        return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(), 
330                null));
331      }
332    
333      /**
334       * Returns an <code>AttributedCharacterIterator</code> that
335       * will iterate over the entire string.  This iterator will return information
336       * about the list of attributes in the specified array.  Attributes not in
337       * the array may or may not be returned by the iterator.  If the specified
338       * array is <code>null</code>, all attributes will be returned.
339       *
340       * @param attributes A list of attributes to include in the returned iterator.
341       *
342       * @return An <code>AttributedCharacterIterator</code> for this string.
343       */
344      public AttributedCharacterIterator getIterator(
345              AttributedCharacterIterator.Attribute[] attributes)
346      {
347        return(getIterator(attributes, 0, sci.getEndIndex()));
348      }
349    
350      /**
351       * Returns an <code>AttributedCharacterIterator</code> that
352       * will iterate over the specified subrange.  This iterator will return 
353       * information about the list of attributes in the specified array.  
354       * Attributes not in the array may or may not be returned by the iterator.  
355       * If the specified array is <code>null</code>, all attributes will be 
356       * returned.  
357       *
358       * @param attributes A list of attributes to include in the returned iterator.
359       * @param beginIndex The beginning index of the subrange.
360       * @param endIndex The ending index of the subrange.
361       *
362       * @return An <code>AttributedCharacterIterator</code> for this string.
363       */
364      public AttributedCharacterIterator getIterator(
365              AttributedCharacterIterator.Attribute[] attributes, 
366              int beginIndex, int endIndex)
367      {
368        if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
369            (endIndex < beginIndex))
370          throw new IllegalArgumentException("bad range");
371    
372        return(new AttributedStringIterator(sci, attribs, beginIndex, endIndex,
373                                            attributes));
374      }
375    
376    } // class AttributedString