//
// 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.ewe_pat;
/** This class is used internally to search ahead for some
optimized Regex objects. It searches within a String
for occrences of a given String -- like a more flexible
version of String.indexOf.
@see com.stevesoft.ewe_pat.Skip2
@see com.stevesoft.ewe_pat.SkipBMH
*/
public class Skip {
static int mkmask(int c) {
char x = (char)c;
return ~( CaseMgr.toUpperCase(x) |
CaseMgr.toLowerCase(x) |
CaseMgr.toTitleCase(x));
}
static { int x = Regex.BackRefOffset; }
String src;
int c,mask;
int offset;
boolean ign,m1;
/** Examine a Regex to determine what String it will
attempt to skip to when searching for patterns.
Return -1 if we aren't doing this. */
public static String string(Regex r) {
return r.skipper == null ? null : r.skipper.src;
}
/** Determine the offset of the String within the pattern
that we are skipping to. Return -1 if we aren't doing
this. */
public static int offset(Regex r) {
return r.skipper == null ? -1 : r.skipper.offset;
}
/** Initialize, give it a String to search for, tell it
whether or not to ignoreCase, and what the offset is
of the String within the String to be searched. */
public Skip(String s,boolean ign,int o) {
src = s;
c = s.charAt(0);
if(ign) {
mask = mkmask(c);
} else mask = 0;
offset = o;
this.ign = ign;
m1 = (s.length()==1);
}
/** The same as find(s,0,s.length()) */
public final int find(StringLike s) {
return find(s,0,s.length());
}
static final int min(int a,int b) { return a<b ? a : b; }
/** Searches a given region of text beginning at position start
and ending at position end for the skip object. */
public int find(StringLike s,int start,int end) {
if(start > end) return -1;
start += offset;
int vend = min(s.length()-1,end+offset);
if(mask != c) {
for(int i=start;i<=vend;i++)
if(0 == (s.charAt(i) & mask))
//if(m1||s.regionMatches(ign,i,src,0,src.length()) )
if(m1||CaseMgr.regionMatches(s,ign,i,src,0,src.length()) )
return i-offset;
} else {
for(int i=start;i<=vend;i++)
if(c == s.charAt(i))
//if(m1||s.regionMatches(ign,i,src,0,src.length()) )
if(m1||CaseMgr.regionMatches(s,ign,i,src,0,src.length()) )
return i-offset;
}
return -1;
}
static Skip findSkip(Regex r) {
return findSkip(r.thePattern,r.ignoreCase,!r.dontMatchInQuotes);
}
// look for things that can be skipped
static Skip findSkip(Pattern p,boolean ignoreCase,boolean trnc) {
StringBuffer sb = new StringBuffer();
Skip subsk = null;
int offset = 0;
int skipc = -1,skipoff=0;
for(;p != null;p = p.next) {
if(p instanceof oneChar) {
skipc = ((oneChar)p).c;
skipoff = offset;
}
if(p instanceof oneChar && p.next instanceof oneChar) {
Pattern psav = p;
sb.append(((oneChar)p).c);
while(p.next instanceof oneChar) {
sb.append(((oneChar)p.next).c);
p = p.next;
}
String st = sb.toString();
char c0 = st.charAt(0), c1 = st.charAt(1);
Skip sk=null;
if(st.length()>2)
sk = new SkipBMH(st,ignoreCase,offset);
else
sk = new Skip2(st,ignoreCase,offset);
if(trnc && st.length()>2) { // chop out a whole string...
psav.next = new Skipped(st.substring(1));
psav.next.next = p.next;
psav.next.parent = p.parent;
}
return sk;
} else if(p instanceof Or && ((Or)p).v.size()==1
&& !((Or)p).leftForm().equals("(?!")
&& null != (subsk=
findSkip( (Pattern)((Or)p).v.elementAt(0),ignoreCase,trnc) )) {
subsk.offset += offset;
return subsk;
} else if(p.minChars().equals(p.maxChars())) {
offset += p.minChars().intValue();
} else return skipc < 0 ? null :
new Skip(""+(char)skipc,ignoreCase,skipoff);
}
return null;
}
}