Package com.ibm.icu.text

Source Code of com.ibm.icu.text.UnicodeSet$Filter

/*
*******************************************************************************
* Copyright (C) 1996-2012, International Business Machines Corporation and    *
* others. All Rights Reserved.                                                *
*******************************************************************************
*/
package com.ibm.icu.text;

import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.TreeSet;

import com.ibm.icu.impl.BMPSet;
import com.ibm.icu.impl.Norm2AllModes;
import com.ibm.icu.impl.PatternProps;
import com.ibm.icu.impl.RuleCharacterIterator;
import com.ibm.icu.impl.SortedSetRelation;
import com.ibm.icu.impl.UBiDiProps;
import com.ibm.icu.impl.UCaseProps;
import com.ibm.icu.impl.UCharacterProperty;
import com.ibm.icu.impl.UPropertyAliases;
import com.ibm.icu.impl.UnicodeSetStringSpan;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.lang.CharSequences;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.lang.UScript;
import com.ibm.icu.util.Freezable;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.VersionInfo;

/**
* A mutable set of Unicode characters and multicharacter strings. Objects of this class represent <em>character classes</em> used in
* regular expressions. A character specifies a subset of Unicode code points. Legal code points are U+0000 to U+10FFFF, inclusive.
*
* Note: method freeze() will not only makes the set immutable, but also makes important methods much higher performance: contains(c),
* containsNone(...), span(...), spanBack(...) etc. After the object is frozen, any subsequent call that wants to change the object will
* throw UnsupportedOperationException.
*
* <p>
* The UnicodeSet class is not designed to be subclassed.
*
* <p>
* <code>UnicodeSet</code> supports two APIs. The first is the <em>operand</em> API that allows the caller to modify the value of a
* <code>UnicodeSet</code> object. It conforms to Java 2's <code>java.util.Set</code> interface, although <code>UnicodeSet</code> does not
* actually implement that interface. All methods of <code>Set</code> are supported, with the modification that they take a character range
* or single character instead of an <code>Object</code>, and they take a <code>UnicodeSet</code> instead of a <code>Collection</code>. The
* operand API may be thought of in terms of boolean logic: a boolean OR is implemented by <code>add</code>, a boolean AND is implemented by
* <code>retain</code>, a boolean XOR is implemented by <code>complement</code> taking an argument, and a boolean NOT is implemented by
* <code>complement</code> with no argument. In terms of traditional set theory function names, <code>add</code> is a union,
* <code>retain</code> is an intersection, <code>remove</code> is an asymmetric difference, and <code>complement</code> with no argument is
* a set complement with respect to the superset range <code>MIN_VALUE-MAX_VALUE</code>
*
* <p>
* The second API is the <code>applyPattern()</code>/<code>toPattern()</code> API from the <code>java.text.Format</code>-derived classes.
* Unlike the methods that add characters, add categories, and control the logic of the set, the method <code>applyPattern()</code> sets all
* attributes of a <code>UnicodeSet</code> at once, based on a string pattern.
*
* <p>
* <b>Pattern syntax</b>
* </p>
*
* Patterns are accepted by the constructors and the <code>applyPattern()</code> methods and returned by the <code>toPattern()</code>
* method. These patterns follow a syntax similar to that employed by version 8 regular expression character classes. Here are some simple
* examples:
*
* <blockquote>
* <table>
* <tr align="top">
* <td nowrap valign="top" align="left"><code>[]</code></td>
* <td valign="top">No characters</td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="left"><code>[a]</code></td>
* <td valign="top">The character 'a'</td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="left"><code>[ae]</code></td>
* <td valign="top">The characters 'a' and 'e'</td>
* </tr>
* <tr>
* <td nowrap valign="top" align="left"><code>[a-e]</code></td>
* <td valign="top">The characters 'a' through 'e' inclusive, in Unicode code point order</td>
* </tr>
* <tr>
* <td nowrap valign="top" align="left"><code>[\\u4E01]</code></td>
* <td valign="top">The character U+4E01</td>
* </tr>
* <tr>
* <td nowrap valign="top" align="left"><code>[a{ab}{ac}]</code></td>
* <td valign="top">The character 'a' and the multicharacter strings &quot;ab&quot; and &quot;ac&quot;</td>
* </tr>
* <tr>
* <td nowrap valign="top" align="left"><code>[\p{Lu}]</code></td>
* <td valign="top">All characters in the general category Uppercase Letter</td>
* </tr>
* </table>
* </blockquote>
*
* Any character may be preceded by a backslash in order to remove any special meaning. White space characters, as defined by the Unicode
* Pattern_White_Space property, are ignored, unless they are escaped.
*
* <p>
* Property patterns specify a set of characters having a certain property as defined by the Unicode standard. Both the POSIX-like "[:Lu:]"
* and the Perl-like syntax "\p{Lu}" are recognized. For a complete list of supported property patterns, see the User's Guide for UnicodeSet
* at <a href="http://www.icu-project.org/userguide/unicodeSet.html"> http://www.icu-project.org/userguide/unicodeSet.html</a>. Actual
* determination of property data is defined by the underlying Unicode database as implemented by UCharacter.
*
* <p>
* Patterns specify individual characters, ranges of characters, and Unicode property sets. When elements are concatenated, they specify
* their union. To complement a set, place a '^' immediately after the opening '['. Property patterns are inverted by modifying their
* delimiters; "[:^foo]" and "\P{foo}". In any other location, '^' has no special meaning.
*
* <p>
* Ranges are indicated by placing two a '-' between two characters, as in "a-z". This specifies the range of all characters from the left
* to the right, in Unicode order. If the left character is greater than or equal to the right character it is a syntax error. If a '-'
* occurs as the first character after the opening '[' or '[^', or if it occurs as the last character before the closing ']', then it is
* taken as a literal. Thus "[a\\-b]", "[-ab]", and "[ab-]" all indicate the same set of three characters, 'a', 'b', and '-'.
*
* <p>
* Sets may be intersected using the '&' operator or the asymmetric set difference may be taken using the '-' operator, for example,
* "[[:L:]&[\\u0000-\\u0FFF]]" indicates the set of all Unicode letters with values less than 4096. Operators ('&' and '|') have equal
* precedence and bind left-to-right. Thus "[[:L:]-[a-z]-[\\u0100-\\u01FF]]" is equivalent to "[[[:L:]-[a-z]]-[\\u0100-\\u01FF]]". This only
* really matters for difference; intersection is commutative.
*
* <table>
* <tr valign=top>
* <td nowrap><code>[a]</code>
* <td>The set containing 'a'
* <tr valign=top>
* <td nowrap><code>[a-z]</code>
* <td>The set containing 'a' through 'z' and all letters in between, in Unicode order
* <tr valign=top>
* <td nowrap><code>[^a-z]</code>
* <td>The set containing all characters but 'a' through 'z', that is, U+0000 through 'a'-1 and 'z'+1 through U+10FFFF
* <tr valign=top>
* <td nowrap><code>[[<em>pat1</em>][<em>pat2</em>]]</code>
* <td>The union of sets specified by <em>pat1</em> and <em>pat2</em>
* <tr valign=top>
* <td nowrap><code>[[<em>pat1</em>]&[<em>pat2</em>]]</code>
* <td>The intersection of sets specified by <em>pat1</em> and <em>pat2</em>
* <tr valign=top>
* <td nowrap><code>[[<em>pat1</em>]-[<em>pat2</em>]]</code>
* <td>The asymmetric difference of sets specified by <em>pat1</em> and <em>pat2</em>
* <tr valign=top>
* <td nowrap><code>[:Lu:] or \p{Lu}</code>
* <td>The set of characters having the specified Unicode property; in this case, Unicode uppercase letters
* <tr valign=top>
* <td nowrap><code>[:^Lu:] or \P{Lu}</code>
* <td>The set of characters <em>not</em> having the given Unicode property
* </table>
*
* <p>
* <b>Warning</b>: you cannot add an empty string ("") to a UnicodeSet.
* </p>
*
* <p>
* <b>Formal syntax</b>
* </p>
*
* <blockquote>
* <table>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>pattern :=&nbsp; </code></td>
* <td valign="top"><code>('[' '^'? item* ']') |
*       property</code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>item :=&nbsp; </code></td>
* <td valign="top"><code>char | (char '-' char) | pattern-expr<br>
*       </code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>pattern-expr :=&nbsp; </code></td>
* <td valign="top"><code>pattern | pattern-expr pattern |
*       pattern-expr op pattern<br>
*       </code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>op :=&nbsp; </code></td>
* <td valign="top"><code>'&amp;' | '-'<br>
*       </code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>special :=&nbsp; </code></td>
* <td valign="top"><code>'[' | ']' | '-'<br>
*       </code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>char :=&nbsp; </code></td>
* <td valign="top"><em>any character that is not</em><code> special<br>
*       | ('\\' </code><em>any character</em><code>)<br>
*       | ('&#92;u' hex hex hex hex)<br>
*       </code></td>
* </tr>
* <tr align="top">
* <td nowrap valign="top" align="right"><code>hex :=&nbsp; </code></td>
* <td valign="top"><em>any character for which
*       </em><code>Character.digit(c, 16)</code><em>
*       returns a non-negative result</em></td>
* </tr>
* <tr>
* <td nowrap valign="top" align="right"><code>property :=&nbsp; </code></td>
* <td valign="top"><em>a Unicode property set pattern</td>
* </tr>
* </table>
* <br>
* <table border="1">
* <tr>
* <td>Legend:
* <table>
* <tr>
* <td nowrap valign="top"><code>a := b</code></td>
* <td width="20" valign="top">&nbsp;</td>
* <td valign="top"><code>a</code> may be replaced by <code>b</code></td>
* </tr>
* <tr>
* <td nowrap valign="top"><code>a?</code></td>
* <td valign="top"></td>
* <td valign="top">zero or one instance of <code>a</code><br>
* </td>
* </tr>
* <tr>
* <td nowrap valign="top"><code>a*</code></td>
* <td valign="top"></td>
* <td valign="top">one or more instances of <code>a</code><br>
* </td>
* </tr>
* <tr>
* <td nowrap valign="top"><code>a | b</code></td>
* <td valign="top"></td>
* <td valign="top">either <code>a</code> or <code>b</code><br>
* </td>
* </tr>
* <tr>
* <td nowrap valign="top"><code>'a'</code></td>
* <td valign="top"></td>
* <td valign="top">the literal string between the quotes</td>
* </tr>
* </table>
* </td>
* </tr>
* </table>
* </blockquote>
* <p>
* To iterate over contents of UnicodeSet, use UnicodeSetIterator class.
*
* @author Alan Liu
* @stable ICU 2.0
* @see UnicodeSetIterator
*/
@SuppressWarnings("deprecation")
public class UnicodeSet extends UnicodeFilter implements Iterable<String>, Comparable<UnicodeSet>, Freezable<UnicodeSet> {

  /**
   * Constant for the empty set.
   *
   * @stable ICU 4.8
   */
  public static final UnicodeSet EMPTY = new UnicodeSet().freeze();
  /**
   * Constant for the set of all code points. (Since UnicodeSets can include strings, does not include everything that a UnicodeSet can.)
   *
   * @stable ICU 4.8
   */
  public static final UnicodeSet ALL_CODE_POINTS = new UnicodeSet(0, 0x10FFFF).freeze();

  private static XSymbolTable XSYMBOL_TABLE = null; // for overriding the the function processing

  private static final int LOW = 0x000000; // LOW <= all valid values. ZERO for codepoints
  private static final int HIGH = 0x110000; // HIGH > all valid values. 10000 for code units.
  // 110000 for codepoints

  /**
   * Minimum value that can be stored in a UnicodeSet.
   *
   * @stable ICU 2.0
   */
  public static final int MIN_VALUE = LOW;

  /**
   * Maximum value that can be stored in a UnicodeSet.
   *
   * @stable ICU 2.0
   */
  public static final int MAX_VALUE = HIGH - 1;

  private int len;      // length used; list may be longer to minimize reallocs
  private int[] list;   // MUST be terminated with HIGH
  private int[] rangeList; // internal buffer
  private int[] buffer; // internal buffer

  // NOTE: normally the field should be of type SortedSet; but that is missing a public clone!!
  // is not private so that UnicodeSetIterator can get access
  TreeSet<String> strings = new TreeSet<String>();

  /**
   * The pattern representation of this set. This may not be the most economical pattern. It is the pattern supplied to applyPattern(),
   * with variables substituted and whitespace removed. For sets constructed without applyPattern(), or modified using the non-pattern
   * API, this string will be null, indicating that toPattern() must generate a pattern representation from the inversion list.
   */
  private String pat = null;

  private static final int START_EXTRA = 16;         // initial storage. Must be >= 0
  private static final int GROW_EXTRA = START_EXTRA; // extra amount for growth. Must be >= 0

  // Special property set IDs
  private static final String ANY_ID = "ANY";   // [\u0000-\U0010FFFF]
  private static final String ASCII_ID = "ASCII"; // [\u0000-\u007F]
  private static final String ASSIGNED = "Assigned"; // [:^Cn:]

  /**
   * A set of all characters _except_ the second through last characters of certain ranges. These ranges are ranges of characters whose
   * properties are all exactly alike, e.g. CJK Ideographs from U+4E00 to U+9FA5.
   */
  private static UnicodeSet INCLUSIONS[] = null;

  private BMPSet bmpSet; // The set is frozen iff either bmpSet or stringSpan is not null.
  private UnicodeSetStringSpan stringSpan;

  //----------------------------------------------------------------
  // Public API
  //----------------------------------------------------------------

  /**
   * Constructs an empty set.
   *
   * @stable ICU 2.0
   */
  public UnicodeSet() {
    list = new int[1 + START_EXTRA];
    list[len++] = HIGH;
  }

  /**
   * Constructs a copy of an existing set.
   *
   * @stable ICU 2.0
   */
  public UnicodeSet(final UnicodeSet other) {
    set(other);
  }

  /**
   * Constructs a set containing the given range. If <code>end >
   * start</code> then an empty set is created.
   *
   * @param start
   *            first character, inclusive, of range
   * @param end
   *            last character, inclusive, of range
   * @stable ICU 2.0
   */
  public UnicodeSet(final int start, final int end) {
    this();
    complement(start, end);
  }

  /**
   * Quickly constructs a set from a set of ranges <s0, e0, s1, e1, s2, e2, ..., sn, en>. There must be an even number of integers, and
   * they must be all greater than zero, all less than or equal to Character.MAX_CODE_POINT. In each pair (..., si, ei, ...) it must be
   * true that si <= ei Between adjacent pairs (...ei, sj...), it must be true that ei+1 < sj
   *
   * @param pairs
   *            pairs of character representing ranges
   * @stable ICU 4.4
   */
  public UnicodeSet(final int... pairs) {
    if ((pairs.length & 1) != 0) {
      throw new IllegalArgumentException("Must have even number of integers");
    }
    list = new int[pairs.length + 1]; // don't allocate extra space, because it is likely that this is a fixed set.
    len = list.length;
    int last = -1; // used to ensure that the results are monotonically increasing.
    int i = 0;
    while (i < pairs.length) {
      // start of pair
      int start = pairs[i];
      if (last >= start) {
        throw new IllegalArgumentException("Must be monotonically increasing.");
      }
      list[i++] = last = start;
      // end of pair
      int end = pairs[i] + 1;
      if (last >= end) {
        throw new IllegalArgumentException("Must be monotonically increasing.");
      }
      list[i++] = last = end;
    }
    list[i] = HIGH; // terminate
  }

  /**
   * Constructs a set from the given pattern. See the class description for the syntax of the pattern language. Whitespace is ignored.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 2.0
   */
  public UnicodeSet(final String pattern) {
    this();
    applyPattern(pattern, null, null, IGNORE_SPACE);
  }

  /**
   * Constructs a set from the given pattern. See the class description for the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param ignoreWhitespace
   *            if true, ignore Unicode Pattern_White_Space characters
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 2.0
   */
  public UnicodeSet(final String pattern, final boolean ignoreWhitespace) {
    this();
    applyPattern(pattern, null, null, ignoreWhitespace ? IGNORE_SPACE : 0);
  }

  /**
   * Constructs a set from the given pattern. See the class description for the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param options
   *            a bitmask indicating which options to apply. Valid options are IGNORE_SPACE and CASE.
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 3.8
   */
  public UnicodeSet(final String pattern, final int options) {
    this();
    applyPattern(pattern, null, null, options);
  }

  /**
   * Constructs a set from the given pattern. See the class description for the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param pos
   *            on input, the position in pattern at which to start parsing. On output, the position after the last character parsed.
   * @param symbols
   *            a symbol table mapping variables to char[] arrays and chars to UnicodeSets
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 2.0
   */
  public UnicodeSet(final String pattern, final ParsePosition pos, final SymbolTable symbols) {
    this();
    applyPattern(pattern, pos, symbols, IGNORE_SPACE);
  }

  /**
   * Constructs a set from the given pattern. See the class description for the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param pos
   *            on input, the position in pattern at which to start parsing. On output, the position after the last character parsed.
   * @param symbols
   *            a symbol table mapping variables to char[] arrays and chars to UnicodeSets
   * @param options
   *            a bitmask indicating which options to apply. Valid options are IGNORE_SPACE and CASE.
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 3.2
   */
  public UnicodeSet(final String pattern, final ParsePosition pos, final SymbolTable symbols, final int options) {
    this();
    applyPattern(pattern, pos, symbols, options);
  }

  /**
   * Return a new set that is equivalent to this one.
   *
   * @stable ICU 2.0
   */
  @Override
  public Object clone() {
    UnicodeSet result = new UnicodeSet(this);
    result.bmpSet = this.bmpSet;
    result.stringSpan = this.stringSpan;
    return result;
  }

