AttributedString.java 12.8 KB
Newer Older
Tom Tromey committed
1
/* AttributedString.java -- Models text with attributes
2
   Copyright (C) 1998, 1999, 2004, 2005, 2006, 2012 Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
10

Tom Tromey committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.text;

41 42
import gnu.java.lang.CPStringBuilder;

Tom Tromey committed
43 44 45 46 47 48 49 50
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

51 52
import static java.text.AttributedCharacterIterator.Attribute;

Tom Tromey committed
53
/**
Tom Tromey committed
54
 * This class models a <code>String</code> with attributes over various
55
 * subranges of the string.  It allows applications to access this
56
 * information via the <code>AttributedCharacterIterator</code> interface.
57
 *
58
 * @since 1.2
Tom Tromey committed
59 60
 *
 * @author Aaron M. Renn (arenn@urbanophile.com)
61
 * @since 1.2
Tom Tromey committed
62
 */
Tom Tromey committed
63
public class AttributedString
Tom Tromey committed
64 65
{

66 67
  /**
   * The attributes and ranges of text over which those attributes apply.
Tom Tromey committed
68 69 70 71 72
   */
  final class AttributeRange
  {

    /** A Map of the attributes */
73
    Map<? extends Attribute, ?> attribs;
Tom Tromey committed
74 75

    /** The beginning index of the attributes */
76
    int beginIndex;
Tom Tromey committed
77 78

    /** The ending index of the attributes */
79
    int endIndex;
Tom Tromey committed
80 81 82

    /**
     * Creates a new attribute range.
83
     *
Tom Tromey committed
84
     * @param attribs  the attributes.
85 86
     * @param beginIndex  the start index.
     * @param endIndex  the end index.
Tom Tromey committed
87
     */
88 89
    AttributeRange(Map<? extends Attribute, ?> attribs,
                   int beginIndex, int endIndex)
Tom Tromey committed
90 91
    {
      this.attribs = attribs;
92 93
      this.beginIndex = beginIndex;
      this.endIndex = endIndex;
Tom Tromey committed
94 95
    }

Tom Tromey committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109
  } // Inner class AttributeRange

  /** The string we are representing. */
  private StringCharacterIterator sci;

  /** The attribute information */
  private AttributeRange[] attribs;

  /**
   * Creates a new instance of <code>AttributedString</code>
   * that represents the specified <code>String</code> with no attributes.
   *
   * @param str The <code>String</code> to be attributed (<code>null</code> not
   *            permitted).
110
   *
Tom Tromey committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
   * @throws NullPointerException if <code>str</code> is <code>null</code>.
   */
  public AttributedString(String str)
  {
    sci = new StringCharacterIterator(str);
    attribs = new AttributeRange[0];
  }

  /**
   * Creates a new instance of <code>AttributedString</code>
   * that represents that specified <code>String</code> with the specified
   * attributes over the entire length of the <code>String</code>.
   *
   * @param str The <code>String</code> to be attributed.
   * @param attributes The attribute list.
   */
127
  public AttributedString(String str,
128
                          Map<? extends Attribute, ?> attributes)
Tom Tromey committed
129 130 131 132 133 134 135 136 137 138 139 140
  {
    this(str);

    attribs = new AttributeRange[1];
    attribs[0] = new AttributeRange(attributes, 0, str.length());
  }

  /**
   * Initializes a new instance of <code>AttributedString</code>
   * that will use the text and attribute information from the specified
   * <code>AttributedCharacterIterator</code>.
   *
141 142
   * @param aci The <code>AttributedCharacterIterator</code> containing the
   *            text and attribute information (<code>null</code> not
Tom Tromey committed
143
   *            permitted).
144
   *
Tom Tromey committed
145 146 147 148 149 150 151 152 153 154 155 156
   * @throws NullPointerException if <code>aci</code> is <code>null</code>.
   */
  public AttributedString(AttributedCharacterIterator aci)
  {
    this(aci, aci.getBeginIndex(), aci.getEndIndex(), null);
  }

  /**
   * Initializes a new instance of <code>AttributedString</code>
   * that will use the text and attribute information from the specified
   * subrange of the specified <code>AttributedCharacterIterator</code>.
   *
157
   * @param aci The <code>AttributedCharacterIterator</code> containing the
Tom Tromey committed
158
   *            text and attribute information.
159 160
   * @param beginIndex The beginning index of the text subrange.
   * @param endIndex The ending index of the text subrange.
Tom Tromey committed
161
   */
162 163
  public AttributedString(AttributedCharacterIterator aci, int beginIndex,
                          int endIndex)
Tom Tromey committed
164
  {
165
    this(aci, beginIndex, endIndex, null);
Tom Tromey committed
166 167 168 169 170 171 172 173 174 175
  }

  /**
   * Initializes a new instance of <code>AttributedString</code>
   * that will use the text and attribute information from the specified
   * subrange of the specified <code>AttributedCharacterIterator</code>.
   * Only attributes from the source iterator that are present in the
   * specified array of attributes will be included in the attribute list
   * for this object.
   *
176
   * @param aci The <code>AttributedCharacterIterator</code> containing the
Tom Tromey committed
177
   *            text and attribute information.
178 179
   * @param begin The beginning index of the text subrange.
   * @param end The ending index of the text subrange.
180
   * @param attributes A list of attributes to include from the iterator, or
Tom Tromey committed
181 182
   *                   <code>null</code> to include all attributes.
   */
183
  public AttributedString(AttributedCharacterIterator aci, int begin, int end,
184
                          Attribute[] attributes)
Tom Tromey committed
185 186
  {
    // Validate some arguments
187
    if ((begin < 0) || (end < begin) || end > aci.getEndIndex())
Tom Tromey committed
188 189
      throw new IllegalArgumentException("Bad index values");

190
    CPStringBuilder sb = new CPStringBuilder("");
Tom Tromey committed
191 192

    // Get the valid attribute list
193
    Set<Attribute> allAttribs = aci.getAllAttributeKeys();
Tom Tromey committed
194
    if (attributes != null)
195
      allAttribs.retainAll(Arrays.asList(attributes));
Tom Tromey committed
196 197

    // Loop through and extract the attributes
198
    char c = aci.setIndex(begin);
Tom Tromey committed
199

200
    ArrayList<AttributeRange> accum = new ArrayList<AttributeRange>();
Tom Tromey committed
201
    do
202
      {
Tom Tromey committed
203 204
        sb.append(c);

205
        Iterator<Attribute> iter = allAttribs.iterator();
Tom Tromey committed
206 207 208 209 210
        while(iter.hasNext())
          {
            Object obj = iter.next();

            // What should we do if this is not true?
211
            if (!(obj instanceof Attribute))
Tom Tromey committed
212 213
              continue;

214
            Attribute attrib = (Attribute)obj;
Tom Tromey committed
215 216

            // Make sure the attribute is defined.
217 218
            Object attribObj = aci.getAttribute(attrib);
            if (attribObj == null)
Tom Tromey committed
219
              continue;
220
            int rl = aci.getRunLimit(attrib);
221 222 223
            if (rl > end)
              rl = end;
            rl -= begin;
Tom Tromey committed
224 225 226

            // Check to see if we already processed this one
            int rs = aci.getRunStart(attrib);
227
            if ((rs < aci.getIndex()) && (aci.getIndex() != begin))
Tom Tromey committed
228 229 230 231
              continue;

            // If the attribute run starts before the beginning index, we
            // need to junk it if it is an Annotation.
232
            rs -= begin;
233
            if (rs < 0)
Tom Tromey committed
234
              {
235
                if (attribObj instanceof Annotation)
Tom Tromey committed
236 237
                   continue;

238
                rs = 0;
Tom Tromey committed
239 240 241
              }

            // Create a map object.  Yes this will only contain one attribute
242
            Map<Attribute,Object> newMap = new Hashtable<Attribute,Object>();
243
            newMap.put(attrib, attribObj);
Tom Tromey committed
244 245

            // Add it to the attribute list.
246
            accum.add(new AttributeRange(newMap, rs, rl));
Tom Tromey committed
247 248 249 250
          }

        c = aci.next();
      }
251
    while( aci.getIndex() < end );
Tom Tromey committed
252 253

    attribs = new AttributeRange[accum.size()];
254
    attribs = accum.toArray(attribs);
Tom Tromey committed
255 256 257 258 259 260 261 262 263 264

    sci = new StringCharacterIterator(sb.toString());
  }

  /**
   * Adds a new attribute that will cover the entire string.
   *
   * @param attrib The attribute to add.
   * @param value The value of the attribute.
   */
265
  public void addAttribute(Attribute attrib, Object value)
Tom Tromey committed
266 267 268 269 270 271 272 273 274 275
  {
    addAttribute(attrib, value, 0, sci.getEndIndex());
  }

  /**
   * Adds a new attribute that will cover the specified subrange
   * of the string.
   *
   * @param attrib The attribute to add.
   * @param value The value of the attribute, which may be <code>null</code>.
276 277
   * @param begin The beginning index of the subrange.
   * @param end The ending index of the subrange.
Tom Tromey committed
278
   *
279
   * @exception IllegalArgumentException If attribute is <code>null</code> or
Tom Tromey committed
280 281
   *            the subrange is not valid.
   */
282
  public void addAttribute(Attribute attrib, Object value, int begin, int end)
Tom Tromey committed
283 284 285
  {
    if (attrib == null)
      throw new IllegalArgumentException("null attribute");
286 287
    if (end <= begin)
      throw new IllegalArgumentException("Requires end > begin");
288
    HashMap<Attribute,Object> hm = new HashMap<Attribute,Object>();
Tom Tromey committed
289 290
    hm.put(attrib, value);

291
    addAttributes(hm, begin, end);
Tom Tromey committed
292 293 294 295 296 297 298
  }

  /**
   * Adds all of the attributes in the specified list to the
   * specified subrange of the string.
   *
   * @param attributes The list of attributes.
299 300
   * @param beginIndex The beginning index.
   * @param endIndex The ending index
Tom Tromey committed
301
   *
302
   * @throws NullPointerException if <code>attributes</code> is
303 304
   *         <code>null</code>.
   * @throws IllegalArgumentException if the subrange is not valid.
Tom Tromey committed
305
   */
306
  public void addAttributes(Map<? extends Attribute, ?> attributes,
307
                            int beginIndex, int endIndex)
Tom Tromey committed
308 309
  {
    if (attributes == null)
310
      throw new NullPointerException("null attribute");
Tom Tromey committed
311

312 313
    if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
        (endIndex <= beginIndex))
Tom Tromey committed
314 315 316 317 318
      throw new IllegalArgumentException("bad range");

    AttributeRange[] new_list = new AttributeRange[attribs.length + 1];
    System.arraycopy(attribs, 0, new_list, 0, attribs.length);
    attribs = new_list;
319
    attribs[attribs.length - 1] = new AttributeRange(attributes, beginIndex,
320
                                                     endIndex);
321
  }
