/** * Evaluates the analyze-string function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item analyzeString(final byte[] val, final QueryContext ctx) throws QueryException { final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); if (p.matcher("").matches()) REGROUP.thrw(info); final String str = string(val); final Matcher m = p.matcher(str); final FElem root = new FElem(Q_ANALYZE, new Atts(FN, FNURI)); int s = 0; while (m.find()) { if (s != m.start()) nonmatch(str.substring(s, m.start()), root); match(m, str, root, 0); s = m.end(); } if (s != str.length()) nonmatch(str.substring(s), root); return root; }
/** * Constructor. * * @param value value * @param type item type * @param ii input info * @throws QueryException query exception */ private Dur(final byte[] value, final Type type, final InputInfo ii) throws QueryException { this(type); final String val = Token.string(value).trim(); final Matcher mt = DUR.matcher(val); if (!mt.matches() || val.endsWith("P") || val.endsWith("T")) throw dateError(value, XDURR, ii); yearMonth(value, mt, ii); dayTime(value, mt, 6, ii); }
/** * Evaluates the tokenize function. * * @param ctx query context * @return function result * @throws QueryException query exception */ private Value tokenize(final QueryContext ctx) throws QueryException { final byte[] val = checkEStr(expr[0], ctx); final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); if (p.matcher("").matches()) REGROUP.thrw(info); final TokenList tl = new TokenList(); final String str = string(val); if (!str.isEmpty()) { final Matcher m = p.matcher(str); int s = 0; while (m.find()) { tl.add(str.substring(s, m.start())); s = m.end(); } tl.add(str.substring(s, str.length())); } return StrSeq.get(tl); }
/** * Evaluates the replace function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item replace(final byte[] val, final QueryContext ctx) throws QueryException { final byte[] rep = checkStr(expr[2], ctx); for (int i = 0; i < rep.length; ++i) { if (rep[i] == '\\') { if (i + 1 == rep.length || rep[i + 1] != '\\' && rep[i + 1] != '$') FUNREPBS.thrw(info); ++i; } if (rep[i] == '$' && (i == 0 || rep[i - 1] != '\\') && (i + 1 == rep.length || !digit(rep[i + 1]))) FUNREPDOL.thrw(info); } final Pattern p = pattern(expr[1], expr.length == 4 ? expr[3] : null, ctx); if (p.pattern().isEmpty()) REGROUP.thrw(info); String r = string(rep); if ((p.flags() & Pattern.LITERAL) != 0) { r = SLASH.matcher(BSLASH.matcher(r).replaceAll("\\\\\\\\")).replaceAll("\\\\\\$"); } try { return Str.get(p.matcher(string(val)).replaceAll(r)); } catch (final Exception ex) { if (ex.getMessage().contains("No group")) REGROUP.thrw(info); throw REGPAT.thrw(info, ex); } }
/** * Duration item ({@code xs:duration}). * * @author BaseX Team 2005-16, BSD License * @author Christian Gruen */ public class Dur extends ADateDur { /** Pattern for one or more digits. */ static final String DP = "(\\d+)"; /** Date pattern. */ private static final Pattern DUR = Pattern.compile( "(-?)P(" + DP + "Y)?(" + DP + "M)?(" + DP + "D)?(T(" + DP + "H)?(" + DP + "M)?((\\d+|\\d*\\.\\d+)?S)?)?"); /** Number of months. */ long mon; /** * Constructor. * * @param value value * @param ii input info * @throws QueryException query exception */ public Dur(final byte[] value, final InputInfo ii) throws QueryException { this(value, AtomType.DUR, ii); } /** * Constructor. * * @param type item type */ Dur(final Type type) { super(type); } /** * Constructor. * * @param dur duration */ public Dur(final Dur dur) { this(dur, AtomType.DUR); } /** * Constructor. * * @param dur duration * @param type item type */ private Dur(final Dur dur, final Type type) { this(type); mon = dur.mon; sec = dur.sec == null ? BigDecimal.ZERO : dur.sec; } /** * Constructor. * * @param value value * @param type item type * @param ii input info * @throws QueryException query exception */ private Dur(final byte[] value, final Type type, final InputInfo ii) throws QueryException { this(type); final String val = Token.string(value).trim(); final Matcher mt = DUR.matcher(val); if (!mt.matches() || val.endsWith("P") || val.endsWith("T")) throw dateError(value, XDURR, ii); yearMonth(value, mt, ii); dayTime(value, mt, 6, ii); } /** * Initializes the yearMonth component. * * @param vl value * @param mt matcher * @param ii input info * @throws QueryException query exception */ void yearMonth(final byte[] vl, final Matcher mt, final InputInfo ii) throws QueryException { final long y = mt.group(2) != null ? toLong(mt.group(3), true, ii) : 0; final long m = mt.group(4) != null ? toLong(mt.group(5), true, ii) : 0; mon = y * 12 + m; double v = y * 12d + m; if (!mt.group(1).isEmpty()) { mon = -mon; v = -v; } if (v <= Long.MIN_VALUE || v >= Long.MAX_VALUE) throw DURRANGE_X_X.get(ii, type, vl); } /** * Initializes the dayTime component. * * @param vl value * @param mt matcher * @param p first matching position * @param ii input info * @throws QueryException query exception */ void dayTime(final byte[] vl, final Matcher mt, final int p, final InputInfo ii) throws QueryException { final long d = mt.group(p) != null ? toLong(mt.group(p + 1), true, ii) : 0; final long h = mt.group(p + 3) != null ? toLong(mt.group(p + 4), true, ii) : 0; final long m = mt.group(p + 5) != null ? toLong(mt.group(p + 6), true, ii) : 0; final BigDecimal s = mt.group(p + 7) != null ? toDecimal(mt.group(p + 8), true, ii) : BigDecimal.ZERO; sec = s.add(BigDecimal.valueOf(d).multiply(DAYSECONDS)) .add(BigDecimal.valueOf(h).multiply(BD3600)) .add(BigDecimal.valueOf(m).multiply(BD60)); if (!mt.group(1).isEmpty()) sec = sec.negate(); final double v = sec.doubleValue(); if (v <= Long.MIN_VALUE || v >= Long.MAX_VALUE) throw DURRANGE_X_X.get(ii, type, vl); } @Override public final long yea() { return mon / 12; } @Override public final long mon() { return mon % 12; } @Override public final long day() { return sec.divideToIntegralValue(DAYSECONDS).longValue(); } @Override public final long hou() { return tim() / 3600; } @Override public final long min() { return tim() % 3600 / 60; } @Override public final BigDecimal sec() { return sec.remainder(BD60); } /** * Returns the time. * * @return time */ private long tim() { return sec.remainder(DAYSECONDS).longValue(); } @Override public byte[] string(final InputInfo ii) { final TokenBuilder tb = new TokenBuilder(); final int ss = sec.signum(); if (mon < 0 || ss < 0) tb.add('-'); date(tb); time(tb); if (mon == 0 && ss == 0) tb.add("T0S"); return tb.finish(); } /** * Adds the date to the specified token builder. * * @param tb token builder */ final void date(final TokenBuilder tb) { tb.add('P'); final long y = yea(); if (y != 0) { tb.addLong(Math.abs(y)); tb.add('Y'); } final long m = mon(); if (m != 0) { tb.addLong(Math.abs(m)); tb.add('M'); } final long d = day(); if (d != 0) { tb.addLong(Math.abs(d)); tb.add('D'); } } /** * Adds the time to the specified token builder. * * @param tb token builder */ final void time(final TokenBuilder tb) { if (sec.remainder(DAYSECONDS).signum() == 0) return; tb.add('T'); final long h = hou(); if (h != 0) { tb.addLong(Math.abs(h)); tb.add('H'); } final long m = min(); if (m != 0) { tb.addLong(Math.abs(m)); tb.add('M'); } final BigDecimal sc = sec(); if (sc.signum() == 0) return; tb.add(Token.chopNumber(Token.token(sc.abs().toPlainString()))).add('S'); } @Override public final boolean eq( final Item it, final Collation coll, final StaticContext sc, final InputInfo ii) throws QueryException { final Dur d = (Dur) (it instanceof Dur ? it : type.cast(it, null, null, ii)); final BigDecimal s1 = sec == null ? BigDecimal.ZERO : sec; final BigDecimal s2 = d.sec == null ? BigDecimal.ZERO : d.sec; return mon == d.mon && s1.compareTo(s2) == 0; } @Override public int diff(final Item it, final Collation coll, final InputInfo ii) throws QueryException { throw diffError(ii, it, this); } @Override public final Duration toJava() { return ADate.DF.newDuration(Token.string(string(null))); } @Override public final int hash(final InputInfo ii) { return (int) (31 * mon + (sec == null ? 0 : sec.doubleValue())); } @Override public final String toString() { return Util.info("\"%\"", string(null)); } }
/** * String pattern functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class FNPat extends StandardFunc { /** Pattern cache. */ private final TokenObjMap<Pattern> patterns = new TokenObjMap<Pattern>(); /** Slash pattern. */ private static final Pattern SLASH = Pattern.compile("\\$"); /** Slash pattern. */ private static final Pattern BSLASH = Pattern.compile("\\\\"); /** Root element for the analyze-string-result function. */ private static final QNm Q_ANALYZE = new QNm("fn:analyze-string-result", FNURI); /** Element for the analyze-string-result function. */ private static final QNm Q_MATCH = new QNm("fn:match", FNURI); /** Element for the analyze-string-result function. */ private static final QNm Q_NONMATCH = new QNm("fn:non-match", FNURI); /** Element for the analyze-string-result function. */ private static final QNm Q_MGROUP = new QNm("fn:group", FNURI); /** Attribute for the analyze-string-result function. */ private static final QNm Q_NR = new QNm("nr"); /** * Constructor. * * @param ii input info * @param f function definition * @param e arguments */ public FNPat(final InputInfo ii, final Function f, final Expr... e) { super(ii, f, e); } @Override public Iter iter(final QueryContext ctx) throws QueryException { switch (sig) { case TOKENIZE: return tokenize(ctx).iter(); default: return super.iter(ctx); } } @Override public Value value(final QueryContext ctx) throws QueryException { switch (sig) { case TOKENIZE: return tokenize(ctx); default: return super.value(ctx); } } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { switch (sig) { case MATCHES: return matches(checkEStr(expr[0], ctx), ctx); case REPLACE: return replace(checkEStr(expr[0], ctx), ctx); case ANALYZE_STRING: return analyzeString(checkEStr(expr[0], ctx), ctx); default: return super.item(ctx, ii); } } /** * Evaluates the match function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item matches(final byte[] val, final QueryContext ctx) throws QueryException { final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); return Bln.get(p.matcher(string(val)).find()); } /** * Evaluates the analyze-string function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item analyzeString(final byte[] val, final QueryContext ctx) throws QueryException { final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); if (p.matcher("").matches()) REGROUP.thrw(info); final String str = string(val); final Matcher m = p.matcher(str); final FElem root = new FElem(Q_ANALYZE, new Atts(FN, FNURI)); int s = 0; while (m.find()) { if (s != m.start()) nonmatch(str.substring(s, m.start()), root); match(m, str, root, 0); s = m.end(); } if (s != str.length()) nonmatch(str.substring(s), root); return root; } /** * Processes a match. * * @param m matcher * @param str string * @param par parent * @param g group number * @return next group number and position in string */ private static int[] match(final Matcher m, final String str, final FElem par, final int g) { final FElem nd = new FElem(g == 0 ? Q_MATCH : Q_MGROUP, new Atts(FN, FNURI)); if (g > 0) nd.add(Q_NR, token(g)); final int start = m.start(g), end = m.end(g), gc = m.groupCount(); int[] pos = {g + 1, start}; // group and position in string while (pos[0] <= gc && m.end(pos[0]) <= end) { final int st = m.start(pos[0]); if (st >= 0) { // group matched if (pos[1] < st) nd.add(str.substring(pos[1], st)); pos = match(m, str, nd, pos[0]); } else pos[0]++; // skip it } if (pos[1] < end) { nd.add(str.substring(pos[1], end)); pos[1] = end; } par.add(nd); return pos; } /** * Processes a non-match. * * @param text text * @param par root node */ private static void nonmatch(final String text, final FElem par) { par.add(new FElem(Q_NONMATCH, new Atts(FN, FNURI)).add(text)); } /** * Evaluates the replace function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item replace(final byte[] val, final QueryContext ctx) throws QueryException { final byte[] rep = checkStr(expr[2], ctx); for (int i = 0; i < rep.length; ++i) { if (rep[i] == '\\') { if (i + 1 == rep.length || rep[i + 1] != '\\' && rep[i + 1] != '$') FUNREPBS.thrw(info); ++i; } if (rep[i] == '$' && (i == 0 || rep[i - 1] != '\\') && (i + 1 == rep.length || !digit(rep[i + 1]))) FUNREPDOL.thrw(info); } final Pattern p = pattern(expr[1], expr.length == 4 ? expr[3] : null, ctx); if (p.pattern().isEmpty()) REGROUP.thrw(info); String r = string(rep); if ((p.flags() & Pattern.LITERAL) != 0) { r = SLASH.matcher(BSLASH.matcher(r).replaceAll("\\\\\\\\")).replaceAll("\\\\\\$"); } try { return Str.get(p.matcher(string(val)).replaceAll(r)); } catch (final Exception ex) { if (ex.getMessage().contains("No group")) REGROUP.thrw(info); throw REGPAT.thrw(info, ex); } } /** * Evaluates the tokenize function. * * @param ctx query context * @return function result * @throws QueryException query exception */ private Value tokenize(final QueryContext ctx) throws QueryException { final byte[] val = checkEStr(expr[0], ctx); final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); if (p.matcher("").matches()) REGROUP.thrw(info); final TokenList tl = new TokenList(); final String str = string(val); if (!str.isEmpty()) { final Matcher m = p.matcher(str); int s = 0; while (m.find()) { tl.add(str.substring(s, m.start())); s = m.end(); } tl.add(str.substring(s, str.length())); } return StrSeq.get(tl); } /** * Returns a regular expression pattern. * * @param pattern input pattern * @param modifier modifier item * @param ctx query context * @return pattern modifier * @throws QueryException query exception */ private Pattern pattern(final Expr pattern, final Expr modifier, final QueryContext ctx) throws QueryException { final byte[] pat = checkStr(pattern, ctx); final byte[] mod = modifier != null ? checkStr(modifier, ctx) : null; final TokenBuilder tb = new TokenBuilder(pat); if (mod != null) tb.add(0).add(mod); final byte[] key = tb.finish(); Pattern p = patterns.get(key); if (p == null) { p = RegExParser.parse(pat, mod, ctx.sc.xquery3(), info); patterns.add(key, p); } return p; } @Override public boolean xquery3() { return sig == ANALYZE_STRING; } @Override public boolean uses(final Use u) { return u == Use.X30 && xquery3() || u == Use.CNS && sig == ANALYZE_STRING || super.uses(u); } }
/** * Evaluates the match function. * * @param val input value * @param ctx query context * @return function result * @throws QueryException query exception */ private Item matches(final byte[] val, final QueryContext ctx) throws QueryException { final Pattern p = pattern(expr[1], expr.length == 3 ? expr[2] : null, ctx); return Bln.get(p.matcher(string(val)).find()); }