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