| /* |
| * 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); |
| } |
| } |
| if (q.startsWith("AND ")) { |
| q = q.substring(3).trim(); |
| } |
| if (q.startsWith("OR ")) { |
| q = q.substring(2).trim(); |
| } |
| 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(); |
| } |
| } |