-package org.simantics.db.layer0.genericrelation;\r
-\r
-\r
-/**\r
- * This class contains utilities related to queries made into Lucene indexes,\r
- * such as escaping search terms.\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public class IndexQueries {\r
-\r
- /**\r
- * Same as calling {@link #escape(String, boolean, boolean)} with\r
- * escapeKeywords set to <code>true</code>.\r
- * \r
- * @param s\r
- * @param escapeWildcards\r
- * @return escaped string\r
- */\r
- public static String escape(String s, boolean escapeWildcards) {\r
- return escape(s, escapeWildcards, true);\r
- }\r
-\r
- /**\r
- * Returns a String where those characters that QueryParser expects to be\r
- * escaped are escaped by a preceding <code>\</code>.\r
- * \r
- * Copied from\r
- * {@link org.apache.lucene.queryParser.QueryParser#escape(String)} but\r
- * disabled escaping of wildcard characters '*' and '?'. Clients must escape\r
- * wildcards themselves to allow use of wildcards in queries.\r
- * \r
- * @param s\r
- * lucene query to escape\r
- * @param escapeWildcards\r
- * <code>true</code> to escape also wildcard characters\r
- * @param escapeKeywords\r
- * <code>true</code> to escape keywords like AND, OR, etc.\r
- * @return escaped string\r
- */\r
- public static String escape(String s, boolean escapeWildcards, boolean escapeKeywords) {\r
- if (!needsEscaping(s, escapeWildcards, escapeKeywords))\r
- return s;\r
-\r
- StringBuilder sb = new StringBuilder(s.length() + 8);\r
- int len = s.length();\r
- // The beginning of the line is the same as the last character being\r
- // whitespace.\r
- boolean lastWhitespace = true;\r
- for (int i = 0; i < len;) {\r
- char c = s.charAt(i);\r
- // These characters are part of the query syntax and must be escaped\r
- if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'\r
- || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'\r
- || c == '|' || c == '&' || c == '/' || (escapeWildcards && (c == '*' || c == '?'))) {\r
- sb.append('\\');\r
- sb.append(c);\r
- lastWhitespace = false;\r
- } else if (Character.isWhitespace(c)) {\r
- sb.append(c);\r
- lastWhitespace = true;\r
- } else {\r
- if (escapeKeywords && lastWhitespace) {\r
- int reslen = processReservedWords(s, i, sb);\r
- if (reslen > 0) {\r
- i += reslen;\r
- lastWhitespace = false;\r
- continue;\r
- }\r
- }\r
- sb.append(c);\r
- lastWhitespace = false;\r
- }\r
- ++i;\r
- }\r
- return sb.toString();\r
- }\r
-\r
- /**\r
- * Same logic as in {@link #escape(String, boolean, boolean)} but this one\r
- * simply checks whether the input string needs escaping at all or not.\r
- * \r
- * @param s\r
- * @param escapeWildcards\r
- * @param escapeKeywords\r
- * @return\r
- */\r
- private static boolean needsEscaping(String s, boolean escapeWildcards, boolean escapeKeywords) {\r
- int len = s.length();\r
- // The beginning of the line is the same as the last character being\r
- // whitespace.\r
- boolean lastWhitespace = true;\r
- for (int i = 0; i < len;) {\r
- char c = s.charAt(i);\r
- // These characters are part of the query syntax and must be escaped\r
- if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'\r
- || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'\r
- || c == '|' || c == '&' || c == '/' || (escapeWildcards && (c == '*' || c == '?'))) {\r
- return true;\r
- } else if (Character.isWhitespace(c)) {\r
- lastWhitespace = true;\r
- } else {\r
- if (escapeKeywords && lastWhitespace) {\r
- int reslen = processReservedWords(s, i, null);\r
- if (reslen > 0)\r
- return true;\r
- }\r
- lastWhitespace = false;\r
- }\r
- ++i;\r
- }\r
- return false;\r
- }\r
-\r
- private static final String[] RESERVED_WORDS = {\r
- "AND", "and",\r
- "OR", "or"\r
- };\r
-\r
- /**\r
- * Lucene reserved words are case-sensitive for its query parser. Therefore\r
- * only case-sensitive hits need to be looked for.\r
- * \r
- * @param s\r
- * @param fromIndex\r
- * @return length of the reserved word in the input or 0 if no reserved word\r
- * in the input\r
- */\r
- private static int processReservedWords(String s, int fromIndex, StringBuilder sb) {\r
- final int total = RESERVED_WORDS.length;\r
- for (int w = 0; w < total; w += 2) {\r
- String word = RESERVED_WORDS[w];\r
- int len = word.length();\r
- if (s.regionMatches(false, fromIndex, word, 0, len)) {\r
- if (sb != null) {\r
- String replacement = RESERVED_WORDS[w+1];\r
- sb.append(replacement);\r
- }\r
- return len;\r
- }\r
- }\r
- return 0;\r
- }\r
-\r
- /**\r
- * Returns a String where those characters that QueryParser expects to be\r
- * escaped are escaped by a preceding <code>\</code>.\r
- */\r
- public static String escape(String s) {\r
- return escape(s, false);\r
- }\r
-\r
- public static StringBuilder escapeTerm(String field, String term, boolean escapeWildcards, StringBuilder result) {\r
- if (field != null)\r
- result.append(field).append(':');\r
- result.append( escape(term, escapeWildcards) );\r
- return result;\r
- }\r
-\r
- public static String escapeTerm(String field, String term, boolean escapeWildcards) {\r
- StringBuilder sb = new StringBuilder();\r
- return escapeTerm(field, term, escapeWildcards, sb).toString();\r
- }\r
-\r
-// public static void main(String[] args) {\r
-// System.out.println("esc: " + escape("AND01", true, true));\r
-// System.out.println("esc: " + escape("AND 01", true, true));\r
-// System.out.println("esc: " + escape(" AND 01", true, true));\r
-// }\r
-\r
-}\r
+package org.simantics.db.layer0.genericrelation;
+
+import java.util.Collection;
+
+import org.simantics.datatypes.literal.GUID;
+import org.simantics.db.Resource;
+
+/**
+ * This class contains utilities related to queries made into Lucene indexes,
+ * such as escaping search terms.
+ *
+ * @author Tuukka Lehtonen
+ */
+public class IndexQueries {
+
+ /**
+ * Same as calling {@link #escape(String, boolean, boolean)} with
+ * escapeKeywords set to <code>true</code>.
+ *
+ * @param s
+ * @param escapeWildcards
+ * @return escaped string
+ */
+ public static String escape(String s, boolean escapeWildcards) {
+ return escape(s, escapeWildcards, true);
+ }
+
+ /**
+ * Returns a String where those characters that QueryParser expects to be
+ * escaped are escaped by a preceding <code>\</code>.
+ *
+ * Copied from
+ * {@link org.apache.lucene.queryParser.QueryParser#escape(String)} but
+ * disabled escaping of wildcard characters '*' and '?'. Clients must escape
+ * wildcards themselves to allow use of wildcards in queries.
+ *
+ * @param s
+ * lucene query to escape
+ * @param escapeWildcards
+ * <code>true</code> to escape also wildcard characters
+ * @param escapeKeywords
+ * <code>true</code> to escape keywords like AND, OR, etc.
+ * @return escaped string
+ */
+ public static String escape(String s, boolean escapeWildcards, boolean escapeKeywords) {
+ if (!needsEscaping(s, escapeWildcards, escapeKeywords))
+ return s;
+
+ StringBuilder sb = new StringBuilder(s.length() + 8);
+ int len = s.length();
+ // The beginning of the line is the same as the last character being
+ // whitespace.
+ boolean lastWhitespace = true;
+ for (int i = 0; i < len;) {
+ char c = s.charAt(i);
+ // These characters are part of the query syntax and must be escaped
+ if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
+ || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
+ || c == '|' || c == '&' || c == '/' || c == ' ' || (escapeWildcards && (c == '*' || c == '?'))) {
+ sb.append('\\');
+ sb.append(c);
+ lastWhitespace = false;
+ } else if (Character.isWhitespace(c)) {
+ sb.append(c);
+ lastWhitespace = true;
+ } else {
+ if (escapeKeywords && lastWhitespace) {
+ int reslen = processReservedWords(s, i, sb);
+ if (reslen > 0) {
+ i += reslen;
+ lastWhitespace = false;
+ continue;
+ }
+ }
+ sb.append(c);
+ lastWhitespace = false;
+ }
+ ++i;
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Same logic as in {@link #escape(String, boolean, boolean)} but this one
+ * simply checks whether the input string needs escaping at all or not.
+ *
+ * @param s
+ * @param escapeWildcards
+ * @param escapeKeywords
+ * @return
+ */
+ private static boolean needsEscaping(String s, boolean escapeWildcards, boolean escapeKeywords) {
+ int len = s.length();
+ // The beginning of the line is the same as the last character being
+ // whitespace.
+ boolean lastWhitespace = true;
+ for (int i = 0; i < len;) {
+ char c = s.charAt(i);
+ // These characters are part of the query syntax and must be escaped
+ if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
+ || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
+ || c == '|' || c == '&' || c == '/' || c == ' ' || (escapeWildcards && (c == '*' || c == '?'))) {
+ return true;
+ } else if (Character.isWhitespace(c)) {
+ lastWhitespace = true;
+ } else {
+ if (escapeKeywords && lastWhitespace) {
+ int reslen = processReservedWords(s, i, null);
+ if (reslen > 0)
+ return true;
+ }
+ lastWhitespace = false;
+ }
+ ++i;
+ }
+ return false;
+ }
+
+ private static final String[] RESERVED_WORDS = {
+ "AND", "\\AND",
+ "OR", "\\OR",
+ "NOT", "\\NOT",
+ };
+
+ /**
+ * Lucene reserved words are case-sensitive for its query parser. Therefore
+ * only case-sensitive hits need to be looked for.
+ *
+ * @param s
+ * @param fromIndex
+ * @return length of the reserved word in the input or 0 if no reserved word
+ * in the input
+ */
+ private static int processReservedWords(String s, int fromIndex, StringBuilder sb) {
+ final int total = RESERVED_WORDS.length;
+ for (int w = 0; w < total; w += 2) {
+ String word = RESERVED_WORDS[w];
+ int len = word.length();
+ if (s.regionMatches(false, fromIndex, word, 0, len)) {
+ if (sb != null) {
+ String replacement = RESERVED_WORDS[w+1];
+ sb.append(replacement);
+ }
+ return len;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a String where those characters that QueryParser expects to be
+ * escaped are escaped by a preceding <code>\</code>.
+ */
+ public static String escape(String s) {
+ return escape(s, false);
+ }
+
+ public static StringBuilder escapeTerm(String field, String term, boolean escapeWildcards, StringBuilder result) {
+ if (field != null)
+ result.append(field).append(':');
+ result.append( escape(term, escapeWildcards) );
+ return result;
+ }
+
+ public static String escapeTerm(String field, String term, boolean escapeWildcards) {
+ return escapeTerm(field, term, escapeWildcards, new StringBuilder()).toString();
+ }
+
+ public static StringBuilder quoteTerm(String field, String term, StringBuilder result) {
+ if (field != null)
+ result.append(field).append(':');
+ result.append("\"");
+ result.append(term.replaceAll("(\"|\\\\)", "\\\\$0"));
+ result.append("\"");
+ return result;
+ }
+
+ public static String quoteTerm(String term) {
+ return quoteTerm(null, term, new StringBuilder(term.length()*2)).toString();
+ }
+
+ public static String quoteTerm(String field, String term) {
+ return quoteTerm(field, term,
+ new StringBuilder(
+ term.length()*2
+ + (field != null ? field.length() + 1 : 0))
+ ).toString();
+ }
+
+ public static StringBuilder appendLongTerm(StringBuilder sb, String field, long term) {
+ return sb.append(field).append(':').append(term);
+ }
+
+ public static String longTerm(String field, long term) {
+ return appendLongTerm(new StringBuilder(), field, term).toString();
+ }
+
+ public static StringBuilder appendResourceIdTerm(StringBuilder sb, String field, Resource term) {
+ return appendLongTerm(sb, field, term.getResourceId());
+ }
+
+ public static String resourceIdTerm(String field, Resource term) {
+ return appendLongTerm(new StringBuilder(), field, term.getResourceId()).toString();
+ }
+
+ private static String join(String withString, String... exps) {
+ if (exps.length == 0)
+ return "";
+ StringBuilder sb = new StringBuilder(128);
+ for (int i = 0; i < exps.length - 1; ++i) {
+ sb.append(exps[i]).append(withString);
+ }
+ sb.append(exps[exps.length - 1]);
+ return sb.toString();
+ }
+
+ public static String and(String exp1, String exp2) {
+ return exp1 + " AND " + exp2;
+ }
+
+ public static String and(String... exps) {
+ return join(" AND ", exps);
+ }
+
+ public static String or(String exp1, String exp2) {
+ return exp1 + " OR " + exp2;
+ }
+
+ public static String or(String... exps) {
+ return join(" OR ", exps);
+ }
+
+ public static String idFromGUID(GUID guid) {
+ return guid != null ? guid.indexString() : "";
+ }
+
+ public static String toResourceIdString(Resource r, Collection<Resource> rs) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(r.getResourceId());
+ for (Resource rr : rs)
+ sb.append(' ').append(rr.getResourceId());
+ return sb.toString();
+ }
+
+ public static String toResourceIdString(Collection<Resource> rs) {
+ if (rs.isEmpty())
+ return "";
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Resource rr : rs) {
+ if (!first)
+ sb.append(' ');
+ first = false;
+ sb.append(rr.getResourceId());
+ }
+ return sb.toString();
+ }
+
+// public static void main(String[] args) {
+// System.out.println("esc: " + escape("AND01", true, true));
+// System.out.println("esc: " + escape("AND 01", true, true));
+// System.out.println("esc: " + escape(" AND 01", true, true));
+// }
+
+}