001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.api.issue;
021    
022    import com.google.common.base.Preconditions;
023    import com.google.common.collect.ImmutableSet;
024    import org.apache.commons.lang.builder.ReflectionToStringBuilder;
025    import org.sonar.api.rule.RuleKey;
026    import org.sonar.api.web.UserRole;
027    
028    import javax.annotation.CheckForNull;
029    import javax.annotation.Nullable;
030    
031    import java.util.Collection;
032    import java.util.Collections;
033    import java.util.Date;
034    import java.util.Set;
035    
036    /**
037     * @since 3.6
038     */
039    public class IssueQuery {
040    
041      public static final int DEFAULT_PAGE_INDEX = 1;
042      public static final int DEFAULT_PAGE_SIZE = 100;
043      public static final int MAX_RESULTS = 10000;
044      public static final int MAX_PAGE_SIZE = 500;
045    
046      /**
047       * @deprecated since 3.7. It's replaced by IssueQuery#MAX_PAGE_SIZE.
048       */
049      @Deprecated
050      public static final int MAX_ISSUE_KEYS = MAX_PAGE_SIZE;
051    
052      public static final String SORT_BY_CREATION_DATE = "CREATION_DATE";
053      public static final String SORT_BY_UPDATE_DATE = "UPDATE_DATE";
054      public static final String SORT_BY_CLOSE_DATE = "CLOSE_DATE";
055      public static final String SORT_BY_ASSIGNEE = "ASSIGNEE";
056      public static final String SORT_BY_SEVERITY = "SEVERITY";
057      public static final String SORT_BY_STATUS = "STATUS";
058      public static final Set<String> SORTS = ImmutableSet.of(SORT_BY_CREATION_DATE, SORT_BY_UPDATE_DATE, SORT_BY_CLOSE_DATE, SORT_BY_ASSIGNEE, SORT_BY_SEVERITY, SORT_BY_STATUS);
059    
060      private final Collection<String> issueKeys;
061      private final Collection<String> severities;
062      private final Collection<String> statuses;
063      private final Collection<String> resolutions;
064      private final Collection<String> components;
065      private final Collection<String> componentRoots;
066      private final Collection<RuleKey> rules;
067      private final Collection<String> actionPlans;
068      private final Collection<String> reporters;
069      private final Collection<String> assignees;
070      private final Collection<String> languages;
071      private final Boolean assigned;
072      private final Boolean planned;
073      private final Boolean resolved;
074      private final Boolean hideRules;
075      private final Date createdAt;
076      private final Date createdAfter;
077      private final Date createdBefore;
078      private final String sort;
079      private final Boolean asc;
080      private final String requiredRole;
081    
082      // max results per page
083      private final int pageSize;
084    
085      // index of selected page. Start with 1.
086      private final int pageIndex;
087    
088      private IssueQuery(Builder builder) {
089        this.issueKeys = defaultCollection(builder.issueKeys);
090        this.severities = defaultCollection(builder.severities);
091        this.statuses = defaultCollection(builder.statuses);
092        this.resolutions = defaultCollection(builder.resolutions);
093        this.components = defaultCollection(builder.components);
094        this.componentRoots = defaultCollection(builder.componentRoots);
095        this.rules = defaultCollection(builder.rules);
096        this.actionPlans = defaultCollection(builder.actionPlans);
097        this.reporters = defaultCollection(builder.reporters);
098        this.assignees = defaultCollection(builder.assignees);
099        this.languages = defaultCollection(builder.languages);
100        this.assigned = builder.assigned;
101        this.planned = builder.planned;
102        this.resolved = builder.resolved;
103        this.hideRules = builder.hideRules;
104        this.createdAt = builder.createdAt;
105        this.createdAfter = builder.createdAfter;
106        this.createdBefore = builder.createdBefore;
107        this.sort = builder.sort;
108        this.asc = builder.asc;
109        this.pageSize = builder.pageSize;
110        this.pageIndex = builder.pageIndex;
111        this.requiredRole = builder.requiredRole;
112      }
113    
114      public Collection<String> issueKeys() {
115        return issueKeys;
116      }
117    
118      public Collection<String> severities() {
119        return severities;
120      }
121    
122      public Collection<String> statuses() {
123        return statuses;
124      }
125    
126      public Collection<String> resolutions() {
127        return resolutions;
128      }
129    
130      public Collection<String> components() {
131        return components;
132      }
133    
134      public Collection<String> componentRoots() {
135        return componentRoots;
136      }
137    
138      public Collection<RuleKey> rules() {
139        return rules;
140      }
141    
142      public Collection<String> actionPlans() {
143        return actionPlans;
144      }
145    
146      public Collection<String> reporters() {
147        return reporters;
148      }
149    
150      public Collection<String> assignees() {
151        return assignees;
152      }
153      public Collection<String> languages() {
154        return languages;
155      }
156    
157      @CheckForNull
158      public Boolean assigned() {
159        return assigned;
160      }
161    
162      @CheckForNull
163      public Boolean planned() {
164        return planned;
165      }
166    
167      @CheckForNull
168      public Boolean resolved() {
169        return resolved;
170      }
171    
172      /**
173       * @since 4.2
174       */
175      @CheckForNull
176      public Boolean hideRules() {
177        return hideRules;
178      }
179    
180      @CheckForNull
181      public Date createdAfter() {
182        return createdAfter == null ? null : new Date(createdAfter.getTime());
183      }
184    
185      @CheckForNull
186      public Date createdAt() {
187        return createdAt == null ? null : new Date(createdAt.getTime());
188      }
189    
190      @CheckForNull
191      public Date createdBefore() {
192        return createdBefore == null ? null : new Date(createdBefore.getTime());
193      }
194    
195      @CheckForNull
196      public String sort() {
197        return sort;
198      }
199    
200      @CheckForNull
201      public Boolean asc() {
202        return asc;
203      }
204    
205      public int pageSize() {
206        return pageSize;
207      }
208    
209      public int pageIndex() {
210        return pageIndex;
211      }
212    
213      public int maxResults() {
214        return MAX_RESULTS;
215      }
216    
217      public String requiredRole() {
218        return requiredRole;
219      }
220    
221      @Override
222      public String toString() {
223        return ReflectionToStringBuilder.toString(this);
224      }
225    
226      public static Builder builder() {
227        return new Builder();
228      }
229    
230      public static class Builder {
231        private Collection<String> issueKeys;
232        private Collection<String> severities;
233        private Collection<String> statuses;
234        private Collection<String> resolutions;
235        private Collection<String> components;
236        private Collection<String> componentRoots;
237        private Collection<RuleKey> rules;
238        private Collection<String> actionPlans;
239        private Collection<String> reporters;
240        private Collection<String> assignees;
241        private Collection<String> languages;
242        private Boolean assigned = null;
243        private Boolean planned = null;
244        private Boolean resolved = null;
245        private Boolean hideRules = false;
246        private Date createdAt;
247        private Date createdAfter;
248        private Date createdBefore;
249        private String sort;
250        private Boolean asc = false;
251        private Integer pageSize;
252        private Integer pageIndex;
253        private String requiredRole = UserRole.USER;
254    
255        private Builder() {
256        }
257    
258        public Builder issueKeys(@Nullable Collection<String> l) {
259          this.issueKeys = l;
260          return this;
261        }
262    
263        public Builder severities(@Nullable Collection<String> l) {
264          this.severities = l;
265          return this;
266        }
267    
268        public Builder statuses(@Nullable Collection<String> l) {
269          this.statuses = l;
270          return this;
271        }
272    
273        public Builder resolutions(@Nullable Collection<String> l) {
274          this.resolutions = l;
275          return this;
276        }
277    
278        public Builder components(@Nullable Collection<String> l) {
279          this.components = l;
280          return this;
281        }
282    
283        public Builder componentRoots(@Nullable Collection<String> l) {
284          this.componentRoots = l;
285          return this;
286        }
287    
288        public Builder rules(@Nullable Collection<RuleKey> rules) {
289          this.rules = rules;
290          return this;
291        }
292    
293        public Builder actionPlans(@Nullable Collection<String> l) {
294          this.actionPlans = l;
295          return this;
296        }
297    
298        public Builder reporters(@Nullable Collection<String> l) {
299          this.reporters = l;
300          return this;
301        }
302    
303        public Builder assignees(@Nullable Collection<String> l) {
304          this.assignees = l;
305          return this;
306        }
307    
308        public Builder languages(@Nullable Collection<String> l) {
309          this.languages = l;
310          return this;
311        }
312    
313        /**
314         * If true, it will return all issues assigned to someone
315         * If false, it will return all issues not assigned to someone
316         */
317        public Builder assigned(@Nullable Boolean b) {
318          this.assigned = b;
319          return this;
320        }
321    
322        /**
323         * If true, it will return all issues linked to an action plan
324         * If false, it will return all issues not linked to an action plan
325         */
326        public Builder planned(@Nullable Boolean planned) {
327          this.planned = planned;
328          return this;
329        }
330    
331        /**
332         * If true, it will return all resolved issues
333         * If false, it will return all none resolved issues
334         */
335        public Builder resolved(@Nullable Boolean resolved) {
336          this.resolved = resolved;
337          return this;
338        }
339    
340        /**
341         * If true, rules will not be loaded
342         * If false, rules will be loaded
343         *
344         * @since 4.2
345         *
346         */
347        public Builder hideRules(@Nullable Boolean b) {
348          this.hideRules = b;
349          return this;
350        }
351    
352        public Builder createdAt(@Nullable Date d) {
353          this.createdAt = d == null ? null : new Date(d.getTime());
354          return this;
355        }
356    
357        public Builder createdAfter(@Nullable Date d) {
358          this.createdAfter = d == null ? null : new Date(d.getTime());
359          return this;
360        }
361    
362        public Builder createdBefore(@Nullable Date d) {
363          this.createdBefore = d == null ? null : new Date(d.getTime());
364          return this;
365        }
366    
367        public Builder sort(@Nullable String s) {
368          if (s != null && !SORTS.contains(s)) {
369            throw new IllegalArgumentException("Bad sort field: " + s);
370          }
371          this.sort = s;
372          return this;
373        }
374    
375        public Builder asc(@Nullable Boolean asc) {
376          this.asc = asc;
377          return this;
378        }
379    
380        public Builder pageSize(@Nullable Integer i) {
381          this.pageSize = i;
382          return this;
383        }
384    
385        public Builder pageIndex(@Nullable Integer i) {
386          this.pageIndex = i;
387          return this;
388        }
389    
390        public Builder requiredRole(@Nullable String s) {
391          this.requiredRole = s;
392          return this;
393        }
394    
395        public IssueQuery build() {
396          initPageIndex();
397          initPageSize();
398          if (issueKeys != null) {
399            Preconditions.checkArgument(issueKeys.size() <= MAX_PAGE_SIZE, "Number of issue keys must be less than " + MAX_PAGE_SIZE + " (got " + issueKeys.size() + ")");
400          }
401          return new IssueQuery(this);
402        }
403    
404        private void initPageSize() {
405          if (components != null && components.size() == 1 && pageSize == null) {
406            pageSize = 999999;
407          } else {
408            if (pageSize == null) {
409              pageSize = DEFAULT_PAGE_SIZE;
410            } else if (pageSize <= 0 || pageSize > MAX_PAGE_SIZE) {
411              pageSize = MAX_PAGE_SIZE;
412            }
413          }
414        }
415    
416        private void initPageIndex() {
417          if (pageIndex == null) {
418            pageIndex = DEFAULT_PAGE_INDEX;
419          }
420          Preconditions.checkArgument(pageIndex > 0, "Page index must be greater than 0 (got " + pageIndex + ")");
421        }
422      }
423    
424      private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
425        return c == null ? Collections.<T>emptyList() : Collections.unmodifiableCollection(c);
426      }
427    }