0N/A/*
2362N/A * Copyright (c) 1995, 2001, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.misc;
0N/Aimport java.io.*;
0N/A
0N/A/**
0N/A * A class to represent a pool of regular expressions. A string
0N/A * can be matched against the whole pool all at once. It is much
0N/A * faster than doing individual regular expression matches one-by-one.
0N/A *
0N/A * @see java.misc.RegexpTarget
0N/A * @author James Gosling
0N/A */
0N/A
0N/Apublic class RegexpPool {
0N/A private RegexpNode prefixMachine = new RegexpNode();
0N/A private RegexpNode suffixMachine = new RegexpNode();
0N/A private static final int BIG = 0x7FFFFFFF;
0N/A private int lastDepth = BIG;
0N/A
0N/A public RegexpPool () {
0N/A }
0N/A
0N/A /**
0N/A * Add a regular expression to the pool of regular expressions.
0N/A * @param re The regular expression to add to the pool.
0N/A For now, only handles strings that either begin or end with
0N/A a '*'.
0N/A * @param ret The object to be returned when this regular expression is
0N/A matched. If ret is an instance of the RegexpTarget class, ret.found
0N/A is called with the string fragment that matched the '*' as its
0N/A parameter.
0N/A * @exception REException error
0N/A */
0N/A public void add(String re, Object ret) throws REException {
0N/A add(re, ret, false);
0N/A }
0N/A
0N/A /**
0N/A * Replace the target for the regular expression with a different
0N/A * target.
0N/A *
0N/A * @param re The regular expression to be replaced in the pool.
0N/A * For now, only handles strings that either begin or end with
0N/A * a '*'.
0N/A * @param ret The object to be returned when this regular expression is
0N/A * matched. If ret is an instance of the RegexpTarget class, ret.found
0N/A * is called with the string fragment that matched the '*' as its
0N/A * parameter.
0N/A */
0N/A public void replace(String re, Object ret) {
0N/A try {
0N/A add(re, ret, true);
0N/A } catch(Exception e) {
0N/A // should never occur if replace is true
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Delete the regular expression and its target.
0N/A * @param re The regular expression to be deleted from the pool.
0N/A * must begin or end with a '*'
0N/A * @return target - the old target.
0N/A */
0N/A public Object delete(String re) {
0N/A Object o = null;
0N/A RegexpNode p = prefixMachine;
0N/A RegexpNode best = p;
0N/A int len = re.length() - 1;
0N/A int i;
0N/A boolean prefix = true;
0N/A
0N/A if (!re.startsWith("*") ||
0N/A !re.endsWith("*"))
0N/A len++;
0N/A
0N/A if (len <= 0)
0N/A return null;
0N/A
0N/A /* March forward through the prefix machine */
0N/A for (i = 0; p != null; i++) {
0N/A if (p.result != null && p.depth < BIG
0N/A && (!p.exact || i == len)) {
0N/A best = p;
0N/A }
0N/A if (i >= len)
0N/A break;
0N/A p = p.find(re.charAt(i));
0N/A }
0N/A
0N/A /* march backward through the suffix machine */
0N/A p = suffixMachine;
0N/A for (i = len; --i >= 0 && p != null;) {
0N/A if (p.result != null && p.depth < BIG) {
0N/A prefix = false;
0N/A best = p;
0N/A }
0N/A p = p.find(re.charAt(i));
0N/A }
0N/A
0N/A // delete only if there is an exact match
0N/A if (prefix) {
0N/A if (re.equals(best.re)) {
0N/A o = best.result;
0N/A best.result = null;
0N/A
0N/A }
0N/A } else {
0N/A if (re.equals(best.re)) {
0N/A o = best.result;
0N/A best.result = null;
0N/A }
0N/A }
0N/A return o;
0N/A }
0N/A
0N/A /** Search for a match to a string & return the object associated
0N/A with it with the match. When multiple regular expressions
0N/A would match the string, the best match is returned first.
0N/A The next best match is returned the next time matchNext is
0N/A called.
0N/A @param s The string to match against the regular expressions
0N/A in the pool.
0N/A @return null on failure, otherwise the object associated with
0N/A the regular expression when it was added to the pool.
0N/A If the object is an instance of RegexpTarget, then
0N/A the return value is the result from calling
0N/A return.found(string_that_matched_wildcard).
0N/A */
0N/A public Object match(String s) {
0N/A return matchAfter(s, BIG);
0N/A }
0N/A
0N/A /** Identical to match except that it will only find matches to
0N/A regular expressions that were added to the pool <i>after</i>
0N/A the last regular expression that matched in the last call
0N/A to match() or matchNext() */
0N/A public Object matchNext(String s) {
0N/A return matchAfter(s, lastDepth);
0N/A }
0N/A
0N/A private void add(String re, Object ret, boolean replace) throws REException {
0N/A int len = re.length();
0N/A RegexpNode p;
0N/A if (re.charAt(0) == '*') {
0N/A p = suffixMachine;
0N/A while (len > 1)
0N/A p = p.add(re.charAt(--len));
0N/A } else {
0N/A boolean exact = false;
0N/A if (re.charAt(len - 1) == '*')
0N/A len--;
0N/A else
0N/A exact = true;
0N/A p = prefixMachine;
0N/A for (int i = 0; i < len; i++)
0N/A p = p.add(re.charAt(i));
0N/A p.exact = exact;
0N/A }
0N/A
0N/A if (p.result != null && !replace)
0N/A throw new REException(re + " is a duplicate");
0N/A
0N/A p.re = re;
0N/A p.result = ret;
0N/A }
0N/A
0N/A private Object matchAfter(String s, int lastMatchDepth) {
0N/A RegexpNode p = prefixMachine;
0N/A RegexpNode best = p;
0N/A int bst = 0;
0N/A int bend = 0;
0N/A int len = s.length();
0N/A int i;
0N/A if (len <= 0)
0N/A return null;
0N/A /* March forward through the prefix machine */
0N/A for (i = 0; p != null; i++) {
0N/A if (p.result != null && p.depth < lastMatchDepth
0N/A && (!p.exact || i == len)) {
0N/A lastDepth = p.depth;
0N/A best = p;
0N/A bst = i;
0N/A bend = len;
0N/A }
0N/A if (i >= len)
0N/A break;
0N/A p = p.find(s.charAt(i));
0N/A }
0N/A /* march backward through the suffix machine */
0N/A p = suffixMachine;
0N/A for (i = len; --i >= 0 && p != null;) {
0N/A if (p.result != null && p.depth < lastMatchDepth) {
0N/A lastDepth = p.depth;
0N/A best = p;
0N/A bst = 0;
0N/A bend = i+1;
0N/A }
0N/A p = p.find(s.charAt(i));
0N/A }
0N/A Object o = best.result;
0N/A if (o != null && o instanceof RegexpTarget)
0N/A o = ((RegexpTarget) o).found(s.substring(bst, bend));
0N/A return o;
0N/A }
0N/A
0N/A /** Resets the pool so that the next call to matchNext looks
0N/A at all regular expressions in the pool. match(s); is equivalent
0N/A to reset(); matchNext(s);
0N/A <p><b>Multithreading note:</b> reset/nextMatch leave state in the
0N/A regular expression pool. If multiple threads could be using this
0N/A pool this way, they should be syncronized to avoid race hazards.
0N/A match() was done in such a way that there are no such race
0N/A hazards: multiple threads can be matching in the same pool
0N/A simultaneously. */
0N/A public void reset() {
0N/A lastDepth = BIG;
0N/A }
0N/A
0N/A /** Print this pool to standard output */
0N/A public void print(PrintStream out) {
0N/A out.print("Regexp pool:\n");
0N/A if (suffixMachine.firstchild != null) {
0N/A out.print(" Suffix machine: ");
0N/A suffixMachine.firstchild.print(out);
0N/A out.print("\n");
0N/A }
0N/A if (prefixMachine.firstchild != null) {
0N/A out.print(" Prefix machine: ");
0N/A prefixMachine.firstchild.print(out);
0N/A out.print("\n");
0N/A }
0N/A }
0N/A
0N/A}
0N/A
0N/A/* A node in a regular expression finite state machine. */
0N/Aclass RegexpNode {
0N/A char c;
0N/A RegexpNode firstchild;
0N/A RegexpNode nextsibling;
0N/A int depth;
0N/A boolean exact;
0N/A Object result;
0N/A String re = null;
0N/A
0N/A RegexpNode () {
0N/A c = '#';
0N/A depth = 0;
0N/A }
0N/A RegexpNode (char C, int depth) {
0N/A c = C;
0N/A this.depth = depth;
0N/A }
0N/A RegexpNode add(char C) {
0N/A RegexpNode p = firstchild;
0N/A if (p == null)
0N/A p = new RegexpNode (C, depth+1);
0N/A else {
0N/A while (p != null)
0N/A if (p.c == C)
0N/A return p;
0N/A else
0N/A p = p.nextsibling;
0N/A p = new RegexpNode (C, depth+1);
0N/A p.nextsibling = firstchild;
0N/A }
0N/A firstchild = p;
0N/A return p;
0N/A }
0N/A RegexpNode find(char C) {
0N/A for (RegexpNode p = firstchild;
0N/A p != null;
0N/A p = p.nextsibling)
0N/A if (p.c == C)
0N/A return p;
0N/A return null;
0N/A }
0N/A void print(PrintStream out) {
0N/A if (nextsibling != null) {
0N/A RegexpNode p = this;
0N/A out.print("(");
0N/A while (p != null) {
0N/A out.write(p.c);
0N/A if (p.firstchild != null)
0N/A p.firstchild.print(out);
0N/A p = p.nextsibling;
0N/A out.write(p != null ? '|' : ')');
0N/A }
0N/A } else {
0N/A out.write(c);
0N/A if (firstchild != null)
0N/A firstchild.print(out);
0N/A }
0N/A }
0N/A}