Tom Tromey committed
322 323

  /**
324
   * Returns an <code>AttributedCharacterIterator</code> that
Tom Tromey committed
325 326 327 328 329 330
   * will iterate over the entire string.
   *
   * @return An <code>AttributedCharacterIterator</code> for the entire string.
   */
  public AttributedCharacterIterator getIterator()
  {
331
    return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(),
Tom Tromey committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345
            null));
  }

  /**
   * Returns an <code>AttributedCharacterIterator</code> that
   * will iterate over the entire string.  This iterator will return information
   * about the list of attributes in the specified array.  Attributes not in
   * the array may or may not be returned by the iterator.  If the specified
   * array is <code>null</code>, all attributes will be returned.
   *
   * @param attributes A list of attributes to include in the returned iterator.
   *
   * @return An <code>AttributedCharacterIterator</code> for this string.
   */
346
  public AttributedCharacterIterator getIterator(Attribute[] attributes)
Tom Tromey committed
347 348 349 350 351 352
  {
    return(getIterator(attributes, 0, sci.getEndIndex()));
  }

  /**
   * Returns an <code>AttributedCharacterIterator</code> that
353 354 355 356 357
   * will iterate over the specified subrange.  This iterator will return
   * information about the list of attributes in the specified array.
   * Attributes not in the array may or may not be returned by the iterator.
   * If the specified array is <code>null</code>, all attributes will be
   * returned.
Tom Tromey committed
358 359
   *
   * @param attributes A list of attributes to include in the returned iterator.
360 361
   * @param beginIndex The beginning index of the subrange.
   * @param endIndex The ending index of the subrange.
Tom Tromey committed
362 363 364
   *
   * @return An <code>AttributedCharacterIterator</code> for this string.
   */
365
  public AttributedCharacterIterator getIterator(Attribute[] attributes,
366
          int beginIndex, int endIndex)
Tom Tromey committed
367
  {
368 369
    if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) ||
        (endIndex < beginIndex))
Tom Tromey committed
370 371
      throw new IllegalArgumentException("bad range");

372
    return(new AttributedStringIterator(sci, attribs, beginIndex, endIndex,
Tom Tromey committed
373 374
                                        attributes));
  }
Tom Tromey committed
375 376

} // class AttributedString