1 /* gnu/regexp/RETokenOneOf.java
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu
.java
.util
.regex
;
40 import gnu
.java
.lang
.CPStringBuilder
;
42 import java
.util
.ArrayDeque
;
43 import java
.util
.ArrayList
;
44 import java
.util
.Deque
;
45 import java
.util
.List
;
47 final class RETokenOneOf
extends REToken
49 private final List
< REToken
> options
;
50 private boolean negative
;
51 // True if this RETokenOneOf is supposed to match only one character,
52 // which is typically the case of a character class expression.
53 private boolean matchesOneChar
;
55 private final List
< Object
> addition
;
56 // This ArrayList addition is used to store nested character classes.
57 // For example, if the original expression is
58 // [2-7a-c[f-k][m-z]&&[^p-v][st]]
59 // the basic part /2-7a-c/ is stored in the ArrayList options, and
60 // the additional part /[f-k][m-z]&&[^p-v][st]/ is stored in the
61 // ArrayList addition in the following order (Reverse Polish Notation):
62 // -- The matching result of the basic part is assumed here.
74 // As it is clear from the explanation above, the ArrayList addition is
75 // effective only when this REToken originates from a character class
78 // This constructor is used for convenience when we know the set beforehand,
79 // e.g. \d --> new RETokenOneOf("0123456789",false, ..)
80 // \D --> new RETokenOneOf("0123456789",true, ..)
82 RETokenOneOf (int subIndex
, String optionsStr
, boolean negative
,
86 options
= new ArrayList
< REToken
> ();
87 this.negative
= negative
;
88 for (int i
= 0; i
< optionsStr
.length (); i
++)
89 options
.add (new RETokenChar (subIndex
, optionsStr
.charAt (i
), insens
));
90 matchesOneChar
= true;
94 RETokenOneOf (int subIndex
, List
< REToken
> options
, boolean negative
)
96 this (subIndex
, options
, null, negative
);
99 RETokenOneOf (int subIndex
, List
< REToken
> options
,
100 List
< Object
> addition
, boolean negative
)
103 this.options
= options
;
104 this.addition
= addition
;
105 this.negative
= negative
;
106 matchesOneChar
= (negative
|| addition
!= null);
109 int getMinimumLength ()
113 int min
= Integer
.MAX_VALUE
;
115 for (REToken t
:options
)
117 if ((x
= t
.getMinimumLength ()) < min
)
123 int getMaximumLength ()
129 for (REToken t
:options
)
131 if ((x
= t
.getMaximumLength ()) > max
)
137 boolean match (CharIndexed input
, REMatch mymatch
)
139 setHitEnd (input
, mymatch
);
141 return matchOneChar (input
, mymatch
);
143 return matchOneRE (input
, mymatch
);
146 boolean matchOneChar (CharIndexed input
, REMatch mymatch
)
150 if (addition
== null)
157 tryMatch
= (REMatch
) mymatch
.clone ();
160 boolean b
= negative ?
161 matchN (input
, tryMatch
, tryOnly
) : matchP (input
, tryMatch
, tryOnly
);
162 if (addition
== null)
165 final Deque
< Boolean
> stack
= new ArrayDeque
< Boolean
> ();
166 stack
.push (new Boolean (b
));
167 for (Object obj
:addition
)
169 if (obj
instanceof REToken
)
171 b
= ((REToken
) obj
).match (input
, (REMatch
) mymatch
.clone ());
172 stack
.push (new Boolean (b
));
174 else if (obj
instanceof Boolean
)
176 stack
.push ((Boolean
) obj
);
178 else if (obj
.equals ("|"))
181 b
= stack
.pop () || b
;
182 stack
.push (new Boolean (b
));
184 else if (obj
.equals ("&"))
187 b
= stack
.pop () && b
;
188 stack
.push (new Boolean (b
));
192 throw new RuntimeException ("Invalid object found");
198 return next (input
, mymatch
);
203 private boolean matchN (CharIndexed input
, REMatch mymatch
, boolean tryOnly
)
205 if (input
.charAt (mymatch
.index
) == CharIndexed
.OUT_OF_BOUNDS
)
208 for (REToken tk
:options
)
210 REMatch tryMatch
= (REMatch
) mymatch
.clone ();
211 if (tk
.match (input
, tryMatch
))
212 { // match was successful
220 return next (input
, mymatch
);
223 private boolean matchP (CharIndexed input
, REMatch mymatch
, boolean tryOnly
)
225 for (REToken tk
:options
)
227 REMatch tryMatch
= (REMatch
) mymatch
.clone ();
228 if (tk
.match (input
, tryMatch
))
229 { // match was successful
232 if (next (input
, tryMatch
))
234 mymatch
.assignFrom (tryMatch
);
242 private boolean matchOneRE (CharIndexed input
, REMatch mymatch
)
244 REMatch newMatch
= findMatch (input
, mymatch
);
245 if (newMatch
!= null)
247 mymatch
.assignFrom (newMatch
);
253 REMatch
findMatch (CharIndexed input
, REMatch mymatch
)
256 return super.findMatch (input
, mymatch
);
257 return findMatch (input
, mymatch
, 0);
260 REMatch
backtrack (CharIndexed input
, REMatch mymatch
, Object param
)
262 return findMatch (input
, mymatch
, ((Integer
) param
).intValue ());
265 private REMatch
findMatch (CharIndexed input
, REMatch mymatch
,
268 for (int i
= optionIndex
; i
< options
.size (); i
++)
270 REToken tk
= options
.get (i
);
271 tk
= (REToken
) tk
.clone ();
272 tk
.chain (getNext ());
273 REMatch tryMatch
= (REMatch
) mymatch
.clone ();
274 if (tryMatch
.backtrackStack
== null)
276 tryMatch
.backtrackStack
= new BacktrackStack ();
278 boolean stackPushed
= false;
279 if (i
+ 1 < options
.size ())
281 tryMatch
.backtrackStack
.push (new BacktrackStack
.
282 Backtrack (this, input
, mymatch
,
286 if (tk
.match (input
, tryMatch
))
291 tryMatch
.backtrackStack
.pop ();
296 boolean returnsFixedLengthMatches ()
298 return matchesOneChar
;
301 int findFixedLengthMatches (CharIndexed input
, REMatch mymatch
, int max
)
304 return super.findFixedLengthMatches (input
, mymatch
, max
);
306 REMatch m
= (REMatch
) mymatch
.clone ();
307 REToken tk
= (REToken
) this.clone ();
311 if (numRepeats
>= max
)
313 m
= tk
.findMatch (input
, m
);
321 void dump (CPStringBuilder os
)
323 os
.append (negative ?
"[^" : "(?:");
324 for (int i
= 0; i
< options
.size (); i
++)
326 if (!negative
&& (i
> 0))
328 options
.get (i
).dumpAll (os
);
330 os
.append (negative ?
']' : ')');