PageRenderTime 100ms CodeModel.GetById 84ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre5/gnu/regexp/RETokenRepeated.java

#
Java | 207 lines | 125 code | 24 blank | 58 comment | 48 complexity | 96c761d3132eda4406b3140f292eeaa3 MD5 | raw file
  1/*
  2 *  gnu/regexp/RETokenRepeated.java
  3 *  Copyright (C) 1998-2001 Wes Biggs
  4 *
  5 *  This library is free software; you can redistribute it and/or modify
  6 *  it under the terms of the GNU Lesser General Public License as published
  7 *  by the Free Software Foundation; either version 2.1 of the License, or
  8 *  (at your option) any later version.
  9 *
 10 *  This library is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 *  GNU Lesser General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU Lesser General Public License
 16 *  along with this program; if not, write to the Free Software
 17 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 18 */
 19
 20package gnu.regexp;
 21import java.util.Vector;
 22
 23final class RETokenRepeated extends REToken {
 24    private REToken token;
 25    private int min,max;
 26    private boolean stingy;
 27    
 28    RETokenRepeated(int subIndex, REToken token, int min, int max) {
 29	super(subIndex);
 30	this.token = token;
 31	this.min = min;
 32	this.max = max;
 33    }
 34
 35    /** Sets the minimal matching mode to true. */
 36    void makeStingy() {
 37	stingy = true;
 38    }
 39    
 40    /** Queries if this token has minimal matching enabled. */
 41    boolean isStingy() {
 42	return stingy;
 43    }
 44    
 45    /**
 46     * The minimum length of a repeated token is the minimum length
 47     * of the token multiplied by the minimum number of times it must
 48     * match.
 49     */
 50    int getMinimumLength() {
 51	return (min * token.getMinimumLength());
 52    }
 53
 54    // We do need to save every possible point, but the number of clone()
 55    // invocations here is really a killer for performance on non-stingy
 56    // repeat operators.  I'm open to suggestions...
 57
 58    // Hypothetical question: can you have a RE that matches 1 times,
 59    // 3 times, 5 times, but not 2 times or 4 times?  Does having
 60    // the subexpression back-reference operator allow that?
 61
 62    boolean match(CharIndexed input, REMatch mymatch) {
 63	// number of times we've matched so far
 64	int numRepeats = 0; 
 65	
 66	// Possible positions for the next repeat to match at
 67	REMatch newMatch = mymatch;
 68	REMatch last = null;
 69	REMatch current;
 70
 71	// Add the '0-repeats' index
 72	// positions.elementAt(z) == position [] in input after <<z>> matches
 73	Vector positions = new Vector();
 74	positions.addElement(newMatch);
 75	
 76	// Declare variables used in loop
 77	REMatch doables;
 78	REMatch doablesLast;
 79	REMatch recurrent;
 80
 81	do {
 82	    // Check for stingy match for each possibility.
 83	    if (stingy && (numRepeats >= min)) {
 84		REMatch result = matchRest(input, newMatch);
 85		if (result != null) {
 86		    mymatch.assignFrom(result);
 87		    return true;
 88		}
 89	    }
 90
 91	    doables = null;
 92	    doablesLast = null;
 93
 94	    // try next repeat at all possible positions
 95	    for (current = newMatch; current != null; current = current.next) {
 96		recurrent = (REMatch) current.clone();
 97		if (token.match(input, recurrent)) {
 98		    // add all items in current to doables array
 99		    if (doables == null) {
100			doables = recurrent;
101			doablesLast = recurrent;
102		    } else {
103			// Order these from longest to shortest
104			// Start by assuming longest (more repeats)
105			doablesLast.next = recurrent;
106		    }
107		    // Find new doablesLast
108		    while (doablesLast.next != null) {
109			doablesLast = doablesLast.next;
110		    }
111		}
112	    }
113	    // if none of the possibilities worked out, break out of do/while
114	    if (doables == null) break;
115	    
116	    // reassign where the next repeat can match
117	    newMatch = doables;
118	    
119	    // increment how many repeats we've successfully found
120	    ++numRepeats;
121	    
122	    positions.addElement(newMatch);
123	} while (numRepeats < max);
124	
125	// If there aren't enough repeats, then fail
126	if (numRepeats < min) return false;
127	
128	// We're greedy, but ease off until a true match is found 
129	int posIndex = positions.size();
130	
131	// At this point we've either got too many or just the right amount.
132	// See if this numRepeats works with the rest of the regexp.
133	REMatch allResults = null;
134	REMatch allResultsLast = null;
135
136	REMatch results = null;
137	while (--posIndex >= min) {
138	    newMatch = (REMatch) positions.elementAt(posIndex);
139	    results = matchRest(input, newMatch);
140	    if (results != null) {
141		if (allResults == null) {
142		    allResults = results;
143		    allResultsLast = results;
144		} else {
145		    // Order these from longest to shortest
146		    // Start by assuming longest (more repeats)
147		    allResultsLast.next = results;
148		}
149		// Find new doablesLast
150		while (allResultsLast.next != null) {
151		    allResultsLast = allResultsLast.next;
152		}
153	    }
154	    // else did not match rest of the tokens, try again on smaller sample
155	}
156	if (allResults != null) {
157	    mymatch.assignFrom(allResults); // does this get all?
158	    return true;
159	}
160	// If we fall out, no matches.
161	return false;
162    }
163
164    private REMatch matchRest(CharIndexed input, final REMatch newMatch) {
165	REMatch current, single;
166	REMatch doneIndex = null;
167	REMatch doneIndexLast = null;
168	// Test all possible matches for this number of repeats
169	for (current = newMatch; current != null; current = current.next) {
170	    // clone() separates a single match from the chain
171	    single = (REMatch) current.clone();
172	    if (next(input, single)) {
173		// chain results to doneIndex
174		if (doneIndex == null) {
175		    doneIndex = single;
176		    doneIndexLast = single;
177		} else {
178		    doneIndexLast.next = single;
179		}
180		// Find new doneIndexLast
181		while (doneIndexLast.next != null) {
182		    doneIndexLast = doneIndexLast.next;
183		}
184	    }
185	}
186	return doneIndex;
187    }
188
189    void dump(StringBuffer os) {
190	os.append("(?:");
191	token.dumpAll(os);
192	os.append(')');
193	if ((max == Integer.MAX_VALUE) && (min <= 1))
194	    os.append( (min == 0) ? '*' : '+' );
195	else if ((min == 0) && (max == 1))
196	    os.append('?');
197	else {
198	    os.append('{').append(min);
199	    if (max > min) {
200		os.append(',');
201		if (max != Integer.MAX_VALUE) os.append(max);
202	    }
203	    os.append('}');
204	}
205	if (stingy) os.append('?');
206    }
207}