  /**
   * Make this object represent the range <code>start - end</code>. If <code>end > start</code> then this object is set to an an empty
   * range.
   *
   * @param start
   *            first character in the set, inclusive
   * @param end
   *            last character in the set, inclusive
   * @stable ICU 2.0
   */
  public UnicodeSet set(final int start, final int end) {
    checkFrozen();
    clear();
    complement(start, end);
    return this;
  }

  /**
   * Make this object represent the same set as <code>other</code>.
   *
   * @param other
   *            a <code>UnicodeSet</code> whose value will be copied to this object
   * @stable ICU 2.0
   */
  public UnicodeSet set(final UnicodeSet other) {
    checkFrozen();
    list = other.list.clone();
    len = other.len;
    pat = other.pat;
    strings = new TreeSet<String>(other.strings);
    return this;
  }

  /**
   * Modifies this set to represent the set specified by the given pattern. See the class description for the syntax of the pattern
   * language. Whitespace is ignored.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 2.0
   */
  public final UnicodeSet applyPattern(final String pattern) {
    checkFrozen();
    return applyPattern(pattern, null, null, IGNORE_SPACE);
  }

  /**
   * Modifies this set to represent the set specified by the given pattern, optionally ignoring whitespace. See the class description for
   * the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param ignoreWhitespace
   *            if true then Unicode Pattern_White_Space characters are ignored
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 2.0
   */
  public UnicodeSet applyPattern(final String pattern, final boolean ignoreWhitespace) {
    checkFrozen();
    return applyPattern(pattern, null, null, ignoreWhitespace ? IGNORE_SPACE : 0);
  }

  /**
   * Modifies this set to represent the set specified by the given pattern, optionally ignoring whitespace. See the class description for
   * the syntax of the pattern language.
   *
   * @param pattern
   *            a string specifying what characters are in the set
   * @param options
   *            a bitmask indicating which options to apply. Valid options are IGNORE_SPACE and CASE.
   * @exception java.lang.IllegalArgumentException
   *                if the pattern contains a syntax error.
   * @stable ICU 3.8
   */
  public UnicodeSet applyPattern(final String pattern, final int options) {
    checkFrozen();
    return applyPattern(pattern, null, null, options);
  }

  /**
   * Return true if the given position, in the given pattern, appears to be the start of a UnicodeSet pattern.
   *
   * @stable ICU 2.0
   */
  public static boolean resemblesPattern(final String pattern, final int pos) {
    return ((pos + 1) < pattern.length() && pattern.charAt(pos) == '[') || resemblesPropertyPattern(pattern, pos);
  }

  /**
   * Append the <code>toPattern()</code> representation of a string to the given <code>StringBuffer</code>.
   */
  private static void _appendToPat(final StringBuffer buf, final String s, final boolean escapeUnprintable) {
    int cp;
    for (int i = 0; i < s.length(); i += Character.charCount(cp)) {
      cp = s.codePointAt(i);
      _appendToPat(buf, cp, escapeUnprintable);
    }
  }

  /**
   * Append the <code>toPattern()</code> representation of a character to the given <code>StringBuffer</code>.
   */
  private static void _appendToPat(final StringBuffer buf, final int c, final boolean escapeUnprintable) {
    // "Utility.isUnprintable(c)" seems redundant since the the call
    //      "Utility.escapeUnprintable(buf, c)" does it again inside the if statement
    if (escapeUnprintable && Utility.isUnprintable(c)) {
      // Use hex escape notation (<backslash>uxxxx or <backslash>Uxxxxxxxx) for anything
      // unprintable
      if (Utility.escapeUnprintable(buf, c)) {
        return;
      }
    }
    // Okay to let ':' pass through
    switch (c) {
    case '[': // SET_OPEN:
    case ']': // SET_CLOSE:
    case '-': // HYPHEN:
    case '^': // COMPLEMENT:
    case '&': // INTERSECTION:
    case '\\': //BACKSLASH:
    case '{':
    case '}':
    case '$':
    case ':':
      buf.append('\\');
      break;
    default:
      // Escape whitespace
      if (PatternProps.isWhiteSpace(c)) {
        buf.append('\\');
      }
      break;
    }
    UTF16.append(buf, c);
  }

  /**
   * Returns a string representation of this set. If the result of calling this function is passed to a UnicodeSet constructor, it will
   * produce another set that is equal to this one.
   *
   * @stable ICU 2.0
   */
  public String toPattern(final boolean escapeUnprintable) {
    StringBuffer result = new StringBuffer();
    return _toPattern(result, escapeUnprintable).toString();
  }

  /**
   * Append a string representation of this set to result. This will be a cleaned version of the string passed to applyPattern(), if there
   * is one. Otherwise it will be generated.
   */
  private StringBuffer _toPattern(final StringBuffer result, final boolean escapeUnprintable) {
    if (pat != null) {
      int i;
      int backslashCount = 0;
      for (i = 0; i < pat.length();) {
        int c = UTF16.charAt(pat, i);
        i += UTF16.getCharCount(c);
        if (escapeUnprintable && Utility.isUnprintable(c)) {
          // If the unprintable character is preceded by an odd
          // number of backslashes, then it has been escaped.
          // Before unescaping it, we delete the final
          // backslash.
          if (backslashCount % 2 != 0) {
            result.setLength(result.length() - 1);
          }
          Utility.escapeUnprintable(result, c);
          backslashCount = 0;
        } else {
          UTF16.append(result, c);
          if (c == '\\') {
            ++backslashCount;
          } else {
            backslashCount = 0;
          }
        }
      }
      return result;
    }

    return _generatePattern(result, escapeUnprintable, true);
  }

  /**
   * Generate and append a string representation of this set to result. This does not use this.pat, the cleaned up copy of the string
   * passed to applyPattern().
   *
   * @param result
   *            the buffer into which to generate the pattern
   * @param escapeUnprintable
   *            escape unprintable characters if true
   * @stable ICU 2.0
   */
  public StringBuffer _generatePattern(final StringBuffer result, final boolean escapeUnprintable) {
    return _generatePattern(result, escapeUnprintable, true);
  }

  /**
   * Generate and append a string representation of this set to result. This does not use this.pat, the cleaned up copy of the string
   * passed to applyPattern().
   *
   * @param includeStrings
   *            if false, doesn't include the strings.
   * @stable ICU 3.8
   */
  public StringBuffer _generatePattern(final StringBuffer result, final boolean escapeUnprintable, final boolean includeStrings) {
    result.append('[');

    //      // Check against the predefined categories.  We implicitly build
    //      // up ALL category sets the first time toPattern() is called.
    //      for (int cat=0; cat<CATEGORY_COUNT; ++cat) {
    //          if (this.equals(getCategorySet(cat))) {
    //              result.append(':');
    //              result.append(CATEGORY_NAMES.substring(cat*2, cat*2+2));
    //              return result.append(":]");
    //          }
    //      }

    int count = getRangeCount();

    // If the set contains at least 2 intervals and includes both
    // MIN_VALUE and MAX_VALUE, then the inverse representation will
    // be more economical.
    if (count > 1 && getRangeStart(0) == MIN_VALUE && getRangeEnd(count - 1) == MAX_VALUE) {

      // Emit the inverse
      result.append('^');

      for (int i = 1; i < count; ++i) {
        int start = getRangeEnd(i - 1) + 1;
        int end = getRangeStart(i) - 1;
        _appendToPat(result, start, escapeUnprintable);
        if (start != end) {
          if ((start + 1) != end) {
            result.append('-');
          }
          _appendToPat(result, end, escapeUnprintable);
        }
      }
    }

    // Default; emit the ranges as pairs
    else {
      for (int i = 0; i < count; ++i) {
        int start = getRangeStart(i);
        int end = getRangeEnd(i);
        _appendToPat(result, start, escapeUnprintable);
        if (start != end) {
          if ((start + 1) != end) {
            result.append('-');
          }
          _appendToPat(result, end, escapeUnprintable);
        }
      }
    }

    if (includeStrings && strings.size() > 0) {
      for (String s : strings) {
        result.append('{');
        _appendToPat(result, s, escapeUnprintable);
        result.append('}');
      }
    }
    return result.append(']');
  }

  /**
   * Returns the number of elements in this set (its cardinality) Note than the elements of a set may include both individual codepoints
   * and strings.
   *
   * @return the number of elements in this set (its cardinality).
   * @stable ICU 2.0
   */
  public int size() {
    int n = 0;
    int count = getRangeCount();
    for (int i = 0; i < count; ++i) {
      n += getRangeEnd(i) - getRangeStart(i) + 1;
    }
    return n + strings.size();
  }

  /**
   * Returns <tt>true</tt> if this set contains no elements.
   *
   * @return <tt>true</tt> if this set contains no elements.
   * @stable ICU 2.0
   */
  public boolean isEmpty() {
    return len == 1 && strings.size() == 0;
  }

  /**
   * Implementation of UnicodeMatcher API. Returns <tt>true</tt> if this set contains any character whose low byte is the given value.
   * This is used by <tt>RuleBasedTransliterator</tt> for indexing.
   *
   * @stable ICU 2.0
   */
  public boolean matchesIndexValue(final int v) {
    /* The index value v, in the range [0,255], is contained in this set if
     * it is contained in any pair of this set.  Pairs either have the high
     * bytes equal, or unequal.  If the high bytes are equal, then we have
     * aaxx..aayy, where aa is the high byte.  Then v is contained if xx <=
     * v <= yy.  If the high bytes are unequal we have aaxx..bbyy, bb>aa.
     * Then v is contained if xx <= v || v <= yy.  (This is identical to the
     * time zone month containment logic.)
     */
    for (int i = 0; i < getRangeCount(); ++i) {
      int low = getRangeStart(i);
      int high = getRangeEnd(i);
      if ((low & ~0xFF) == (high & ~0xFF)) {
        if ((low & 0xFF) <= v && v <= (high & 0xFF)) {
          return true;
        }
      } else if ((low & 0xFF) <= v || v <= (high & 0xFF)) {
        return true;
      }
    }
    if (strings.size() != 0) {
      for (String s : strings) {
        //if (s.length() == 0) {
        //    // Empty strings match everything
        //    return true;
        //}
        // assert(s.length() != 0); // We enforce this elsewhere
        int c = UTF16.charAt(s, 0);
        if ((c & 0xFF) == v) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Implementation of UnicodeMatcher.matches(). Always matches the longest possible multichar string.
   *
   * @stable ICU 2.0
   */
  @Override
  public int matches(final Replaceable text, final int[] offset, final int limit, final boolean incremental) {

    if (offset[0] == limit) {
      // Strings, if any, have length != 0, so we don't worry
      // about them here.  If we ever allow zero-length strings
      // we much check for them here.
      if (contains(UnicodeMatcher.ETHER)) {
        return incremental ? U_PARTIAL_MATCH : U_MATCH;
      } else {
        return U_MISMATCH;
      }
    } else {
      if (strings.size() != 0) { // try strings first

        // might separate forward and backward loops later
        // for now they are combined

        // TODO Improve efficiency of this, at least in the forward
        // direction, if not in both.  In the forward direction we
        // can assume the strings are sorted.

        boolean forward = offset[0] < limit;

        // firstChar is the leftmost char to match in the
        // forward direction or the rightmost char to match in
        // the reverse direction.
        char firstChar = text.charAt(offset[0]);

        // If there are multiple strings that can match we
        // return the longest match.
        int highWaterLength = 0;

        for (String trial : strings) {
          //if (trial.length() == 0) {
          //    return U_MATCH; // null-string always matches
          //}
          // assert(trial.length() != 0); // We ensure this elsewhere

          char c = trial.charAt(forward ? 0 : trial.length() - 1);

          // Strings are sorted, so we can optimize in the
          // forward direction.
          if (forward && c > firstChar)
            break;
          if (c != firstChar)
            continue;

          int length = matchRest(text, offset[0], limit, trial);

          if (incremental) {
            int maxLen = forward ? limit - offset[0] : offset[0] - limit;
            if (length == maxLen) {
              // We have successfully matched but only up to limit.
              return U_PARTIAL_MATCH;
            }
          }

          if (length == trial.length()) {
            // We have successfully matched the whole string.
            if (length > highWaterLength) {
              highWaterLength = length;
            }
            // In the forward direction we know strings
            // are sorted so we can bail early.
            if (forward && length < highWaterLength) {
              break;
            }
            continue;
          }
        }

        // We've checked all strings without a partial match.
        // If we have full matches, return the longest one.
        if (highWaterLength != 0) {
          offset[0] += forward ? highWaterLength : -highWaterLength;
          return U_MATCH;
        }
      }
      return super.matches(text, offset, limit, incremental);
    }
  }

  /**
   * Returns the longest match for s in text at the given position. If limit > start then match forward from start+1 to limit matching all
   * characters except s.charAt(0). If limit < start, go backward starting from start-1 matching all characters except
   * s.charAt(s.length()-1). This method assumes that the first character, text.charAt(start), matches s, so it does not check it.
   *
   * @param text
   *            the text to match
   * @param start
   *            the first character to match. In the forward direction, text.charAt(start) is matched against s.charAt(0). In the reverse
   *            direction, it is matched against s.charAt(s.length()-1).
   * @param limit
   *            the limit offset for matching, either last+1 in the forward direction, or last-1 in the reverse direction, where last is
   *            the index of the last character to match.
   * @return If part of s matches up to the limit, return |limit - start|. If all of s matches before reaching the limit, return
   *         s.length(). If there is a mismatch between s and text, return 0
   */
  private static int matchRest(final Replaceable text, final int start, final int limit, final String s) {
    int maxLen;
    int slen = s.length();
    if (start < limit) {
      maxLen = limit - start;
      if (maxLen > slen)
        maxLen = slen;
      for (int i = 1; i < maxLen; ++i) {
        if (text.charAt(start + i) != s.charAt(i))
          return 0;
      }
    } else {
      maxLen = start - limit;
      if (maxLen > slen)
        maxLen = slen;
      --slen; // <=> slen = s.length() - 1;
      for (int i = 1; i < maxLen; ++i) {
        if (text.charAt(start - i) != s.charAt(slen - i))
          return 0;
      }
    }
    return maxLen;
  }

  /**
   * Tests whether the text matches at the offset. If so, returns the end of the longest substring that it matches. If not, returns -1.
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public int matchesAt(final CharSequence text, final int offset) {
    int lastLen = -1;
    strings: if (strings.size() != 0) {
      char firstChar = text.charAt(offset);
      String trial = null;
      // find the first string starting with firstChar
      Iterator<String> it = strings.iterator();
      while (it.hasNext()) {
        trial = it.next();
        char firstStringChar = trial.charAt(0);
        if (firstStringChar < firstChar)
          continue;
        if (firstStringChar > firstChar)
          break strings;
      }

      // now keep checking string until we get the longest one
      for (;;) {
        int tempLen = matchesAt(text, offset, trial);
        if (lastLen > tempLen)
          break strings;
        lastLen = tempLen;
        if (!it.hasNext())
          break;
        trial = it.next();
      }
    }

    if (lastLen < 2) {
      int cp = UTF16.charAt(text, offset);
      if (contains(cp))
        lastLen = UTF16.getCharCount(cp);
    }

    return offset + lastLen;
  }

  /**
   * Does one string contain another, starting at a specific offset?
   *
   * @param text
   *            text to match
   * @param offsetInText
   *            offset within that text
   * @param substring
   *            substring to match at offset in text
   * @return -1 if match fails, otherwise other.length()
   */
  // Note: This method was moved from CollectionUtilities
  private static int matchesAt(final CharSequence text, final int offsetInText, final CharSequence substring) {
    int len = substring.length();
    int textLength = text.length();
    if (textLength + offsetInText > len) {
      return -1;
    }
    int i = 0;
    for (int j = offsetInText; i < len; ++i, ++j) {
      char pc = substring.charAt(i);
      char tc = text.charAt(j);
      if (pc != tc)
        return -1;
    }
    return i;
  }

  /**
   * Implementation of UnicodeMatcher API. Union the set of all characters that may be matched by this object into the given set.
   *
   * @param toUnionTo
   *            the set into which to union the source characters
   * @stable ICU 2.2
   */
  public void addMatchSetTo(final UnicodeSet toUnionTo) {
    toUnionTo.addAll(this);
  }

  /**
   * Returns the index of the given character within this set, where the set is ordered by ascending code point. If the character is not
   * in this set, return -1. The inverse of this method is <code>charAt()</code>.
   *
   * @return an index from 0..size()-1, or -1
   * @stable ICU 2.0
   */
  public int indexOf(final int c) {
    if (c < MIN_VALUE || c > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));
    }
    int i = 0;
    int n = 0;
    for (;;) {
      int start = list[i++];
      if (c < start) {
        return -1;
      }
      int limit = list[i++];
      if (c < limit) {
        return n + c - start;
      }
      n += limit - start;
    }
  }

  /**
   * Returns the character at the given index within this set, where the set is ordered by ascending code point. If the index is out of
   * range, return -1. The inverse of this method is <code>indexOf()</code>.
   *
   * @param index
   *            an index from 0..size()-1
   * @return the character at the given index, or -1.
   * @stable ICU 2.0
   */
  public int charAt(int index) {
    if (index >= 0) {
      // len2 is the largest even integer <= len, that is, it is len
      // for even values and len-1 for odd values.  With odd values
      // the last entry is UNICODESET_HIGH.
      int len2 = len & ~1;
      for (int i = 0; i < len2;) {
        int start = list[i++];
        int count = list[i++] - start;
        if (index < count) {
          return start + index;
        }
        index -= count;
      }
    }
    return -1;
  }

  /**
   * Adds the specified range to this set if it is not already present. If this set already contains the specified range, the call leaves
   * this set unchanged. If <code>end > start</code> then an empty range is added, leaving the set unchanged.
   *
   * @param start
   *            first character, inclusive, of range to be added to this set.
   * @param end
   *            last character, inclusive, of range to be added to this set.
   * @stable ICU 2.0
   */
  public UnicodeSet add(final int start, final int end) {
    checkFrozen();
    return add_unchecked(start, end);
  }

  /**
   * Adds all characters in range (uses preferred naming convention).
   *
   * @param start
   *            The index of where to start on adding all characters.
   * @param end
   *            The index of where to end on adding all characters.
   * @return a reference to this object
   * @stable ICU 4.4
   */
  public UnicodeSet addAll(final int start, final int end) {
    checkFrozen();
    return add_unchecked(start, end);
  }

  // for internal use, after checkFrozen has been called
  private UnicodeSet add_unchecked(final int start, final int end) {
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    if (start < end) {
      add(range(start, end), 2, 0);
    } else if (start == end) {
      add(start);
    }
    return this;
  }

  //    /**
  //     * Format out the inversion list as a string, for debugging.  Uncomment when
  //     * needed.
  //     */
  //    public final String dump() {
  //        StringBuffer buf = new StringBuffer("[");
  //        for (int i=0; i<len; ++i) {
  //            if (i != 0) buf.append(", ");
  //            int c = list[i];
  //            //if (c <= 0x7F && c != '\n' && c != '\r' && c != '\t' && c != ' ') {
  //            //    buf.append((char) c);
  //            //} else {
  //                buf.append("U+").append(Utility.hex(c, (c<0x10000)?4:6));
  //            //}
  //        }
  //        buf.append("]");
  //        return buf.toString();
  //    }

  /**
   * Adds the specified character to this set if it is not already present. If this set already contains the specified character, the call
   * leaves this set unchanged.
   *
   * @stable ICU 2.0
   */
  public final UnicodeSet add(final int c) {
    checkFrozen();
    return add_unchecked(c);
  }

  // for internal use only, after checkFrozen has been called
  private final UnicodeSet add_unchecked(final int c) {
    if (c < MIN_VALUE || c > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));
    }

