diff options
Diffstat (limited to 'src/main/java/com/gitblit/tickets/QueryBuilder.java')
-rw-r--r-- | src/main/java/com/gitblit/tickets/QueryBuilder.java | 222 |
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 |