package org.basex.core.cmd; import static org.basex.util.Token.*; import org.basex.core.Context; import org.basex.query.path.Axis; import org.basex.util.Array; import org.basex.util.TokenBuilder; import org.basex.util.XMLToken; import org.basex.util.list.BoolList; import org.basex.util.list.StringList; import org.basex.util.list.TokenList; /** * Evaluates the 'find' command and processes a simplified request as XQuery. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Find extends AQuery { /** * Default constructor. * @param query simplified query */ public Find(final String query) { super(DATAREF, query); } @Override protected boolean run() { return query(find(args[0], context, false)); } /** * Creates an XQuery representation for the specified query. * @param query query * @param ctx database context * @param root root flag * @return query */ public static String find(final String query, final Context ctx, final boolean root) { // treat input as XQuery if(query.startsWith("/")) return query; final boolean r = root || ctx.root(); if(query.isEmpty()) return r ? "/" : "."; // parse user input final String qu = query.replaceAll(" \\+", " "); final String[] terms = split(qu); String pre = ""; String preds = ""; final String tag = "*"; for(String term : terms) { if(term.startsWith("@=")) { preds += "[@* = \"" + term.substring(2) + "\"]"; } else if(term.startsWith("=")) { preds += "[text() = \"" + term.substring(1) + "\"]"; } else if(term.startsWith("~")) { preds += "[text() contains text \"" + term.substring(1) + "\" using fuzzy]"; } else if(term.startsWith("@")) { if(term.length() == 1) continue; preds += "[@* contains text \"" + term.substring(1) + "\"]"; term = term.substring(1); // add valid name tests if(XMLToken.isName(token(term))) { pre += (r ? "" : ".") + "//@" + term + " | "; } } else { preds += "[text() contains text \"" + term + "\"]"; // add valid name tests if(XMLToken.isName(token(term))) { pre += (r ? "/" : "") + Axis.DESC + "::*:" + term + " | "; } } } if(pre.isEmpty() && preds.isEmpty()) return root ? "/" : "."; // create final string final TokenBuilder tb = new TokenBuilder(); //if(opt.size() != 0) tb.add("declare ft-option" + opt + "; "); tb.add(pre + (r ? "/" : "") + Axis.DESCORSELF + "::" + tag + preds); return tb.toString(); } /** * Creates an XQuery representation for the specified table query. * @param filter filter terms * @param cols filter columns * @param elem element flag * @param tag root tag * @param root root flag * @return query */ public static String findTable(final StringList filter, final TokenList cols, final BoolList elem, final byte[] tag, final boolean root) { final TokenBuilder tb = new TokenBuilder(); final int is = filter.size(); for(int i = 0; i < is; ++i) { final String[] spl = split(filter.get(i)); for(final String s : spl) { byte[] term = token(s); if(contains(term, '"')) term = replace(term, '"', ' '); term = trim(term); if(term.length == 0) continue; tb.add('['); final boolean elm = elem.get(i); tb.add(elm ? ".//" : "@"); tb.add("*:"); tb.add(cols.get(i)); if(term[0] == '<' || term[0] == '>') { tb.add(term[0]); tb.addLong(calcNum(substring(term, 1))); } else { tb.add(" contains text \""); tb.add(term); tb.add('"'); } tb.add(']'); } } return tb.size() == 0 ? "/" : (root ? "/" : "") + Axis.DESCORSELF + "::*:" + string(tag) + tb; } /** * Returns an long value for the specified token. The suffixes "kb", "mb" * and "gb" are considered in the calculation. * @param tok token to be converted * @return long */ private static long calcNum(final byte[] tok) { int tl = tok.length; final int s1 = tok.length < 1 ? 0 : lc(tok[tl - 1]); final int s2 = tok.length < 2 ? 0 : lc(tok[tl - 2]); int f = 0; // evaluate suffixes if(s1 == 'k') { tl -= 1; f = 10; } if(s1 == 'm') { tl -= 1; f = 20; } if(s1 == 'g') { tl -= 1; f = 30; } if(s1 == 'b' && s2 == 'k') { tl -= 2; f = 10; } if(s1 == 'b' && s2 == 'm') { tl -= 2; f = 20; } if(s1 == 'b' && s2 == 'g') { tl -= 2; f = 30; } final long i = toLong(tok, 0, tl) << f; return i == Long.MIN_VALUE ? 0 : i; } /** * Splits the string and returns an array. * @param str string to be split * @return array */ private static String[] split(final String str) { final int l = str.length(); final String[] split = new String[l]; int s = 0; char delim = 0; final StringBuilder sb = new StringBuilder(); for(int i = 0; i < l; ++i) { final char c = str.charAt(i); if(delim == 0) { if(c == '\'' || c == '"') { delim = c; } else if(!XMLToken.isChar(c) && c != '@' && c != '=' && c != '<' && c != '>' && c != '~') { if(sb.length() != 0) { split[s++] = sb.toString(); sb.setLength(0); } } else { sb.append(c); } } else { if(c == delim) { delim = 0; if(sb.length() != 0) { split[s++] = sb.toString(); sb.setLength(0); } } else { if(c != '\'' && c != '"') sb.append(c); } } } if(sb.length() != 0) split[s++] = sb.toString(); return Array.copyOf(split, s); } }