    // find smallest i such that c < list[i]
    // if odd, then it is IN the set
    // if even, then it is OUT of the set
    int i = findCodePoint(c);

    // already in set?
    if ((i & 1) != 0)
      return this;

    // HIGH is 0x110000
    // assert(list[len-1] == HIGH);

    // empty = [HIGH]
    // [start_0, limit_0, start_1, limit_1, HIGH]

    // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH]
    //                             ^
    //                             list[i]

    // i == 0 means c is before the first range
    // TODO: Is the "list[i]-1" a typo? Even if you pass MAX_VALUE into
    //      add_unchecked, the maximum value that "c" will be compared to
    //      is "MAX_VALUE-1" meaning that "if (c == MAX_VALUE)" will
    //      never be reached according to this logic.
    if (c == list[i] - 1) {
      // c is before start of next range
      list[i] = c;
      // if we touched the HIGH mark, then add a new one
      if (c == MAX_VALUE) {
        ensureCapacity(len + 1);
        list[len++] = HIGH;
      }
      if (i > 0 && c == list[i - 1]) {
        // collapse adjacent ranges

        // [..., start_k-1, c, c, limit_k, ..., HIGH]
        //                     ^
        //                     list[i]
        System.arraycopy(list, i + 1, list, i - 1, len - i - 1);
        len -= 2;
      }
    }

    else if (i > 0 && c == list[i - 1]) {
      // c is after end of prior range
      list[i - 1]++;
      // no need to chcek for collapse here
    }

    else {
      // At this point we know the new char is not adjacent to
      // any existing ranges, and it is not 10FFFF.

      // [..., start_k-1, limit_k-1, start_k, limit_k, ..., HIGH]
      //                             ^
      //                             list[i]

      // [..., start_k-1, limit_k-1, c, c+1, start_k, limit_k, ..., HIGH]
      //                             ^
      //                             list[i]

      // Don't use ensureCapacity() to save on copying.
      // NOTE: This has no measurable impact on performance,
      // but it might help in some usage patterns.
      if (len + 2 > list.length) {
        int[] temp = new int[len + 2 + GROW_EXTRA];
        if (i != 0)
          System.arraycopy(list, 0, temp, 0, i);
        System.arraycopy(list, i, temp, i + 2, len - i);
        list = temp;
      } else {
        System.arraycopy(list, i, list, i + 2, len - i);
      }

      list[i] = c;
      list[i + 1] = c + 1;
      len += 2;
    }

