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

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 geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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