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 gnu.java.lang.CPStringBuilder;
042    
043    import java.util.ArrayList;
044    import java.util.Arrays;
045    import java.util.HashMap;
046    import java.util.Hashtable;
047    import java.util.Iterator;
048    import java.util.Map;
049    import java.util.Set;
050    
051    /**
052     * This class models a <code>String</code> with attributes over various
053     * subranges of the string.  It allows applications to access this 
054     * information via the <code>AttributedCharacterIterator</code> interface.
055     * 
056     * @since 1.2
057     *
058     * @author Aaron M. Renn (arenn@urbanophile.com)
059     * @since 1.2
060     */
061    public class AttributedString
062    {
063    
064      /** 
065       * The attributes and ranges of text over which those attributes apply. 
066       */
067      final class AttributeRange
068      {
069    
070        /** A Map of the attributes */
071        Map attribs;
072    
073        /** The beginning index of the attributes */
074        int beginIndex;
075    
076        /** The ending index of the attributes */
077        int endIndex;
078    
079        /**
080         * Creates a new attribute range.
081         * 
082         * @param attribs  the attributes.
083         * @param beginIndex  the start index.
084         * @param endIndex  the end index.
085         */
086        AttributeRange(Map attribs, int beginIndex, int endIndex) 
087        {
088          this.attribs = attribs;
089          this.beginIndex = beginIndex;
090          this.endIndex = endIndex;
091        }
092    
093      } // Inner class AttributeRange
094    
095      /** The string we are representing. */
096      private StringCharacterIterator sci;
097    
098      /** The attribute information */
099      private AttributeRange[] attribs;
100    
101      /**
102       * Creates a new instance of <code>AttributedString</code>
103       * that represents the specified <code>String</code> with no attributes.
104       *
105       * @param str The <code>String</code> to be attributed (<code>null</code> not
106       *            permitted).
107       * 
108       * @throws NullPointerException if <code>str</code> is <code>null</code>.
109       */
110      public AttributedString(String str)
111      {
112        sci = new StringCharacterIterator(str);
113        attribs = new AttributeRange[0];
114      }
115    
116      /**
117       * Creates a new instance of <code>AttributedString</code>
118       * that represents that specified <code>String</code> with the specified
119       * attributes over the entire length of the <code>String</code>.
120       *
121       * @param str The <code>String</code> to be attributed.
122       * @param attributes The attribute list.
123       */
124      public AttributedString(String str,
125                              Map<? extends AttributedCharacterIterator.Attribute, ?> attributes)
126      {
127        this(str);
128    
129        attribs = new AttributeRange[1];
130        attribs[0] = new AttributeRange(attributes, 0, str.length());
131      }
132    
133      /**
134       * Initializes a new instance of <code>AttributedString</code>
135       * that will use the text and attribute information from the specified
136       * <code>AttributedCharacterIterator</code>.
137       *
138       * @param aci The <code>AttributedCharacterIterator</code> containing the 
139       *            text and attribute information (<code>null</code> not 
140       *            permitted).
141       * 
142       * @throws NullPointerException if <code>aci</code> is <code>null</code>.
143       */
144      public AttributedString(AttributedCharacterIterator aci)
145      {
146        this(aci, aci.getBeginIndex(), aci.getEndIndex(), null);
147      }
148    
149      /**
150       * Initializes a new instance of <code>AttributedString</code>
151       * that will use the text and attribute information from the specified
152       * subrange of the specified <code>AttributedCharacterIterator</code>.
153       *
154       * @param aci The <code>AttributedCharacterIterator</code> containing the 
155       *            text and attribute information.
156       * @param beginIndex The beginning index of the text subrange.
157       * @param endIndex The ending index of the text subrange.
158       */
159      public AttributedString(AttributedCharacterIterator aci, int beginIndex,
160                              int endIndex)
161      {
162        this(aci, beginIndex, endIndex, null);
163      }
164    
165      /**
166       * Initializes a new instance of <code>AttributedString</code>
167       * that will use the text and attribute information from the specified
168       * subrange of the specified <code>AttributedCharacterIterator</code>.
169       * Only attributes from the source iterator that are present in the
170       * specified array of attributes will be included in the attribute list
171       * for this object.
172       *
173       * @param aci The <code>AttributedCharacterIterator</code> containing the 
174       *            text and attribute information.
175       * @param begin The beginning index of the text subrange.
176       * @param end The ending index of the text subrange.
177       * @param attributes A list of attributes to include from the iterator, or 
178       *                   <code>null</code> to include all attributes.
179       */
180      public AttributedString(AttributedCharacterIterator aci, int begin, int end, 
181                              AttributedCharacterIterator.Attribute[] attributes)
182      {
183        // Validate some arguments
184        if ((begin < 0) || (end < begin) || end > aci.getEndIndex())
185          throw new IllegalArgumentException("Bad index values");
186    
187        CPStringBuilder sb = new CPStringBuilder("");
188    
189        // Get the valid attribute list
190        Set allAttribs = aci.getAllAttributeKeys();
191        if (attributes != null)
192          allAttribs.retainAll(Arrays.asList(attributes));
193    
194        // Loop through and extract the attributes
195        char c = aci.setIndex(begin);
196    
197        ArrayList accum = new ArrayList();
198        do
199          { 
200            sb.append(c);
201    
202            Iterator iter = allAttribs.iterator();
203            while(iter.hasNext())
204              {
205                Object obj = iter.next();
206    
207                // What should we do if this is not true?
208                if (!(obj instanceof AttributedCharacterIterator.Attribute))
209                  continue;
210    
211                AttributedCharacterIterator.Attribute attrib = 
212                  (AttributedCharacterIterator.Attribute)obj;
213    
214                // Make sure the attribute is defined.
215                Object attribObj = aci.getAttribute(attrib);
216                if (attribObj == null)
217                  continue;
218                int rl = aci.getRunLimit(attrib);
219                if (rl > end)
220                  rl = end;
221                rl -= begin;
222    
223                // Check to see if we already processed this one
224                int rs = aci.getRunStart(attrib);
225                if ((rs < aci.getIndex()) && (aci.getIndex() != begin))
226                  continue;
227    
228                // If the attribute run starts before the beginning index, we
229                // need to junk it if it is an Annotation.
230                rs -= begin;
231                if (rs < 0)
232                  {
233                    if (attribObj instanceof Annotation)
234                       continue;
235    
236                    rs = 0;
237                  }
238    
239                // Create a map object.  Yes this will only contain one attribute
240                Map newMap = new Hashtable();
241                newMap.put(attrib, attribObj);
242    
243                // Add it to the attribute list.
244                accum.add(new AttributeRange(newMap, rs, rl));
245              }
246    
247            c = aci.next();
248          }
249        while( aci.getIndex() < end );
250    
251        attribs = new AttributeRange[accum.size()];
252        attribs = (AttributeRange[]) accum.toArray(attribs);
253    
254        sci = new StringCharacterIterator(sb.toString());
255      }
256    
257      /**
258       * Adds a new attribute that will cover the entire string.
259       *
260       * @param attrib The attribute to add.
261       * @param value The value of the attribute.
262       */
263      public void addAttribute(AttributedCharacterIterator.Attribute attrib, 
264              Object value)
265      {
266        addAttribute(attrib, value, 0, sci.getEndIndex());
267      }
268    
269      /**
270       * Adds a new attribute that will cover the specified subrange
271       * of the string.
272       *
273       * @param attrib The attribute to add.
274       * @param value The value of the attribute, which may be <code>null</code>.
275       * @param begin The beginning index of the subrange.
276       * @param end The ending index of the subrange.
277       *
278       * @exception IllegalArgumentException If attribute is <code>null</code> or 
279       *            the subrange is not valid.
280       */
281      public void addAttribute(AttributedCharacterIterator.Attribute attrib, 
282              Object value, int begin, int end)
283      {
284        if (attrib == null)
285          throw new IllegalArgumentException("null attribute");
286        if (end <= begin)
287          throw new IllegalArgumentException("Requires end > begin");
288        HashMap hm = new HashMap();
289        hm.put(attrib, value);
290    
291        addAttributes(hm, begin, end);
292      }
293    
294      /**
295       * Adds all of the attributes in the specified list to the
296       * specified subrange of the string.
297       *
298       * @param attributes The list of attributes.
299       * @param beginIndex The beginning index.
300       * @param endIndex The ending index
301       *
302       * @throws NullPointerException if <code>attributes</code> is 
303       *         <code>null</code>.
304       * @throws IllegalArgumentException if the subrange is not valid.
305       */
306      public void addAttributes(Map<? extends AttributedCharacterIterator.Attribute, ?> attributes,
307                                int beginIndex, int endIndex)
308      {
309        if (attributes == null)
310          throw new NullPointerException("null attribute");
311    
312        if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
313            (endIndex <= beginIndex))
314          throw new IllegalArgumentException("bad range");
315    
316        AttributeRange[] new_list = new AttributeRange[attribs.length + 1];
317        System.arraycopy(attribs, 0, new_list, 0, attribs.length);
318        attribs = new_list;
319        attribs[attribs.length - 1] = new AttributeRange(attributes, beginIndex, 
320                                                         endIndex);
321      } 
322    
323      /**
324       * Returns an <code>AttributedCharacterIterator</code> that 
325       * will iterate over the entire string.
326       *
327       * @return An <code>AttributedCharacterIterator</code> for the entire string.
328       */
329      public AttributedCharacterIterator getIterator()
330      {
331        return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(), 
332                null));
333      }
334    
335      /**
336       * Returns an <code>AttributedCharacterIterator</code> that
337       * will iterate over the entire string.  This iterator will return information
338       * about the list of attributes in the specified array.  Attributes not in
339       * the array may or may not be returned by the iterator.  If the specified
340       * array is <code>null</code>, all attributes will be returned.
341       *
342       * @param attributes A list of attributes to include in the returned iterator.
343       *
344       * @return An <code>AttributedCharacterIterator</code> for this string.
345       */
346      public AttributedCharacterIterator getIterator(
347              AttributedCharacterIterator.Attribute[] attributes)
348      {
349        return(getIterator(attributes, 0, sci.getEndIndex()));
350      }
351    
352      /**
353       * Returns an <code>AttributedCharacterIterator</code> that
354       * will iterate over the specified subrange.  This iterator will return 
355       * information about the list of attributes in the specified array.  
356       * Attributes not in the array may or may not be returned by the iterator.  
357       * If the specified array is <code>null</code>, all attributes will be 
358       * returned.  
359       *
360       * @param attributes A list of attributes to include in the returned iterator.
361       * @param beginIndex The beginning index of the subrange.
362       * @param endIndex The ending index of the subrange.
363       *
364       * @return An <code>AttributedCharacterIterator</code> for this string.
365       */
366      public AttributedCharacterIterator getIterator(
367              AttributedCharacterIterator.Attribute[] attributes, 
368              int beginIndex, int endIndex)
369      {
370        if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
371            (endIndex < beginIndex))
372          throw new IllegalArgumentException("bad range");
373    
374        return(new AttributedStringIterator(sci, attribs, beginIndex, endIndex,
375                                            attributes));
376      }
377    
378    } // class AttributedString