Source Code Cross Referenced for AttributedString.java in  » 6.0-JDK-Core » text » java » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » text » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package java.text;
0027
0028        import java.util.*;
0029        import java.text.AttributedCharacterIterator.Attribute;
0030
0031        /**
0032         * An AttributedString holds text and related attribute information. It
0033         * may be used as the actual data storage in some cases where a text
0034         * reader wants to access attributed text through the AttributedCharacterIterator
0035         * interface.
0036         *
0037         * <p>
0038         * An attribute is a key/value pair, identified by the key.  No two
0039         * attributes on a given character can have the same key.
0040         *
0041         * <p>The values for an attribute are immutable, or must not be mutated
0042         * by clients or storage.  They are always passed by reference, and not
0043         * cloned.
0044         *
0045         * @see AttributedCharacterIterator
0046         * @see Annotation
0047         * @since 1.2
0048         */
0049
0050        public class AttributedString {
0051
0052            // since there are no vectors of int, we have to use arrays.
0053            // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
0054            private static final int ARRAY_SIZE_INCREMENT = 10;
0055
0056            // field holding the text
0057            String text;
0058
0059            // fields holding run attribute information
0060            // run attributes are organized by run
0061            int runArraySize; // current size of the arrays
0062            int runCount; // actual number of runs, <= runArraySize
0063            int runStarts[]; // start index for each run
0064            Vector runAttributes[]; // vector of attribute keys for each run
0065            Vector runAttributeValues[]; // parallel vector of attribute values for each run
0066
0067            /**
0068             * Constructs an AttributedString instance with the given
0069             * AttributedCharacterIterators.
0070             *
0071             * @param iterators AttributedCharacterIterators to construct
0072             * AttributedString from.
0073             * @throws NullPointerException if iterators is null
0074             */
0075            AttributedString(AttributedCharacterIterator[] iterators) {
0076                if (iterators == null) {
0077                    throw new NullPointerException("Iterators must not be null");
0078                }
0079                if (iterators.length == 0) {
0080                    text = "";
0081                } else {
0082                    // Build the String contents
0083                    StringBuffer buffer = new StringBuffer();
0084                    for (int counter = 0; counter < iterators.length; counter++) {
0085                        appendContents(buffer, iterators[counter]);
0086                    }
0087
0088                    text = buffer.toString();
0089
0090                    if (text.length() > 0) {
0091                        // Determine the runs, creating a new run when the attributes
0092                        // differ.
0093                        int offset = 0;
0094                        Map last = null;
0095
0096                        for (int counter = 0; counter < iterators.length; counter++) {
0097                            AttributedCharacterIterator iterator = iterators[counter];
0098                            int start = iterator.getBeginIndex();
0099                            int end = iterator.getEndIndex();
0100                            int index = start;
0101
0102                            while (index < end) {
0103                                iterator.setIndex(index);
0104
0105                                Map attrs = iterator.getAttributes();
0106
0107                                if (mapsDiffer(last, attrs)) {
0108                                    setAttributes(attrs, index - start + offset);
0109                                }
0110                                last = attrs;
0111                                index = iterator.getRunLimit();
0112                            }
0113                            offset += (end - start);
0114                        }
0115                    }
0116                }
0117            }
0118
0119            /**
0120             * Constructs an AttributedString instance with the given text.
0121             * @param text The text for this attributed string.
0122             * @exception NullPointerException if <code>text</code> is null.
0123             */
0124            public AttributedString(String text) {
0125                if (text == null) {
0126                    throw new NullPointerException();
0127                }
0128                this .text = text;
0129            }
0130
0131            /**
0132             * Constructs an AttributedString instance with the given text and attributes.
0133             * @param text The text for this attributed string.
0134             * @param attributes The attributes that apply to the entire string.
0135             * @exception NullPointerException if <code>text</code> or
0136             *            <code>attributes</code> is null.
0137             * @exception IllegalArgumentException if the text has length 0
0138             * and the attributes parameter is not an empty Map (attributes
0139             * cannot be applied to a 0-length range).
0140             */
0141            public AttributedString(String text,
0142                    Map<? extends Attribute, ?> attributes) {
0143                if (text == null || attributes == null) {
0144                    throw new NullPointerException();
0145                }
0146                this .text = text;
0147
0148                if (text.length() == 0) {
0149                    if (attributes.isEmpty())
0150                        return;
0151                    throw new IllegalArgumentException(
0152                            "Can't add attribute to 0-length text");
0153                }
0154
0155                int attributeCount = attributes.size();
0156                if (attributeCount > 0) {
0157                    createRunAttributeDataVectors();
0158                    Vector newRunAttributes = new Vector(attributeCount);
0159                    Vector newRunAttributeValues = new Vector(attributeCount);
0160                    runAttributes[0] = newRunAttributes;
0161                    runAttributeValues[0] = newRunAttributeValues;
0162                    Iterator iterator = attributes.entrySet().iterator();
0163                    while (iterator.hasNext()) {
0164                        Map.Entry entry = (Map.Entry) iterator.next();
0165                        newRunAttributes.addElement(entry.getKey());
0166                        newRunAttributeValues.addElement(entry.getValue());
0167                    }
0168                }
0169            }
0170
0171            /**
0172             * Constructs an AttributedString instance with the given attributed
0173             * text represented by AttributedCharacterIterator.
0174             * @param text The text for this attributed string.
0175             * @exception NullPointerException if <code>text</code> is null.
0176             */
0177            public AttributedString(AttributedCharacterIterator text) {
0178                // If performance is critical, this constructor should be
0179                // implemented here rather than invoking the constructor for a
0180                // subrange. We can avoid some range checking in the loops.
0181                this (text, text.getBeginIndex(), text.getEndIndex(), null);
0182            }
0183
0184            /**
0185             * Constructs an AttributedString instance with the subrange of
0186             * the given attributed text represented by
0187             * AttributedCharacterIterator. If the given range produces an
0188             * empty text, all attributes will be discarded.  Note that any
0189             * attributes wrapped by an Annotation object are discarded for a
0190             * subrange of the original attribute range.
0191             *
0192             * @param text The text for this attributed string.
0193             * @param beginIndex Index of the first character of the range.
0194             * @param endIndex Index of the character following the last character
0195             * of the range.
0196             * @exception NullPointerException if <code>text</code> is null.
0197             * @exception IllegalArgumentException if the subrange given by
0198             * beginIndex and endIndex is out of the text range.
0199             * @see java.text.Annotation
0200             */
0201            public AttributedString(AttributedCharacterIterator text,
0202                    int beginIndex, int endIndex) {
0203                this (text, beginIndex, endIndex, null);
0204            }
0205
0206            /**
0207             * Constructs an AttributedString instance with the subrange of
0208             * the given attributed text represented by
0209             * AttributedCharacterIterator.  Only attributes that match the
0210             * given attributes will be incorporated into the instance. If the
0211             * given range produces an empty text, all attributes will be
0212             * discarded. Note that any attributes wrapped by an Annotation
0213             * object are discarded for a subrange of the original attribute
0214             * range.
0215             *
0216             * @param text The text for this attributed string.
0217             * @param beginIndex Index of the first character of the range.
0218             * @param endIndex Index of the character following the last character
0219             * of the range.
0220             * @param attributes Specifies attributes to be extracted
0221             * from the text. If null is specified, all available attributes will
0222             * be used.
0223             * @exception NullPointerException if <code>text</code> is null.
0224             * @exception IllegalArgumentException if the subrange given by
0225             * beginIndex and endIndex is out of the text range.
0226             * @see java.text.Annotation
0227             */
0228            public AttributedString(AttributedCharacterIterator text,
0229                    int beginIndex, int endIndex, Attribute[] attributes) {
0230                if (text == null) {
0231                    throw new NullPointerException();
0232                }
0233
0234                // Validate the given subrange
0235                int textBeginIndex = text.getBeginIndex();
0236                int textEndIndex = text.getEndIndex();
0237                if (beginIndex < textBeginIndex || endIndex > textEndIndex
0238                        || beginIndex > endIndex)
0239                    throw new IllegalArgumentException(
0240                            "Invalid substring range");
0241
0242                // Copy the given string
0243                StringBuffer textBuffer = new StringBuffer();
0244                text.setIndex(beginIndex);
0245                for (char c = text.current(); text.getIndex() < endIndex; c = text
0246                        .next())
0247                    textBuffer.append(c);
0248                this .text = textBuffer.toString();
0249
0250                if (beginIndex == endIndex)
0251                    return;
0252
0253                // Select attribute keys to be taken care of
0254                HashSet keys = new HashSet();
0255                if (attributes == null) {
0256                    keys.addAll(text.getAllAttributeKeys());
0257                } else {
0258                    for (int i = 0; i < attributes.length; i++)
0259                        keys.add(attributes[i]);
0260                    keys.retainAll(text.getAllAttributeKeys());
0261                }
0262                if (keys.isEmpty())
0263                    return;
0264
0265                // Get and set attribute runs for each attribute name. Need to
0266                // scan from the top of the text so that we can discard any
0267                // Annotation that is no longer applied to a subset text segment.
0268                Iterator itr = keys.iterator();
0269                while (itr.hasNext()) {
0270                    Attribute attributeKey = (Attribute) itr.next();
0271                    text.setIndex(textBeginIndex);
0272                    while (text.getIndex() < endIndex) {
0273                        int start = text.getRunStart(attributeKey);
0274                        int limit = text.getRunLimit(attributeKey);
0275                        Object value = text.getAttribute(attributeKey);
0276
0277                        if (value != null) {
0278                            if (value instanceof  Annotation) {
0279                                if (start >= beginIndex && limit <= endIndex) {
0280                                    addAttribute(attributeKey, value, start
0281                                            - beginIndex, limit - beginIndex);
0282                                } else {
0283                                    if (limit > endIndex)
0284                                        break;
0285                                }
0286                            } else {
0287                                // if the run is beyond the given (subset) range, we
0288                                // don't need to process further.
0289                                if (start >= endIndex)
0290                                    break;
0291                                if (limit > beginIndex) {
0292                                    // attribute is applied to any subrange
0293                                    if (start < beginIndex)
0294                                        start = beginIndex;
0295                                    if (limit > endIndex)
0296                                        limit = endIndex;
0297                                    if (start != limit) {
0298                                        addAttribute(attributeKey, value, start
0299                                                - beginIndex, limit
0300                                                - beginIndex);
0301                                    }
0302                                }
0303                            }
0304                        }
0305                        text.setIndex(limit);
0306                    }
0307                }
0308            }
0309
0310            /**
0311             * Adds an attribute to the entire string.
0312             * @param attribute the attribute key
0313             * @param value the value of the attribute; may be null
0314             * @exception NullPointerException if <code>attribute</code> is null.
0315             * @exception IllegalArgumentException if the AttributedString has length 0
0316             * (attributes cannot be applied to a 0-length range).
0317             */
0318            public void addAttribute(Attribute attribute, Object value) {
0319
0320                if (attribute == null) {
0321                    throw new NullPointerException();
0322                }
0323
0324                int len = length();
0325                if (len == 0) {
0326                    throw new IllegalArgumentException(
0327                            "Can't add attribute to 0-length text");
0328                }
0329
0330                addAttributeImpl(attribute, value, 0, len);
0331            }
0332
0333            /**
0334             * Adds an attribute to a subrange of the string.
0335             * @param attribute the attribute key
0336             * @param value The value of the attribute. May be null.
0337             * @param beginIndex Index of the first character of the range.
0338             * @param endIndex Index of the character following the last character of the range.
0339             * @exception NullPointerException if <code>attribute</code> is null.
0340             * @exception IllegalArgumentException if beginIndex is less then 0, endIndex is
0341             * greater than the length of the string, or beginIndex and endIndex together don't
0342             * define a non-empty subrange of the string.
0343             */
0344            public void addAttribute(Attribute attribute, Object value,
0345                    int beginIndex, int endIndex) {
0346
0347                if (attribute == null) {
0348                    throw new NullPointerException();
0349                }
0350
0351                if (beginIndex < 0 || endIndex > length()
0352                        || beginIndex >= endIndex) {
0353                    throw new IllegalArgumentException(
0354                            "Invalid substring range");
0355                }
0356
0357                addAttributeImpl(attribute, value, beginIndex, endIndex);
0358            }
0359
0360            /**
0361             * Adds a set of attributes to a subrange of the string.
0362             * @param attributes The attributes to be added to the string.
0363             * @param beginIndex Index of the first character of the range.
0364             * @param endIndex Index of the character following the last
0365             * character of the range.
0366             * @exception NullPointerException if <code>attributes</code> is null.
0367             * @exception IllegalArgumentException if beginIndex is less then
0368             * 0, endIndex is greater than the length of the string, or
0369             * beginIndex and endIndex together don't define a non-empty
0370             * subrange of the string and the attributes parameter is not an
0371             * empty Map.
0372             */
0373            public void addAttributes(Map<? extends Attribute, ?> attributes,
0374                    int beginIndex, int endIndex) {
0375                if (attributes == null) {
0376                    throw new NullPointerException();
0377                }
0378
0379                if (beginIndex < 0 || endIndex > length()
0380                        || beginIndex > endIndex) {
0381                    throw new IllegalArgumentException(
0382                            "Invalid substring range");
0383                }
0384                if (beginIndex == endIndex) {
0385                    if (attributes.isEmpty())
0386                        return;
0387                    throw new IllegalArgumentException(
0388                            "Can't add attribute to 0-length text");
0389                }
0390
0391                // make sure we have run attribute data vectors
0392                if (runCount == 0) {
0393                    createRunAttributeDataVectors();
0394                }
0395
0396                // break up runs if necessary
0397                int beginRunIndex = ensureRunBreak(beginIndex);
0398                int endRunIndex = ensureRunBreak(endIndex);
0399
0400                Iterator iterator = attributes.entrySet().iterator();
0401                while (iterator.hasNext()) {
0402                    Map.Entry entry = (Map.Entry) iterator.next();
0403                    addAttributeRunData((Attribute) entry.getKey(), entry
0404                            .getValue(), beginRunIndex, endRunIndex);
0405                }
0406            }
0407
0408            private synchronized void addAttributeImpl(Attribute attribute,
0409                    Object value, int beginIndex, int endIndex) {
0410
0411                // make sure we have run attribute data vectors
0412                if (runCount == 0) {
0413                    createRunAttributeDataVectors();
0414                }
0415
0416                // break up runs if necessary
0417                int beginRunIndex = ensureRunBreak(beginIndex);
0418                int endRunIndex = ensureRunBreak(endIndex);
0419
0420                addAttributeRunData(attribute, value, beginRunIndex,
0421                        endRunIndex);
0422            }
0423
0424            private final void createRunAttributeDataVectors() {
0425                // use temporary variables so things remain consistent in case of an exception
0426                int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
0427                Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
0428                Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
0429                runStarts = newRunStarts;
0430                runAttributes = newRunAttributes;
0431                runAttributeValues = newRunAttributeValues;
0432                runArraySize = ARRAY_SIZE_INCREMENT;
0433                runCount = 1; // assume initial run starting at index 0
0434            }
0435
0436            // ensure there's a run break at offset, return the index of the run
0437            private final int ensureRunBreak(int offset) {
0438                return ensureRunBreak(offset, true);
0439            }
0440
0441            /**
0442             * Ensures there is a run break at offset, returning the index of
0443             * the run. If this results in splitting a run, two things can happen:
0444             * <ul>
0445             * <li>If copyAttrs is true, the attributes from the existing run
0446             *     will be placed in both of the newly created runs.
0447             * <li>If copyAttrs is false, the attributes from the existing run
0448             * will NOT be copied to the run to the right (>= offset) of the break,
0449             * but will exist on the run to the left (< offset).
0450             * </ul>
0451             */
0452            private final int ensureRunBreak(int offset, boolean copyAttrs) {
0453                if (offset == length()) {
0454                    return runCount;
0455                }
0456
0457                // search for the run index where this offset should be
0458                int runIndex = 0;
0459                while (runIndex < runCount && runStarts[runIndex] < offset) {
0460                    runIndex++;
0461                }
0462
0463                // if the offset is at a run start already, we're done
0464                if (runIndex < runCount && runStarts[runIndex] == offset) {
0465                    return runIndex;
0466                }
0467
0468                // we'll have to break up a run
0469                // first, make sure we have enough space in our arrays
0470                if (runCount == runArraySize) {
0471                    int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
0472                    int newRunStarts[] = new int[newArraySize];
0473                    Vector newRunAttributes[] = new Vector[newArraySize];
0474                    Vector newRunAttributeValues[] = new Vector[newArraySize];
0475                    for (int i = 0; i < runArraySize; i++) {
0476                        newRunStarts[i] = runStarts[i];
0477                        newRunAttributes[i] = runAttributes[i];
0478                        newRunAttributeValues[i] = runAttributeValues[i];
0479                    }
0480                    runStarts = newRunStarts;
0481                    runAttributes = newRunAttributes;
0482                    runAttributeValues = newRunAttributeValues;
0483                    runArraySize = newArraySize;
0484                }
0485
0486                // make copies of the attribute information of the old run that the new one used to be part of
0487                // use temporary variables so things remain consistent in case of an exception
0488                Vector newRunAttributes = null;
0489                Vector newRunAttributeValues = null;
0490
0491                if (copyAttrs) {
0492                    Vector oldRunAttributes = runAttributes[runIndex - 1];
0493                    Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
0494                    if (oldRunAttributes != null) {
0495                        newRunAttributes = (Vector) oldRunAttributes.clone();
0496                    }
0497                    if (oldRunAttributeValues != null) {
0498                        newRunAttributeValues = (Vector) oldRunAttributeValues
0499                                .clone();
0500                    }
0501                }
0502
0503                // now actually break up the run
0504                runCount++;
0505                for (int i = runCount - 1; i > runIndex; i--) {
0506                    runStarts[i] = runStarts[i - 1];
0507                    runAttributes[i] = runAttributes[i - 1];
0508                    runAttributeValues[i] = runAttributeValues[i - 1];
0509                }
0510                runStarts[runIndex] = offset;
0511                runAttributes[runIndex] = newRunAttributes;
0512                runAttributeValues[runIndex] = newRunAttributeValues;
0513
0514                return runIndex;
0515            }
0516
0517            // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex
0518            private void addAttributeRunData(Attribute attribute, Object value,
0519                    int beginRunIndex, int endRunIndex) {
0520
0521                for (int i = beginRunIndex; i < endRunIndex; i++) {
0522                    int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
0523                    if (runAttributes[i] == null) {
0524                        Vector newRunAttributes = new Vector();
0525                        Vector newRunAttributeValues = new Vector();
0526                        runAttributes[i] = newRunAttributes;
0527                        runAttributeValues[i] = newRunAttributeValues;
0528                    } else {
0529                        // check whether we have an entry already
0530                        keyValueIndex = runAttributes[i].indexOf(attribute);
0531                    }
0532
0533                    if (keyValueIndex == -1) {
0534                        // create new entry
0535                        int oldSize = runAttributes[i].size();
0536                        runAttributes[i].addElement(attribute);
0537                        try {
0538                            runAttributeValues[i].addElement(value);
0539                        } catch (Exception e) {
0540                            runAttributes[i].setSize(oldSize);
0541                            runAttributeValues[i].setSize(oldSize);
0542                        }
0543                    } else {
0544                        // update existing entry
0545                        runAttributeValues[i].set(keyValueIndex, value);
0546                    }
0547                }
0548            }
0549
0550            /**
0551             * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
0552             * this string.
0553             *
0554             * @return An iterator providing access to the text and its attributes.
0555             */
0556            public AttributedCharacterIterator getIterator() {
0557                return getIterator(null, 0, length());
0558            }
0559
0560            /**
0561             * Creates an AttributedCharacterIterator instance that provides access to
0562             * selected contents of this string.
0563             * Information about attributes not listed in attributes that the
0564             * implementor may have need not be made accessible through the iterator.
0565             * If the list is null, all available attribute information should be made
0566             * accessible.
0567             *
0568             * @param attributes a list of attributes that the client is interested in
0569             * @return an iterator providing access to the entire text and its selected attributes
0570             */
0571            public AttributedCharacterIterator getIterator(
0572                    Attribute[] attributes) {
0573                return getIterator(attributes, 0, length());
0574            }
0575
0576            /**
0577             * Creates an AttributedCharacterIterator instance that provides access to
0578             * selected contents of this string.
0579             * Information about attributes not listed in attributes that the
0580             * implementor may have need not be made accessible through the iterator.
0581             * If the list is null, all available attribute information should be made
0582             * accessible.
0583             *
0584             * @param attributes a list of attributes that the client is interested in
0585             * @param beginIndex the index of the first character
0586             * @param endIndex the index of the character following the last character
0587             * @return an iterator providing access to the text and its attributes
0588             * @exception IllegalArgumentException if beginIndex is less then 0,
0589             * endIndex is greater than the length of the string, or beginIndex is
0590             * greater than endIndex.
0591             */
0592            public AttributedCharacterIterator getIterator(
0593                    Attribute[] attributes, int beginIndex, int endIndex) {
0594                return new AttributedStringIterator(attributes, beginIndex,
0595                        endIndex);
0596            }
0597
0598            // all (with the exception of length) reading operations are private,
0599            // since AttributedString instances are accessed through iterators.
0600
0601            // length is package private so that CharacterIteratorFieldDelegate can
0602            // access it without creating an AttributedCharacterIterator.
0603            int length() {
0604                return text.length();
0605            }
0606
0607            private char charAt(int index) {
0608                return text.charAt(index);
0609            }
0610
0611            private synchronized Object getAttribute(Attribute attribute,
0612                    int runIndex) {
0613                Vector currentRunAttributes = runAttributes[runIndex];
0614                Vector currentRunAttributeValues = runAttributeValues[runIndex];
0615                if (currentRunAttributes == null) {
0616                    return null;
0617                }
0618                int attributeIndex = currentRunAttributes.indexOf(attribute);
0619                if (attributeIndex != -1) {
0620                    return currentRunAttributeValues.elementAt(attributeIndex);
0621                } else {
0622                    return null;
0623                }
0624            }
0625
0626            // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex
0627            private Object getAttributeCheckRange(Attribute attribute,
0628                    int runIndex, int beginIndex, int endIndex) {
0629                Object value = getAttribute(attribute, runIndex);
0630                if (value instanceof  Annotation) {
0631                    // need to check whether the annotation's range extends outside the iterator's range
0632                    if (beginIndex > 0) {
0633                        int currIndex = runIndex;
0634                        int runStart = runStarts[currIndex];
0635                        while (runStart >= beginIndex
0636                                && valuesMatch(value, getAttribute(attribute,
0637                                        currIndex - 1))) {
0638                            currIndex--;
0639                            runStart = runStarts[currIndex];
0640                        }
0641                        if (runStart < beginIndex) {
0642                            // annotation's range starts before iterator's range
0643                            return null;
0644                        }
0645                    }
0646                    int textLength = length();
0647                    if (endIndex < textLength) {
0648                        int currIndex = runIndex;
0649                        int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1]
0650                                : textLength;
0651                        while (runLimit <= endIndex
0652                                && valuesMatch(value, getAttribute(attribute,
0653                                        currIndex + 1))) {
0654                            currIndex++;
0655                            runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1]
0656                                    : textLength;
0657                        }
0658                        if (runLimit > endIndex) {
0659                            // annotation's range ends after iterator's range
0660                            return null;
0661                        }
0662                    }
0663                    // annotation's range is subrange of iterator's range,
0664                    // so we can return the value
0665                }
0666                return value;
0667            }
0668
0669            // returns whether all specified attributes have equal values in the runs with the given indices
0670            private boolean attributeValuesMatch(Set attributes, int runIndex1,
0671                    int runIndex2) {
0672                Iterator iterator = attributes.iterator();
0673                while (iterator.hasNext()) {
0674                    Attribute key = (Attribute) iterator.next();
0675                    if (!valuesMatch(getAttribute(key, runIndex1),
0676                            getAttribute(key, runIndex2))) {
0677                        return false;
0678                    }
0679                }
0680                return true;
0681            }
0682
0683            // returns whether the two objects are either both null or equal
0684            private final static boolean valuesMatch(Object value1,
0685                    Object value2) {
0686                if (value1 == null) {
0687                    return value2 == null;
0688                } else {
0689                    return value1.equals(value2);
0690                }
0691            }
0692
0693            /**
0694             * Appends the contents of the CharacterIterator iterator into the
0695             * StringBuffer buf.
0696             */
0697            private final void appendContents(StringBuffer buf,
0698                    CharacterIterator iterator) {
0699                int index = iterator.getBeginIndex();
0700                int end = iterator.getEndIndex();
0701
0702                while (index < end) {
0703                    iterator.setIndex(index++);
0704                    buf.append(iterator.current());
0705                }
0706            }
0707
0708            /**
0709             * Sets the attributes for the range from offset to the next run break 
0710             * (typically the end of the text) to the ones specified in attrs.
0711             * This is only meant to be called from the constructor!
0712             */
0713            private void setAttributes(Map attrs, int offset) {
0714                if (runCount == 0) {
0715                    createRunAttributeDataVectors();
0716                }
0717
0718                int index = ensureRunBreak(offset, false);
0719                int size;
0720
0721                if (attrs != null && (size = attrs.size()) > 0) {
0722                    Vector runAttrs = new Vector(size);
0723                    Vector runValues = new Vector(size);
0724                    Iterator iterator = attrs.entrySet().iterator();
0725
0726                    while (iterator.hasNext()) {
0727                        Map.Entry entry = (Map.Entry) iterator.next();
0728
0729                        runAttrs.add(entry.getKey());
0730                        runValues.add(entry.getValue());
0731                    }
0732                    runAttributes[index] = runAttrs;
0733                    runAttributeValues[index] = runValues;
0734                }
0735            }
0736
0737            /**
0738             * Returns true if the attributes specified in last and attrs differ.
0739             */
0740            private static boolean mapsDiffer(Map last, Map attrs) {
0741                if (last == null) {
0742                    return (attrs != null && attrs.size() > 0);
0743                }
0744                return (!last.equals(attrs));
0745            }
0746
0747            // the iterator class associated with this string class
0748
0749            final private class AttributedStringIterator implements 
0750                    AttributedCharacterIterator {
0751
0752                // note on synchronization:
0753                // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
0754                // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
0755
0756                // start and end index for our iteration
0757                private int beginIndex;
0758                private int endIndex;
0759
0760                // attributes that our client is interested in
0761                private Attribute[] relevantAttributes;
0762
0763                // the current index for our iteration
0764                // invariant: beginIndex <= currentIndex <= endIndex
0765                private int currentIndex;
0766
0767                // information about the run that includes currentIndex
0768                private int currentRunIndex;
0769                private int currentRunStart;
0770                private int currentRunLimit;
0771
0772                // constructor
0773                AttributedStringIterator(Attribute[] attributes,
0774                        int beginIndex, int endIndex) {
0775
0776                    if (beginIndex < 0 || beginIndex > endIndex
0777                            || endIndex > length()) {
0778                        throw new IllegalArgumentException(
0779                                "Invalid substring range");
0780                    }
0781
0782                    this .beginIndex = beginIndex;
0783                    this .endIndex = endIndex;
0784                    this .currentIndex = beginIndex;
0785                    updateRunInfo();
0786                    if (attributes != null) {
0787                        relevantAttributes = (Attribute[]) attributes.clone();
0788                    }
0789                }
0790
0791                // Object methods. See documentation in that class.
0792
0793                public boolean equals(Object obj) {
0794                    if (this  == obj) {
0795                        return true;
0796                    }
0797                    if (!(obj instanceof  AttributedStringIterator)) {
0798                        return false;
0799                    }
0800
0801                    AttributedStringIterator that = (AttributedStringIterator) obj;
0802
0803                    if (AttributedString.this  != that.getString())
0804                        return false;
0805                    if (currentIndex != that.currentIndex
0806                            || beginIndex != that.beginIndex
0807                            || endIndex != that.endIndex)
0808                        return false;
0809                    return true;
0810                }
0811
0812                public int hashCode() {
0813                    return text.hashCode() ^ currentIndex ^ beginIndex
0814                            ^ endIndex;
0815                }
0816
0817                public Object clone() {
0818                    try {
0819                        AttributedStringIterator other = (AttributedStringIterator) super 
0820                                .clone();
0821                        return other;
0822                    } catch (CloneNotSupportedException e) {
0823                        throw new InternalError();
0824                    }
0825                }
0826
0827                // CharacterIterator methods. See documentation in that interface.
0828
0829                public char first() {
0830                    return internalSetIndex(beginIndex);
0831                }
0832
0833                public char last() {
0834                    if (endIndex == beginIndex) {
0835                        return internalSetIndex(endIndex);
0836                    } else {
0837                        return internalSetIndex(endIndex - 1);
0838                    }
0839                }
0840
0841                public char current() {
0842                    if (currentIndex == endIndex) {
0843                        return DONE;
0844                    } else {
0845                        return charAt(currentIndex);
0846                    }
0847                }
0848
0849                public char next() {
0850                    if (currentIndex < endIndex) {
0851                        return internalSetIndex(currentIndex + 1);
0852                    } else {
0853                        return DONE;
0854                    }
0855                }
0856
0857                public char previous() {
0858                    if (currentIndex > beginIndex) {
0859                        return internalSetIndex(currentIndex - 1);
0860                    } else {
0861                        return DONE;
0862                    }
0863                }
0864
0865                public char setIndex(int position) {
0866                    if (position < beginIndex || position > endIndex)
0867                        throw new IllegalArgumentException("Invalid index");
0868                    return internalSetIndex(position);
0869                }
0870
0871                public int getBeginIndex() {
0872                    return beginIndex;
0873                }
0874
0875                public int getEndIndex() {
0876                    return endIndex;
0877                }
0878
0879                public int getIndex() {
0880                    return currentIndex;
0881                }
0882
0883                // AttributedCharacterIterator methods. See documentation in that interface.
0884
0885                public int getRunStart() {
0886                    return currentRunStart;
0887                }
0888
0889                public int getRunStart(Attribute attribute) {
0890                    if (currentRunStart == beginIndex || currentRunIndex == -1) {
0891                        return currentRunStart;
0892                    } else {
0893                        Object value = getAttribute(attribute);
0894                        int runStart = currentRunStart;
0895                        int runIndex = currentRunIndex;
0896                        while (runStart > beginIndex
0897                                && valuesMatch(value, AttributedString.this 
0898                                        .getAttribute(attribute, runIndex - 1))) {
0899                            runIndex--;
0900                            runStart = runStarts[runIndex];
0901                        }
0902                        if (runStart < beginIndex) {
0903                            runStart = beginIndex;
0904                        }
0905                        return runStart;
0906                    }
0907                }
0908
0909                public int getRunStart(Set<? extends Attribute> attributes) {
0910                    if (currentRunStart == beginIndex || currentRunIndex == -1) {
0911                        return currentRunStart;
0912                    } else {
0913                        int runStart = currentRunStart;
0914                        int runIndex = currentRunIndex;
0915                        while (runStart > beginIndex
0916                                && AttributedString.this .attributeValuesMatch(
0917                                        attributes, currentRunIndex,
0918                                        runIndex - 1)) {
0919                            runIndex--;
0920                            runStart = runStarts[runIndex];
0921                        }
0922                        if (runStart < beginIndex) {
0923                            runStart = beginIndex;
0924                        }
0925                        return runStart;
0926                    }
0927                }
0928
0929                public int getRunLimit() {
0930                    return currentRunLimit;
0931                }
0932
0933                public int getRunLimit(Attribute attribute) {
0934                    if (currentRunLimit == endIndex || currentRunIndex == -1) {
0935                        return currentRunLimit;
0936                    } else {
0937                        Object value = getAttribute(attribute);
0938                        int runLimit = currentRunLimit;
0939                        int runIndex = currentRunIndex;
0940                        while (runLimit < endIndex
0941                                && valuesMatch(value, AttributedString.this 
0942                                        .getAttribute(attribute, runIndex + 1))) {
0943                            runIndex++;
0944                            runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1]
0945                                    : endIndex;
0946                        }
0947                        if (runLimit > endIndex) {
0948                            runLimit = endIndex;
0949                        }
0950                        return runLimit;
0951                    }
0952                }
0953
0954                public int getRunLimit(Set<? extends Attribute> attributes) {
0955                    if (currentRunLimit == endIndex || currentRunIndex == -1) {
0956                        return currentRunLimit;
0957                    } else {
0958                        int runLimit = currentRunLimit;
0959                        int runIndex = currentRunIndex;
0960                        while (runLimit < endIndex
0961                                && AttributedString.this .attributeValuesMatch(
0962                                        attributes, currentRunIndex,
0963                                        runIndex + 1)) {
0964                            runIndex++;
0965                            runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1]
0966                                    : endIndex;
0967                        }
0968                        if (runLimit > endIndex) {
0969                            runLimit = endIndex;
0970                        }
0971                        return runLimit;
0972                    }
0973                }
0974
0975                public Map<Attribute, Object> getAttributes() {
0976                    if (runAttributes == null || currentRunIndex == -1
0977                            || runAttributes[currentRunIndex] == null) {
0978                        // ??? would be nice to return null, but current spec doesn't allow it
0979                        // returning Hashtable saves AttributeMap from dealing with emptiness
0980                        return new Hashtable();
0981                    }
0982                    return new AttributeMap(currentRunIndex, beginIndex,
0983                            endIndex);
0984                }
0985
0986                public Set<Attribute> getAllAttributeKeys() {
0987                    // ??? This should screen out attribute keys that aren't relevant to the client
0988                    if (runAttributes == null) {
0989                        // ??? would be nice to return null, but current spec doesn't allow it
0990                        // returning HashSet saves us from dealing with emptiness
0991                        return new HashSet();
0992                    }
0993                    synchronized (AttributedString.this ) {
0994                        // ??? should try to create this only once, then update if necessary,
0995                        // and give callers read-only view
0996                        Set keys = new HashSet();
0997                        int i = 0;
0998                        while (i < runCount) {
0999                            if (runStarts[i] < endIndex
1000                                    && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {
1001                                Vector currentRunAttributes = runAttributes[i];
1002                                if (currentRunAttributes != null) {
1003                                    int j = currentRunAttributes.size();
1004                                    while (j-- > 0) {
1005                                        keys.add(currentRunAttributes.get(j));
1006                                    }
1007                                }
1008                            }
1009                            i++;
1010                        }
1011                        return keys;
1012                    }
1013                }
1014
1015                public Object getAttribute(Attribute attribute) {
1016                    int runIndex = currentRunIndex;
1017                    if (runIndex < 0) {
1018                        return null;
1019                    }
1020                    return AttributedString.this .getAttributeCheckRange(
1021                            attribute, runIndex, beginIndex, endIndex);
1022                }
1023
1024                // internally used methods
1025
1026                private AttributedString getString() {
1027                    return AttributedString.this ;
1028                }
1029
1030                // set the current index, update information about the current run if necessary,
1031                // return the character at the current index
1032                private char internalSetIndex(int position) {
1033                    currentIndex = position;
1034                    if (position < currentRunStart
1035                            || position >= currentRunLimit) {
1036                        updateRunInfo();
1037                    }
1038                    if (currentIndex == endIndex) {
1039                        return DONE;
1040                    } else {
1041                        return charAt(position);
1042                    }
1043                }
1044
1045                // update the information about the current run
1046                private void updateRunInfo() {
1047                    if (currentIndex == endIndex) {
1048                        currentRunStart = currentRunLimit = endIndex;
1049                        currentRunIndex = -1;
1050                    } else {
1051                        synchronized (AttributedString.this ) {
1052                            int runIndex = -1;
1053                            while (runIndex < runCount - 1
1054                                    && runStarts[runIndex + 1] <= currentIndex)
1055                                runIndex++;
1056                            currentRunIndex = runIndex;
1057                            if (runIndex >= 0) {
1058                                currentRunStart = runStarts[runIndex];
1059                                if (currentRunStart < beginIndex)
1060                                    currentRunStart = beginIndex;
1061                            } else {
1062                                currentRunStart = beginIndex;
1063                            }
1064                            if (runIndex < runCount - 1) {
1065                                currentRunLimit = runStarts[runIndex + 1];
1066                                if (currentRunLimit > endIndex)
1067                                    currentRunLimit = endIndex;
1068                            } else {
1069                                currentRunLimit = endIndex;
1070                            }
1071                        }
1072                    }
1073                }
1074
1075            }
1076
1077            // the map class associated with this string class, giving access to the attributes of one run
1078
1079            final private class AttributeMap extends
1080                    AbstractMap<Attribute, Object> {
1081
1082                int runIndex;
1083                int beginIndex;
1084                int endIndex;
1085
1086                AttributeMap(int runIndex, int beginIndex, int endIndex) {
1087                    this .runIndex = runIndex;
1088                    this .beginIndex = beginIndex;
1089                    this .endIndex = endIndex;
1090                }
1091
1092                public Set entrySet() {
1093                    HashSet set = new HashSet();
1094                    synchronized (AttributedString.this ) {
1095                        int size = runAttributes[runIndex].size();
1096                        for (int i = 0; i < size; i++) {
1097                            Attribute key = (Attribute) runAttributes[runIndex]
1098                                    .get(i);
1099                            Object value = runAttributeValues[runIndex].get(i);
1100                            if (value instanceof  Annotation) {
1101                                value = AttributedString.this 
1102                                        .getAttributeCheckRange(key, runIndex,
1103                                                beginIndex, endIndex);
1104                                if (value == null) {
1105                                    continue;
1106                                }
1107                            }
1108                            Map.Entry entry = new AttributeEntry(key, value);
1109                            set.add(entry);
1110                        }
1111                    }
1112                    return set;
1113                }
1114
1115                public Object get(Object key) {
1116                    return AttributedString.this .getAttributeCheckRange(
1117                            (Attribute) key, runIndex, beginIndex, endIndex);
1118                }
1119            }
1120        }
1121
1122        class AttributeEntry implements  Map.Entry {
1123
1124            private Attribute key;
1125            private Object value;
1126
1127            AttributeEntry(Attribute key, Object value) {
1128                this .key = key;
1129                this .value = value;
1130            }
1131
1132            public boolean equals(Object o) {
1133                if (!(o instanceof  AttributeEntry)) {
1134                    return false;
1135                }
1136                AttributeEntry other = (AttributeEntry) o;
1137                return other.key.equals(key)
1138                        && (value == null ? other.value == null : other.value
1139                                .equals(value));
1140            }
1141
1142            public Object getKey() {
1143                return key;
1144            }
1145
1146            public Object getValue() {
1147                return value;
1148            }
1149
1150            public Object setValue(Object newValue) {
1151                throw new UnsupportedOperationException();
1152            }
1153
1154            public int hashCode() {
1155                return key.hashCode() ^ (value == null ? 0 : value.hashCode());
1156            }
1157
1158            public String toString() {
1159                return key.toString() + "=" + value.toString();
1160            }
1161        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.