    pat = null;
    return this;
  }

  /**
   * Adds the specified multicharacter to this set if it is not already present. If this set already contains the multicharacter, the call
   * leaves this set unchanged. Thus "ch" => {"ch"} <br>
   * <b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>
   *
   * @param s
   *            the source string
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet add(final CharSequence s) {
    checkFrozen();
    int cp = getSingleCP(s);
    if (cp < 0) {
      strings.add(s.toString());
      pat = null;
    } else {
      add_unchecked(cp, cp);
    }
    return this;
  }

  /**
   * @return a code point IF the string consists of a single one. otherwise returns -1.
   * @param string
   *            to test
   */
  private static int getSingleCP(final CharSequence s) {
    if (s.length() < 1) {
      throw new IllegalArgumentException("Can't use zero-length strings in UnicodeSet");
    }
    if (s.length() > 2)
      return -1;
    if (s.length() == 1)
      return s.charAt(0);

    // at this point, len = 2
    int cp = UTF16.charAt(s, 0);
    if (cp > 0xFFFF) { // is surrogate pair
      return cp;
    }
    return -1;
  }

  /**
   * Adds each of the characters in this string to the set. Thus "ch" => {"c", "h"} If this set already any particular character, it has
   * no effect on that character.
   *
   * @param s
   *            the source string
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet addAll(final CharSequence s) {
    checkFrozen();
    int cp;
    for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {
      cp = UTF16.charAt(s, i);
      add_unchecked(cp, cp);
    }
    return this;
  }

  /**
   * Retains EACH of the characters in this string. Note: "ch" == {"c", "h"} If this set already any particular character, it has no
   * effect on that character.
   *
   * @param s
   *            the source string
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet retainAll(final String s) {
    return retainAll(fromAll(s));
  }

  /**
   * Complement EACH of the characters in this string. Note: "ch" == {"c", "h"} If this set already any particular character, it has no
   * effect on that character.
   *
   * @param s
   *            the source string
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet complementAll(final String s) {
    return complementAll(fromAll(s));
  }

  /**
   * Remove EACH of the characters in this string. Note: "ch" == {"c", "h"} If this set already any particular character, it has no effect
   * on that character.
   *
   * @param s
   *            the source string
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet removeAll(final String s) {
    return removeAll(fromAll(s));
  }

  /**
   * Remove all strings from this UnicodeSet
   *
   * @return this object, for chaining
   * @stable ICU 4.2
   */
  public final UnicodeSet removeAllStrings() {
    checkFrozen();
    if (strings.size() != 0) {
      strings.clear();
      pat = null;
    }
    return this;
  }

  /**
   * Makes a set from a multicharacter string. Thus "ch" => {"ch"} <br>
   * <b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>
   *
   * @param s
   *            the source string
   * @return a newly created set containing the given string
   * @stable ICU 2.0
   */
  public static UnicodeSet from(final String s) {
    return new UnicodeSet().add(s);
  }

  /**
   * Makes a set from each of the characters in the string. Thus "ch" => {"c", "h"}
   *
   * @param s
   *            the source string
   * @return a newly created set containing the given characters
   * @stable ICU 2.0
   */
  public static UnicodeSet fromAll(final String s) {
    return new UnicodeSet().addAll(s);
  }

  /**
   * Retain only the elements in this set that are contained in the specified range. If <code>end > start</code> then an empty range is
   * retained, leaving the set empty.
   *
   * @param start
   *            first character, inclusive, of range to be retained to this set.
   * @param end
   *            last character, inclusive, of range to be retained to this set.
   * @stable ICU 2.0
   */
  public UnicodeSet retain(final int start, final int end) {
    checkFrozen();
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    if (start <= end) {
      retain(range(start, end), 2, 0);
    } else {
      clear();
    }
    return this;
  }

  /**
   * Retain the specified character from this set if it is present. Upon return this set will be empty if it did not contain c, or will
   * only contain c if it did contain c.
   *
   * @param c
   *            the character to be retained
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet retain(final int c) {
    return retain(c, c);
  }

  /**
   * Retain the specified string in this set if it is present. Upon return this set will be empty if it did not contain s, or will only
   * contain s if it did contain s.
   *
   * @param s
   *            the string to be retained
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet retain(final String s) {
    int cp = getSingleCP(s);
    if (cp < 0) {
      boolean isIn = strings.contains(s);
      if (isIn && size() == 1) {
        return this;
      }
      clear();
      strings.add(s);
      pat = null;
    } else {
      retain(cp, cp);
    }
    return this;
  }

  /**
   * Removes the specified range from this set if it is present. The set will not contain the specified range once the call returns. If
   * <code>end > start</code> then an empty range is removed, leaving the set unchanged.
   *
   * @param start
   *            first character, inclusive, of range to be removed from this set.
   * @param end
   *            last character, inclusive, of range to be removed from this set.
   * @stable ICU 2.0
   */
  public UnicodeSet remove(final int start, final int end) {
    checkFrozen();
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    if (start <= end) {
      retain(range(start, end), 2, 2);
    }
    return this;
  }

  /**
   * Removes the specified character from this set if it is present. The set will not contain the specified character once the call
   * returns.
   *
   * @param c
   *            the character to be removed
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet remove(final int c) {
    return remove(c, c);
  }

  /**
   * Removes the specified string from this set if it is present. The set will not contain the specified string once the call returns.
   *
   * @param s
   *            the string to be removed
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet remove(final String s) {
    int cp = getSingleCP(s);
    if (cp < 0) {
      strings.remove(s);
      pat = null;
    } else {
      remove(cp, cp);
    }
    return this;
  }

  /**
   * Complements the specified range in this set. Any character in the range will be removed if it is in this set, or will be added if it
   * is not in this set. If <code>end > start</code> then an empty range is complemented, leaving the set unchanged.
   *
   * @param start
   *            first character, inclusive, of range to be removed from this set.
   * @param end
   *            last character, inclusive, of range to be removed from this set.
   * @stable ICU 2.0
   */
  public UnicodeSet complement(final int start, final int end) {
    checkFrozen();
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    if (start <= end) {
      xor(range(start, end), 2, 0);
    }
    pat = null;
    return this;
  }

  /**
   * Complements the specified character in this set. The character will be removed if it is in this set, or will be added if it is not in
   * this set.
   *
   * @stable ICU 2.0
   */
  public final UnicodeSet complement(final int c) {
    return complement(c, c);
  }

  /**
   * This is equivalent to <code>complement(MIN_VALUE, MAX_VALUE)</code>.
   *
   * @stable ICU 2.0
   */
  public UnicodeSet complement() {
    checkFrozen();
    if (list[0] == LOW) {
      System.arraycopy(list, 1, list, 0, len - 1);
      --len;
    } else {
      ensureCapacity(len + 1);
      System.arraycopy(list, 0, list, 1, len);
      list[0] = LOW;
      ++len;
    }
    pat = null;
    return this;
  }

  /**
   * Complement the specified string in this set. The set will not contain the specified string once the call returns. <br>
   * <b>Warning: you cannot add an empty string ("") to a UnicodeSet.</b>
   *
   * @param s
   *            the string to complement
   * @return this object, for chaining
   * @stable ICU 2.0
   */
  public final UnicodeSet complement(final String s) {
    checkFrozen();
    int cp = getSingleCP(s);
    if (cp < 0) {
      if (strings.contains(s)) {
        strings.remove(s);
      } else {
        strings.add(s);
      }
      pat = null;
    } else {
      complement(cp, cp);
    }
    return this;
  }

  /**
   * Returns true if this set contains the given character.
   *
   * @param c
   *            character to be checked for containment
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  @Override
  public boolean contains(final int c) {
    if (c < MIN_VALUE || c > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(c, 6));
    }
    if (bmpSet != null) {
      return bmpSet.contains(c);
    }
    if (stringSpan != null) {
      return stringSpan.contains(c);
    }

    /*
    // Set i to the index of the start item greater than ch
    // We know we will terminate without length test!
    int i = -1;
    while (true) {
        if (c < list[++i]) break;
    }
     */

    int i = findCodePoint(c);

    return ((i & 1) != 0); // return true if odd
  }

  /**
   * Returns the smallest value i such that c < list[i]. Caller must ensure that c is a legal value or this method will enter an infinite
   * loop. This method performs a binary search.
   *
   * @param c
   *            a character in the range MIN_VALUE..MAX_VALUE inclusive
   * @return the smallest integer i in the range 0..len-1, inclusive, such that c < list[i]
   */
  private final int findCodePoint(final int c) {
    /* Examples:
                                       findCodePoint(c)
       set              list[]         c=0 1 3 4 7 8
       ===              ==============   ===========
       []               [110000]         0 0 0 0 0 0
       [\u0000-\u0003]  [0, 4, 110000]   1 1 1 2 2 2
       [\u0004-\u0007]  [4, 8, 110000]   0 0 0 1 1 2
       [:all:]          [0, 110000]      1 1 1 1 1 1
     */

    // Return the smallest i such that c < list[i].  Assume
    // list[len - 1] == HIGH and that c is legal (0..HIGH-1).
    if (c < list[0])
      return 0;
    // High runner test.  c is often after the last range, so an
    // initial check for this condition pays off.
    if (len >= 2 && c >= list[len - 2])
      return len - 1;
    int lo = 0;
    int hi = len - 1;
    // invariant: c >= list[lo]
    // invariant: c < list[hi]
    for (;;) {
      int i = (lo + hi) >>> 1;
      if (i == lo)
        return hi;
      if (c < list[i]) {
        hi = i;
      } else {
        lo = i;
      }
    }
  }

  //    //----------------------------------------------------------------
  //    // Unrolled binary search
  //    //----------------------------------------------------------------
  //
  //    private int validLen = -1; // validated value of len
  //    private int topOfLow;
  //    private int topOfHigh;
  //    private int power;
  //    private int deltaStart;
  //
  //    private void validate() {
  //        if (len <= 1) {
  //            throw new IllegalArgumentException("list.len==" + len + "; must be >1");
  //        }
  //
  //        // find greatest power of 2 less than or equal to len
  //        for (power = exp2.length-1; power > 0 && exp2[power] > len; power--) {}
  //
  //        // assert(exp2[power] <= len);
  //
  //        // determine the starting points
  //        topOfLow = exp2[power] - 1;
  //        topOfHigh = len - 1;
  //        deltaStart = exp2[power-1];
  //        validLen = len;
  //    }
  //
  //    private static final int exp2[] = {
  //        0x1, 0x2, 0x4, 0x8,
  //        0x10, 0x20, 0x40, 0x80,
  //        0x100, 0x200, 0x400, 0x800,
  //        0x1000, 0x2000, 0x4000, 0x8000,
  //        0x10000, 0x20000, 0x40000, 0x80000,
  //        0x100000, 0x200000, 0x400000, 0x800000,
  //        0x1000000, 0x2000000, 0x4000000, 0x8000000,
  //        0x10000000, 0x20000000 // , 0x40000000 // no unsigned int in Java
  //    };
  //
  //    /**
  //     * Unrolled lowest index GT.
  //     */
  //    private final int leastIndexGT(int searchValue) {
  //
  //        if (len != validLen) {
  //            if (len == 1) return 0;
  //            validate();
  //        }
  //        int temp;
  //
  //        // set up initial range to search. Each subrange is a power of two in length
  //        int high = searchValue < list[topOfLow] ? topOfLow : topOfHigh;
  //
  //        // Completely unrolled binary search, folhighing "Programming Pearls"
  //        // Each case deliberately falls through to the next
  //        // Logically, list[-1] < all_search_values && list[count] > all_search_values
  //        // although the values -1 and count are never actually touched.
  //
  //        // The bounds at each point are low & high,
  //        // where low == high - delta*2
  //        // so high - delta is the midpoint
  //
  //        // The invariant AFTER each line is that list[low] < searchValue <= list[high]
  //
  //        switch (power) {
  //        //case 31: if (searchValue < list[temp = high-0x40000000]) high = temp; // no unsigned int in Java
  //        case 30: if (searchValue < list[temp = high-0x20000000]) high = temp;
  //        case 29: if (searchValue < list[temp = high-0x10000000]) high = temp;
  //
  //        case 28: if (searchValue < list[temp = high- 0x8000000]) high = temp;
  //        case 27: if (searchValue < list[temp = high- 0x4000000]) high = temp;
  //        case 26: if (searchValue < list[temp = high- 0x2000000]) high = temp;
  //        case 25: if (searchValue < list[temp = high- 0x1000000]) high = temp;
  //
  //        case 24: if (searchValue < list[temp = high-  0x800000]) high = temp;
  //        case 23: if (searchValue < list[temp = high-  0x400000]) high = temp;
  //        case 22: if (searchValue < list[temp = high-  0x200000]) high = temp;
  //        case 21: if (searchValue < list[temp = high-  0x100000]) high = temp;
  //
  //        case 20: if (searchValue < list[temp = high-   0x80000]) high = temp;
  //        case 19: if (searchValue < list[temp = high-   0x40000]) high = temp;
  //        case 18: if (searchValue < list[temp = high-   0x20000]) high = temp;
  //        case 17: if (searchValue < list[temp = high-   0x10000]) high = temp;
  //
  //        case 16: if (searchValue < list[temp = high-    0x8000]) high = temp;
  //        case 15: if (searchValue < list[temp = high-    0x4000]) high = temp;
  //        case 14: if (searchValue < list[temp = high-    0x2000]) high = temp;
  //        case 13: if (searchValue < list[temp = high-    0x1000]) high = temp;
  //
  //        case 12: if (searchValue < list[temp = high-     0x800]) high = temp;
  //        case 11: if (searchValue < list[temp = high-     0x400]) high = temp;
  //        case 10: if (searchValue < list[temp = high-     0x200]) high = temp;
  //        case  9: if (searchValue < list[temp = high-     0x100]) high = temp;
  //
  //        case  8: if (searchValue < list[temp = high-      0x80]) high = temp;
  //        case  7: if (searchValue < list[temp = high-      0x40]) high = temp;
  //        case  6: if (searchValue < list[temp = high-      0x20]) high = temp;
  //        case  5: if (searchValue < list[temp = high-      0x10]) high = temp;
  //
  //        case  4: if (searchValue < list[temp = high-       0x8]) high = temp;
  //        case  3: if (searchValue < list[temp = high-       0x4]) high = temp;
  //        case  2: if (searchValue < list[temp = high-       0x2]) high = temp;
  //        case  1: if (searchValue < list[temp = high-       0x1]) high = temp;
  //        }
  //
  //        return high;
  //    }
  //
  //    // For debugging only
  //    public int len() {
  //        return len;
  //    }
  //
  //    //----------------------------------------------------------------
  //    //----------------------------------------------------------------

  /**
   * Returns true if this set contains every character of the given range.
   *
   * @param start
   *            first character, inclusive, of the range
   * @param end
   *            last character, inclusive, of the range
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean contains(final int start, final int end) {
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    //int i = -1;
    //while (true) {
    //    if (start < list[++i]) break;
    //}
    int i = findCodePoint(start);
    return ((i & 1) != 0 && end < list[i]);
  }

  /**
   * Returns <tt>true</tt> if this set contains the given multicharacter string.
   *
   * @param s
   *            string to be checked for containment
   * @return <tt>true</tt> if this set contains the specified string
   * @stable ICU 2.0
   */
  public final boolean contains(final String s) {

    int cp = getSingleCP(s);
    if (cp < 0) {
      return strings.contains(s);
    } else {
      return contains(cp);
    }
  }

  /**
   * Returns true if this set contains all the characters and strings of the given set.
   *
   * @param b
   *            set to be checked for containment
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean containsAll(final UnicodeSet b) {
    // The specified set is a subset if all of its pairs are contained in
    // this set. This implementation accesses the lists directly for speed.
    // TODO: this could be faster if size() were cached. But that would affect building speed
    // so it needs investigation.
    int[] listB = b.list;
    boolean needA = true;
    boolean needB = true;
    int aPtr = 0;
    int bPtr = 0;
    int aLen = len - 1;
    int bLen = b.len - 1;
    int startA = 0, startB = 0, limitA = 0, limitB = 0;
    while (true) {
      // double iterations are such a pain...
      if (needA) {
        if (aPtr >= aLen) {
          // ran out of A. If B is also exhausted, then break;
          if (needB && bPtr >= bLen) {
            break;
          }
          return false;
        }
        startA = list[aPtr++];
        limitA = list[aPtr++];
      }
      if (needB) {
        if (bPtr >= bLen) {
          // ran out of B. Since we got this far, we have an A and we are ok so far
          break;
        }
        startB = listB[bPtr++];
        limitB = listB[bPtr++];
      }
      // if B doesn't overlap and is greater than A, get new A
      if (startB >= limitA) {
        needA = true;
        needB = false;
        continue;
      }
      // if B is wholy contained in A, then get a new B
      if (startB >= startA && limitB <= limitA) {
        needA = false;
        needB = true;
        continue;
      }
      // all other combinations mean we fail
      return false;
    }

    if (!strings.containsAll(b.strings))
      return false;
    return true;
  }

  //    /**
  //     * Returns true if this set contains all the characters and strings
  //     * of the given set.
  //     * @param c set to be checked for containment
  //     * @return true if the test condition is met
  //     * @stable ICU 2.0
  //     */
  //    public boolean containsAllOld(UnicodeSet c) {
  //        // The specified set is a subset if all of its pairs are contained in
  //        // this set.  It's possible to code this more efficiently in terms of
  //        // direct manipulation of the inversion lists if the need arises.
  //        int n = c.getRangeCount();
  //        for (int i=0; i<n; ++i) {
  //            if (!contains(c.getRangeStart(i), c.getRangeEnd(i))) {
  //                return false;
  //            }
  //        }
  //        if (!strings.containsAll(c.strings)) return false;
  //        return true;
  //    }

  /**
   * Returns true if there is a partition of the string such that this set contains each of the partitioned strings. For example, for the
   * Unicode set [a{bc}{cd}]<br>
   * containsAll is true for each of: "a", "bc", ""cdbca"<br>
   * containsAll is false for each of: "acb", "bcda", "bcx"<br>
   *
   * @param s
   *            string containing characters to be checked for containment
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean containsAll(final String s) {
    int cp;
    for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {
      cp = UTF16.charAt(s, i);
      if (!contains(cp)) {
        if (strings.size() == 0) {
          return false;
        }
        return containsAll(s, 0);
      }
    }
    return true;
  }

  /**
   * Recursive routine called if we fail to find a match in containsAll, and there are strings
   *
   * @param s
   *            source string
   * @param i
   *            point to match to the end on
   * @return true if ok
   */
  private boolean containsAll(final String s, final int i) {
    if (i >= s.length()) {
      return true;
    }
    int cp = UTF16.charAt(s, i);
    if (contains(cp) && containsAll(s, i + UTF16.getCharCount(cp))) {
      return true;
    }
    for (String setStr : strings) {
      if (s.startsWith(setStr, i) && containsAll(s, i + setStr.length())) {
        return true;
      }
    }
    return false;

  }

  /**
   * Get the Regex equivalent for this UnicodeSet
   *
   * @return regex pattern equivalent to this UnicodeSet
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public String getRegexEquivalent() {
    if (strings.size() == 0) {
      return toString();
    }
    StringBuffer result = new StringBuffer("(?:");
    _generatePattern(result, true, false);
    for (String s : strings) {
      result.append('|');
      _appendToPat(result, s, true);
    }
    return result.append(")").toString();
  }

  /**
   * Returns true if this set contains none of the characters of the given range.
   *
   * @param start
   *            first character, inclusive, of the range
   * @param end
   *            last character, inclusive, of the range
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean containsNone(final int start, final int end) {
    if (start < MIN_VALUE || start > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(start, 6));
    }
    if (end < MIN_VALUE || end > MAX_VALUE) {
      throw new IllegalArgumentException("Invalid code point U+" + Utility.hex(end, 6));
    }
    int i = -1;
    while (true) {
      if (start < list[++i])
        break;
    }
    return ((i & 1) == 0 && end < list[i]);
  }

  /**
   * Returns true if none of the characters or strings in this UnicodeSet appears in the string. For example, for the Unicode set
   * [a{bc}{cd}]<br>
   * containsNone is true for: "xy", "cb"<br>
   * containsNone is false for: "a", "bc", "bcd"<br>
   *
   * @param b
   *            set to be checked for containment
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean containsNone(final UnicodeSet b) {
    // The specified set is a subset if some of its pairs overlap with some of this set's pairs.
    // This implementation accesses the lists directly for speed.
    int[] listB = b.list;
    boolean needA = true;
    boolean needB = true;
    int aPtr = 0;
    int bPtr = 0;
    int aLen = len - 1;
    int bLen = b.len - 1;
    int startA = 0, startB = 0, limitA = 0, limitB = 0;
    while (true) {
      // double iterations are such a pain...
      if (needA) {
        if (aPtr >= aLen) {
          // ran out of A: break so we test strings
          break;
        }
        startA = list[aPtr++];
        limitA = list[aPtr++];
      }
      if (needB) {
        if (bPtr >= bLen) {
          // ran out of B: break so we test strings
          break;
        }
        startB = listB[bPtr++];
        limitB = listB[bPtr++];
      }
      // if B is higher than any part of A, get new A
      if (startB >= limitA) {
        needA = true;
        needB = false;
        continue;
      }
      // if A is higher than any part of B, get new B
      if (startA >= limitB) {
        needA = false;
        needB = true;
        continue;
      }
      // all other combinations mean we fail
      return false;
    }

    if (!SortedSetRelation.hasRelation(strings, SortedSetRelation.DISJOINT, b.strings))
      return false;
    return true;
  }

  //    /**
  //     * Returns true if none of the characters or strings in this UnicodeSet appears in the string.
  //     * For example, for the Unicode set [a{bc}{cd}]<br>
  //     * containsNone is true for: "xy", "cb"<br>
  //     * containsNone is false for: "a", "bc", "bcd"<br>
  //     * @param c set to be checked for containment
  //     * @return true if the test condition is met
  //     * @stable ICU 2.0
  //     */
  //    public boolean containsNoneOld(UnicodeSet c) {
  //        // The specified set is a subset if all of its pairs are contained in
  //        // this set.  It's possible to code this more efficiently in terms of
  //        // direct manipulation of the inversion lists if the need arises.
  //        int n = c.getRangeCount();
  //        for (int i=0; i<n; ++i) {
  //            if (!containsNone(c.getRangeStart(i), c.getRangeEnd(i))) {
  //                return false;
  //            }
  //        }
  //        if (!SortedSetRelation.hasRelation(strings, SortedSetRelation.DISJOINT, c.strings)) return false;
  //        return true;
  //    }

  /**
   * Returns true if this set contains none of the characters of the given string.
   *
   * @param s
   *            string containing characters to be checked for containment
   * @return true if the test condition is met
   * @stable ICU 2.0
   */
  public boolean containsNone(final String s) {
    return span(s, SpanCondition.NOT_CONTAINED) == s.length();
  }

  /**
   * Returns true if this set contains one or more of the characters in the given range.
   *
   * @param start
   *            first character, inclusive, of the range
   * @param end
   *            last character, inclusive, of the range
   * @return true if the condition is met
   * @stable ICU 2.0
   */
  public final boolean containsSome(final int start, final int end) {
    return !containsNone(start, end);
  }

  /**
   * Returns true if this set contains one or more of the characters and strings of the given set.
   *
   * @param s
   *            set to be checked for containment
   * @return true if the condition is met
   * @stable ICU 2.0
   */
  public final boolean containsSome(final UnicodeSet s) {
    return !containsNone(s);
  }

  /**
   * Returns true if this set contains one or more of the characters of the given string.
   *
   * @param s
   *            string containing characters to be checked for containment
   * @return true if the condition is met
   * @stable ICU 2.0
   */
  public final boolean containsSome(final String s) {
    return !containsNone(s);
  }

  /**
   * Adds all of the elements in the specified set to this set if they're not already present. This operation effectively modifies this
   * set so that its value is the <i>union</i> of the two sets. The behavior of this operation is unspecified if the specified collection
   * is modified while the operation is in progress.
   *
   * @param c
   *            set whose elements are to be added to this set.
   * @stable ICU 2.0
   */
  public UnicodeSet addAll(final UnicodeSet c) {
    checkFrozen();
    add(c.list, c.len, 0);
    strings.addAll(c.strings);
    return this;
  }

  /**
   * Retains only the elements in this set that are contained in the specified set. In other words, removes from this set all of its
   * elements that are not contained in the specified set. This operation effectively modifies this set so that its value is the
   * <i>intersection</i> of the two sets.
   *
   * @param c
   *            set that defines which elements this set will retain.
   * @stable ICU 2.0
   */
  public UnicodeSet retainAll(final UnicodeSet c) {
    checkFrozen();
    retain(c.list, c.len, 0);
    strings.retainAll(c.strings);
    return this;
  }

  /**
   * Removes from this set all of its elements that are contained in the specified set. This operation effectively modifies this set so
   * that its value is the <i>asymmetric set difference</i> of the two sets.
   *
   * @param c
   *            set that defines which elements will be removed from this set.
   * @stable ICU 2.0
   */
  public UnicodeSet removeAll(final UnicodeSet c) {
    checkFrozen();
    retain(c.list, c.len, 2);
    strings.removeAll(c.strings);
    return this;
  }

  /**
   * Complements in this set all elements contained in the specified set. Any character in the other set will be removed if it is in this
   * set, or will be added if it is not in this set.
   *
   * @param c
   *            set that defines which elements will be complemented from this set.
   * @stable ICU 2.0
   */
  public UnicodeSet complementAll(final UnicodeSet c) {
    checkFrozen();
    xor(c.list, c.len, 0);
    SortedSetRelation.doOperation(strings, SortedSetRelation.COMPLEMENTALL, c.strings);
    return this;
  }

  /**
   * Removes all of the elements from this set. This set will be empty after this call returns.
   *
   * @stable ICU 2.0
   */
  public UnicodeSet clear() {
    checkFrozen();
    list[0] = HIGH;
    len = 1;
    pat = null;
    strings.clear();
    return this;
  }

  /**
   * Iteration method that returns the number of ranges contained in this set.
   *
   * @see #getRangeStart
   * @see #getRangeEnd
   * @stable ICU 2.0
   */
  public int getRangeCount() {
    return len / 2;
  }

  /**
   * Iteration method that returns the first character in the specified range of this set.
   *
   * @exception ArrayIndexOutOfBoundsException
   *                if index is outside the range <code>0..getRangeCount()-1</code>
   * @see #getRangeCount
   * @see #getRangeEnd
   * @stable ICU 2.0
   */
  public int getRangeStart(final int index) {
    return list[index * 2];
  }

  /**
   * Iteration method that returns the last character in the specified range of this set.
   *
   * @exception ArrayIndexOutOfBoundsException
   *                if index is outside the range <code>0..getRangeCount()-1</code>
   * @see #getRangeStart
   * @see #getRangeEnd
   * @stable ICU 2.0
   */
  public int getRangeEnd(final int index) {
    return (list[index * 2 + 1] - 1);
  }

  /**
   * Reallocate this objects internal structures to take up the least possible space, without changing this object's value.
   *
   * @stable ICU 2.0
   */
  public UnicodeSet compact() {
    checkFrozen();
    if (len != list.length) {
      int[] temp = new int[len];
      System.arraycopy(list, 0, temp, 0, len);
      list = temp;
    }
    rangeList = null;
    buffer = null;
    return this;
  }

  /**
   * Compares the specified object with this set for equality. Returns <tt>true</tt> if the specified object is also a set, the two sets
   * have the same size, and every member of the specified set is contained in this set (or equivalently, every member of this set is
   * contained in the specified set).
   *
   * @param o
   *            Object to be compared for equality with this set.
   * @return <tt>true</tt> if the specified Object is equal to this set.
   * @stable ICU 2.0
   */
  @Override
  public boolean equals(final Object o) {
    if (o == null) {
      return false;
    }
    if (this == o) {
      return true;
    }
    try {
      UnicodeSet that = (UnicodeSet) o;
      if (len != that.len)
        return false;
      for (int i = 0; i < len; ++i) {
        if (list[i] != that.list[i])
          return false;
      }
      if (!strings.equals(that.strings))
        return false;
    } catch (Exception e) {
      return false;
    }
    return true;
  }

  /**
   * Returns the hash code value for this set.
   *
   * @return the hash code value for this set.
   * @see java.lang.Object#hashCode()
   * @stable ICU 2.0
   */
  @Override
  public int hashCode() {
    int result = len;
    for (int i = 0; i < len; ++i) {
      result *= 1000003;
      result += list[i];
    }
    return result;
  }

  /**
   * Return a programmer-readable string representation of this object.
   *
   * @stable ICU 2.0
   */
  @Override
  public String toString() {
    return toPattern(true);
  }

  //----------------------------------------------------------------
  // Implementation: Pattern parsing
  //----------------------------------------------------------------

  /**
   * Parses the given pattern, starting at the given position. The character at pattern.charAt(pos.getIndex()) must be '[', or the parse
   * fails. Parsing continues until the corresponding closing ']'. If a syntax error is encountered between the opening and closing brace,
   * the parse fails. Upon return from a successful parse, the ParsePosition is updated to point to the character following the closing
   * ']', and an inversion list for the parsed pattern is returned. This method calls itself recursively to parse embedded subpatterns.
   *
   * @param pattern
   *            the string containing the pattern to be parsed. The portion of the string from pos.getIndex(), which must be a '[', to the
   *            corresponding closing ']', is parsed.
   * @param pos
   *            upon entry, the position at which to being parsing. The character at pattern.charAt(pos.getIndex()) must be a '['. Upon
   *            return from a successful parse, pos.getIndex() is either the character after the closing ']' of the parsed pattern, or
   *            pattern.length() if the closing ']' is the last character of the pattern string.
   * @return an inversion list for the parsed substring of <code>pattern</code>
   * @exception java.lang.IllegalArgumentException
   *                if the parse fails.
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public UnicodeSet applyPattern(final String pattern, ParsePosition pos, final SymbolTable symbols, final int options) {

    // Need to build the pattern in a temporary string because
    // _applyPattern calls add() etc., which set pat to empty.
    boolean parsePositionWasNull = pos == null;
    if (parsePositionWasNull) {
      pos = new ParsePosition(0);
    }

    StringBuffer rebuiltPat = new StringBuffer();
    RuleCharacterIterator chars = new RuleCharacterIterator(pattern, symbols, pos);
    applyPattern(chars, symbols, rebuiltPat, options);
    if (chars.inVariable()) {
      syntaxError(chars, "Extra chars in variable value");
    }
    pat = rebuiltPat.toString();
    if (parsePositionWasNull) {
      int i = pos.getIndex();

      // Skip over trailing whitespace
      if ((options & IGNORE_SPACE) != 0) {
        i = PatternProps.skipWhiteSpace(pattern, i);
      }

      if (i != pattern.length()) {
        throw new IllegalArgumentException("Parse of \"" + pattern + "\" failed at " + i);
      }
    }
    return this;
  }

  /**
   * Parse the pattern from the given RuleCharacterIterator. The iterator is advanced over the parsed pattern.
   *
   * @param chars
   *            iterator over the pattern characters. Upon return it will be advanced to the first character after the parsed pattern, or
   *            the end of the iteration if all characters are parsed.
   * @param symbols
   *            symbol table to use to parse and dereference variables, or null if none.
   * @param rebuiltPat
   *            the pattern that was parsed, rebuilt or copied from the input pattern, as appropriate.
   * @param options
   *            a bit mask of zero or more of the following: IGNORE_SPACE, CASE.
   */
  void applyPattern(final RuleCharacterIterator chars, final SymbolTable symbols, final StringBuffer rebuiltPat, final int options) {

    // Syntax characters: [ ] ^ - & { }

    // Recognized special forms for chars, sets: c-c s-s s&s

    int opts = RuleCharacterIterator.PARSE_VARIABLES | RuleCharacterIterator.PARSE_ESCAPES;
    if ((options & IGNORE_SPACE) != 0) {
      opts |= RuleCharacterIterator.SKIP_WHITESPACE;
    }

    StringBuffer patBuf = new StringBuffer(), buf = null;
    boolean usePat = false;
    UnicodeSet scratch = null;
    Object backup = null;

    // mode: 0=before [, 1=between [...], 2=after ]
    // lastItem: 0=none, 1=char, 2=set
    int lastItem = 0, lastChar = 0, mode = 0;
    char op = 0;

    boolean invert = false;

    clear();

    while (mode != 2 && !chars.atEnd()) {
      //Eclipse stated the following is "dead code"
      /*
      if (false) {
          // Debugging assertion
          if (!((lastItem == 0 && op == 0) ||
                  (lastItem == 1 && (op == 0 || op == '-')) ||
                  (lastItem == 2 && (op == 0 || op == '-' || op == '&')))) {
              throw new IllegalArgumentException();
          }
      }*/

      int c = 0;
      boolean literal = false;
      UnicodeSet nested = null;

      // -------- Check for property pattern

      // setMode: 0=none, 1=unicodeset, 2=propertypat, 3=preparsed
      int setMode = 0;
      if (resemblesPropertyPattern(chars, opts)) {
        setMode = 2;
      }

      // -------- Parse '[' of opening delimiter OR nested set.
      // If there is a nested set, use `setMode' to define how
      // the set should be parsed.  If the '[' is part of the
      // opening delimiter for this pattern, parse special
      // strings "[", "[^", "[-", and "[^-".  Check for stand-in
      // characters representing a nested set in the symbol
      // table.

      else {
        // Prepare to backup if necessary
        backup = chars.getPos(backup);
        c = chars.next(opts);
        literal = chars.isEscaped();

        if (c == '[' && !literal) {
          if (mode == 1) {
            chars.setPos(backup); // backup
            setMode = 1;
          } else {
            // Handle opening '[' delimiter
            mode = 1;
            patBuf.append('[');
            backup = chars.getPos(backup); // prepare to backup
            c = chars.next(opts);
            literal = chars.isEscaped();
            if (c == '^' && !literal) {
              invert = true;
              patBuf.append('^');
              backup = chars.getPos(backup); // prepare to backup
              c = chars.next(opts);
              literal = chars.isEscaped();
            }
            // Fall through to handle special leading '-';
            // otherwise restart loop for nested [], \p{}, etc.
            if (c == '-') {
              literal = true;
              // Fall through to handle literal '-' below
            } else {
              chars.setPos(backup); // backup
              continue;
            }
          }
        } else if (symbols != null) {
          UnicodeMatcher m = symbols.lookupMatcher(c); // may be null
          if (m != null) {
            try {
              nested = (UnicodeSet) m;
              setMode = 3;
            } catch (ClassCastException e) {
              syntaxError(chars, "Syntax error");
            }
          }
        }
      }

      // -------- Handle a nested set.  This either is inline in
      // the pattern or represented by a stand-in that has
      // previously been parsed and was looked up in the symbol
      // table.

      if (setMode != 0) {
        if (lastItem == 1) {
          if (op != 0) {
            syntaxError(chars, "Char expected after operator");
          }
          add_unchecked(lastChar, lastChar);
          _appendToPat(patBuf, lastChar, false);
          lastItem = op = 0;
        }

        if (op == '-' || op == '&') {
          patBuf.append(op);
        }

        if (nested == null) {
          if (scratch == null)
            scratch = new UnicodeSet();
          nested = scratch;
        }
        switch (setMode) {
        case 1:
          nested.applyPattern(chars, symbols, patBuf, options);
          break;
        case 2:
          chars.skipIgnored(opts);
          nested.applyPropertyPattern(chars, patBuf, symbols);
          break;
        case 3: // `nested' already parsed
          nested._toPattern(patBuf, false);
          break;
        }

        usePat = true;

        if (mode == 0) {
          // Entire pattern is a category; leave parse loop
          set(nested);
          mode = 2;
          break;
        }

        switch (op) {
        case '-':
          removeAll(nested);
          break;
        case '&':
          retainAll(nested);
          break;
        case 0:
          addAll(nested);
          break;
        }

        op = 0;
        lastItem = 2;

        continue;
      }

      if (mode == 0) {
        syntaxError(chars, "Missing '['");
      }

      // -------- Parse special (syntax) characters.  If the
      // current character is not special, or if it is escaped,
      // then fall through and handle it below.

      if (!literal) {
        switch (c) {
        case ']':
          if (lastItem == 1) {
            add_unchecked(lastChar, lastChar);
            _appendToPat(patBuf, lastChar, false);
          }
          // Treat final trailing '-' as a literal
          if (op == '-') {
            add_unchecked(op, op);
            patBuf.append(op);
          } else if (op == '&') {
            syntaxError(chars, "Trailing '&'");
          }
          patBuf.append(']');
          mode = 2;
          continue;
        case '-':
          if (op == 0) {
            if (lastItem != 0) {
              op = (char) c;
              continue;
            } else {
              // Treat final trailing '-' as a literal
              add_unchecked(c, c);
              c = chars.next(opts);
              literal = chars.isEscaped();
              if (c == ']' && !literal) {
                patBuf.append("-]");
                mode = 2;
                continue;
              }
            }
          }
          syntaxError(chars, "'-' not after char or set");
          break;
        case '&':
          if (lastItem == 2 && op == 0) {
            op = (char) c;
            continue;
          }
          syntaxError(chars, "'&' not after set");
          break;
        case '^':
          syntaxError(chars, "'^' not after '['");
          break;
        case '{':
          if (op != 0) {
            syntaxError(chars, "Missing operand after operator");
          }
          if (lastItem == 1) {
            add_unchecked(lastChar, lastChar);
            _appendToPat(patBuf, lastChar, false);
          }
          lastItem = 0;
          if (buf == null) {
            buf = new StringBuffer();
          } else {
            buf.setLength(0);
          }
          boolean ok = false;
          while (!chars.atEnd()) {
            c = chars.next(opts);
            literal = chars.isEscaped();
            if (c == '}' && !literal) {
              ok = true;
              break;
            }
            UTF16.append(buf, c);
          }
          if (buf.length() < 1 || !ok) {
            syntaxError(chars, "Invalid multicharacter string");
          }
          // We have new string. Add it to set and continue;
          // we don't need to drop through to the further
          // processing
          add(buf.toString());
          patBuf.append('{');
          _appendToPat(patBuf, buf.toString(), false);
          patBuf.append('}');
          continue;
        case SymbolTable.SYMBOL_REF:
          //         symbols  nosymbols
          // [a-$]   error    error (ambiguous)
          // [a$]    anchor   anchor
          // [a-$x]  var "x"* literal '$'
          // [a-$.]  error    literal '$'
          // *We won't get here in the case of var "x"
          backup = chars.getPos(backup);
          c = chars.next(opts);
          literal = chars.isEscaped();
          boolean anchor = (c == ']' && !literal);
          if (symbols == null && !anchor) {
            c = SymbolTable.SYMBOL_REF;
            chars.setPos(backup);
            break; // literal '$'
          }
          if (anchor && op == 0) {
            if (lastItem == 1) {
              add_unchecked(lastChar, lastChar);
              _appendToPat(patBuf, lastChar, false);
            }
            add_unchecked(UnicodeMatcher.ETHER);
            usePat = true;
            patBuf.append(SymbolTable.SYMBOL_REF).append(']');
            mode = 2;
            continue;
          }
          syntaxError(chars, "Unquoted '$'");
          break;
        default:
          break;
        }
      }

      // -------- Parse literal characters.  This includes both
      // escaped chars ("\u4E01") and non-syntax characters
      // ("a").

      switch (lastItem) {
      case 0:
        lastItem = 1;
        lastChar = c;
        break;
      case 1:
        if (op == '-') {
          if (lastChar >= c) {
            // Don't allow redundant (a-a) or empty (b-a) ranges;
            // these are most likely typos.
            syntaxError(chars, "Invalid range");
          }
          add_unchecked(lastChar, c);
          _appendToPat(patBuf, lastChar, false);
          patBuf.append(op);
          _appendToPat(patBuf, c, false);
          lastItem = op = 0;
        } else {
          add_unchecked(lastChar, lastChar);
          _appendToPat(patBuf, lastChar, false);
          lastChar = c;
        }
        break;
      case 2:
        if (op != 0) {
          syntaxError(chars, "Set expected after operator");
        }
        lastChar = c;
        lastItem = 1;
        break;
      }
    }

    if (mode != 2) {
      syntaxError(chars, "Missing ']'");
    }

    chars.skipIgnored(opts);

    /**
     * Handle global flags (invert, case insensitivity). If this pattern should be compiled case-insensitive, then we need to close over
     * case BEFORE COMPLEMENTING. This makes patterns like /[^abc]/i work.
     */
    if ((options & CASE) != 0) {
      closeOver(CASE);
    }
    if (invert) {
      complement();
    }

    // Use the rebuilt pattern (pat) only if necessary.  Prefer the
    // generated pattern.
    if (usePat) {
      rebuiltPat.append(patBuf.toString());
    } else {
      _generatePattern(rebuiltPat, false, true);
    }
  }

  private static void syntaxError(final RuleCharacterIterator chars, final String msg) {
    throw new IllegalArgumentException("Error: " + msg + " at \"" + Utility.escape(chars.toString()) + '"');
  }

  /**
   * Add the contents of the UnicodeSet (as strings) into a collection.
   *
   * @param target
   *            collection to add into
   * @stable ICU 4.4
   */
  public <T extends Collection<String>> T addAllTo(final T target) {
    return addAllTo(this, target);
  }

  /**
   * Add the contents of the UnicodeSet (as strings) into a collection.
   *
   * @param target
   *            collection to add into
   * @stable ICU 4.4
   */
  public String[] addAllTo(final String[] target) {
    return addAllTo(this, target);
  }

  /**
   * Add the contents of the UnicodeSet (as strings) into an array.
   *
   * @stable ICU 4.4
   */
  public static String[] toArray(final UnicodeSet set) {
    return addAllTo(set, new String[set.size()]);
  }

  /**
   * Add the contents of the collection (as strings) into this UnicodeSet.
   *
   * @param source
   *            the collection to add
   * @return a reference to this object
   * @stable ICU 4.4
   */
  public UnicodeSet add(final Collection<?> source) {
    return addAll(source);
  }

  /**
   * Add the contents of the UnicodeSet (as strings) into a collection. Uses standard naming convention.
   *
   * @param source
   *            collection to add into
   * @return a reference to this object
   * @stable ICU 4.4
   */
  public UnicodeSet addAll(final Collection<?> source) {
    checkFrozen();
    for (Object o : source) {
      add(o.toString());
    }
    return this;
  }

  //----------------------------------------------------------------
  // Implementation: Utility methods
  //----------------------------------------------------------------

  private void ensureCapacity(final int newLen) {
    if (newLen <= list.length)
      return;
    int[] temp = new int[newLen + GROW_EXTRA];
    System.arraycopy(list, 0, temp, 0, len);
    list = temp;
  }

  private void ensureBufferCapacity(final int newLen) {
    if (buffer != null && newLen <= buffer.length)
      return;
    buffer = new int[newLen + GROW_EXTRA];
  }

  /**
   * Assumes start <= end.
   */
  private int[] range(final int start, final int end) {
    if (rangeList == null) {
      rangeList = new int[] { start, end + 1, HIGH };
    } else {
      rangeList[0] = start;
      rangeList[1] = end + 1;
    }
    return rangeList;
  }

  //----------------------------------------------------------------
  // Implementation: Fundamental operations
  //----------------------------------------------------------------

  // polarity = 0, 3 is normal: x xor y
  // polarity = 1, 2: x xor ~y == x === y

  private UnicodeSet xor(final int[] other, final int otherLen, final int polarity) {
    ensureBufferCapacity(len + otherLen);
    int i = 0, j = 0, k = 0;
    int a = list[i++];
    int b;
    // TODO: Based on the call hierarchy, polarity of 1 or 2 is never used
    //      so the following if statement will not be called.
    ///CLOVER:OFF
    if (polarity == 1 || polarity == 2) {
      b = LOW;
      if (other[j] == LOW) { // skip base if already LOW
        ++j;
        b = other[j];
      }
      ///CLOVER:ON
    } else {
      b = other[j++];
    }
    // simplest of all the routines
    // sort the values, discarding identicals!
    while (true) {
      if (a < b) {
        buffer[k++] = a;
        a = list[i++];
      } else if (b < a) {
        buffer[k++] = b;
        b = other[j++];
      } else if (a != HIGH) { // at this point, a == b
        // discard both values!
        a = list[i++];
        b = other[j++];
      } else { // DONE!
        buffer[k++] = HIGH;
        len = k;
        break;
      }
    }
    // swap list and buffer
    int[] temp = list;
    list = buffer;
    buffer = temp;
    pat = null;
    return this;
  }

  // polarity = 0 is normal: x union y
  // polarity = 2: x union ~y
  // polarity = 1: ~x union y
  // polarity = 3: ~x union ~y

  private UnicodeSet add(final int[] other, final int otherLen, int polarity) {
    ensureBufferCapacity(len + otherLen);
    int i = 0, j = 0, k = 0;
    int a = list[i++];
    int b = other[j++];
    // change from xor is that we have to check overlapping pairs
    // polarity bit 1 means a is second, bit 2 means b is.
    main: while (true) {
      switch (polarity) {
      case 0: // both first; take lower if unequal
        if (a < b) { // take a
          // Back up over overlapping ranges in buffer[]
          if (k > 0 && a <= buffer[k - 1]) {
            // Pick latter end value in buffer[] vs. list[]
            a = max(list[i], buffer[--k]);
          } else {
            // No overlap
            buffer[k++] = a;
            a = list[i];
          }
          i++; // Common if/else code factored out
          polarity ^= 1;
        } else if (b < a) { // take b
          if (k > 0 && b <= buffer[k - 1]) {
            b = max(other[j], buffer[--k]);
          } else {
            buffer[k++] = b;
            b = other[j];
          }
          j++;
          polarity ^= 2;
        } else { // a == b, take a, drop b
          if (a == HIGH)
            break main;
          // This is symmetrical; it doesn't matter if
          // we backtrack with a or b. - liu
          if (k > 0 && a <= buffer[k - 1]) {
            a = max(list[i], buffer[--k]);
          } else {
            // No overlap
            buffer[k++] = a;
            a = list[i];
          }
          i++;
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      case 3: // both second; take higher if unequal, and drop other
        if (b <= a) { // take a
          if (a == HIGH)
            break main;
          buffer[k++] = a;
        } else { // take b
          if (b == HIGH)
            break main;
          buffer[k++] = b;
        }
        a = list[i++];
        polarity ^= 1;   // factored common code
        b = other[j++];
        polarity ^= 2;
        break;
      case 1: // a second, b first; if b < a, overlap
        if (a < b) { // no overlap, take a
          buffer[k++] = a;
          a = list[i++];
          polarity ^= 1;
        } else if (b < a) { // OVERLAP, drop b
          b = other[j++];
          polarity ^= 2;
        } else { // a == b, drop both!
          if (a == HIGH)
            break main;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      case 2: // a first, b second; if a < b, overlap
        if (b < a) { // no overlap, take b
          buffer[k++] = b;
          b = other[j++];
          polarity ^= 2;
        } else if (a < b) { // OVERLAP, drop a
          a = list[i++];
          polarity ^= 1;
        } else { // a == b, drop both!
          if (a == HIGH)
            break main;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      }
    }
    buffer[k++] = HIGH;    // terminate
    len = k;
    // swap list and buffer
    int[] temp = list;
    list = buffer;
    buffer = temp;
    pat = null;
    return this;
  }

  // polarity = 0 is normal: x intersect y
  // polarity = 2: x intersect ~y == set-minus
  // polarity = 1: ~x intersect y
  // polarity = 3: ~x intersect ~y

  private UnicodeSet retain(final int[] other, final int otherLen, int polarity) {
    ensureBufferCapacity(len + otherLen);
    int i = 0, j = 0, k = 0;
    int a = list[i++];
    int b = other[j++];
    // change from xor is that we have to check overlapping pairs
    // polarity bit 1 means a is second, bit 2 means b is.
    main: while (true) {
      switch (polarity) {
      case 0: // both first; drop the smaller
        if (a < b) { // drop a
          a = list[i++];
          polarity ^= 1;
        } else if (b < a) { // drop b
          b = other[j++];
          polarity ^= 2;
        } else { // a == b, take one, drop other
          if (a == HIGH)
            break main;
          buffer[k++] = a;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      case 3: // both second; take lower if unequal
        if (a < b) { // take a
          buffer[k++] = a;
          a = list[i++];
          polarity ^= 1;
        } else if (b < a) { // take b
          buffer[k++] = b;
          b = other[j++];
          polarity ^= 2;
        } else { // a == b, take one, drop other
          if (a == HIGH)
            break main;
          buffer[k++] = a;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      case 1: // a second, b first;
        if (a < b) { // NO OVERLAP, drop a
          a = list[i++];
          polarity ^= 1;
        } else if (b < a) { // OVERLAP, take b
          buffer[k++] = b;
          b = other[j++];
          polarity ^= 2;
        } else { // a == b, drop both!
          if (a == HIGH)
            break main;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      case 2: // a first, b second; if a < b, overlap
        if (b < a) { // no overlap, drop b
          b = other[j++];
          polarity ^= 2;
        } else if (a < b) { // OVERLAP, take a
          buffer[k++] = a;
          a = list[i++];
          polarity ^= 1;
        } else { // a == b, drop both!
          if (a == HIGH)
            break main;
          a = list[i++];
          polarity ^= 1;
          b = other[j++];
          polarity ^= 2;
        }
        break;
      }
    }
    buffer[k++] = HIGH;    // terminate
    len = k;
    // swap list and buffer
    int[] temp = list;
    list = buffer;
    buffer = temp;
    pat = null;
    return this;
  }

  private static final int max(final int a, final int b) {
    return (a > b) ? a : b;
  }

  //----------------------------------------------------------------
  // Generic filter-based scanning code
  //----------------------------------------------------------------

  private static interface Filter {
    boolean contains(int codePoint);
  }

  private static class NumericValueFilter implements Filter {
    double value;

    NumericValueFilter(final double value) {
      this.value = value;
    }

    public boolean contains(final int ch) {
      return UCharacter.getUnicodeNumericValue(ch) == value;
    }
  }

  private static class GeneralCategoryMaskFilter implements Filter {
    int mask;

    GeneralCategoryMaskFilter(final int mask) {
      this.mask = mask;
    }

    public boolean contains(final int ch) {
      return ((1 << UCharacter.getType(ch)) & mask) != 0;
    }
  }

  private static class IntPropertyFilter implements Filter {
    int prop;
    int value;

    IntPropertyFilter(final int prop, final int value) {
      this.prop = prop;
      this.value = value;
    }

    public boolean contains(final int ch) {
      return UCharacter.getIntPropertyValue(ch, prop) == value;
    }
  }

  private static class ScriptExtensionsFilter implements Filter {
    int script;

    ScriptExtensionsFilter(final int script) {
      this.script = script;
    }

    public boolean contains(final int c) {
      return UScript.hasScript(c, script);
    }
  }

  // VersionInfo for unassigned characters
  private static final VersionInfo NO_VERSION = VersionInfo.getInstance(0, 0, 0, 0);

  private static class VersionFilter implements Filter {
    VersionInfo version;

    VersionFilter(final VersionInfo version) {
      this.version = version;
    }

    public boolean contains(final int ch) {
      VersionInfo v = UCharacter.getAge(ch);
      // Reference comparison ok; VersionInfo caches and reuses
      // unique objects.
      return v != NO_VERSION && v.compareTo(version) <= 0;
    }
  }

  private static synchronized UnicodeSet getInclusions(final int src) {
    if (INCLUSIONS == null) {
      INCLUSIONS = new UnicodeSet[UCharacterProperty.SRC_COUNT];
    }
    if (INCLUSIONS[src] == null) {
      UnicodeSet incl = new UnicodeSet();
      switch (src) {
      case UCharacterProperty.SRC_CHAR:
        UCharacterProperty.INSTANCE.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_PROPSVEC:
        UCharacterProperty.INSTANCE.upropsvec_addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_CHAR_AND_PROPSVEC:
        UCharacterProperty.INSTANCE.addPropertyStarts(incl);
        UCharacterProperty.INSTANCE.upropsvec_addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_CASE_AND_NORM:
        Norm2AllModes.getNFCInstance().impl.addPropertyStarts(incl);
        UCaseProps.INSTANCE.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_NFC:
        Norm2AllModes.getNFCInstance().impl.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_NFKC:
        Norm2AllModes.getNFKCInstance().impl.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_NFKC_CF:
        Norm2AllModes.getNFKC_CFInstance().impl.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_NFC_CANON_ITER:
        Norm2AllModes.getNFCInstance().impl.addCanonIterPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_CASE:
        UCaseProps.INSTANCE.addPropertyStarts(incl);
        break;
      case UCharacterProperty.SRC_BIDI:
        UBiDiProps.INSTANCE.addPropertyStarts(incl);
        break;
      default:
        throw new IllegalStateException("UnicodeSet.getInclusions(unknown src " + src + ")");
      }
      INCLUSIONS[src] = incl;
    }
    return INCLUSIONS[src];
  }

  /**
   * Generic filter-based scanning code for UCD property UnicodeSets.
   */
  private UnicodeSet applyFilter(final Filter filter, final int src) {
    // Logically, walk through all Unicode characters, noting the start
    // and end of each range for which filter.contain(c) is
    // true.  Add each range to a set.
    //
    // To improve performance, use an inclusions set which
    // encodes information about character ranges that are known
    // to have identical properties.
    // getInclusions(src) contains exactly the first characters of
    // same-value ranges for the given properties "source".

    clear();

    int startHasProperty = -1;
    UnicodeSet inclusions = getInclusions(src);
    int limitRange = inclusions.getRangeCount();

    for (int j = 0; j < limitRange; ++j) {
      // get current range
      int start = inclusions.getRangeStart(j);
      int end = inclusions.getRangeEnd(j);

      // for all the code points in the range, process
      for (int ch = start; ch <= end; ++ch) {
        // only add to the unicodeset on inflection points --
        // where the hasProperty value changes to false
        if (filter.contains(ch)) {
          if (startHasProperty < 0) {
            startHasProperty = ch;
          }
        } else if (startHasProperty >= 0) {
          add_unchecked(startHasProperty, ch - 1);
          startHasProperty = -1;
        }
      }
    }
    if (startHasProperty >= 0) {
      add_unchecked(startHasProperty, 0x10FFFF);
    }

    return this;
  }

  /**
   * Remove leading and trailing Pattern_White_Space and compress internal Pattern_White_Space to a single space character.
   */
  private static String mungeCharName(String source) {
    source = PatternProps.trimWhiteSpace(source);
    StringBuilder buf = null;
    for (int i = 0; i < source.length(); ++i) {
      char ch = source.charAt(i);
      if (PatternProps.isWhiteSpace(ch)) {
        if (buf == null) {
          buf = new StringBuilder().append(source, 0, i);
        } else if (buf.charAt(buf.length() - 1) == ' ') {
          continue;
        }
        ch = ' '; // convert to ' '
      }
      if (buf != null) {
        buf.append(ch);
      }
    }
    return buf == null ? source : buf.toString();
  }

  //----------------------------------------------------------------
  // Property set API
  //----------------------------------------------------------------

  /**
   * Modifies this set to contain those code points which have the given value for the given binary or enumerated property, as returned by
   * UCharacter.getIntPropertyValue. Prior contents of this set are lost.
   *
   * @param prop
   *            a property in the range UProperty.BIN_START..UProperty.BIN_LIMIT-1 or UProperty.INT_START..UProperty.INT_LIMIT-1 or.
   *            UProperty.MASK_START..UProperty.MASK_LIMIT-1.
   *
   * @param value
   *            a value in the range UCharacter.getIntPropertyMinValue(prop).. UCharacter.getIntPropertyMaxValue(prop), with one
   *            exception. If prop is UProperty.GENERAL_CATEGORY_MASK, then value should not be a UCharacter.getType() result, but rather
   *            a mask value produced by logically ORing (1 << UCharacter.getType()) values together. This allows grouped categories such
   *            as [:L:] to be represented.
   *
   * @return a reference to this set
   *
   * @stable ICU 2.4
   */
  public UnicodeSet applyIntPropertyValue(final int prop, final int value) {
    checkFrozen();
    if (prop == UProperty.GENERAL_CATEGORY_MASK) {
      applyFilter(new GeneralCategoryMaskFilter(value), UCharacterProperty.SRC_CHAR);
    } else if (prop == UProperty.SCRIPT_EXTENSIONS) {
      applyFilter(new ScriptExtensionsFilter(value), UCharacterProperty.SRC_PROPSVEC);
    } else {
      applyFilter(new IntPropertyFilter(prop, value), UCharacterProperty.INSTANCE.getSource(prop));
    }
    return this;
  }

  /**
   * Modifies this set to contain those code points which have the given value for the given property. Prior contents of this set are
   * lost.
   *
   * @param propertyAlias
   *            a property alias, either short or long. The name is matched loosely. See PropertyAliases.txt for names and a description
   *            of loose matching. If the value string is empty, then this string is interpreted as either a General_Category value alias,
   *            a Script value alias, a binary property alias, or a special ID. Special IDs are matched loosely and correspond to the
   *            following sets:
   *
   *            "ANY" = [\u0000-\U0010FFFF], "ASCII" = [\u0000-\u007F].
   *
   * @param valueAlias
   *            a value alias, either short or long. The name is matched loosely. See PropertyValueAliases.txt for names and a description
   *            of loose matching. In addition to aliases listed, numeric values and canonical combining classes may be expressed
   *            numerically, e.g., ("nv", "0.5") or ("ccc", "220"). The value string may also be empty.
   *
   * @return a reference to this set
   *
   * @stable ICU 2.4
   */
  public UnicodeSet applyPropertyAlias(final String propertyAlias, final String valueAlias) {
    return applyPropertyAlias(propertyAlias, valueAlias, null);
  }

  /**
   * Modifies this set to contain those code points which have the given value for the given property. Prior contents of this set are
   * lost.
   *
   * @param propertyAlias
   *            A string of the property alias.
   * @param valueAlias
   *            A string of the value alias.
   * @param symbols
   *            if not null, then symbols are first called to see if a property is available. If true, then everything else is skipped.
   * @return this set
   * @stable ICU 3.2
   */
  public UnicodeSet applyPropertyAlias(final String propertyAlias, final String valueAlias, final SymbolTable symbols) {
    checkFrozen();
    int p;
    int v;
    boolean mustNotBeEmpty = false, invert = false;

    if (symbols != null && (symbols instanceof XSymbolTable)
        && ((XSymbolTable) symbols).applyPropertyAlias(propertyAlias, valueAlias, this)) {
      return this;
    }

    if (XSYMBOL_TABLE != null) {
      if (XSYMBOL_TABLE.applyPropertyAlias(propertyAlias, valueAlias, this)) {
        return this;
      }
    }

    if (valueAlias.length() > 0) {
      p = UCharacter.getPropertyEnum(propertyAlias);

      // Treat gc as gcm
      if (p == UProperty.GENERAL_CATEGORY) {
        p = UProperty.GENERAL_CATEGORY_MASK;
      }

      if ((p >= UProperty.BINARY_START && p < UProperty.BINARY_LIMIT) || (p >= UProperty.INT_START && p < UProperty.INT_LIMIT)
          || (p >= UProperty.MASK_START && p < UProperty.MASK_LIMIT)) {
        try {
          v = UCharacter.getPropertyValueEnum(p, valueAlias);
        } catch (IllegalArgumentException e) {
          // Handle numeric CCC
          if (p == UProperty.CANONICAL_COMBINING_CLASS || p == UProperty.LEAD_CANONICAL_COMBINING_CLASS
              || p == UProperty.TRAIL_CANONICAL_COMBINING_CLASS) {
            v = Integer.parseInt(PatternProps.trimWhiteSpace(valueAlias));
            // If the resultant set is empty then the numeric value
            // was invalid.
            //mustNotBeEmpty = true;
            // old code was wrong; anything between 0 and 255 is valid even if unused.
            if (v < 0 || v > 255)
              throw e;
          } else {
            throw e;
          }
        }
      }

      else {
        switch (p) {
        case UProperty.NUMERIC_VALUE: {
          double value = Double.parseDouble(PatternProps.trimWhiteSpace(valueAlias));
          applyFilter(new NumericValueFilter(value), UCharacterProperty.SRC_CHAR);
          return this;
        }
        case UProperty.NAME: {
          // Must munge name, since
          // UCharacter.charFromName() does not do
          // 'loose' matching.
          String buf = mungeCharName(valueAlias);
          int ch = UCharacter.getCharFromExtendedName(buf);
          if (ch == -1) {
            throw new IllegalArgumentException("Invalid character name");
          }
          clear();
          add_unchecked(ch);
          return this;
        }
        case UProperty.UNICODE_1_NAME:
          // ICU 49 deprecates the Unicode_1_Name property APIs.
          throw new IllegalArgumentException("Unicode_1_Name (na1) not supported");
        case UProperty.AGE: {
          // Must munge name, since
          // VersionInfo.getInstance() does not do
          // 'loose' matching.
          VersionInfo version = VersionInfo.getInstance(mungeCharName(valueAlias));
          applyFilter(new VersionFilter(version), UCharacterProperty.SRC_PROPSVEC);
          return this;
        }
        case UProperty.SCRIPT_EXTENSIONS:
          v = UCharacter.getPropertyValueEnum(UProperty.SCRIPT, valueAlias);
          // fall through to calling applyIntPropertyValue()
          break;
        default:
          // p is a non-binary, non-enumerated property that we
          // don't support (yet).
          throw new IllegalArgumentException("Unsupported property");
        }
      }
    }

    else {
      // valueAlias is empty.  Interpret as General Category, Script,
      // Binary property, or ANY or ASCII.  Upon success, p and v will
      // be set.
      UPropertyAliases pnames = UPropertyAliases.INSTANCE;
      p = UProperty.GENERAL_CATEGORY_MASK;
      v = pnames.getPropertyValueEnum(p, propertyAlias);
      if (v == UProperty.UNDEFINED) {
        p = UProperty.SCRIPT;
        v = pnames.getPropertyValueEnum(p, propertyAlias);
        if (v == UProperty.UNDEFINED) {
          p = pnames.getPropertyEnum(propertyAlias);
          if (p == UProperty.UNDEFINED) {
            p = -1;
          }
          if (p >= UProperty.BINARY_START && p < UProperty.BINARY_LIMIT) {
            v = 1;
          } else if (p == -1) {
            if (0 == UPropertyAliases.compare(ANY_ID, propertyAlias)) {
              set(MIN_VALUE, MAX_VALUE);
              return this;
            } else if (0 == UPropertyAliases.compare(ASCII_ID, propertyAlias)) {
              set(0, 0x7F);
              return this;
            } else if (0 == UPropertyAliases.compare(ASSIGNED, propertyAlias)) {
              // [:Assigned:]=[:^Cn:]
              p = UProperty.GENERAL_CATEGORY_MASK;
              v = (1 << UCharacter.UNASSIGNED);
              invert = true;
            } else {
              // Property name was never matched.
              throw new IllegalArgumentException("Invalid property alias: " + propertyAlias + "=" + valueAlias);
            }
          } else {
            // Valid propery name, but it isn't binary, so the value
            // must be supplied.
            throw new IllegalArgumentException("Missing property value");
          }
        }
      }
    }

    applyIntPropertyValue(p, v);
    if (invert) {
      complement();
    }

    if (mustNotBeEmpty && isEmpty()) {
      // mustNotBeEmpty is set to true if an empty set indicates
      // invalid input.
      throw new IllegalArgumentException("Invalid property value");
    }

    return this;
  }

  //----------------------------------------------------------------
  // Property set patterns
  //----------------------------------------------------------------

  /**
   * Return true if the given position, in the given pattern, appears to be the start of a property set pattern.
   */
  private static boolean resemblesPropertyPattern(final String pattern, final int pos) {
    // Patterns are at least 5 characters long
    if ((pos + 5) > pattern.length()) {
      return false;
    }

    // Look for an opening [:, [:^, \p, or \P
    return pattern.regionMatches(pos, "[:", 0, 2) || pattern.regionMatches(true, pos, "\\p", 0, 2)
        || pattern.regionMatches(pos, "\\N", 0, 2);
  }

  /**
   * Return true if the given iterator appears to point at a property pattern. Regardless of the result, return with the iterator
   * unchanged.
   *
   * @param chars
   *            iterator over the pattern characters. Upon return it will be unchanged.
   * @param iterOpts
   *            RuleCharacterIterator options
   */
  private static boolean resemblesPropertyPattern(final RuleCharacterIterator chars, int iterOpts) {
    boolean result = false;
    iterOpts &= ~RuleCharacterIterator.PARSE_ESCAPES;
    Object pos = chars.getPos(null);
    int c = chars.next(iterOpts);
    if (c == '[' || c == '\\') {
      int d = chars.next(iterOpts & ~RuleCharacterIterator.SKIP_WHITESPACE);
      result = (c == '[') ? (d == ':') : (d == 'N' || d == 'p' || d == 'P');
    }
    chars.setPos(pos);
    return result;
  }

  /**
   * Parse the given property pattern at the given parse position.
   *
   * @param symbols
   *            TODO
   */
  private UnicodeSet applyPropertyPattern(final String pattern, final ParsePosition ppos, final SymbolTable symbols) {
    int pos = ppos.getIndex();

    // On entry, ppos should point to one of the following locations:

    // Minimum length is 5 characters, e.g. \p{L}
    if ((pos + 5) > pattern.length()) {
      return null;
    }

    boolean posix = false; // true for [:pat:], false for \p{pat} \P{pat} \N{pat}
    boolean isName = false; // true for \N{pat}, o/w false
    boolean invert = false;

    // Look for an opening [:, [:^, \p, or \P
    if (pattern.regionMatches(pos, "[:", 0, 2)) {
      posix = true;
      pos = PatternProps.skipWhiteSpace(pattern, (pos + 2));
      if (pos < pattern.length() && pattern.charAt(pos) == '^') {
        ++pos;
        invert = true;
      }
    } else if (pattern.regionMatches(true, pos, "\\p", 0, 2) || pattern.regionMatches(pos, "\\N", 0, 2)) {
      char c = pattern.charAt(pos + 1);
      invert = (c == 'P');
      isName = (c == 'N');
      pos = PatternProps.skipWhiteSpace(pattern, (pos + 2));
      if (pos == pattern.length() || pattern.charAt(pos++) != '{') {
        // Syntax error; "\p" or "\P" not followed by "{"
        return null;
      }
    } else {
      // Open delimiter not seen
      return null;
    }

    // Look for the matching close delimiter, either :] or }
    int close = pattern.indexOf(posix ? ":]" : "}", pos);
    if (close < 0) {
      // Syntax error; close delimiter missing
      return null;
    }

    // Look for an '=' sign.  If this is present, we will parse a
    // medium \p{gc=Cf} or long \p{GeneralCategory=Format}
    // pattern.
    int equals = pattern.indexOf('=', pos);
    String propName, valueName;
    if (equals >= 0 && equals < close && !isName) {
      // Equals seen; parse medium/long pattern
      propName = pattern.substring(pos, equals);
      valueName = pattern.substring(equals + 1, close);
    }

    else {
      // Handle case where no '=' is seen, and \N{}
      propName = pattern.substring(pos, close);
      valueName = "";

      // Handle \N{name}
      if (isName) {
        // This is a little inefficient since it means we have to
        // parse "na" back to UProperty.NAME even though we already
        // know it's UProperty.NAME.  If we refactor the API to
        // support args of (int, String) then we can remove
        // "na" and make this a little more efficient.
        valueName = propName;
        propName = "na";
      }
    }

    applyPropertyAlias(propName, valueName, symbols);

    if (invert) {
      complement();
    }

    // Move to the limit position after the close delimiter
    ppos.setIndex(close + (posix ? 2 : 1));

    return this;
  }

  /**
   * Parse a property pattern.
   *
   * @param chars
   *            iterator over the pattern characters. Upon return it will be advanced to the first character after the parsed pattern, or
   *            the end of the iteration if all characters are parsed.
   * @param rebuiltPat
   *            the pattern that was parsed, rebuilt or copied from the input pattern, as appropriate.
   * @param symbols
   *            TODO
   */
  private void applyPropertyPattern(final RuleCharacterIterator chars, final StringBuffer rebuiltPat, final SymbolTable symbols) {
    String patStr = chars.lookahead();
    ParsePosition pos = new ParsePosition(0);
    applyPropertyPattern(patStr, pos, symbols);
    if (pos.getIndex() == 0) {
      syntaxError(chars, "Invalid property pattern");
    }
    chars.jumpahead(pos.getIndex());
    rebuiltPat.append(patStr.substring(0, pos.getIndex()));
  }

  //----------------------------------------------------------------
  // Case folding API
  //----------------------------------------------------------------

  /**
   * Bitmask for constructor and applyPattern() indicating that white space should be ignored. If set, ignore Unicode Pattern_White_Space
   * characters, unless they are quoted or escaped. This may be ORed together with other selectors.
   *
   * @stable ICU 3.8
   */
  public static final int IGNORE_SPACE = 1;

  /**
   * Bitmask for constructor, applyPattern(), and closeOver() indicating letter case. This may be ORed together with other selectors.
   *
   * Enable case insensitive matching. E.g., "[ab]" with this flag will match 'a', 'A', 'b', and 'B'. "[^ab]" with this flag will match
   * all except 'a', 'A', 'b', and 'B'. This performs a full closure over case mappings, e.g. U+017F for s.
   *
   * The resulting set is a superset of the input for the code points but not for the strings. It performs a case mapping closure of the
   * code points and adds full case folding strings for the code points, and reduces strings of the original set to their full case
   * folding equivalents.
   *
   * This is designed for case-insensitive matches, for example in regular expressions. The full code point case closure allows checking
   * of an input character directly against the closure set. Strings are matched by comparing the case-folded form from the closure set
   * with an incremental case folding of the string in question.
   *
   * The closure set will also contain single code points if the original set contained case-equivalent strings (like U+00DF for "ss" or
   * "Ss" etc.). This is not necessary (that is, redundant) for the above matching method but results in the same closure sets regardless
   * of whether the original set contained the code point or a string.
   *
   * @stable ICU 3.8
   */
  public static final int CASE = 2;

  /**
   * Alias for UnicodeSet.CASE, for ease of porting from C++ where ICU4C also has both USET_CASE and USET_CASE_INSENSITIVE (see uset.h).
   *
   * @see #CASE
   * @stable ICU 3.4
   */
  public static final int CASE_INSENSITIVE = 2;

  /**
   * Bitmask for constructor, applyPattern(), and closeOver() indicating letter case. This may be ORed together with other selectors.
   *
   * Enable case insensitive matching. E.g., "[ab]" with this flag will match 'a', 'A', 'b', and 'B'. "[^ab]" with this flag will match
   * all except 'a', 'A', 'b', and 'B'. This adds the lower-, title-, and uppercase mappings as well as the case folding of each existing
   * element in the set.
   *
   * @stable ICU 3.4
   */
  public static final int ADD_CASE_MAPPINGS = 4;

  //  add the result of a full case mapping to the set
  //  use str as a temporary string to avoid constructing one
  private static final void addCaseMapping(final UnicodeSet set, final int result, final StringBuilder full) {
    if (result >= 0) {
      if (result > UCaseProps.MAX_STRING_LENGTH) {
        // add a single-code point case mapping
        set.add(result);
      } else {
        // add a string case mapping from full with length result
        set.add(full.toString());
        full.setLength(0);
      }
    }
    // result < 0: the code point mapped to itself, no need to add it
    // see UCaseProps
  }

  /**
   * Close this set over the given attribute. For the attribute CASE, the result is to modify this set so that:
   *
   * 1. For each character or string 'a' in this set, all strings 'b' such that foldCase(a) == foldCase(b) are added to this set. (For
   * most 'a' that are single characters, 'b' will have b.length() == 1.)
   *
   * 2. For each string 'e' in the resulting set, if e != foldCase(e), 'e' will be removed.
   *
   * Example: [aq\u00DF{Bc}{bC}{Fi}] => [aAqQ\u00DF\uFB01{ss}{bc}{fi}]
   *
   * (Here foldCase(x) refers to the operation UCharacter.foldCase(x, true), and a == b actually denotes a.equals(b), not pointer
   * comparison.)
   *
   * @param attribute
   *            bitmask for attributes to close over. Currently only the CASE bit is supported. Any undefined bits are ignored.
   * @return a reference to this set.
   * @stable ICU 3.8
   */
  public UnicodeSet closeOver(final int attribute) {
    checkFrozen();
    if ((attribute & (CASE | ADD_CASE_MAPPINGS)) != 0) {
      UCaseProps csp = UCaseProps.INSTANCE;
      UnicodeSet foldSet = new UnicodeSet(this);
      ULocale root = ULocale.ROOT;

      // start with input set to guarantee inclusion
      // CASE: remove strings because the strings will actually be reduced (folded);
      //       therefore, start with no strings and add only those needed
      if ((attribute & CASE) != 0) {
        foldSet.strings.clear();
      }

      int n = getRangeCount();
      int result;
      StringBuilder full = new StringBuilder();
      int locCache[] = new int[1];

      for (int i = 0; i < n; ++i) {
        int start = getRangeStart(i);
        int end = getRangeEnd(i);

        if ((attribute & CASE) != 0) {
          // full case closure
          for (int cp = start; cp <= end; ++cp) {
            csp.addCaseClosure(cp, foldSet);
          }
        } else {
          // add case mappings
          // (does not add long s for regular s, or Kelvin for k, for example)
          for (int cp = start; cp <= end; ++cp) {
            result = csp.toFullLower(cp, null, full, root, locCache);
            addCaseMapping(foldSet, result, full);

            result = csp.toFullTitle(cp, null, full, root, locCache);
            addCaseMapping(foldSet, result, full);

            result = csp.toFullUpper(cp, null, full, root, locCache);
            addCaseMapping(foldSet, result, full);

            result = csp.toFullFolding(cp, full, 0);
            addCaseMapping(foldSet, result, full);
          }
        }
      }
      if (!strings.isEmpty()) {
        if ((attribute & CASE) != 0) {
          for (String s : strings) {
            String str = UCharacter.foldCase(s, 0);
            if (!csp.addStringCaseClosure(str, foldSet)) {
              foldSet.add(str); // does not map to code points: add the folded string itself
            }
          }
        } else {
          BreakIterator bi = BreakIterator.getWordInstance(root);
          for (String str : strings) {
            foldSet.add(UCharacter.toLowerCase(root, str));
            foldSet.add(UCharacter.toTitleCase(root, str, bi));
            foldSet.add(UCharacter.toUpperCase(root, str));
            foldSet.add(UCharacter.foldCase(str, 0));
          }
        }
      }
      set(foldSet);
    }
    return this;
  }

  /**
   * Internal class for customizing UnicodeSet parsing of properties. TODO: extend to allow customizing of codepoint ranges
   *
   * @draft ICU3.8
   * @provisional This API might change or be removed in a future release.
   * @author medavis
   */
  abstract public static class XSymbolTable implements SymbolTable {
    /**
     * Default constructor
     *
     * @draft ICU3.8
     * @provisional This API might change or be removed in a future release.
     */
    public XSymbolTable() {
    }

    /**
     * Supplies default implementation for SymbolTable (no action).
     *
     * @draft ICU3.8
     * @provisional This API might change or be removed in a future release.
     */
    public UnicodeMatcher lookupMatcher(final int i) {
      return null;
    }

    /**
     * Apply a new property alias. Is called when parsing [:xxx=yyy:]. Results are to put into result.
     *
     * @param propertyName
     *            the xxx in [:xxx=yyy:]
     * @param propertyValue
     *            the yyy in [:xxx=yyy:]
     * @param result
     *            where the result is placed
     * @return true if handled
     * @draft ICU3.8
     * @provisional This API might change or be removed in a future release.
     */
    public boolean applyPropertyAlias(final String propertyName, final String propertyValue, final UnicodeSet result) {
      return false;
    }

    /**
     * Supplies default implementation for SymbolTable (no action).
     *
     * @draft ICU3.8
     * @provisional This API might change or be removed in a future release.
     */
    public char[] lookup(final String s) {
      return null;
    }

    /**
     * Supplies default implementation for SymbolTable (no action).
     *
     * @draft ICU3.8
     * @provisional This API might change or be removed in a future release.
     */
    public String parseReference(final String text, final ParsePosition pos, final int limit) {
      return null;
    }
  }

  /**
   * Is this frozen, according to the Freezable interface?
   *
   * @return value
   * @stable ICU 3.8
   */
  public boolean isFrozen() {
    return (bmpSet != null || stringSpan != null);
  }

  /**
   * Freeze this class, according to the Freezable interface.
   *
   * @return this
   * @stable ICU 4.4
   */
  public UnicodeSet freeze() {
    if (!isFrozen()) {
      // Do most of what compact() does before freezing because
      // compact() will not work when the set is frozen.
      // Small modification: Don't shrink if the savings would be tiny (<=GROW_EXTRA).

      // Delete buffer first to defragment memory less.
      buffer = null;
      if (list.length > (len + GROW_EXTRA)) {
        // Make the capacity equal to len or 1.
        // We don't want to realloc of 0 size.
        int capacity = (len == 0) ? 1 : len;
        int[] oldList = list;
        list = new int[capacity];
        for (int i = capacity; i-- > 0;) {
          list[i] = oldList[i];
        }
      }

      // Optimize contains() and span() and similar functions.
      if (!strings.isEmpty()) {
        stringSpan = new UnicodeSetStringSpan(this, new ArrayList<String>(strings), UnicodeSetStringSpan.ALL);
        if (!stringSpan.needsStringSpanUTF16()) {
          // All strings are irrelevant for span() etc. because
          // all of each string's code points are contained in this set.
          // Do not check needsStringSpanUTF8() because UTF-8 has at most as
          // many relevant strings as UTF-16.
          // (Thus needsStringSpanUTF8() implies needsStringSpanUTF16().)
          stringSpan = null;
        }
      }
      if (stringSpan == null) {
        // No span-relevant strings: Optimize for code point spans.
        bmpSet = new BMPSet(list, len);
      }
    }
    return this;
  }

  /**
   * Span a string using this UnicodeSet.
   *
   * @param s
   *            The string to be spanned
   * @param spanCondition
   *            The span condition
   * @return the length of the span
   * @stable ICU 4.4
   */
  public int span(final CharSequence s, final SpanCondition spanCondition) {
    return span(s, 0, spanCondition);
  }

  /**
   * Span a string using this UnicodeSet. If the start index is less than 0, span will start from 0. If the start index is greater than
   * the string length, span returns the string length.
   *
   * @param s
   *            The string to be spanned
   * @param start
   *            The start index that the span begins
   * @param spanCondition
   *            The span condition
   * @return the string index which ends the span (i.e. exclusive)
   * @stable ICU 4.4
   */
  public int span(final CharSequence s, int start, final SpanCondition spanCondition) {
    int end = s.length();
    if (start < 0) {
      start = 0;
    } else if (start >= end) {
      return end;
    }
    if (bmpSet != null) {
      return start + bmpSet.span(s, start, end, spanCondition);
    }
    int len = end - start;
    if (stringSpan != null) {
      return start + stringSpan.span(s, start, len, spanCondition);
    } else if (!strings.isEmpty()) {
      int which = spanCondition == SpanCondition.NOT_CONTAINED ? UnicodeSetStringSpan.FWD_UTF16_NOT_CONTAINED
          : UnicodeSetStringSpan.FWD_UTF16_CONTAINED;
      UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList<String>(strings), which);
      if (strSpan.needsStringSpanUTF16()) {
        return start + strSpan.span(s, start, len, spanCondition);
      }
    }

    // Pin to 0/1 values.
    boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED);

    int c;
    int next = start;
    do {
      c = Character.codePointAt(s, next);
      if (spanContained != contains(c)) {
        break;
      }
      next = Character.offsetByCodePoints(s, next, 1);
    } while (next < end);
    return next;
  }

  /**
   * Span a string backwards (from the end) using this UnicodeSet.
   *
   * @param s
   *            The string to be spanned
   * @param spanCondition
   *            The span condition
   * @return The string index which starts the span (i.e. inclusive).
   * @stable ICU 4.4
   */
  public int spanBack(final CharSequence s, final SpanCondition spanCondition) {
    return spanBack(s, s.length(), spanCondition);
  }

  /**
   * Span a string backwards (from the fromIndex) using this UnicodeSet. If the fromIndex is less than 0, spanBack will return 0. If
   * fromIndex is greater than the string length, spanBack will start from the string length.
   *
   * @param s
   *            The string to be spanned
   * @param fromIndex
   *            The index of the char (exclusive) that the string should be spanned backwards
   * @param spanCondition
   *            The span condition
   * @return The string index which starts the span (i.e. inclusive).
   * @stable ICU 4.4
   */
  public int spanBack(final CharSequence s, int fromIndex, final SpanCondition spanCondition) {
    if (fromIndex <= 0) {
      return 0;
    }
    if (fromIndex > s.length()) {
      fromIndex = s.length();
    }
    if (bmpSet != null) {
      return bmpSet.spanBack(s, fromIndex, spanCondition);
    }
    if (stringSpan != null) {
      return stringSpan.spanBack(s, fromIndex, spanCondition);
    } else if (!strings.isEmpty()) {
      int which = (spanCondition == SpanCondition.NOT_CONTAINED) ? UnicodeSetStringSpan.BACK_UTF16_NOT_CONTAINED
          : UnicodeSetStringSpan.BACK_UTF16_CONTAINED;
      UnicodeSetStringSpan strSpan = new UnicodeSetStringSpan(this, new ArrayList<String>(strings), which);
      if (strSpan.needsStringSpanUTF16()) {
        return strSpan.spanBack(s, fromIndex, spanCondition);
      }
    }

    // Pin to 0/1 values.
    boolean spanContained = (spanCondition != SpanCondition.NOT_CONTAINED);

    int c;
    int prev = fromIndex;
    do {
      c = Character.codePointBefore(s, prev);
      if (spanContained != contains(c)) {
        break;
      }
      prev = Character.offsetByCodePoints(s, prev, -1);
    } while (prev > 0);
    return prev;
  }

  /**
   * Clone a thawed version of this class, according to the Freezable interface.
   *
   * @return this
   * @stable ICU 4.4
   */
  public UnicodeSet cloneAsThawed() {
    UnicodeSet result = (UnicodeSet) clone();
    result.bmpSet = null;
    result.stringSpan = null;
    return result;
  }

  // internal function
  private void checkFrozen() {
    if (isFrozen()) {
      throw new UnsupportedOperationException("Attempt to modify frozen object");
    }
  }

  // ************************
  // Additional methods for integration with Generics and Collections
  // ************************

  /**
   * Returns a string iterator. Uses the same order of iteration as {@link UnicodeSetIterator}.
   *
   * @see java.util.Set#iterator()
   * @stable ICU 4.4
   */
  public Iterator<String> iterator() {
    return new UnicodeSetIterator2(this);
  }

  // Cover for string iteration.
  private static class UnicodeSetIterator2 implements Iterator<String> {
    // Invariants:
    // sourceList != null then sourceList[item] is a valid character
    // sourceList == null then delegates to stringIterator
    private int[] sourceList;
    private int len;
    private int item;
    private int current;
    private int limit;
    private TreeSet<String> sourceStrings;
    private Iterator<String> stringIterator;
    private char[] buffer;

    UnicodeSetIterator2(final UnicodeSet source) {
      // set according to invariants
      len = source.len - 1;
      if (item >= len) {
        stringIterator = source.strings.iterator();
        sourceList = null;
      } else {
        sourceStrings = source.strings;
        sourceList = source.list;
        current = sourceList[item++];
        limit = sourceList[item++];
      }
    }

    /* (non-Javadoc)
     * @see java.util.Iterator#hasNext()
     */
    public boolean hasNext() {
      return sourceList != null || stringIterator.hasNext();
    }

    /* (non-Javadoc)
     * @see java.util.Iterator#next()
     */
    public String next() {
      if (sourceList == null) {
        return stringIterator.next();
      }
      int codepoint = current++;
      // we have the codepoint we need, but we may need to adjust the state
      if (current >= limit) {
        if (item >= len) {
          stringIterator = sourceStrings.iterator();
          sourceList = null;
        } else {
          current = sourceList[item++];
          limit = sourceList[item++];
        }
      }
      // Now return. Single code point is easy
      if (codepoint <= 0xFFFF) {
        return String.valueOf((char) codepoint);
      }
      // But Java lacks a valueOfCodePoint, so we handle ourselves for speed
      // allocate a buffer the first time, to make conversion faster.
      if (buffer == null) {
        buffer = new char[2];
      }
      // compute ourselves, to save tests and calls
      int offset = codepoint - Character.MIN_SUPPLEMENTARY_CODE_POINT;
      buffer[0] = (char) ((offset >>> 10) + Character.MIN_HIGH_SURROGATE);
      buffer[1] = (char) ((offset & 0x3ff) + Character.MIN_LOW_SURROGATE);
      return String.valueOf(buffer);
    }

    /* (non-Javadoc)
     * @see java.util.Iterator#remove()
     */
    public void remove() {
      throw new UnsupportedOperationException();
    }
  }

  /**
   * @see #containsAll(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public boolean containsAll(final Collection<String> collection) {
    for (String o : collection) {
      if (!contains(o)) {
        return false;
      }
    }
    return true;
  }

  /**
   * @see #containsNone(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public boolean containsNone(final Collection<String> collection) {
    for (String o : collection) {
      if (contains(o)) {
        return false;
      }
    }
    return true;
  }

  /**
   * @see #containsAll(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public final boolean containsSome(final Collection<String> collection) {
    return !containsNone(collection);
  }

  /**
   * @see #addAll(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public UnicodeSet addAll(final String... collection) {
    checkFrozen();
    for (String str : collection) {
      add(str);
    }
    return this;
  }

  /**
   * @see #removeAll(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public UnicodeSet removeAll(final Collection<String> collection) {
    checkFrozen();
    for (String o : collection) {
      remove(o);
    }
    return this;
  }

  /**
   * @see #retainAll(com.ibm.icu.text.UnicodeSet)
   * @stable ICU 4.4
   */
  public UnicodeSet retainAll(final Collection<String> collection) {
    checkFrozen();
    // TODO optimize
    UnicodeSet toRetain = new UnicodeSet();
    toRetain.addAll(collection);
    retainAll(toRetain);
    return this;
  }

  /**
   * Comparison style enums used by {@link UnicodeSet#compareTo(UnicodeSet, ComparisonStyle)}.
   *
   * @stable ICU 4.4
   */
  public enum ComparisonStyle {
    /**
     * @stable ICU 4.4
     */
    SHORTER_FIRST,
    /**
     * @stable ICU 4.4
     */
    LEXICOGRAPHIC,
    /**
     * @stable ICU 4.4
     */
    LONGER_FIRST
  }

  /**
   * Compares UnicodeSets, where shorter come first, and otherwise lexigraphically (according to the comparison of the first characters
   * that differ).
   *
   * @see java.lang.Comparable#compareTo(java.lang.Object)
   * @stable ICU 4.4
   */
  public int compareTo(final UnicodeSet o) {
    return compareTo(o, ComparisonStyle.SHORTER_FIRST);
  }

  /**
   * Compares UnicodeSets, in three different ways.
   *
   * @see java.lang.Comparable#compareTo(java.lang.Object)
   * @stable ICU 4.4
   */
  public int compareTo(final UnicodeSet o, final ComparisonStyle style) {
    if (style != ComparisonStyle.LEXICOGRAPHIC) {
      int diff = size() - o.size();
      if (diff != 0) {
        return (diff < 0) == (style == ComparisonStyle.SHORTER_FIRST) ? -1 : 1;
      }
    }
    int result;
    for (int i = 0;; ++i) {
      if (0 != (result = list[i] - o.list[i])) {
        // if either list ran out, compare to the last string
        if (list[i] == HIGH) {
          if (strings.isEmpty())
            return 1;
          String item = strings.first();
          return compare(item, o.list[i]);
        }
        if (o.list[i] == HIGH) {
          if (o.strings.isEmpty())
            return -1;
          String item = o.strings.first();
          return -compare(item, list[i]);
        }
        // otherwise return the result if even index, or the reversal if not
        return (i & 1) == 0 ? result : -result;
      }
      if (list[i] == HIGH) {
        break;
      }
    }
    return compare(strings, o.strings);
  }

  /**
   * @stable ICU 4.4
   */
  public int compareTo(final Iterable<String> other) {
    return compare(this, other);
  }

  /**
   * Utility to compare a string to a code point. Same results as turning the code point into a string (with the [ugly] new
   * StringBuilder().appendCodePoint(codepoint).toString()) and comparing, but much faster (no object creation). Actually, there is one
   * difference; a null compares as less. Note that this (=String) order is UTF-16 order -- *not* code point order.
   *
   * @stable ICU 4.4
   */

  public static int compare(final String string, final int codePoint) {
    return CharSequences.compare(string, codePoint);
  }

  /**
   * Utility to compare a string to a code point. Same results as turning the code point into a string and comparing, but much faster (no
   * object creation). Actually, there is one difference; a null compares as less. Note that this (=String) order is UTF-16 order -- *not*
   * code point order.
   *
   * @stable ICU 4.4
   */
  public static int compare(final int codePoint, final String string) {
    return -CharSequences.compare(string, codePoint);
  }

  /**
   * Utility to compare two iterables. Warning: the ordering in iterables is important. For Collections that are ordered, like Lists, that
   * is expected. However, Sets in Java violate Leibniz's law when it comes to iteration. That means that sets can't be compared directly
   * with this method, unless they are TreeSets without (or with the same) comparator. Unfortunately, it is impossible to reliably detect
   * in Java whether subclass of Collection satisfies the right criteria, so it is left to the user to avoid those circumstances.
   *
   * @stable ICU 4.4
   */
  public static <T extends Comparable<T>> int compare(final Iterable<T> collection1, final Iterable<T> collection2) {
    return compare(collection1.iterator(), collection2.iterator());
  }

  /**
   * Utility to compare two iterators. Warning: the ordering in iterables is important. For Collections that are ordered, like Lists, that
   * is expected. However, Sets in Java violate Leibniz's law when it comes to iteration. That means that sets can't be compared directly
   * with this method, unless they are TreeSets without (or with the same) comparator. Unfortunately, it is impossible to reliably detect
   * in Java whether subclass of Collection satisfies the right criteria, so it is left to the user to avoid those circumstances.
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public static <T extends Comparable<T>> int compare(final Iterator<T> first, final Iterator<T> other) {
    while (true) {
      if (!first.hasNext()) {
        return other.hasNext() ? -1 : 0;
      } else if (!other.hasNext()) {
        return 1;
      }
      T item1 = first.next();
      T item2 = other.next();
      int result = item1.compareTo(item2);
      if (result != 0) {
        return result;
      }
    }
  }

  /**
   * Utility to compare two collections, optionally by size, and then lexicographically.
   *
   * @stable ICU 4.4
   */
  public static <T extends Comparable<T>> int compare(final Collection<T> collection1, final Collection<T> collection2,
      final ComparisonStyle style) {
    if (style != ComparisonStyle.LEXICOGRAPHIC) {
      int diff = collection1.size() - collection2.size();
      if (diff != 0) {
        return (diff < 0) == (style == ComparisonStyle.SHORTER_FIRST) ? -1 : 1;
      }
    }
    return compare(collection1, collection2);
  }

  /**
   * Utility for adding the contents of an iterable to a collection.
   *
   * @stable ICU 4.4
   */
  public static <T, U extends Collection<T>> U addAllTo(final Iterable<T> source, final U target) {
    for (T item : source) {
      target.add(item);
    }
    return target;
  }

  /**
   * Utility for adding the contents of an iterable to a collection.
   *
   * @stable ICU 4.4
   */
  public static <T> T[] addAllTo(final Iterable<T> source, final T[] target) {
    int i = 0;
    for (T item : source) {
      target[i++] = item;
    }
    return target;
  }

  /**
   * For iterating through the strings in the set. Example:
   *
   * <pre>
   * for (String key : myUnicodeSet.strings()) {
   *   doSomethingWith(key);
   * }
   * </pre>
   *
   * @stable ICU 4.4
   */
  public Iterable<String> strings() {
    return Collections.unmodifiableSortedSet(strings);
  }

  /**
   * Return the value of the first code point, if the string is exactly one code point. Otherwise return Integer.MAX_VALUE.
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public static int getSingleCodePoint(final CharSequence s) {
    return CharSequences.getSingleCodePoint(s);
  }

  /**
   * Simplify the ranges in a Unicode set by merging any ranges that are only separated by characters in the dontCare set. For example,
   * the ranges: \\u2E80-\\u2E99\\u2E9B-\\u2EF3\\u2F00-\\u2FD5\\u2FF0-\\u2FFB\\u3000-\\u303E change to \\u2E80-\\u303E if the dontCare set
   * includes unassigned characters (for a particular version of Unicode).
   *
   * @param dontCare
   *            Set with the don't-care characters for spanning
   * @return the input set, modified
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public UnicodeSet addBridges(final UnicodeSet dontCare) {
    UnicodeSet notInInput = new UnicodeSet(this).complement();
    for (UnicodeSetIterator it = new UnicodeSetIterator(notInInput); it.nextRange();) {
      if (it.codepoint != 0 && it.codepoint != UnicodeSetIterator.IS_STRING && it.codepointEnd != 0x10FFFF
          && dontCare.contains(it.codepoint, it.codepointEnd)) {
        add(it.codepoint, it.codepointEnd);
      }
    }
    return this;
  }

  /**
   * Find the first index at or after fromIndex where the UnicodeSet matches at that index. If findNot is true, then reverse the sense of
   * the match: find the first place where the UnicodeSet doesn't match. If there is no match, length is returned.
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public int findIn(final CharSequence value, int fromIndex, final boolean findNot) {
    //TODO add strings, optimize, using ICU4C algorithms
    int cp;
    for (; fromIndex < value.length(); fromIndex += UTF16.getCharCount(cp)) {
      cp = UTF16.charAt(value, fromIndex);
      if (contains(cp) != findNot) {
        break;
      }
    }
    return fromIndex;
  }

  /**
   * Find the last index before fromIndex where the UnicodeSet matches at that index. If findNot is true, then reverse the sense of the
   * match: find the last place where the UnicodeSet doesn't match. If there is no match, -1 is returned. BEFORE index is not in the
   * UnicodeSet.
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public int findLastIn(final CharSequence value, int fromIndex, final boolean findNot) {
    //TODO add strings, optimize, using ICU4C algorithms
    int cp;
    fromIndex -= 1;
    for (; fromIndex >= 0; fromIndex -= UTF16.getCharCount(cp)) {
      cp = UTF16.charAt(value, fromIndex);
      if (contains(cp) != findNot) {
        break;
      }
    }
    return fromIndex < 0 ? -1 : fromIndex;
  }

  /**
   * Strips code points from source. If matches is true, script all that match <i>this</i>. If matches is false, then strip all that
   * <i>don't</i> match.
   *
   * @param source
   *            The source of the CharSequence to strip from.
   * @param matches
   *            A boolean to either strip all that matches or don't match with the current UnicodeSet object.
   * @return The string after it has been stripped.
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public String stripFrom(final CharSequence source, final boolean matches) {
    StringBuilder result = new StringBuilder();
    for (int pos = 0; pos < source.length();) {
      int inside = findIn(source, pos, !matches);
      result.append(source.subSequence(pos, inside));
      pos = findIn(source, inside, matches); // get next start
    }
    return result.toString();
  }

  /**
   * Argument values for whether span() and similar functions continue while the current character is contained vs. not contained in the
   * set.
   * <p>
   * The functionality is straightforward for sets with only single code points, without strings (which is the common case):
   * <ul>
   * <li>CONTAINED and SIMPLE work the same.
   * <li>span() and spanBack() partition any string the same way when alternating between span(NOT_CONTAINED) and span(either "contained"
   * condition).
   * <li>Using a complemented (inverted) set and the opposite span conditions yields the same results.
   * </ul>
   * When a set contains multi-code point strings, then these statements may not be true, depending on the strings in the set (for
   * example, whether they overlap with each other) and the string that is processed. For a set with strings:
   * <ul>
   * <li>The complement of the set contains the opposite set of code points, but the same set of strings. Therefore, complementing both
   * the set and the span conditions may yield different results.
   * <li>When starting spans at different positions in a string (span(s, ...) vs. span(s+1, ...)) the ends of the spans may be different
   * because a set string may start before the later position.
   * <li>span(SIMPLE) may be shorter than span(CONTAINED) because it will not recursively try all possible paths. For example, with a set
   * which contains the three strings "xy", "xya" and "ax", span("xyax", CONTAINED) will return 4 but span("xyax", SIMPLE) will return 3.
   * span(SIMPLE) will never be longer than span(CONTAINED).
   * <li>With either "contained" condition, span() and spanBack() may partition a string in different ways. For example, with a set which
   * contains the two strings "ab" and "ba", and when processing the string "aba", span() will yield contained/not-contained boundaries of
   * { 0, 2, 3 } while spanBack() will yield boundaries of { 0, 1, 3 }.
   * </ul>
   * Note: If it is important to get the same boundaries whether iterating forward or backward through a string, then either only span()
   * should be used and the boundaries cached for backward operation, or an ICU BreakIterator could be used.
   * <p>
   * Note: Unpaired surrogates are treated like surrogate code points. Similarly, set strings match only on code point boundaries, never
   * in the middle of a surrogate pair.
   *
   * @stable ICU 4.4
   */
  public enum SpanCondition {
    /**
     * Continue a span() while there is no set element at the current position. Stops before the first set element (character or
     * string). (For code points only, this is like while contains(current)==FALSE).
     * <p>
     * When span() returns, the substring between where it started and the position it returned consists only of characters that are not
     * in the set, and none of its strings overlap with the span.
     *
     * @stable ICU 4.4
     */
    NOT_CONTAINED,

    /**
     * Continue a span() while there is a set element at the current position. (For characters only, this is like while
     * contains(current)==TRUE).
     * <p>
     * When span() returns, the substring between where it started and the position it returned consists only of set elements
     * (characters or strings) that are in the set.
     * <p>
     * If a set contains strings, then the span will be the longest substring matching any of the possible concatenations of set
     * elements (characters or strings). (There must be a single, non-overlapping concatenation of characters or strings.) This is
     * equivalent to a POSIX regular expression for (OR of each set element)*.
     *
     * @stable ICU 4.4
     */

    CONTAINED,
    /**
     * Continue a span() while there is a set element at the current position. (For characters only, this is like while
     * contains(current)==TRUE).
     * <p>
     * When span() returns, the substring between where it started and the position it returned consists only of set elements
     * (characters or strings) that are in the set.
     * <p>
     * If a set only contains single characters, then this is the same as CONTAINED.
     * <p>
     * If a set contains strings, then the span will be the longest substring with a match at each position with the longest single set
     * element (character or string).
     * <p>
     * Use this span condition together with other longest-match algorithms, such as ICU converters (ucnv_getUnicodeSet()).
     *
     * @stable ICU 4.4
     */

    SIMPLE,
    /**
     * One more than the last span condition.
     *
     * @stable ICU 4.4
     */
    CONDITION_COUNT
  }

  /**
   * Get the default symbol table. Null means ordinary processing. For internal use only.
   *
   * @return the symbol table
   * @internal
   */
  public static XSymbolTable getDefaultXSymbolTable() {
    return XSYMBOL_TABLE;
  }

  /**
   * Set the default symbol table. Null means ordinary processing. For internal use only. Will affect all subsequent parsing of
   * UnicodeSets.
   * <p>
   * WARNING: If this function is used with a UnicodeProperty, and the Unassigned characters (gc=Cn) are different than in ICU other than
   * in ICU, you MUST call {@code UnicodeProperty.ResetCacheProperties} afterwards. If you then call
   * {@code UnicodeSet.setDefaultXSymbolTable} with null to clear the value, you MUST also call
   * {@code UnicodeProperty.ResetCacheProperties}.
   *
   * @param xSymbolTable
   *            the new default symbol table.
   * @internal
   */
  public static void setDefaultXSymbolTable(final XSymbolTable xSymbolTable) {
    XSYMBOL_TABLE = xSymbolTable;
  }
}
//eof
TOP

Related Classes of com.ibm.icu.text.UnicodeSet$Filter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.