📄 attributedstring.java
字号:
int beginIndex, int endIndex) { // make sure we have run attribute data vectors if (runCount == 0) { createRunAttributeDataVectors(); } // break up runs if necessary int beginRunIndex = ensureRunBreak(beginIndex); int endRunIndex = ensureRunBreak(endIndex); addAttributeRunData(attribute, value, beginRunIndex, endRunIndex); } private final void createRunAttributeDataVectors() { // use temporary variables so things remain consistent in case of an exception int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT]; Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT]; Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT]; runStarts = newRunStarts; runAttributes = newRunAttributes; runAttributeValues = newRunAttributeValues; runArraySize = ARRAY_SIZE_INCREMENT; runCount = 1; // assume initial run starting at index 0 } // ensure there's a run break at offset, return the index of the run private final int ensureRunBreak(int offset) { return ensureRunBreak(offset, true); } /** * Ensures there is a run break at offset, returning the index of * the run. If this results in splitting a run, two things can happen: * <ul> * <li>If copyAttrs is true, the attributes from the existing run * will be placed in both of the newly created runs. * <li>If copyAttrs is false, the attributes from the existing run * will NOT be copied to the run to the right (>= offset) of the break, * but will exist on the run to the left (< offset). * </ul> */ private final int ensureRunBreak(int offset, boolean copyAttrs) { if (offset == length()) { return runCount; } // search for the run index where this offset should be int runIndex = 0; while (runIndex < runCount && runStarts[runIndex] < offset) { runIndex++; } // if the offset is at a run start already, we're done if (runIndex < runCount && runStarts[runIndex] == offset) { return runIndex; } // we'll have to break up a run // first, make sure we have enough space in our arrays if (runCount == runArraySize) { int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT; int newRunStarts[] = new int[newArraySize]; Vector newRunAttributes[] = new Vector[newArraySize]; Vector newRunAttributeValues[] = new Vector[newArraySize]; for (int i = 0; i < runArraySize; i++) { newRunStarts[i] = runStarts[i]; newRunAttributes[i] = runAttributes[i]; newRunAttributeValues[i] = runAttributeValues[i]; } runStarts = newRunStarts; runAttributes = newRunAttributes; runAttributeValues = newRunAttributeValues; runArraySize = newArraySize; } // make copies of the attribute information of the old run that the new one used to be part of // use temporary variables so things remain consistent in case of an exception Vector newRunAttributes = null; Vector newRunAttributeValues = null; if (copyAttrs) { Vector oldRunAttributes = runAttributes[runIndex - 1]; Vector oldRunAttributeValues = runAttributeValues[runIndex - 1]; if (oldRunAttributes != null) { newRunAttributes = (Vector) oldRunAttributes.clone(); } if (oldRunAttributeValues != null) { newRunAttributeValues = (Vector) oldRunAttributeValues.clone(); } } // now actually break up the run runCount++; for (int i = runCount - 1; i > runIndex; i--) { runStarts[i] = runStarts[i - 1]; runAttributes[i] = runAttributes[i - 1]; runAttributeValues[i] = runAttributeValues[i - 1]; } runStarts[runIndex] = offset; runAttributes[runIndex] = newRunAttributes; runAttributeValues[runIndex] = newRunAttributeValues; return runIndex; } // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex private void addAttributeRunData(Attribute attribute, Object value, int beginRunIndex, int endRunIndex) { for (int i = beginRunIndex; i < endRunIndex; i++) { int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet if (runAttributes[i] == null) { Vector newRunAttributes = new Vector(); Vector newRunAttributeValues = new Vector(); runAttributes[i] = newRunAttributes; runAttributeValues[i] = newRunAttributeValues; } else { // check whether we have an entry already keyValueIndex = runAttributes[i].indexOf(attribute); } if (keyValueIndex == -1) { // create new entry int oldSize = runAttributes[i].size(); runAttributes[i].addElement(attribute); try { runAttributeValues[i].addElement(value); } catch (Exception e) { runAttributes[i].setSize(oldSize); runAttributeValues[i].setSize(oldSize); } } else { // update existing entry runAttributeValues[i].set(keyValueIndex, value); } } } /** * Creates an AttributedCharacterIterator instance that provides access to the entire contents of * this string. * * @return An iterator providing access to the text and its attributes. */ public AttributedCharacterIterator getIterator() { return getIterator(null, 0, length()); } /** * Creates an AttributedCharacterIterator instance that provides access to * selected contents of this string. * Information about attributes not listed in attributes that the * implementor may have need not be made accessible through the iterator. * If the list is null, all available attribute information should be made * accessible. * * @param attributes a list of attributes that the client is interested in * @return an iterator providing access to the text and its attributes */ public AttributedCharacterIterator getIterator(Attribute[] attributes) { return getIterator(attributes, 0, length()); } /** * Creates an AttributedCharacterIterator instance that provides access to * selected contents of this string. * Information about attributes not listed in attributes that the * implementor may have need not be made accessible through the iterator. * If the list is null, all available attribute information should be made * accessible. * * @param attributes a list of attributes that the client is interested in * @param beginIndex the index of the first character * @param endIndex the index of the character following the last character * @return an iterator providing access to the text and its attributes * @exception IllegalArgumentException if beginIndex is less then 0, * endIndex is greater than the length of the string, or beginIndex is * greater than endIndex. */ public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) { return new AttributedStringIterator(attributes, beginIndex, endIndex); } // all (with the exception of length) reading operations are private, // since AttributedString instances are accessed through iterators. // length is package private so that CharacterIteratorFieldDelegate can // access it without creating an AttributedCharacterIterator. int length() { return text.length(); } private char charAt(int index) { return text.charAt(index); } private synchronized Object getAttribute(Attribute attribute, int runIndex) { Vector currentRunAttributes = runAttributes[runIndex]; Vector currentRunAttributeValues = runAttributeValues[runIndex]; if (currentRunAttributes == null) { return null; } int attributeIndex = currentRunAttributes.indexOf(attribute); if (attributeIndex != -1) { return currentRunAttributeValues.elementAt(attributeIndex); } else { return null; } } // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) { Object value = getAttribute(attribute, runIndex); if (value instanceof Annotation) { // need to check whether the annotation's range extends outside the iterator's range if (beginIndex > 0) { int currIndex = runIndex; int runStart = runStarts[currIndex]; while (runStart >= beginIndex && valuesMatch(value, getAttribute(attribute, currIndex - 1))) { currIndex--; runStart = runStarts[currIndex]; } if (runStart < beginIndex) { // annotation's range starts before iterator's range return null; } } int textLength = length(); if (endIndex < textLength) { int currIndex = runIndex; int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; while (runLimit <= endIndex && valuesMatch(value, getAttribute(attribute, currIndex + 1))) { currIndex++; runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength; } if (runLimit > endIndex) { // annotation's range ends after iterator's range return null; } } // annotation's range is subrange of iterator's range, // so we can return the value } return value; } // returns whether all specified attributes have equal values in the runs with the given indices private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) { Iterator iterator = attributes.iterator(); while (iterator.hasNext()) { Attribute key = (Attribute) iterator.next(); if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) { return false; } } return true; } // returns whether the two objects are either both null or equal private final static boolean valuesMatch(Object value1, Object value2) { if (value1 == null) { return value2 == null; } else { return value1.equals(value2); } } /** * Appends the contents of the CharacterIterator iterator into the * StringBuffer buf. */ private final void appendContents(StringBuffer buf, CharacterIterator iterator) { int index = iterator.getBeginIndex(); int end = iterator.getEndIndex(); while (index < end) { iterator.setIndex(index++); buf.append(iterator.current()); } } /** * Sets the attributes for the range from offset to the the next run break * (typically the end of the text) to the ones specified in attrs. * This is only meant to be called from the constructor! */ private void setAttributes(Map attrs, int offset) { if (runCount == 0) { createRunAttributeDataVectors(); } int index = ensureRunBreak(offset, false); int size; if (attrs != null && (size = attrs.size()) > 0) { Vector runAttrs = new Vector(size); Vector runValues = new Vector(size); Iterator iterator = attrs.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry)iterator.next(); runAttrs.add(entry.getKey()); runValues.add(entry.getValue()); } runAttributes[index] = runAttrs; runAttributeValues[index] = runValues; } } /** * Returns true if the attributes specified in last and attrs differ. */ private static boolean mapsDiffer(Map last, Map attrs) { if (last == null) { return (attrs != null && attrs.size() > 0); } return (!last.equals(attrs)); } // the iterator class associated with this string class final private class AttributedStringIterator implements AttributedCharacterIterator { // note on synchronization: // we don't synchronize on the iterator, assuming that an iterator is only used in one thread. // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads. // start and end index for our iteration private int beginIndex; private int endIndex; // attributes that our client is interested in private Attribute[] relevantAttributes; // the current index for our iteration // invariant: beginIndex <= currentIndex <= endIndex private int currentIndex; // information about the run that includes currentIndex private int currentRunIndex; private int currentRunStart; private int currentRunLimit; // constructor AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) { if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) { throw new IllegalArgumentException("Invalid substring range"); } this.beginIndex = beginIndex; this.endIndex = endIndex; this.currentIndex = beginIndex;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -