merge from 2_4_Release branch
[jalview.git] / src / com / stevesoft / pat / RegOpt.java
index f70df39..0d65139 100755 (executable)
-//\r
-// This software is now distributed according to\r
-// the Lesser Gnu Public License.  Please see\r
-// http://www.gnu.org/copyleft/lesser.txt for\r
-// the details.\r
-//    -- Happy Computing!\r
-//\r
-package com.stevesoft.pat;\r
-\r
-import java.util.*;\r
-\r
-/** This class is just like oneChar, but doesn't worry about case. */\r
-class FastChar\r
-    extends oneChar\r
-{\r
-  FastChar(char c)\r
-  {\r
-    super(c);\r
-  }\r
-\r
-  public int matchInternal(int p, Pthings pt)\r
-  {\r
-    return (p < pt.src.length()\r
-            && pt.src.charAt(p) == c) ?\r
-        nextMatch(p + 1, pt) : -1;\r
-  }\r
-\r
-  Pattern clone1(Hashtable h)\r
-  {\r
-    return new FastChar(c);\r
-  }\r
-}\r
-\r
-/** This class is a hashtable keyed by Character\r
- * Objects.  It is used to match things of the\r
- * form (?:a..|b..|c..|..) match with greater efficiency --\r
- * by using a Hashtable that indexes into the group\r
- * of patterns.\r
- */\r
-class Branch\r
-    extends Pattern\r
-{\r
-  Hashtable h = new Hashtable();\r
-  // We need to keep track of the order\r
-  // of the keys -- if we don't then\r
-  // recompiling the output of toString\r
-  // may produce errors by re-ordering\r
-  // ()'s and changing the id number of\r
-  // the backreference associated with\r
-  // a subpattern.\r
-  Vector keys = new Vector();\r
-  Branch()\r
-  {}\r
-\r
-  Pattern clone1(Hashtable x)\r
-  {\r
-    Branch b = new Branch();\r
-    b.keys = (Vector) keys.clone();\r
-    x.put(this, b);\r
-    x.put(b, b);\r
-\r
-    for (int i = 0; i < keys.size(); i++)\r
-    {\r
-      Pattern p = (Pattern) h.get(keys.elementAt(i));\r
-      b.h.put(keys.elementAt(i), p.clone(x));\r
-    }\r
-    return b;\r
-  }\r
-\r
-  // this function eliminates Branches with 0 or 1 elements.\r
-  final Pattern reduce(boolean ignoreCase, boolean dontMinQ)\r
-  {\r
-    if (h.size() == 1)\r
-    {\r
-      Enumeration e = h.keys();\r
-      Character c = (Character) e.nextElement();\r
-      Pattern oc;\r
-      if (ignoreCase || dontMinQ)\r
-      {\r
-        oc = new oneChar(c.charValue());\r
-      }\r
-      else\r
-      {\r
-        oc = new FastChar(c.charValue());\r
-      }\r
-      oc.next = (Pattern) h.get(c);\r
-      oc.add(next);\r
-      return oc;\r
-    }\r
-    else if (h.size() == 0)\r
-    {\r
-      return null;\r
-    }\r
-    return this;\r
-  }\r
-\r
-  public patInt maxChars()\r
-  {\r
-    Enumeration e = h.keys();\r
-    patInt count = new patInt(0);\r
-    while (e.hasMoreElements())\r
-    {\r
-      Object key = e.nextElement();\r
-      Pattern pa = (Pattern) h.get(key);\r
-      patInt pi = pa.maxChars();\r
-      pi.inc();\r
-      count.maxeq(pi);\r
-    }\r
-    return count;\r
-  }\r
-\r
-  public patInt minChars()\r
-  {\r
-    Enumeration e = h.keys();\r
-    patInt count = new patInt(0);\r
-    while (e.hasMoreElements())\r
-    {\r
-      Object key = e.nextElement();\r
-      Pattern pa = (Pattern) h.get(key);\r
-      patInt pi = pa.minChars();\r
-      pi.inc();\r
-      count.mineq(pi);\r
-    }\r
-    return count;\r
-  }\r
-\r
-  // adds a oneChar object to this Branch\r
-  void addc(oneChar o, boolean ignoreCase, boolean dontMinQ)\r
-  {\r
-    Pattern n = o.next;\r
-    if (n == null)\r
-    {\r
-      n = new NullPattern();\r
-    }\r
-    else\r
-    {\r
-      n = RegOpt.opt(n, ignoreCase, dontMinQ);\r
-    }\r
-    n.setParent(this);\r
-    set(new Character(o.c), n, ignoreCase, dontMinQ);\r
-    if (ignoreCase)\r
-    {\r
-      if (o.c != o.altc)\r
-      {\r
-        set(new Character(o.altc), n, ignoreCase, dontMinQ);\r
-      }\r
-      if (o.c != o.altc2 && o.altc != o.altc2)\r
-      {\r
-        set(new Character(o.altc2), n, ignoreCase, dontMinQ);\r
-      }\r
-    }\r
-  }\r
-\r
-  void set(Character c, Pattern n, boolean igc, boolean dontMinQ)\r
-  {\r
-    Pattern p = (Pattern) h.get(c);\r
-    next = null;\r
-    // This letter is not yet used in the Branch object.\r
-    // We need to add it.\r
-    if (p == null)\r
-    {\r
-      if (n instanceof Or)\r
-      {\r
-        // A NullPattern is prepended to an Or\r
-        // to prevent confusing this object.\r
-        // For example: (boo|bug) => (b(?:oo|ug))\r
-        // during this process.  However, we\r
-        // want (b(?:oo|ell)|bug)\r
-        NullPattern np = new NullPattern();\r
-        np.add(n);\r
-        h.put(c, np);\r
-      }\r
-      else\r
-      {\r
-        h.put(c, n);\r
-      }\r
-      // Make sure we remember the order things were\r
-      // added into the Branch object so that we can\r
-      // properly convert it to a String.\r
-      keys.addElement(c);\r
-    }\r
-    else if (p instanceof Or)\r
-    {\r
-      ( (Or) p).addOr(n);\r
-    }\r
-    else if (p instanceof oneChar && n instanceof oneChar\r
-             && ( (oneChar) p).c != ( (oneChar) n).c)\r
-    {\r
-      Branch b = new Branch();\r
-      b.addc( (oneChar) p, igc, dontMinQ);\r
-      b.addc( (oneChar) n, igc, dontMinQ);\r
-      h.put(c, b);\r
-      b.setParent(this);\r
-    }\r
-    else if (p instanceof Branch && n instanceof oneChar)\r
-    {\r
-      ( (Branch) p).addc( (oneChar) n, igc, dontMinQ);\r
-      n.setParent(p);\r
-    }\r
-    else\r
-    {\r
-      // Create an Or object to receive the variety\r
-      // of branches in the pattern if the current letter\r
-      // is matched.  We do not attempt to make these\r
-      // sub-branches into a Branch object yet.\r
-      Or o = new Or();\r
-      o.setParent(this);\r
-\r
-      // Remove NullPattern from p -- it's no longer needed.\r
-      if (p instanceof NullPattern\r
-          && p.parent == null && p.next != null)\r
-      {\r
-        o.addOr(p.next);\r
-      }\r
-      else\r
-      {\r
-        o.addOr(p);\r
-      }\r
-      o.addOr(n);\r
-\r
-      Pattern optpat = RegOpt.opt(o, igc, dontMinQ);\r
-      h.put(c, optpat);\r
-      optpat.setParent(this);\r
-    }\r
-  }\r
-\r
-  public String toString()\r
-  {\r
-    StringBuffer sb = new StringBuffer();\r
-    // should protect this...\r
-    sb.append("(?:(?#branch)"); // Hashtable)");\r
-    for (int i = 0; i < keys.size(); i++)\r
-    {\r
-      Character c = (Character) keys.elementAt(i);\r
-      sb.append(c);\r
-      sb.append(h.get(c));\r
-      if (i + 1 < keys.size())\r
-      {\r
-        sb.append("|");\r
-      }\r
-    }\r
-    sb.append(")");\r
-    sb.append(nextString());\r
-    return sb.toString();\r
-  }\r
-\r
-  public int matchInternal(int pos, Pthings pt)\r
-  {\r
-    if (pos >= pt.src.length())\r
-    {\r
-      return -1;\r
-    }\r
-    Pattern n = (Pattern) h.get(new Character(pt.src.charAt(pos)));\r
-    if (n == null)\r
-    {\r
-      return -1;\r
-    }\r
-    if (pt.cbits != null && pt.cbits.get(pos))\r
-    {\r
-      return -1;\r
-    }\r
-    return n.matchInternal(pos + 1, pt);\r
-  }\r
-}\r
-\r
-/** This is just a place to put the optimizing function.\r
-    It is never instantiated as an Object.  It just sorts\r
-    through the RegOpt looking for things it can change\r
-    and make faster. */\r
-public class RegOpt\r
-{\r
-  static Pattern opt(Pattern p, boolean ignoreCase,\r
-                     boolean dontMinQ)\r
-  {\r
-    if (p == null)\r
-    {\r
-      return p;\r
-    }\r
-    if (p instanceof Bracket)\r
-    {\r
-      Bracket b = (Bracket) p;\r
-      // FastBracket is the only special\r
-      // optimized class to have its own\r
-      // source file.\r
-      p = FastBracket.process(b, ignoreCase);\r
-      //if(!(p instanceof FastBracket)\r
-      //p = Switch.process(b,ignoreCase);\r
-      p.next = b.next;\r
-      p.parent = b.parent;\r
-    }\r
-    else if (p instanceof oneChar && !ignoreCase\r
-             && !dontMinQ)\r
-    {\r
-      oneChar o = (oneChar) p;\r
-      p = new FastChar(o.c);\r
-      p.next = o.next;\r
-      p.parent = o.parent;\r
-    }\r
-    else if (p instanceof Or\r
-             && ( (Or) p).leftForm().equals("(?:")\r
-             && ( (Or) p).v.size() == 1)\r
-    { // Eliminate this Or Object.\r
-      Or o = (Or) p;\r
-      p = (Pattern) o.v.elementAt(0);\r
-      p.setParent(null);\r
-      p = RegOpt.opt(p, ignoreCase, dontMinQ);\r
-      p.add(o.next);\r
-    }\r
-    else if (p instanceof Or)\r
-    {\r
-      Or o = (Or) p;\r
-      o.pv = null;\r
-      Vector v = o.v;\r
-      o.v = new Vector();\r
-      Branch b = new Branch();\r
-      b.parent = o.parent;\r
-      for (int i = 0; i < v.size(); i++)\r
-      {\r
-        Pattern pp = (Pattern) v.elementAt(i);\r
-        // We want to have at least two oneChar's in\r
-        // the Or Object to consider making a Branch.\r
-        if (pp instanceof oneChar && (b.h.size() >= 1 ||\r
-                                      (i + 1 < v.size() &&\r
-                                       v.elementAt(i + 1) instanceof oneChar)))\r
-        {\r
-          b.addc( (oneChar) pp, ignoreCase, dontMinQ);\r
-        }\r
-        else\r
-        {\r
-          if (b.keys.size() > 0)\r
-          {\r
-            Pattern p2 = (Pattern) b.reduce(ignoreCase, dontMinQ);\r
-            if (p2 != null)\r
-            {\r
-              o.addOr(p2);\r
-              b = new Branch();\r
-              b.parent = o.parent;\r
-            }\r
-          }\r
-          o.addOr(opt(pp, ignoreCase, dontMinQ));\r
-        }\r
-      }\r
-      if (b.keys.size() > 0)\r
-      {\r
-        Pattern p2 = (Pattern) b.reduce(ignoreCase, dontMinQ);\r
-        if (p2 != null)\r
-        {\r
-          o.addOr(p2);\r
-        }\r
-      }\r
-      if (o.v.size() == 1\r
-          && o.leftForm().equals("(?:"))\r
-      { // Eliminate Or Object\r
-        p = (Pattern) o.v.elementAt(0);\r
-        p.setParent(null);\r
-        p = RegOpt.opt(p, ignoreCase, dontMinQ);\r
-        p.add(o.next);\r
-      }\r
-    }\r
-    else if (p instanceof FastMulti)\r
-    {\r
-      PatternSub ps = (PatternSub) p;\r
-      ps.sub = RegOpt.opt(ps.sub, ignoreCase, dontMinQ);\r
-    }\r
-    else if (p instanceof Multi && safe4fm( ( (PatternSub) p).sub))\r
-    {\r
-      Multi m = (Multi) p;\r
-      FastMulti fm = null;\r
-      try\r
-      {\r
-        fm = new FastMulti(m.a, m.b,\r
-                           opt(m.sub, ignoreCase, dontMinQ));\r
-      }\r
-      catch (RegSyntax rs)\r
-      {}\r
-      fm.parent = m.parent;\r
-      fm.matchFewest = m.matchFewest;\r
-      fm.next = m.next;\r
-      p = fm;\r
-    }\r
-    if (p.next != null)\r
-    {\r
-      p.next = opt(p.next, ignoreCase, dontMinQ);\r
-    }\r
-    return p;\r
-  }\r
-\r
-  final static boolean safe4fm(Pattern x)\r
-  {\r
-    while (x != null)\r
-    {\r
-      if (x instanceof Bracket)\r
-      {\r
-        ;\r
-      }\r
-      else if (x instanceof Range)\r
-      {\r
-        ;\r
-      }\r
-      else if (x instanceof oneChar)\r
-      {\r
-        ;\r
-      }\r
-      else if (x instanceof Any)\r
-      {\r
-        ;\r
-      }\r
-      else if (x instanceof Custom\r
-               && ( (Custom) x).v instanceof UniValidator)\r
-      {\r
-        ;\r
-      }\r
-      else if (x instanceof Or)\r
-      {\r
-        Or o = (Or) x;\r
-        if (!o.leftForm().equals("(?:"))\r
-        {\r
-          return false;\r
-        }\r
-        patInt lo = o.countMinChars();\r
-        patInt hi = o.countMaxChars();\r
-        if (!lo.equals(hi))\r
-        {\r
-          return false;\r
-        }\r
-        for (int i = 0; i < o.v.size(); i++)\r
-        {\r
-          if (!safe4fm( (Pattern) o.v.elementAt(i)))\r
-          {\r
-            return false;\r
-          }\r
-        }\r
-      }\r
-      else\r
-      {\r
-        return false;\r
-      }\r
-      x = x.next;\r
-    }\r
-    return true;\r
-  }\r
-  /*\r
-       public static void setParents(Regex r) {\r
-    setParents(r.thePattern,null);\r
-       }\r
-       static void setParents(Pattern p,Pattern x) {\r
-    if(p instanceof PatternSub && !(p instanceof FastMulti)\r
-    && !(p instanceof DotMulti))\r
-      RegOpt.setParents( ((PatternSub)p).sub, p);\r
-    else if(p instanceof Or && !(p instanceof Bracket)) {\r
-      Or o = (Or)p;\r
-      for(int i=0;i<o.v.size();i++)\r
-        RegOpt.setParents((Pattern)o.v.elementAt(i),o);\r
-    } else if(p instanceof Branch) {\r
-      Branch b = (Branch)p;\r
-      Enumeration e = b.h.keys();\r
-      while(e.hasMoreElements()) {\r
-        Object o = e.nextElement();\r
-        RegOpt.setParents( (Pattern)b.h.get(o), b);\r
-      }\r
-    }\r
-    if(p.next == null)\r
-      p.parent = x;\r
-    else {\r
-      p.parent = null;\r
-      RegOpt.setParents(p.next,x);\r
-    }\r
-       }*/\r
-}\r
+//
+// This software is now distributed according to
+// the Lesser Gnu Public License.  Please see
+// http://www.gnu.org/copyleft/lesser.txt for
+// the details.
+//    -- Happy Computing!
+//
+package com.stevesoft.pat;
+
+import java.util.*;
+
+/** This class is just like oneChar, but doesn't worry about case. */
+class FastChar extends oneChar
+{
+  FastChar(char c)
+  {
+    super(c);
+  }
+
+  public int matchInternal(int p, Pthings pt)
+  {
+    return (p < pt.src.length() && pt.src.charAt(p) == c) ? nextMatch(
+            p + 1, pt) : -1;
+  }
+
+  Pattern clone1(Hashtable h)
+  {
+    return new FastChar(c);
+  }
+}
+
+/**
+ * This class is a hashtable keyed by Character Objects. It is used to match
+ * things of the form (?:a..|b..|c..|..) match with greater efficiency -- by
+ * using a Hashtable that indexes into the group of patterns.
+ */
+class Branch extends Pattern
+{
+  Hashtable h = new Hashtable();
+
+  // We need to keep track of the order
+  // of the keys -- if we don't then
+  // recompiling the output of toString
+  // may produce errors by re-ordering
+  // ()'s and changing the id number of
+  // the backreference associated with
+  // a subpattern.
+  Vector keys = new Vector();
+
+  Branch()
+  {
+  }
+
+  Pattern clone1(Hashtable x)
+  {
+    Branch b = new Branch();
+    b.keys = (Vector) keys.clone();
+    x.put(this, b);
+    x.put(b, b);
+
+    for (int i = 0; i < keys.size(); i++)
+    {
+      Pattern p = (Pattern) h.get(keys.elementAt(i));
+      b.h.put(keys.elementAt(i), p.clone(x));
+    }
+    return b;
+  }
+
+  // this function eliminates Branches with 0 or 1 elements.
+  final Pattern reduce(boolean ignoreCase, boolean dontMinQ)
+  {
+    if (h.size() == 1)
+    {
+      Enumeration e = h.keys();
+      Character c = (Character) e.nextElement();
+      Pattern oc;
+      if (ignoreCase || dontMinQ)
+      {
+        oc = new oneChar(c.charValue());
+      }
+      else
+      {
+        oc = new FastChar(c.charValue());
+      }
+      oc.next = (Pattern) h.get(c);
+      oc.add(next);
+      return oc;
+    }
+    else if (h.size() == 0)
+    {
+      return null;
+    }
+    return this;
+  }
+
+  public patInt maxChars()
+  {
+    Enumeration e = h.keys();
+    patInt count = new patInt(0);
+    while (e.hasMoreElements())
+    {
+      Object key = e.nextElement();
+      Pattern pa = (Pattern) h.get(key);
+      patInt pi = pa.maxChars();
+      pi.inc();
+      count.maxeq(pi);
+    }
+    return count;
+  }
+
+  public patInt minChars()
+  {
+    Enumeration e = h.keys();
+    patInt count = new patInt(0);
+    while (e.hasMoreElements())
+    {
+      Object key = e.nextElement();
+      Pattern pa = (Pattern) h.get(key);
+      patInt pi = pa.minChars();
+      pi.inc();
+      count.mineq(pi);
+    }
+    return count;
+  }
+
+  // adds a oneChar object to this Branch
+  void addc(oneChar o, boolean ignoreCase, boolean dontMinQ)
+  {
+    Pattern n = o.next;
+    if (n == null)
+    {
+      n = new NullPattern();
+    }
+    else
+    {
+      n = RegOpt.opt(n, ignoreCase, dontMinQ);
+    }
+    n.setParent(this);
+    set(new Character(o.c), n, ignoreCase, dontMinQ);
+    if (ignoreCase)
+    {
+      if (o.c != o.altc)
+      {
+        set(new Character(o.altc), n, ignoreCase, dontMinQ);
+      }
+      if (o.c != o.altc2 && o.altc != o.altc2)
+      {
+        set(new Character(o.altc2), n, ignoreCase, dontMinQ);
+      }
+    }
+  }
+
+  void set(Character c, Pattern n, boolean igc, boolean dontMinQ)
+  {
+    Pattern p = (Pattern) h.get(c);
+    next = null;
+    // This letter is not yet used in the Branch object.
+    // We need to add it.
+    if (p == null)
+    {
+      if (n instanceof Or)
+      {
+        // A NullPattern is prepended to an Or
+        // to prevent confusing this object.
+        // For example: (boo|bug) => (b(?:oo|ug))
+        // during this process. However, we
+        // want (b(?:oo|ell)|bug)
+        NullPattern np = new NullPattern();
+        np.add(n);
+        h.put(c, np);
+      }
+      else
+      {
+        h.put(c, n);
+      }
+      // Make sure we remember the order things were
+      // added into the Branch object so that we can
+      // properly convert it to a String.
+      keys.addElement(c);
+    }
+    else if (p instanceof Or)
+    {
+      ((Or) p).addOr(n);
+    }
+    else if (p instanceof oneChar && n instanceof oneChar
+            && ((oneChar) p).c != ((oneChar) n).c)
+    {
+      Branch b = new Branch();
+      b.addc((oneChar) p, igc, dontMinQ);
+      b.addc((oneChar) n, igc, dontMinQ);
+      h.put(c, b);
+      b.setParent(this);
+    }
+    else if (p instanceof Branch && n instanceof oneChar)
+    {
+      ((Branch) p).addc((oneChar) n, igc, dontMinQ);
+      n.setParent(p);
+    }
+    else
+    {
+      // Create an Or object to receive the variety
+      // of branches in the pattern if the current letter
+      // is matched. We do not attempt to make these
+      // sub-branches into a Branch object yet.
+      Or o = new Or();
+      o.setParent(this);
+
+      // Remove NullPattern from p -- it's no longer needed.
+      if (p instanceof NullPattern && p.parent == null && p.next != null)
+      {
+        o.addOr(p.next);
+      }
+      else
+      {
+        o.addOr(p);
+      }
+      o.addOr(n);
+
+      Pattern optpat = RegOpt.opt(o, igc, dontMinQ);
+      h.put(c, optpat);
+      optpat.setParent(this);
+    }
+  }
+
+  public String toString()
+  {
+    StringBuffer sb = new StringBuffer();
+    // should protect this...
+    sb.append("(?:(?#branch)"); // Hashtable)");
+    for (int i = 0; i < keys.size(); i++)
+    {
+      Character c = (Character) keys.elementAt(i);
+      sb.append(c);
+      sb.append(h.get(c));
+      if (i + 1 < keys.size())
+      {
+        sb.append("|");
+      }
+    }
+    sb.append(")");
+    sb.append(nextString());
+    return sb.toString();
+  }
+
+  public int matchInternal(int pos, Pthings pt)
+  {
+    if (pos >= pt.src.length())
+    {
+      return -1;
+    }
+    Pattern n = (Pattern) h.get(new Character(pt.src.charAt(pos)));
+    if (n == null)
+    {
+      return -1;
+    }
+    if (pt.cbits != null && pt.cbits.get(pos))
+    {
+      return -1;
+    }
+    return n.matchInternal(pos + 1, pt);
+  }
+}
+
+/**
+ * This is just a place to put the optimizing function. It is never instantiated
+ * as an Object. It just sorts through the RegOpt looking for things it can
+ * change and make faster.
+ */
+public class RegOpt
+{
+  static Pattern opt(Pattern p, boolean ignoreCase, boolean dontMinQ)
+  {
+    if (p == null)
+    {
+      return p;
+    }
+    if (p instanceof Bracket)
+    {
+      Bracket b = (Bracket) p;
+      // FastBracket is the only special
+      // optimized class to have its own
+      // source file.
+      p = FastBracket.process(b, ignoreCase);
+      // if(!(p instanceof FastBracket)
+      // p = Switch.process(b,ignoreCase);
+      p.next = b.next;
+      p.parent = b.parent;
+    }
+    else if (p instanceof oneChar && !ignoreCase && !dontMinQ)
+    {
+      oneChar o = (oneChar) p;
+      p = new FastChar(o.c);
+      p.next = o.next;
+      p.parent = o.parent;
+    }
+    else if (p instanceof Or && ((Or) p).leftForm().equals("(?:")
+            && ((Or) p).v.size() == 1)
+    { // Eliminate this Or Object.
+      Or o = (Or) p;
+      p = (Pattern) o.v.elementAt(0);
+      p.setParent(null);
+      p = RegOpt.opt(p, ignoreCase, dontMinQ);
+      p.add(o.next);
+    }
+    else if (p instanceof Or)
+    {
+      Or o = (Or) p;
+      o.pv = null;
+      Vector v = o.v;
+      o.v = new Vector();
+      Branch b = new Branch();
+      b.parent = o.parent;
+      for (int i = 0; i < v.size(); i++)
+      {
+        Pattern pp = (Pattern) v.elementAt(i);
+        // We want to have at least two oneChar's in
+        // the Or Object to consider making a Branch.
+        if (pp instanceof oneChar
+                && (b.h.size() >= 1 || (i + 1 < v.size() && v
+                        .elementAt(i + 1) instanceof oneChar)))
+        {
+          b.addc((oneChar) pp, ignoreCase, dontMinQ);
+        }
+        else
+        {
+          if (b.keys.size() > 0)
+          {
+            Pattern p2 = (Pattern) b.reduce(ignoreCase, dontMinQ);
+            if (p2 != null)
+            {
+              o.addOr(p2);
+              b = new Branch();
+              b.parent = o.parent;
+            }
+          }
+          o.addOr(opt(pp, ignoreCase, dontMinQ));
+        }
+      }
+      if (b.keys.size() > 0)
+      {
+        Pattern p2 = (Pattern) b.reduce(ignoreCase, dontMinQ);
+        if (p2 != null)
+        {
+          o.addOr(p2);
+        }
+      }
+      if (o.v.size() == 1 && o.leftForm().equals("(?:"))
+      { // Eliminate Or Object
+        p = (Pattern) o.v.elementAt(0);
+        p.setParent(null);
+        p = RegOpt.opt(p, ignoreCase, dontMinQ);
+        p.add(o.next);
+      }
+    }
+    else if (p instanceof FastMulti)
+    {
+      PatternSub ps = (PatternSub) p;
+      ps.sub = RegOpt.opt(ps.sub, ignoreCase, dontMinQ);
+    }
+    else if (p instanceof Multi && safe4fm(((PatternSub) p).sub))
+    {
+      Multi m = (Multi) p;
+      FastMulti fm = null;
+      try
+      {
+        fm = new FastMulti(m.a, m.b, opt(m.sub, ignoreCase, dontMinQ));
+      } catch (RegSyntax rs)
+      {
+      }
+      fm.parent = m.parent;
+      fm.matchFewest = m.matchFewest;
+      fm.next = m.next;
+      p = fm;
+    }
+    if (p.next != null)
+    {
+      p.next = opt(p.next, ignoreCase, dontMinQ);
+    }
+    return p;
+  }
+
+  final static boolean safe4fm(Pattern x)
+  {
+    while (x != null)
+    {
+      if (x instanceof Bracket)
+      {
+        ;
+      }
+      else if (x instanceof Range)
+      {
+        ;
+      }
+      else if (x instanceof oneChar)
+      {
+        ;
+      }
+      else if (x instanceof Any)
+      {
+        ;
+      }
+      else if (x instanceof Custom
+              && ((Custom) x).v instanceof UniValidator)
+      {
+        ;
+      }
+      else if (x instanceof Or)
+      {
+        Or o = (Or) x;
+        if (!o.leftForm().equals("(?:"))
+        {
+          return false;
+        }
+        patInt lo = o.countMinChars();
+        patInt hi = o.countMaxChars();
+        if (!lo.equals(hi))
+        {
+          return false;
+        }
+        for (int i = 0; i < o.v.size(); i++)
+        {
+          if (!safe4fm((Pattern) o.v.elementAt(i)))
+          {
+            return false;
+          }
+        }
+      }
+      else
+      {
+        return false;
+      }
+      x = x.next;
+    }
+    return true;
+  }
+  /*
+   * public static void setParents(Regex r) { setParents(r.thePattern,null); }
+   * static void setParents(Pattern p,Pattern x) { if(p instanceof PatternSub &&
+   * !(p instanceof FastMulti) && !(p instanceof DotMulti)) RegOpt.setParents(
+   * ((PatternSub)p).sub, p); else if(p instanceof Or && !(p instanceof
+   * Bracket)) { Or o = (Or)p; for(int i=0;i<o.v.size();i++)
+   * RegOpt.setParents((Pattern)o.v.elementAt(i),o); } else if(p instanceof
+   * Branch) { Branch b = (Branch)p; Enumeration e = b.h.keys();
+   * while(e.hasMoreElements()) { Object o = e.nextElement(); RegOpt.setParents(
+   * (Pattern)b.h.get(o), b); } } if(p.next == null) p.parent = x; else {
+   * p.parent = null; RegOpt.setParents(p.next,x); } }
+   */
+}