View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.changes.jira;
20  
21  import java.io.UnsupportedEncodingException;
22  import java.net.URLEncoder;
23  import java.util.List;
24  import java.util.Locale;
25  
26  import org.apache.maven.plugin.logging.Log;
27  
28  /**
29   * Builder for a JIRA query using the JIRA query language. Only a limited set of JQL is supported.
30   *
31   * @author ton.swieb@finalist.com
32   * @version $Id$
33   * @since 2.8
34   */
35  public class JqlQueryBuilder {
36      private String filter = "";
37  
38      private boolean urlEncode = true;
39  
40      /**
41       * Log for debug output.
42       */
43      private Log log;
44  
45      private StringBuilder orderBy = new StringBuilder();
46  
47      private StringBuilder query = new StringBuilder();
48  
49      public JqlQueryBuilder(Log log) {
50          this.log = log;
51      }
52  
53      public String build() {
54          try {
55              String jqlQuery;
56              // If the user has defined a filter - use that
57              if ((this.filter != null) && (this.filter.length() > 0)) {
58                  jqlQuery = filter;
59              } else {
60                  jqlQuery = query.toString() + orderBy.toString();
61              }
62  
63              if (urlEncode) {
64                  getLog().debug("Encoding JQL query " + jqlQuery);
65                  String encodedQuery = URLEncoder.encode(jqlQuery, "UTF-8");
66                  getLog().debug("Encoded JQL query " + encodedQuery);
67                  return encodedQuery;
68              } else {
69                  return jqlQuery;
70              }
71          } catch (UnsupportedEncodingException e) {
72              getLog().error("Unable to encode JQL query with UTF-8", e);
73              throw new RuntimeException(e);
74          }
75      }
76  
77      public JqlQueryBuilder components(String components) {
78          addCommaSeparatedValues("component", components);
79          return this;
80      }
81  
82      public JqlQueryBuilder components(List<String> components) {
83          addValues("component", components);
84          return this;
85      }
86  
87      public JqlQueryBuilder filter(String filter) {
88          this.filter = filter;
89          return this;
90      }
91  
92      /**
93       * When both {@code #fixVersion(String)} and {@link #fixVersionIds(String)} are used, then you will probably end up
94       * with a JQL query that is valid, but returns nothing. Unless they both only reference the same fixVersion
95       *
96       * @param fixVersion a single fix version
97       * @return the builder.
98       */
99      public JqlQueryBuilder fixVersion(String fixVersion) {
100         addSingleValue("fixVersion", fixVersion);
101         return this;
102     }
103 
104     /**
105      * When both {@link #fixVersion(String)} and {@link #fixVersionIds(String)} are used then you will probably end up
106      * with a JQL query that is valid, but returns nothing. Unless they both only reference the same fixVersion
107      *
108      * @param fixVersionIds a comma-separated list of version ids.
109      * @return the builder.
110      */
111     public JqlQueryBuilder fixVersionIds(String fixVersionIds) {
112         addCommaSeparatedValues("fixVersion", fixVersionIds);
113         return this;
114     }
115 
116     /**
117      * Add a sequence of version IDs already in a list.
118      *
119      * @param fixVersionIds the version ids.
120      * @return the builder.
121      */
122     public JqlQueryBuilder fixVersionIds(List<String> fixVersionIds) {
123         addValues("fixVersion", fixVersionIds);
124         return this;
125     }
126 
127     public Log getLog() {
128         return log;
129     }
130 
131     public JqlQueryBuilder priorityIds(String priorityIds) {
132         addCommaSeparatedValues("priority", priorityIds);
133         return this;
134     }
135 
136     public JqlQueryBuilder priorityIds(List<String> priorityIds) {
137         addValues("priority", priorityIds);
138         return this;
139     }
140 
141     public JqlQueryBuilder project(String project) {
142         addSingleValue("project", project);
143         return this;
144     }
145 
146     public JqlQueryBuilder resolutionIds(String resolutionIds) {
147         addCommaSeparatedValues("resolution", resolutionIds);
148         return this;
149     }
150 
151     public JqlQueryBuilder resolutionIds(List<String> resolutionIds) {
152         addValues("resolution", resolutionIds);
153         return this;
154     }
155 
156     public JqlQueryBuilder sortColumnNames(String sortColumnNames) {
157         if (sortColumnNames != null) {
158             orderBy.append(" ORDER BY ");
159 
160             String[] sortColumnNamesArray = sortColumnNames.split(",");
161 
162             for (int i = 0; i < sortColumnNamesArray.length - 1; i++) {
163                 addSingleSortColumn(sortColumnNamesArray[i]);
164                 orderBy.append(", ");
165             }
166             addSingleSortColumn(sortColumnNamesArray[sortColumnNamesArray.length - 1]);
167         }
168         return this;
169     }
170 
171     public JqlQueryBuilder statusIds(String statusIds) {
172         addCommaSeparatedValues("status", statusIds);
173         return this;
174     }
175 
176     public JqlQueryBuilder statusIds(List<String> statusIds) {
177         addValues("status", statusIds);
178         return this;
179     }
180 
181     public JqlQueryBuilder typeIds(String typeIds) {
182         addCommaSeparatedValues("type", typeIds);
183         return this;
184     }
185 
186     public JqlQueryBuilder typeIds(List<String> typeIds) {
187         addValues("type", typeIds);
188         return this;
189     }
190 
191     public JqlQueryBuilder urlEncode(boolean doEncoding) {
192         urlEncode = doEncoding;
193         return this;
194     }
195 
196     public boolean urlEncode() {
197         return urlEncode;
198     }
199 
200     /* --------------------------------------------------------------------- */
201     /* Private methods */
202     /* --------------------------------------------------------------------- */
203 
204     private void addCommaSeparatedValues(String key, String values) {
205         if (values != null) {
206             if (query.length() > 0) {
207                 query.append(" AND ");
208             }
209 
210             query.append(key).append(" in (");
211 
212             String[] valuesArr = values.split(",");
213 
214             for (int i = 0; i < (valuesArr.length - 1); i++) {
215                 trimAndQuoteValue(valuesArr[i]);
216                 query.append(", ");
217             }
218             trimAndQuoteValue(valuesArr[valuesArr.length - 1]);
219             query.append(")");
220         }
221     }
222 
223     private void addValues(String key, List<String> values) {
224         if (values != null && values.size() > 0) {
225             if (query.length() > 0) {
226                 query.append(" AND ");
227             }
228 
229             query.append(key).append(" in (");
230 
231             for (int i = 0; i < (values.size() - 1); i++) {
232                 trimAndQuoteValue(values.get(i));
233                 query.append(", ");
234             }
235             trimAndQuoteValue(values.get(values.size() - 1));
236             query.append(")");
237         }
238     }
239 
240     private void addSingleSortColumn(String name) {
241         boolean descending = false;
242         name = name.trim().toLowerCase(Locale.ENGLISH);
243         if (name.endsWith("desc")) {
244             descending = true;
245             name = name.substring(0, name.length() - 4).trim();
246         } else if (name.endsWith("asc")) {
247             descending = false;
248             name = name.substring(0, name.length() - 3).trim();
249         }
250         // Strip any spaces from the column name, or it will trip up JIRA's JQL parser
251         name = name.replaceAll(" ", "");
252         orderBy.append(name);
253         orderBy.append(descending ? " DESC" : " ASC");
254     }
255 
256     private void addSingleValue(String key, String value) {
257         if (value != null) {
258             if (query.length() > 0) {
259                 query.append(" AND ");
260             }
261             query.append(key).append(" = ");
262             trimAndQuoteValue(value);
263         }
264     }
265 
266     private void trimAndQuoteValue(String value) {
267         String trimmedValue = value.trim();
268         if (trimmedValue.contains(" ") || trimmedValue.contains(".")) {
269             query.append("\"").append(trimmedValue).append("\"");
270         } else {
271             query.append(trimmedValue);
272         }
273     }
274 }