Modified: pivot/trunk/core/src/org/apache/pivot/serialization/MacroReader.java
URL: 
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/serialization/MacroReader.java?rev=1913470&r1=1913469&r2=1913470&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/serialization/MacroReader.java 
(original)
+++ pivot/trunk/core/src/org/apache/pivot/serialization/MacroReader.java Tue 
Oct 31 19:15:47 2023
@@ -1,249 +1,249 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pivot.serialization;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Queue;
-
-
-/**
- * This is a {@link Reader} that can be instantiated inline with any other
- * {@code Reader} to provide macro capabilities.
- * <p> We recognize <code>#define <i>NAME value</i></code> as definitions
- * as well as <code>#undef <i>NAME</i></code> to remove a previous definition.
- * <p> The macro name must correspond to the Unicode naming conventions (see
- * {@link Character#isUnicodeIdentifierStart} and {@link 
Character#isUnicodeIdentifierPart}).
- * <p> Macro substitutions are recognized as <code>${<i>NAME</i>}</code> 
anywhere
- * in the underlying stream. Nested macros are supported, and are expanded at 
the
- * point of definition, if defined, or at the point of expansion if defined 
later.
- */
-public final class MacroReader extends Reader {
-    /** The input reader we are mapping. */
-    private Reader in;
-
-    /** The map of our defined variables and their values. */
-    private Map<String, String> variableMap = new HashMap<>();
-
-    /** The lookahead queue, set either by one-character lookahead (such as
-     * while recognizing "$NAME") or from macro expansion.
-     */
-    private Queue<Integer> lookaheadQueue = new ArrayDeque<>();
-
-    /** The previous character read. */
-    private int lastCh = -1;
-
-    /**
-     * Construct a new macro reader on top of the given reader.
-     * @param reader The input reader we are going to wrap.
-     */
-    public MacroReader(final Reader reader) {
-        this.in = reader;
-    }
-
-    @Override
-    public void close() throws IOException {
-        in.close();
-    }
-
-    /**
-     * Add the given character to the lookahead queue for reprocessing.
-     * @param ch The character to queue.
-     */
-    private void queue(final int ch) {
-        if (ch != -1) {
-            lookaheadQueue.add(ch);
-        }
-    }
-
-    /**
-     * Add all the characters of the given character sequence to the
-     * lookahead queue for reprocessing.
-     * @param seq The character sequence to be queued up again.
-     */
-    private void queue(final CharSequence seq) {
-        for (int i = 0; i < seq.length(); i++) {
-            lookaheadQueue.add((int) seq.charAt(i));
-        }
-    }
-
-    /**
-     * Parse out the next word in the stream (according to Unicode
-     * Identifier semantics) as the macro name, skipping leading whitespace.
-     * @return The next parsed "word" string.
-     * @throws IOException for errors reading the stream.
-     */
-    private String getNextWord() throws IOException {
-        StringBuilder buf = new StringBuilder();
-        int ch;
-        do {
-            ch = getNextChar(true);
-        } while (ch != -1 && Character.isWhitespace(ch));
-
-        if (ch != -1) {
-            buf.append((char) ch);
-            while ((ch = getNextChar(true)) != -1
-                   && ((buf.length() == 0 && 
Character.isUnicodeIdentifierStart(ch))
-                    || (buf.length() > 0 && 
Character.isUnicodeIdentifierPart(ch)))) {
-                buf.append((char) ch);
-            }
-
-            // Re-queue the character that terminated the word
-            queue(ch);
-        }
-        return buf.toString();
-    }
-
-    /**
-     * Get the next character in the input stream, either from the
-     * {@link #lookaheadQueue} if anything is queued, or by reading
-     * from the underlying {@link Reader}.
-     * <p> This is the heart of the processing that handles both
-     * macro definition and expansion.
-     * @param   handleMacros   set to {@code false} only when
-     *                         invoking this method recursively
-     *                         to ignore unknown macro commands
-     *                         or undefined macros
-     * @return  The next character in the stream.
-     * @throws IOException if there are errors reading the stream.
-     */
-    private int getNextChar(final boolean handleMacros) throws IOException {
-        int ret = -1;
-        Integer queuedChar = lookaheadQueue.poll();
-        if (queuedChar != null) {
-            ret = queuedChar.intValue();
-        } else {
-            ret = in.read();
-        }
-
-        if (handleMacros) {
-            int ch;
-
-            // Check for macro define or undefine (starting with "#"
-            // at the beginning of a line) (unless we're recursing to
-            // skip an unknown declaration keyword).
-            if (ret == '#' && lastCh == '\n') {
-                String keyword = getNextWord();
-
-                if (keyword.equalsIgnoreCase("undef")) {
-                    String name = getNextWord();
-
-                    do {
-                        ch = getNextChar(false);
-                    } while (ch != -1 && ch != '\n');
-
-                    variableMap.remove(name);
-
-                    return getNextChar(true);
-                } else if (!keyword.equalsIgnoreCase("define")) {
-                    // Basically ignore any commands we don't understand
-                    // by simply queueing the text back to be read again
-                    // but with the flag set to ignore this command (so
-                    // we don't get into infinite recursion!)
-                    ch = getNextChar(false);
-                    queue(ret);
-                    queue(keyword);
-                    queue(ch);
-
-                    return getNextChar(false);
-                }
-
-                // Define a macro
-                String name = getNextWord();
-                StringBuilder buf = new StringBuilder();
-
-                // Skip whitespace after the macro name
-                do {
-                    ch = getNextChar(true);
-                } while (ch != -1 && Character.isWhitespace(ch) && ch != '\\' 
&& ch != '\n');
-
-                queue(ch);
-
-                do {
-                    while ((ch = getNextChar(true)) != -1 && ch != '\\' && ch 
!= '\n') {
-                        buf.append((char) ch);
-                    }
-
-                    // Check for line continuation character
-                    if (ch == '\\') {
-                        int next = getNextChar(true);
-                        if (next == '\n') {
-                            buf.append((char) next);
-                        } else {
-                            buf.append((char) ch);
-                            buf.append((char) next);
-                        }
-                    }
-                } while (ch != -1 && ch != '\n');
-
-                variableMap.put(name, buf.toString());
-
-                return getNextChar(true);
-            } else if (ret == '$') {
-                // Check for macro expansion
-                // Note: this allows for nested expansion
-                int next = getNextChar(true);
-                if (next == '{') {
-                    StringBuilder buf = new StringBuilder();
-
-                    while ((ch = getNextChar(true)) != -1 && ch != '}') {
-                        buf.append((char) ch);
-                    }
-
-                    String expansion = variableMap.get(buf.toString());
-                    if (expansion == null) {
-                        // If the macro is undefined, then we put back the
-                        // original text to be read again, but without 
expansion
-                        queue(ret);
-                        queue(next);
-                        queue(buf);
-                        queue(ch);
-
-                        ret = getNextChar(false);
-                    } else {
-                        queue(expansion);
-
-                        // By allowing expansion here, we get recursive 
expansion
-                        ret = getNextChar(true);
-                    }
-                } else {
-                    queue(next);
-                }
-            }
-        }
-
-        return (lastCh = ret);
-    }
-
-    @Override
-    public int read(final char[] cbuf, final int off, final int len) throws 
IOException {
-        int read = -1;
-        for (int i = 0; i < len; i++) {
-            int ch = getNextChar(true);
-            if (ch == -1) {
-                break;
-            }
-            read = i;
-            cbuf[off + i] = (char) ch;
-        }
-        return (read == -1) ? read : read + 1;
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.serialization;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+
+
+/**
+ * This is a {@link Reader} that can be instantiated inline with any other
+ * {@code Reader} to provide macro capabilities.
+ * <p> We recognize <code>#define <i>NAME value</i></code> as definitions
+ * as well as <code>#undef <i>NAME</i></code> to remove a previous definition.
+ * <p> The macro name must correspond to the Unicode naming conventions (see
+ * {@link Character#isUnicodeIdentifierStart} and {@link 
Character#isUnicodeIdentifierPart}).
+ * <p> Macro substitutions are recognized as <code>${<i>NAME</i>}</code> 
anywhere
+ * in the underlying stream. Nested macros are supported, and are expanded at 
the
+ * point of definition, if defined, or at the point of expansion if defined 
later.
+ */
+public final class MacroReader extends Reader {
+    /** The input reader we are mapping. */
+    private Reader in;
+
+    /** The map of our defined variables and their values. */
+    private Map<String, String> variableMap = new HashMap<>();
+
+    /** The lookahead queue, set either by one-character lookahead (such as
+     * while recognizing "$NAME") or from macro expansion.
+     */
+    private Queue<Integer> lookaheadQueue = new ArrayDeque<>();
+
+    /** The previous character read. */
+    private int lastCh = -1;
+
+    /**
+     * Construct a new macro reader on top of the given reader.
+     * @param reader The input reader we are going to wrap.
+     */
+    public MacroReader(final Reader reader) {
+        this.in = reader;
+    }
+
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    /**
+     * Add the given character to the lookahead queue for reprocessing.
+     * @param ch The character to queue.
+     */
+    private void queue(final int ch) {
+        if (ch != -1) {
+            lookaheadQueue.add(ch);
+        }
+    }
+
+    /**
+     * Add all the characters of the given character sequence to the
+     * lookahead queue for reprocessing.
+     * @param seq The character sequence to be queued up again.
+     */
+    private void queue(final CharSequence seq) {
+        for (int i = 0; i < seq.length(); i++) {
+            lookaheadQueue.add((int) seq.charAt(i));
+        }
+    }
+
+    /**
+     * Parse out the next word in the stream (according to Unicode
+     * Identifier semantics) as the macro name, skipping leading whitespace.
+     * @return The next parsed "word" string.
+     * @throws IOException for errors reading the stream.
+     */
+    private String getNextWord() throws IOException {
+        StringBuilder buf = new StringBuilder();
+        int ch;
+        do {
+            ch = getNextChar(true);
+        } while (ch != -1 && Character.isWhitespace(ch));
+
+        if (ch != -1) {
+            buf.append((char) ch);
+            while ((ch = getNextChar(true)) != -1
+                   && ((buf.length() == 0 && 
Character.isUnicodeIdentifierStart(ch))
+                    || (buf.length() > 0 && 
Character.isUnicodeIdentifierPart(ch)))) {
+                buf.append((char) ch);
+            }
+
+            // Re-queue the character that terminated the word
+            queue(ch);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Get the next character in the input stream, either from the
+     * {@link #lookaheadQueue} if anything is queued, or by reading
+     * from the underlying {@link Reader}.
+     * <p> This is the heart of the processing that handles both
+     * macro definition and expansion.
+     * @param   handleMacros   set to {@code false} only when
+     *                         invoking this method recursively
+     *                         to ignore unknown macro commands
+     *                         or undefined macros
+     * @return  The next character in the stream.
+     * @throws IOException if there are errors reading the stream.
+     */
+    private int getNextChar(final boolean handleMacros) throws IOException {
+        int ret = -1;
+        Integer queuedChar = lookaheadQueue.poll();
+        if (queuedChar != null) {
+            ret = queuedChar.intValue();
+        } else {
+            ret = in.read();
+        }
+
+        if (handleMacros) {
+            int ch;
+
+            // Check for macro define or undefine (starting with "#"
+            // at the beginning of a line) (unless we're recursing to
+            // skip an unknown declaration keyword).
+            if (ret == '#' && lastCh == '\n') {
+                String keyword = getNextWord();
+
+                if (keyword.equalsIgnoreCase("undef")) {
+                    String name = getNextWord();
+
+                    do {
+                        ch = getNextChar(false);
+                    } while (ch != -1 && ch != '\n');
+
+                    variableMap.remove(name);
+
+                    return getNextChar(true);
+                } else if (!keyword.equalsIgnoreCase("define")) {
+                    // Basically ignore any commands we don't understand
+                    // by simply queueing the text back to be read again
+                    // but with the flag set to ignore this command (so
+                    // we don't get into infinite recursion!)
+                    ch = getNextChar(false);
+                    queue(ret);
+                    queue(keyword);
+                    queue(ch);
+
+                    return getNextChar(false);
+                }
+
+                // Define a macro
+                String name = getNextWord();
+                StringBuilder buf = new StringBuilder();
+
+                // Skip whitespace after the macro name
+                do {
+                    ch = getNextChar(true);
+                } while (ch != -1 && Character.isWhitespace(ch) && ch != '\\' 
&& ch != '\n');
+
+                queue(ch);
+
+                do {
+                    while ((ch = getNextChar(true)) != -1 && ch != '\\' && ch 
!= '\n') {
+                        buf.append((char) ch);
+                    }
+
+                    // Check for line continuation character
+                    if (ch == '\\') {
+                        int next = getNextChar(true);
+                        if (next == '\n') {
+                            buf.append((char) next);
+                        } else {
+                            buf.append((char) ch);
+                            buf.append((char) next);
+                        }
+                    }
+                } while (ch != -1 && ch != '\n');
+
+                variableMap.put(name, buf.toString());
+
+                return getNextChar(true);
+            } else if (ret == '$') {
+                // Check for macro expansion
+                // Note: this allows for nested expansion
+                int next = getNextChar(true);
+                if (next == '{') {
+                    StringBuilder buf = new StringBuilder();
+
+                    while ((ch = getNextChar(true)) != -1 && ch != '}') {
+                        buf.append((char) ch);
+                    }
+
+                    String expansion = variableMap.get(buf.toString());
+                    if (expansion == null) {
+                        // If the macro is undefined, then we put back the
+                        // original text to be read again, but without 
expansion
+                        queue(ret);
+                        queue(next);
+                        queue(buf);
+                        queue(ch);
+
+                        ret = getNextChar(false);
+                    } else {
+                        queue(expansion);
+
+                        // By allowing expansion here, we get recursive 
expansion
+                        ret = getNextChar(true);
+                    }
+                } else {
+                    queue(next);
+                }
+            }
+        }
+
+        return (lastCh = ret);
+    }
+
+    @Override
+    public int read(final char[] cbuf, final int off, final int len) throws 
IOException {
+        int read = -1;
+        for (int i = 0; i < len; i++) {
+            int ch = getNextChar(true);
+            if (ch == -1) {
+                break;
+            }
+            read = i;
+            cbuf[off + i] = (char) ch;
+        }
+        return (read == -1) ? read : read + 1;
+    }
+
+}

Propchange: pivot/trunk/core/src/org/apache/pivot/serialization/MacroReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
pivot/trunk/core/src/org/apache/pivot/text/AttributedStringCharacterIterator.java
URL: 
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/text/AttributedStringCharacterIterator.java?rev=1913470&r1=1913469&r2=1913470&view=diff
==============================================================================
--- 
pivot/trunk/core/src/org/apache/pivot/text/AttributedStringCharacterIterator.java
 (original)
+++ 
pivot/trunk/core/src/org/apache/pivot/text/AttributedStringCharacterIterator.java
 Tue Oct 31 19:15:47 2023
@@ -1,243 +1,243 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pivot.text;
-
-import java.awt.Font;
-import java.awt.font.TextAttribute;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.util.Map;
-import java.util.Set;
-import org.apache.pivot.util.Utils;
-
-
-/**
- * A sequence of text along with associated attributes, backed by a
- * {@link AttributedString}, which itself implements all of these
- * methods.
- */
-public class AttributedStringCharacterIterator implements 
AttributedCharacterIterator {
-    private AttributedString storage = null;
-    private int length = 0;
-    private AttributedCharacterIterator.Attribute[] attributes = null;
-    private AttributedCharacterIterator iterator = null;
-
-    public AttributedStringCharacterIterator() {
-        this.storage = new AttributedString("");
-        this.length = 0;
-    }
-
-    public AttributedStringCharacterIterator(String text) {
-        Utils.checkNull(text, "text");
-        this.storage = new AttributedString(text);
-    }
-
-    private void addAttribute(AttributedCharacterIterator.Attribute attribute, 
Object value) {
-        if (this.length != 0) {
-            this.storage.addAttribute(attribute, value);
-        }
-    }
-
-    public AttributedStringCharacterIterator(CharSequence charSequence, Font 
font) {
-        this.storage = new AttributedString(charSequence.toString());
-        this.length = charSequence.length();
-        addAttribute(TextAttribute.FONT, font);
-    }
-
-    public AttributedStringCharacterIterator(CharSequence charSequence, int 
beginIndex, Font font) {
-        this.storage = new 
AttributedString(charSequence.subSequence(beginIndex, 
charSequence.length()).toString());
-        this.length = charSequence.length() - beginIndex;
-        addAttribute(TextAttribute.FONT, font);
-    }
-
-    public AttributedStringCharacterIterator(String text, int beginIndex, int 
endIndex) {
-        Utils.checkNull(text, "text");
-        this.storage = new AttributedString(text.substring(beginIndex, 
endIndex));
-        this.length = endIndex - beginIndex;
-    }
-
-    public AttributedStringCharacterIterator(AttributedCharacterIterator iter, 
int beginIndex, int endIndex) {
-        this.storage = new AttributedString(iter, beginIndex, endIndex);
-        this.length = endIndex - beginIndex;
-    }
-
-    public AttributedStringCharacterIterator(String text, 
AttributedCharacterIterator.Attribute[] attributes) {
-        Utils.checkNull(text, "text");
-        this.storage = new AttributedString(text);
-        this.length = text.length();
-        this.attributes = attributes;
-    }
-
-    public AttributedStringCharacterIterator(String text, int beginIndex, int 
endIndex,
-        AttributedCharacterIterator.Attribute[] attributes) {
-        Utils.checkNull(text, "text");
-        this.storage = new AttributedString(text.substring(beginIndex, 
endIndex));
-        this.length = endIndex - beginIndex;
-        this.attributes = attributes;
-    }
-
-    private AttributedStringCharacterIterator(AttributedString 
attributedString) {
-        // TODO: maybe we should make a copy? instead of adding the 
reference???
-        this.storage = attributedString;
-    }
-
-    // TODO: many more constructors needed, esp. those with attributes already 
in place
-
-    public void addUnderlineAttribute(boolean underline) {
-        addAttribute(TextAttribute.UNDERLINE,
-            underline ? TextAttribute.UNDERLINE_ON : Integer.valueOf(-1));
-    }
-
-    public void addStrikethroughAttribute(boolean strikethrough) {
-        addAttribute(TextAttribute.STRIKETHROUGH,
-            strikethrough ? TextAttribute.STRIKETHROUGH_ON : Boolean.FALSE);
-    }
-
-    // TODO: do we need more parameters here?  for start position or anything 
else?
-    private AttributedCharacterIterator getIter() {
-        Utils.checkNull(this.storage, "source text");
-
-        if (this.iterator == null) {
-            if (this.attributes != null) {
-                this.iterator = storage.getIterator(attributes);
-            } else {
-                this.iterator = storage.getIterator();
-            }
-        }
-        return this.iterator;
-    }
-
-    /**
-     * Reset this iterator, meaning recreate the underlying iterator
-     * on the next call.
-     */
-    public void reset() {
-        this.iterator = null;
-    }
-
-    @Override
-    public Set<Attribute> getAllAttributeKeys() {
-        return getIter().getAllAttributeKeys();
-    }
-
-    @Override
-    public Object getAttribute(Attribute attribute) {
-        return getIter().getAttribute(attribute);
-    }
-
-    @Override
-    public Map<Attribute, Object> getAttributes() {
-        return getIter().getAttributes();
-    }
-
-    @Override
-    public int getRunLimit() {
-        return getIter().getRunLimit();
-    }
-
-    @Override
-    public int getRunLimit(Attribute attribute) {
-        return getIter().getRunLimit(attribute);
-    }
-
-    @Override
-    public int getRunLimit(Set<? extends Attribute> attributes) {
-        return getIter().getRunLimit(attributes);
-    }
-
-    @Override
-    public int getRunStart() {
-        return getIter().getRunStart();
-    }
-
-    @Override
-    public int getRunStart(Attribute attribute) {
-        return getIter().getRunStart(attribute);
-    }
-
-    @Override
-    public int getRunStart(Set<? extends Attribute> attributes) {
-        return getIter().getRunStart(attributes);
-    }
-
-    @Override
-    public char first() {
-        return getIter().first();
-    }
-
-    @Override
-    public char last() {
-        return getIter().last();
-    }
-
-    @Override
-    public char current() {
-        return getIter().current();
-    }
-
-    @Override
-    public char next() {
-        return getIter().next();
-    }
-
-    @Override
-    public char previous() {
-        return getIter().previous();
-    }
-
-    @Override
-    public char setIndex(int position) {
-        return getIter().setIndex(position);
-    }
-
-    @Override
-    public int getBeginIndex() {
-        return getIter().getBeginIndex();
-    }
-
-    @Override
-    public int getEndIndex() {
-        return getIter().getEndIndex();
-    }
-
-    @Override
-    public int getIndex() {
-        return getIter().getIndex();
-    }
-
-    @Override
-    public Object clone() {
-        AttributedStringCharacterIterator obj = new 
AttributedStringCharacterIterator(this.storage);
-        // Copy over the other fields
-        obj.length = this.length;
-        obj.attributes = this.attributes;
-        return obj;
-    }
-
-    @Override
-    public String toString() {
-        AttributedCharacterIterator iter = storage.getIterator();
-        StringBuilder buf = new StringBuilder(iter.getEndIndex());
-        char ch = iter.first();
-        while (ch != DONE) {
-            buf.append(ch);
-            ch = iter.next();
-        }
-        return buf.toString();
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.text;
+
+import java.awt.Font;
+import java.awt.font.TextAttribute;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.util.Map;
+import java.util.Set;
+import org.apache.pivot.util.Utils;
+
+
+/**
+ * A sequence of text along with associated attributes, backed by a
+ * {@link AttributedString}, which itself implements all of these
+ * methods.
+ */
+public class AttributedStringCharacterIterator implements 
AttributedCharacterIterator {
+    private AttributedString storage = null;
+    private int length = 0;
+    private AttributedCharacterIterator.Attribute[] attributes = null;
+    private AttributedCharacterIterator iterator = null;
+
+    public AttributedStringCharacterIterator() {
+        this.storage = new AttributedString("");
+        this.length = 0;
+    }
+
+    public AttributedStringCharacterIterator(String text) {
+        Utils.checkNull(text, "text");
+        this.storage = new AttributedString(text);
+    }
+
+    private void addAttribute(AttributedCharacterIterator.Attribute attribute, 
Object value) {
+        if (this.length != 0) {
+            this.storage.addAttribute(attribute, value);
+        }
+    }
+
+    public AttributedStringCharacterIterator(CharSequence charSequence, Font 
font) {
+        this.storage = new AttributedString(charSequence.toString());
+        this.length = charSequence.length();
+        addAttribute(TextAttribute.FONT, font);
+    }
+
+    public AttributedStringCharacterIterator(CharSequence charSequence, int 
beginIndex, Font font) {
+        this.storage = new 
AttributedString(charSequence.subSequence(beginIndex, 
charSequence.length()).toString());
+        this.length = charSequence.length() - beginIndex;
+        addAttribute(TextAttribute.FONT, font);
+    }
+
+    public AttributedStringCharacterIterator(String text, int beginIndex, int 
endIndex) {
+        Utils.checkNull(text, "text");
+        this.storage = new AttributedString(text.substring(beginIndex, 
endIndex));
+        this.length = endIndex - beginIndex;
+    }
+
+    public AttributedStringCharacterIterator(AttributedCharacterIterator iter, 
int beginIndex, int endIndex) {
+        this.storage = new AttributedString(iter, beginIndex, endIndex);
+        this.length = endIndex - beginIndex;
+    }
+
+    public AttributedStringCharacterIterator(String text, 
AttributedCharacterIterator.Attribute[] attributes) {
+        Utils.checkNull(text, "text");
+        this.storage = new AttributedString(text);
+        this.length = text.length();
+        this.attributes = attributes;
+    }
+
+    public AttributedStringCharacterIterator(String text, int beginIndex, int 
endIndex,
+        AttributedCharacterIterator.Attribute[] attributes) {
+        Utils.checkNull(text, "text");
+        this.storage = new AttributedString(text.substring(beginIndex, 
endIndex));
+        this.length = endIndex - beginIndex;
+        this.attributes = attributes;
+    }
+
+    private AttributedStringCharacterIterator(AttributedString 
attributedString) {
+        // TODO: maybe we should make a copy? instead of adding the 
reference???
+        this.storage = attributedString;
+    }
+
+    // TODO: many more constructors needed, esp. those with attributes already 
in place
+
+    public void addUnderlineAttribute(boolean underline) {
+        addAttribute(TextAttribute.UNDERLINE,
+            underline ? TextAttribute.UNDERLINE_ON : Integer.valueOf(-1));
+    }
+
+    public void addStrikethroughAttribute(boolean strikethrough) {
+        addAttribute(TextAttribute.STRIKETHROUGH,
+            strikethrough ? TextAttribute.STRIKETHROUGH_ON : Boolean.FALSE);
+    }
+
+    // TODO: do we need more parameters here?  for start position or anything 
else?
+    private AttributedCharacterIterator getIter() {
+        Utils.checkNull(this.storage, "source text");
+
+        if (this.iterator == null) {
+            if (this.attributes != null) {
+                this.iterator = storage.getIterator(attributes);
+            } else {
+                this.iterator = storage.getIterator();
+            }
+        }
+        return this.iterator;
+    }
+
+    /**
+     * Reset this iterator, meaning recreate the underlying iterator
+     * on the next call.
+     */
+    public void reset() {
+        this.iterator = null;
+    }
+
+    @Override
+    public Set<Attribute> getAllAttributeKeys() {
+        return getIter().getAllAttributeKeys();
+    }
+
+    @Override
+    public Object getAttribute(Attribute attribute) {
+        return getIter().getAttribute(attribute);
+    }
+
+    @Override
+    public Map<Attribute, Object> getAttributes() {
+        return getIter().getAttributes();
+    }
+
+    @Override
+    public int getRunLimit() {
+        return getIter().getRunLimit();
+    }
+
+    @Override
+    public int getRunLimit(Attribute attribute) {
+        return getIter().getRunLimit(attribute);
+    }
+
+    @Override
+    public int getRunLimit(Set<? extends Attribute> attributes) {
+        return getIter().getRunLimit(attributes);
+    }
+
+    @Override
+    public int getRunStart() {
+        return getIter().getRunStart();
+    }
+
+    @Override
+    public int getRunStart(Attribute attribute) {
+        return getIter().getRunStart(attribute);
+    }
+
+    @Override
+    public int getRunStart(Set<? extends Attribute> attributes) {
+        return getIter().getRunStart(attributes);
+    }
+
+    @Override
+    public char first() {
+        return getIter().first();
+    }
+
+    @Override
+    public char last() {
+        return getIter().last();
+    }
+
+    @Override
+    public char current() {
+        return getIter().current();
+    }
+
+    @Override
+    public char next() {
+        return getIter().next();
+    }
+
+    @Override
+    public char previous() {
+        return getIter().previous();
+    }
+
+    @Override
+    public char setIndex(int position) {
+        return getIter().setIndex(position);
+    }
+
+    @Override
+    public int getBeginIndex() {
+        return getIter().getBeginIndex();
+    }
+
+    @Override
+    public int getEndIndex() {
+        return getIter().getEndIndex();
+    }
+
+    @Override
+    public int getIndex() {
+        return getIter().getIndex();
+    }
+
+    @Override
+    public Object clone() {
+        AttributedStringCharacterIterator obj = new 
AttributedStringCharacterIterator(this.storage);
+        // Copy over the other fields
+        obj.length = this.length;
+        obj.attributes = this.attributes;
+        return obj;
+    }
+
+    @Override
+    public String toString() {
+        AttributedCharacterIterator iter = storage.getIterator();
+        StringBuilder buf = new StringBuilder(iter.getEndIndex());
+        char ch = iter.first();
+        while (ch != DONE) {
+            buf.append(ch);
+            ch = iter.next();
+        }
+        return buf.toString();
+    }
+
+}

Propchange: 
pivot/trunk/core/src/org/apache/pivot/text/AttributedStringCharacterIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/core/src/org/apache/pivot/text/CharSpan.java
URL: 
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/text/CharSpan.java?rev=1913470&r1=1913469&r2=1913470&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/text/CharSpan.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/text/CharSpan.java Tue Oct 31 
19:15:47 2023
@@ -1,276 +1,276 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pivot.text;
-
-import org.apache.pivot.collections.Dictionary;
-import org.apache.pivot.collections.Sequence;
-import org.apache.pivot.json.JSONSerializer;
-import org.apache.pivot.serialization.SerializationException;
-import org.apache.pivot.util.Utils;
-
-/**
- * Immutable class representing a span of characters. The range includes all 
values
- * in the interval <i><code>[start, start+length-1]</code></i> inclusive.  
This is
- * the paradigm used in a lot of places (notably the text controls) to 
indicate a selection.
- * <p> A zero-length span indicates a single caret position at the given start.
- * <p> Negative lengths are not supported and will throw exceptions, as will
- * negative start positions.
- */
-public final class CharSpan {
-    /** The starting location of this span (zero-based). */
-    public final int start;
-    /** The length of this span (non-negative). */
-    public final int length;
-
-    /** The dictionary key used to retrieve the start location. */
-    public static final String START_KEY = "start";
-    /** The dictionary key used to retrieve the length. */
-    public static final String LENGTH_KEY = "length";
-
-    /**
-     * A span of length zero, starting at position zero.
-     */
-    public static final CharSpan ZERO = new CharSpan();
-
-
-    /**
-     * Construct a default span of length zero at location zero.
-     */
-    public CharSpan() {
-        this(0);
-    }
-
-    /**
-     * Construct a new char span of length zero at the given location.
-     *
-     * @param startValue The start of this char span.
-     * @throws IllegalArgumentException if the value is negative.
-     */
-    public CharSpan(final int startValue) {
-        this(startValue, 0);
-    }
-
-    /**
-     * Construct a new char span with the given values.
-     *
-     * @param startValue The start of this char span.
-     * @param lengthValue The length of this char span.
-     * @throws IllegalArgumentException if either value is negative.
-     */
-    public CharSpan(final int startValue, final int lengthValue) {
-        Utils.checkNonNegative(startValue, "start");
-        Utils.checkNonNegative(lengthValue, "length");
-
-        start = startValue;
-        length = lengthValue;
-    }
-
-    /**
-     * Construct a new char span from another one (a "copy constructor").
-     *
-     * @param existingCharSpan An existing char span (which must not be {@code 
null}).
-     * @throws IllegalArgumentException if the given char span is {@code null}.
-     */
-    public CharSpan(final CharSpan existingCharSpan) {
-        Utils.checkNull(existingCharSpan, "existingCharSpan");
-
-        start = existingCharSpan.start;
-        length = existingCharSpan.length;
-    }
-
-    /**
-     * Construct a new char span from the given dictionary which must contain
-     * the {@link #START_KEY} and can also contain the {@link #LENGTH_KEY} key.
-     *
-     * @param charSpanDictionary A dictionary containing start and length 
values.
-     * @throws IllegalArgumentException if the given char span is {@code null},
-     * if the dictionary does not contain at least the start key, or if either 
of
-     * the dictionary values is negative.
-     */
-    public CharSpan(final Dictionary<String, ?> charSpanDictionary) {
-        Utils.checkNull(charSpanDictionary, "charSpanDictionary");
-
-        int startValue;
-        int lengthValue = 0;
-
-        if (charSpanDictionary.containsKey(START_KEY)) {
-            startValue = charSpanDictionary.getInt(START_KEY);
-            Utils.checkNonNegative(startValue, "start");
-        } else {
-            throw new IllegalArgumentException(START_KEY + " is required.");
-        }
-
-        if (charSpanDictionary.containsKey(LENGTH_KEY)) {
-            lengthValue = charSpanDictionary.getInt(LENGTH_KEY);
-            Utils.checkNonNegative(lengthValue, "length");
-        }
-
-        start = startValue;
-        length = lengthValue;
-    }
-
-    /**
-     * Construct a new char span from the given sequence with two
-     * numeric values corresponding to the start and length values
-     * respectively, or one numeric value corresponding to the start
-     * value (length 0).
-     *
-     * @param charSpanSequence A sequence containing the start and length 
values.
-     * @throws IllegalArgumentException if the given char span is {@code 
null}, or
-     * zero length, or length is greater than two.
-     */
-    public CharSpan(final Sequence<?> charSpanSequence) {
-        Utils.checkNull(charSpanSequence, "charSpanSequence");
-
-        int startValue;
-        int lengthValue = 0;
-        int seqLength = charSpanSequence.getLength();
-
-        if (seqLength < 1 || seqLength > 2) {
-            throw new IllegalArgumentException("CharSpan needs one or two 
values in the sequence to construct.");
-        }
-
-        startValue = ((Number) charSpanSequence.get(0)).intValue();
-        Utils.checkNonNegative(startValue, "start");
-
-        if (seqLength == 2) {
-            lengthValue = ((Number) charSpanSequence.get(1)).intValue();
-            Utils.checkNonNegative(lengthValue, "length");
-        }
-
-
-        start = startValue;
-        length = lengthValue;
-    }
-
-    /**
-     * Returns the inclusive end value of this char span, which is the
-     * <code>start + length - 1</code>.  So, if the length is zero,
-     * then the end will be less that the start.
-     *
-     * @return The computed inclusive end value of this char span.
-     */
-    public int getEnd() {
-        return start + length - 1;
-    }
-
-    /**
-     * Returns a new {@link CharSpan} with the start value offset by the given 
amount.
-     *
-     * @param offset The positive or negative amount by which to "move" this
-     * char span (the start value).
-     * @return A new {@link CharSpan} with the updated value.
-     * @throws IllegalArgumentException if the updated start value goes 
negative.
-     */
-    public CharSpan offset(final int offset) {
-        return (offset == 0) ? this : new CharSpan(this.start + offset, 
this.length);
-    }
-
-    /**
-     * Returns a new {@link CharSpan} with the length value offset by the 
given amount
-     * (either positive to lengthen the span or negative to shorten the span).
-     *
-     * @param offset The positive or negative amount by which to "lengthen" 
this
-     * char span (the length value).
-     * @return A new {@link CharSpan} with the updated value.
-     * @throws IllegalArgumentException if the updated length value goes 
negative.
-     */
-    public CharSpan lengthen(final int offset) {
-        return (offset == 0) ? this : new CharSpan(this.start, this.length + 
offset);
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        boolean equal = false;
-
-        if (o == this) {
-            return true;
-        }
-
-        if (o instanceof CharSpan) {
-            CharSpan span = (CharSpan) o;
-            equal = (start == span.start && length == span.length);
-        }
-
-        return equal;
-    }
-
-    @Override
-    public int hashCode() {
-        return 31 * start + length;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + " {start:" + start + ", length:" + 
length + "}";
-    }
-
-    /**
-     * Convert a string into a char span.
-     * <p> If the string value is a JSON map, then parse the map
-     * and construct using the {@link #CharSpan(Dictionary)} method.
-     * <p> If the string value is a JSON list, then parse the list
-     * and construct using the first two values as start and end
-     * respectively, using the {@link #CharSpan(int, int)} constructor.
-     * <p> Also accepted is a simple list of two integer values
-     * separated by comma or semicolon.
-     * <p> Otherwise the string should be a single integer value
-     * that will be used to construct the char span using the {@link 
#CharSpan(int)}
-     * constructor (just the start value, with a zero length).
-     *
-     * @param value The string value to decode into a new char span.
-     * @return The decoded char span.
-     * @throws IllegalArgumentException if the value is {@code null} or empty,
-     * if the string starts with <code>"{"</code> but it cannot be parsed as
-     * a JSON map, if it starts with <code>"["</code> but cannot be parsed
-     * as a JSON list, or cannot be recognized as a simple list of one or
-     * two integers.
-     */
-    public static CharSpan decode(final String value) {
-        Utils.checkNullOrEmpty(value, "value");
-
-        CharSpan charSpan;
-        if (value.startsWith("{")) {
-            try {
-                charSpan = new CharSpan(JSONSerializer.parseMap(value));
-            } catch (SerializationException exception) {
-                throw new IllegalArgumentException(exception);
-            }
-        } else if (value.startsWith("[")) {
-            try {
-                charSpan = new CharSpan(JSONSerializer.parseList(value));
-            } catch (SerializationException exception) {
-                throw new IllegalArgumentException(exception);
-            }
-        } else {
-            String[] parts = value.split("\\s*[,;]\\s*");
-            try {
-                if (parts.length == 2) {
-                    charSpan = new CharSpan(Integer.parseInt(parts[0]), 
Integer.parseInt(parts[1]));
-                } else if (parts.length == 1) {
-                    charSpan = new CharSpan(Integer.parseInt(value));
-                } else {
-                    throw new IllegalArgumentException("Unknown format for 
CharSpan: " + value);
-                }
-            } catch (NumberFormatException ex) {
-                throw new IllegalArgumentException(ex);
-            }
-        }
-
-        return charSpan;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.text;
+
+import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.json.JSONSerializer;
+import org.apache.pivot.serialization.SerializationException;
+import org.apache.pivot.util.Utils;
+
+/**
+ * Immutable class representing a span of characters. The range includes all 
values
+ * in the interval <i><code>[start, start+length-1]</code></i> inclusive.  
This is
+ * the paradigm used in a lot of places (notably the text controls) to 
indicate a selection.
+ * <p> A zero-length span indicates a single caret position at the given start.
+ * <p> Negative lengths are not supported and will throw exceptions, as will
+ * negative start positions.
+ */
+public final class CharSpan {
+    /** The starting location of this span (zero-based). */
+    public final int start;
+    /** The length of this span (non-negative). */
+    public final int length;
+
+    /** The dictionary key used to retrieve the start location. */
+    public static final String START_KEY = "start";
+    /** The dictionary key used to retrieve the length. */
+    public static final String LENGTH_KEY = "length";
+
+    /**
+     * A span of length zero, starting at position zero.
+     */
+    public static final CharSpan ZERO = new CharSpan();
+
+
+    /**
+     * Construct a default span of length zero at location zero.
+     */
+    public CharSpan() {
+        this(0);
+    }
+
+    /**
+     * Construct a new char span of length zero at the given location.
+     *
+     * @param startValue The start of this char span.
+     * @throws IllegalArgumentException if the value is negative.
+     */
+    public CharSpan(final int startValue) {
+        this(startValue, 0);
+    }
+
+    /**
+     * Construct a new char span with the given values.
+     *
+     * @param startValue The start of this char span.
+     * @param lengthValue The length of this char span.
+     * @throws IllegalArgumentException if either value is negative.
+     */
+    public CharSpan(final int startValue, final int lengthValue) {
+        Utils.checkNonNegative(startValue, "start");
+        Utils.checkNonNegative(lengthValue, "length");
+
+        start = startValue;
+        length = lengthValue;
+    }
+
+    /**
+     * Construct a new char span from another one (a "copy constructor").
+     *
+     * @param existingCharSpan An existing char span (which must not be {@code 
null}).
+     * @throws IllegalArgumentException if the given char span is {@code null}.
+     */
+    public CharSpan(final CharSpan existingCharSpan) {
+        Utils.checkNull(existingCharSpan, "existingCharSpan");
+
+        start = existingCharSpan.start;
+        length = existingCharSpan.length;
+    }
+
+    /**
+     * Construct a new char span from the given dictionary which must contain
+     * the {@link #START_KEY} and can also contain the {@link #LENGTH_KEY} key.
+     *
+     * @param charSpanDictionary A dictionary containing start and length 
values.
+     * @throws IllegalArgumentException if the given char span is {@code null},
+     * if the dictionary does not contain at least the start key, or if either 
of
+     * the dictionary values is negative.
+     */
+    public CharSpan(final Dictionary<String, ?> charSpanDictionary) {
+        Utils.checkNull(charSpanDictionary, "charSpanDictionary");
+
+        int startValue;
+        int lengthValue = 0;
+
+        if (charSpanDictionary.containsKey(START_KEY)) {
+            startValue = charSpanDictionary.getInt(START_KEY);
+            Utils.checkNonNegative(startValue, "start");
+        } else {
+            throw new IllegalArgumentException(START_KEY + " is required.");
+        }
+
+        if (charSpanDictionary.containsKey(LENGTH_KEY)) {
+            lengthValue = charSpanDictionary.getInt(LENGTH_KEY);
+            Utils.checkNonNegative(lengthValue, "length");
+        }
+
+        start = startValue;
+        length = lengthValue;
+    }
+
+    /**
+     * Construct a new char span from the given sequence with two
+     * numeric values corresponding to the start and length values
+     * respectively, or one numeric value corresponding to the start
+     * value (length 0).
+     *
+     * @param charSpanSequence A sequence containing the start and length 
values.
+     * @throws IllegalArgumentException if the given char span is {@code 
null}, or
+     * zero length, or length is greater than two.
+     */
+    public CharSpan(final Sequence<?> charSpanSequence) {
+        Utils.checkNull(charSpanSequence, "charSpanSequence");
+
+        int startValue;
+        int lengthValue = 0;
+        int seqLength = charSpanSequence.getLength();
+
+        if (seqLength < 1 || seqLength > 2) {
+            throw new IllegalArgumentException("CharSpan needs one or two 
values in the sequence to construct.");
+        }
+
+        startValue = ((Number) charSpanSequence.get(0)).intValue();
+        Utils.checkNonNegative(startValue, "start");
+
+        if (seqLength == 2) {
+            lengthValue = ((Number) charSpanSequence.get(1)).intValue();
+            Utils.checkNonNegative(lengthValue, "length");
+        }
+
+
+        start = startValue;
+        length = lengthValue;
+    }
+
+    /**
+     * Returns the inclusive end value of this char span, which is the
+     * <code>start + length - 1</code>.  So, if the length is zero,
+     * then the end will be less that the start.
+     *
+     * @return The computed inclusive end value of this char span.
+     */
+    public int getEnd() {
+        return start + length - 1;
+    }
+
+    /**
+     * Returns a new {@link CharSpan} with the start value offset by the given 
amount.
+     *
+     * @param offset The positive or negative amount by which to "move" this
+     * char span (the start value).
+     * @return A new {@link CharSpan} with the updated value.
+     * @throws IllegalArgumentException if the updated start value goes 
negative.
+     */
+    public CharSpan offset(final int offset) {
+        return (offset == 0) ? this : new CharSpan(this.start + offset, 
this.length);
+    }
+
+    /**
+     * Returns a new {@link CharSpan} with the length value offset by the 
given amount
+     * (either positive to lengthen the span or negative to shorten the span).
+     *
+     * @param offset The positive or negative amount by which to "lengthen" 
this
+     * char span (the length value).
+     * @return A new {@link CharSpan} with the updated value.
+     * @throws IllegalArgumentException if the updated length value goes 
negative.
+     */
+    public CharSpan lengthen(final int offset) {
+        return (offset == 0) ? this : new CharSpan(this.start, this.length + 
offset);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        boolean equal = false;
+
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof CharSpan) {
+            CharSpan span = (CharSpan) o;
+            equal = (start == span.start && length == span.length);
+        }
+
+        return equal;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * start + length;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " {start:" + start + ", length:" + 
length + "}";
+    }
+
+    /**
+     * Convert a string into a char span.
+     * <p> If the string value is a JSON map, then parse the map
+     * and construct using the {@link #CharSpan(Dictionary)} method.
+     * <p> If the string value is a JSON list, then parse the list
+     * and construct using the first two values as start and end
+     * respectively, using the {@link #CharSpan(int, int)} constructor.
+     * <p> Also accepted is a simple list of two integer values
+     * separated by comma or semicolon.
+     * <p> Otherwise the string should be a single integer value
+     * that will be used to construct the char span using the {@link 
#CharSpan(int)}
+     * constructor (just the start value, with a zero length).
+     *
+     * @param value The string value to decode into a new char span.
+     * @return The decoded char span.
+     * @throws IllegalArgumentException if the value is {@code null} or empty,
+     * if the string starts with <code>"{"</code> but it cannot be parsed as
+     * a JSON map, if it starts with <code>"["</code> but cannot be parsed
+     * as a JSON list, or cannot be recognized as a simple list of one or
+     * two integers.
+     */
+    public static CharSpan decode(final String value) {
+        Utils.checkNullOrEmpty(value, "value");
+
+        CharSpan charSpan;
+        if (value.startsWith("{")) {
+            try {
+                charSpan = new CharSpan(JSONSerializer.parseMap(value));
+            } catch (SerializationException exception) {
+                throw new IllegalArgumentException(exception);
+            }
+        } else if (value.startsWith("[")) {
+            try {
+                charSpan = new CharSpan(JSONSerializer.parseList(value));
+            } catch (SerializationException exception) {
+                throw new IllegalArgumentException(exception);
+            }
+        } else {
+            String[] parts = value.split("\\s*[,;]\\s*");
+            try {
+                if (parts.length == 2) {
+                    charSpan = new CharSpan(Integer.parseInt(parts[0]), 
Integer.parseInt(parts[1]));
+                } else if (parts.length == 1) {
+                    charSpan = new CharSpan(Integer.parseInt(value));
+                } else {
+                    throw new IllegalArgumentException("Unknown format for 
CharSpan: " + value);
+                }
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException(ex);
+            }
+        }
+
+        return charSpan;
+    }
+}

Propchange: pivot/trunk/core/src/org/apache/pivot/text/CharSpan.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/core/src/org/apache/pivot/text/CompositeIterator.java
URL: 
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/text/CompositeIterator.java?rev=1913470&r1=1913469&r2=1913470&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/text/CompositeIterator.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/text/CompositeIterator.java Tue Oct 
31 19:15:47 2023
@@ -1,185 +1,185 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pivot.text;
-
-import java.text.AttributedCharacterIterator;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.apache.pivot.collections.ArrayList;
-import org.apache.pivot.collections.List;
-import org.apache.pivot.util.Utils;
-
-
-/**
- * An {@link AttributedCharacterIterator} that implements iterating over
- * several (typically two or three) underlying iterators.  The assumption
- * is that no attributes will ever cross the underlying iterator boundaries
- * (which is always the case for insertion of composed but uncommitted text
- * in the middle of two pieces of committed text (for instance).
- */
-public class CompositeIterator implements AttributedCharacterIterator {
-
-    private List<AttributedCharacterIterator> iterators = new ArrayList<>();
-    private int endIndex;
-    private int currentIndex;
-    private AttributedCharacterIterator currentIterator;
-    private int currentIteratorDelta;
-
-    /**
-     * Constructs a CompositeIterator that iterates over the concatenation
-     * of one or more iterators.
-     * @param iterators The base iterators that this composite iterator 
concatenates.
-     */
-    public CompositeIterator(AttributedCharacterIterator... iterators) {
-        int fullLength = 0;
-        for (AttributedCharacterIterator iter : iterators) {
-            int beginIndex = iter.getBeginIndex();    // inclusive
-            int endIndex   = iter.getEndIndex();      // exclusive
-            int range = (endIndex - beginIndex);
-            this.iterators.add(iter);
-            fullLength += range;
-        }
-        this.endIndex = fullLength;
-        setIndex0(0);
-    }
-
-    // CharacterIterator implementations
-
-    public char first() {
-        return setIndex0(0);
-    }
-
-    public char last() {
-        if (endIndex == 0) {
-            return setIndex0(endIndex);
-        } else {
-            return setIndex0(endIndex - 1);
-        }
-    }
-
-    public char next() {
-        if (currentIndex < endIndex) {
-            return setIndex0(currentIndex + 1);
-        } else {
-            return DONE;
-        }
-    }
-
-    public char previous() {
-        if (currentIndex > 0) {
-            return setIndex0(currentIndex - 1);
-        } else {
-            return DONE;
-        }
-    }
-
-    public char current() {
-        return currentIterator.setIndex(currentIndex - currentIteratorDelta);
-    }
-
-    public char setIndex(int position) {
-        // Note: this is a (0 < position <= endIndex) check, since "endIndex" 
is a valid value here
-        Utils.checkIndexBounds(position, 0, endIndex);
-
-        return setIndex0(position);
-    }
-
-    private char setIndex0(int position) {
-        currentIndex = position;
-        int cumLength = 0;
-        for (AttributedCharacterIterator iter : iterators) {
-            int beginIndex = iter.getBeginIndex();
-            int endIndex   = iter.getEndIndex();
-            int range = endIndex - beginIndex;
-            if (currentIndex < endIndex + cumLength) {
-                currentIterator = iter;
-                currentIteratorDelta = beginIndex + cumLength;
-                // TODO: not sure this is going to be correct for > 2 iterators
-                return currentIterator.setIndex(currentIndex - 
currentIteratorDelta);
-            }
-            cumLength += range;
-        }
-        return DONE;
-    }
-
-    public int getBeginIndex() {
-        return 0;
-    }
-
-    public int getEndIndex() {
-        return endIndex;
-    }
-
-    public int getIndex() {
-        return currentIndex;
-    }
-
-    // AttributedCharacterIterator implementations
-
-    public int getRunStart() {
-        return currentIterator.getRunStart() + currentIteratorDelta;
-    }
-
-    public int getRunLimit() {
-        return currentIterator.getRunLimit() + currentIteratorDelta;
-    }
-
-    public int getRunStart(Attribute attribute) {
-        return currentIterator.getRunStart(attribute) + currentIteratorDelta;
-    }
-
-    public int getRunLimit(Attribute attribute) {
-        return currentIterator.getRunLimit(attribute) + currentIteratorDelta;
-    }
-
-    public int getRunStart(Set<? extends Attribute> attributes) {
-        return currentIterator.getRunStart(attributes) + currentIteratorDelta;
-    }
-
-    public int getRunLimit(Set<? extends Attribute> attributes) {
-        return currentIterator.getRunLimit(attributes) + currentIteratorDelta;
-    }
-
-    public Map<Attribute, Object> getAttributes() {
-        return currentIterator.getAttributes();
-    }
-
-    public Set<Attribute> getAllAttributeKeys() {
-        Set<Attribute> keys = new HashSet<>();
-        for (AttributedCharacterIterator iter : iterators) {
-            keys.addAll(iter.getAllAttributeKeys());
-        }
-        return keys;
-    }
-
-    public Object getAttribute(Attribute attribute) {
-        return currentIterator.getAttribute(attribute);
-    }
-
-    // Object implementations
-
-    public Object clone() {
-        try {
-            CompositeIterator other = (CompositeIterator) super.clone();
-            return other;
-        } catch (CloneNotSupportedException e) {
-            throw new InternalError();
-        }
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.text;
+
+import java.text.AttributedCharacterIterator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.pivot.collections.ArrayList;
+import org.apache.pivot.collections.List;
+import org.apache.pivot.util.Utils;
+
+
+/**
+ * An {@link AttributedCharacterIterator} that implements iterating over
+ * several (typically two or three) underlying iterators.  The assumption
+ * is that no attributes will ever cross the underlying iterator boundaries
+ * (which is always the case for insertion of composed but uncommitted text
+ * in the middle of two pieces of committed text (for instance).
+ */
+public class CompositeIterator implements AttributedCharacterIterator {
+
+    private List<AttributedCharacterIterator> iterators = new ArrayList<>();
+    private int endIndex;
+    private int currentIndex;
+    private AttributedCharacterIterator currentIterator;
+    private int currentIteratorDelta;
+
+    /**
+     * Constructs a CompositeIterator that iterates over the concatenation
+     * of one or more iterators.
+     * @param iterators The base iterators that this composite iterator 
concatenates.
+     */
+    public CompositeIterator(AttributedCharacterIterator... iterators) {
+        int fullLength = 0;
+        for (AttributedCharacterIterator iter : iterators) {
+            int beginIndex = iter.getBeginIndex();    // inclusive
+            int endIndex   = iter.getEndIndex();      // exclusive
+            int range = (endIndex - beginIndex);
+            this.iterators.add(iter);
+            fullLength += range;
+        }
+        this.endIndex = fullLength;
+        setIndex0(0);
+    }
+
+    // CharacterIterator implementations
+
+    public char first() {
+        return setIndex0(0);
+    }
+
+    public char last() {
+        if (endIndex == 0) {
+            return setIndex0(endIndex);
+        } else {
+            return setIndex0(endIndex - 1);
+        }
+    }
+
+    public char next() {
+        if (currentIndex < endIndex) {
+            return setIndex0(currentIndex + 1);
+        } else {
+            return DONE;
+        }
+    }
+
+    public char previous() {
+        if (currentIndex > 0) {
+            return setIndex0(currentIndex - 1);
+        } else {
+            return DONE;
+        }
+    }
+
+    public char current() {
+        return currentIterator.setIndex(currentIndex - currentIteratorDelta);
+    }
+
+    public char setIndex(int position) {
+        // Note: this is a (0 < position <= endIndex) check, since "endIndex" 
is a valid value here
+        Utils.checkIndexBounds(position, 0, endIndex);
+
+        return setIndex0(position);
+    }
+
+    private char setIndex0(int position) {
+        currentIndex = position;
+        int cumLength = 0;
+        for (AttributedCharacterIterator iter : iterators) {
+            int beginIndex = iter.getBeginIndex();
+            int endIndex   = iter.getEndIndex();
+            int range = endIndex - beginIndex;
+            if (currentIndex < endIndex + cumLength) {
+                currentIterator = iter;
+                currentIteratorDelta = beginIndex + cumLength;
+                // TODO: not sure this is going to be correct for > 2 iterators
+                return currentIterator.setIndex(currentIndex - 
currentIteratorDelta);
+            }
+            cumLength += range;
+        }
+        return DONE;
+    }
+
+    public int getBeginIndex() {
+        return 0;
+    }
+
+    public int getEndIndex() {
+        return endIndex;
+    }
+
+    public int getIndex() {
+        return currentIndex;
+    }
+
+    // AttributedCharacterIterator implementations
+
+    public int getRunStart() {
+        return currentIterator.getRunStart() + currentIteratorDelta;
+    }
+
+    public int getRunLimit() {
+        return currentIterator.getRunLimit() + currentIteratorDelta;
+    }
+
+    public int getRunStart(Attribute attribute) {
+        return currentIterator.getRunStart(attribute) + currentIteratorDelta;
+    }
+
+    public int getRunLimit(Attribute attribute) {
+        return currentIterator.getRunLimit(attribute) + currentIteratorDelta;
+    }
+
+    public int getRunStart(Set<? extends Attribute> attributes) {
+        return currentIterator.getRunStart(attributes) + currentIteratorDelta;
+    }
+
+    public int getRunLimit(Set<? extends Attribute> attributes) {
+        return currentIterator.getRunLimit(attributes) + currentIteratorDelta;
+    }
+
+    public Map<Attribute, Object> getAttributes() {
+        return currentIterator.getAttributes();
+    }
+
+    public Set<Attribute> getAllAttributeKeys() {
+        Set<Attribute> keys = new HashSet<>();
+        for (AttributedCharacterIterator iter : iterators) {
+            keys.addAll(iter.getAllAttributeKeys());
+        }
+        return keys;
+    }
+
+    public Object getAttribute(Attribute attribute) {
+        return currentIterator.getAttribute(attribute);
+    }
+
+    // Object implementations
+
+    public Object clone() {
+        try {
+            CompositeIterator other = (CompositeIterator) super.clone();
+            return other;
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError();
+        }
+    }
+
+}

Propchange: pivot/trunk/core/src/org/apache/pivot/text/CompositeIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pivot/trunk/core/src/org/apache/pivot/util/BooleanResult.java
URL: 
http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/util/BooleanResult.java?rev=1913470&r1=1913469&r2=1913470&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/util/BooleanResult.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/util/BooleanResult.java Tue Oct 31 
19:15:47 2023
@@ -1,108 +1,108 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.pivot.util;
-
-/**
- * An object holding a boolean value that can be used with a {@code forEach}
- * or lambda expressionm, where the value used during the iteration must be
- * final or effectively final.
- */
-public class BooleanResult {
-    /**
-     * The current boolean value.
-     */
-    private boolean result;
-
-    /**
-     * Construct one of these and set the initial value to the given value.
-     *
-     * @param initialValue The initial boolean value.
-     */
-    public BooleanResult(final boolean initialValue) {
-        result = initialValue;
-    }
-
-    /**
-     * Construct one of these and set the initial boolean value to
-     * {@code false}.
-     */
-    public BooleanResult() {
-        this(false);
-    }
-
-    /**
-     * Update the saved value by <code>OR</code>ing this new value
-     * with the saved value.
-     *
-     * @param value The new value to OR into the saved one.
-     */
-    public void or(final boolean value) {
-        result |= value;
-    }
-
-    /**
-     * Update the saved value by <code>AND</code>ing this new value
-     * with the saved value.
-     *
-     * @param value The new value to AND into the saved one.
-     */
-    public void and(final boolean value) {
-        result &= value;
-    }
-
-    /**
-     * Update the saved value by <code>XOR</code>ing this new value
-     * with the saved value.
-     *
-     * @param value The new value to XOR into the saved one.
-     */
-    public void xor(final boolean value) {
-        result ^= value;
-    }
-
-    /**
-     * Negate the saved value.
-     */
-    public void not() {
-        result = !result;
-    }
-
-    /**
-     * Clear the boolean result to the default value
-     * of {@code false}.
-     */
-    public void clear() {
-        result = false;
-    }
-
-    /**
-     * Set the result value to the given value.
-     *
-     * @param value The new value to set.
-     */
-    public void set(final boolean value) {
-        result = value;
-    }
-
-    /**
-     * @return The final boolean value.
-     */
-    public boolean get() {
-        return result;
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.util;
+
+/**
+ * An object holding a boolean value that can be used with a {@code forEach}
+ * or lambda expressionm, where the value used during the iteration must be
+ * final or effectively final.
+ */
+public class BooleanResult {
+    /**
+     * The current boolean value.
+     */
+    private boolean result;
+
+    /**
+     * Construct one of these and set the initial value to the given value.
+     *
+     * @param initialValue The initial boolean value.
+     */
+    public BooleanResult(final boolean initialValue) {
+        result = initialValue;
+    }
+
+    /**
+     * Construct one of these and set the initial boolean value to
+     * {@code false}.
+     */
+    public BooleanResult() {
+        this(false);
+    }
+
+    /**
+     * Update the saved value by <code>OR</code>ing this new value
+     * with the saved value.
+     *
+     * @param value The new value to OR into the saved one.
+     */
+    public void or(final boolean value) {
+        result |= value;
+    }
+
+    /**
+     * Update the saved value by <code>AND</code>ing this new value
+     * with the saved value.
+     *
+     * @param value The new value to AND into the saved one.
+     */
+    public void and(final boolean value) {
+        result &= value;
+    }
+
+    /**
+     * Update the saved value by <code>XOR</code>ing this new value
+     * with the saved value.
+     *
+     * @param value The new value to XOR into the saved one.
+     */
+    public void xor(final boolean value) {
+        result ^= value;
+    }
+
+    /**
+     * Negate the saved value.
+     */
+    public void not() {
+        result = !result;
+    }
+
+    /**
+     * Clear the boolean result to the default value
+     * of {@code false}.
+     */
+    public void clear() {
+        result = false;
+    }
+
+    /**
+     * Set the result value to the given value.
+     *
+     * @param value The new value to set.
+     */
+    public void set(final boolean value) {
+        result = value;
+    }
+
+    /**
+     * @return The final boolean value.
+     */
+    public boolean get() {
+        return result;
+    }
+
+}

Propchange: pivot/trunk/core/src/org/apache/pivot/util/BooleanResult.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to