summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/gitblit/tickets/QueryBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/gitblit/tickets/QueryBuilder.java')
-rw-r--r--src/main/java/com/gitblit/tickets/QueryBuilder.java222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/tickets/QueryBuilder.java b/src/main/java/com/gitblit/tickets/QueryBuilder.java
new file mode 100644
index 00000000..17aeb988
--- /dev/null
+++ b/src/main/java/com/gitblit/tickets/QueryBuilder.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2013 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.tickets;
+
+import com.gitblit.utils.StringUtils;
+
+/**
+ * A Lucene query builder.
+ *
+ * @author James Moger
+ *
+ */
+public class QueryBuilder {
+
+ private final QueryBuilder parent;
+ private String q;
+ private transient StringBuilder sb;
+ private int opCount;
+
+ public static QueryBuilder q(String kernel) {
+ return new QueryBuilder(kernel);
+ }
+
+ private QueryBuilder(QueryBuilder parent) {
+ this.sb = new StringBuilder();
+ this.parent = parent;
+ }
+
+ public QueryBuilder() {
+ this("");
+ }
+
+ public QueryBuilder(String query) {
+ this.sb = new StringBuilder(query == null ? "" : query);
+ this.parent = null;
+ }
+
+ public boolean containsField(String field) {
+ return sb.toString().contains(field + ":");
+ }
+
+ /**
+ * Creates a new AND subquery. Make sure to call endSubquery to
+ * get return *this* query.
+ *
+ * e.g. field:something AND (subquery)
+ *
+ * @return a subquery
+ */
+ public QueryBuilder andSubquery() {
+ sb.append(" AND (");
+ return new QueryBuilder(this);
+ }
+
+ /**
+ * Creates a new OR subquery. Make sure to call endSubquery to
+ * get return *this* query.
+ *
+ * e.g. field:something OR (subquery)
+ *
+ * @return a subquery
+ */
+ public QueryBuilder orSubquery() {
+ sb.append(" OR (");
+ return new QueryBuilder(this);
+ }
+
+ /**
+ * Ends a subquery and returns the parent query.
+ *
+ * @return the parent query
+ */
+ public QueryBuilder endSubquery() {
+ this.q = sb.toString().trim();
+ if (q.length() > 0) {
+ parent.sb.append(q).append(')');
+ }
+ return parent;
+ }
+
+ /**
+ * Append an OR condition.
+ *
+ * @param condition
+ * @return
+ */
+ public QueryBuilder or(String condition) {
+ return op(condition, " OR ");
+ }
+
+ /**
+ * Append an AND condition.
+ *
+ * @param condition
+ * @return
+ */
+ public QueryBuilder and(String condition) {
+ return op(condition, " AND ");
+ }
+
+ /**
+ * Append an AND NOT condition.
+ *
+ * @param condition
+ * @return
+ */
+ public QueryBuilder andNot(String condition) {
+ return op(condition, " AND NOT ");
+ }
+
+ /**
+ * Nest this query as a subquery.
+ *
+ * e.g. field:something AND field2:something else
+ * ==> (field:something AND field2:something else)
+ *
+ * @return this query nested as a subquery
+ */
+ public QueryBuilder toSubquery() {
+ if (opCount > 1) {
+ sb.insert(0, '(').append(')');
+ }
+ return this;
+ }
+
+ /**
+ * Nest this query as an AND subquery of the condition
+ *
+ * @param condition
+ * @return the query nested as an AND subquery of the specified condition
+ */
+ public QueryBuilder subqueryOf(String condition) {
+ if (!StringUtils.isEmpty(condition)) {
+ toSubquery().and(condition);
+ }
+ return this;
+ }
+
+ /**
+ * Removes a condition from the query.
+ *
+ * @param condition
+ * @return the query
+ */
+ public QueryBuilder remove(String condition) {
+ int start = sb.indexOf(condition);
+ if (start == 0) {
+ // strip first condition
+ sb.replace(0, condition.length(), "");
+ } else if (start > 1) {
+ // locate condition in query
+ int space1 = sb.lastIndexOf(" ", start - 1);
+ int space0 = sb.lastIndexOf(" ", space1 - 1);
+ if (space0 > -1 && space1 > -1) {
+ String conjunction = sb.substring(space0, space1).trim();
+ if ("OR".equals(conjunction) || "AND".equals(conjunction)) {
+ // remove the conjunction
+ sb.replace(space0, start + condition.length(), "");
+ } else {
+ // unknown conjunction
+ sb.replace(start, start + condition.length(), "");
+ }
+ } else {
+ sb.replace(start, start + condition.length(), "");
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Generate the return the Lucene query.
+ *
+ * @return the generated query
+ */
+ public String build() {
+ if (parent != null) {
+ throw new IllegalAccessError("You can not build a subquery! endSubquery() instead!");
+ }
+ this.q = sb.toString().trim();
+
+ // cleanup paranthesis
+ while (q.contains("()")) {
+ q = q.replace("()", "");
+ }
+ if (q.length() > 0) {
+ if (q.charAt(0) == '(' && q.charAt(q.length() - 1) == ')') {
+ // query is wrapped by unnecessary paranthesis
+ q = q.substring(1, q.length() - 1);
+ }
+ }
+ return q;
+ }
+
+ private QueryBuilder op(String condition, String op) {
+ opCount++;
+ if (!StringUtils.isEmpty(condition)) {
+ if (sb.length() != 0) {
+ sb.append(op);
+ }
+ sb.append(condition);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return sb.toString().trim();
+ }
+} \ No newline at end of file