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 Boolean assigned;
071      private final Boolean planned;
072      private final Boolean resolved;
073      private final Boolean hideRules;
074      private final Date createdAt;
075      private final Date createdAfter;
076      private final Date createdBefore;
077      private final String sort;
078      private final Boolean asc;
079      private final String requiredRole;
080    
081      // max results per page
082      private final int pageSize;
083    
084      // index of selected page. Start with 1.
085      private final int pageIndex;
086    
087      private IssueQuery(Builder builder) {
088        this.issueKeys = defaultCollection(builder.issueKeys);
089        this.severities = defaultCollection(builder.severities);
090        this.statuses = defaultCollection(builder.statuses);
091        this.resolutions = defaultCollection(builder.resolutions);
092        this.components = defaultCollection(builder.components);
093        this.componentRoots = defaultCollection(builder.componentRoots);
094        this.rules = defaultCollection(builder.rules);
095        this.actionPlans = defaultCollection(builder.actionPlans);
096        this.reporters = defaultCollection(builder.reporters);
097        this.assignees = defaultCollection(builder.assignees);
098        this.assigned = builder.assigned;
099        this.planned = builder.planned;
100        this.resolved = builder.resolved;
101        this.hideRules = builder.hideRules;
102        this.createdAt = builder.createdAt;
103        this.createdAfter = builder.createdAfter;
104        this.createdBefore = builder.createdBefore;
105        this.sort = builder.sort;
106        this.asc = builder.asc;
107        this.pageSize = builder.pageSize;
108        this.pageIndex = builder.pageIndex;
109        this.requiredRole = builder.requiredRole;
110      }
111    
112      public Collection<String> issueKeys() {
113        return issueKeys;
114      }
115    
116      public Collection<String> severities() {
117        return severities;
118      }
119    
120      public Collection<String> statuses() {
121        return statuses;
122      }
123    
124      public Collection<String> resolutions() {
125        return resolutions;
126      }
127    
128      public Collection<String> components() {
129        return components;
130      }
131    
132      public Collection<String> componentRoots() {
133        return componentRoots;
134      }
135    
136      public Collection<RuleKey> rules() {
137        return rules;
138      }
139    
140      public Collection<String> actionPlans() {
141        return actionPlans;
142      }
143    
144      public Collection<String> reporters() {
145        return reporters;
146      }
147    
148      public Collection<String> assignees() {
149        return assignees;
150      }
151    
152      @CheckForNull
153      public Boolean assigned() {
154        return assigned;
155      }
156    
157      @CheckForNull
158      public Boolean planned() {
159        return planned;
160      }
161    
162      @CheckForNull
163      public Boolean resolved() {
164        return resolved;
165      }
166    
167      /**
168       * @since 4.2
169       */
170      @CheckForNull
171      public Boolean hideRules() {
172        return hideRules;
173      }
174    
175      @CheckForNull
176      public Date createdAfter() {
177        return createdAfter == null ? null : new Date(createdAfter.getTime());
178      }
179    
180      @CheckForNull
181      public Date createdAt() {
182        return createdAt == null ? null : new Date(createdAt.getTime());
183      }
184    
185      @CheckForNull
186      public Date createdBefore() {
187        return createdBefore == null ? null : new Date(createdBefore.getTime());
188      }
189    
190      @CheckForNull
191      public String sort() {
192        return sort;
193      }
194    
195      @CheckForNull
196      public Boolean asc() {
197        return asc;
198      }
199    
200      public int pageSize() {
201        return pageSize;
202      }
203    
204      public int pageIndex() {
205        return pageIndex;
206      }
207    
208      public int maxResults() {
209        return MAX_RESULTS;
210      }
211    
212      public String requiredRole() {
213        return requiredRole;
214      }
215    
216      @Override
217      public String toString() {
218        return ReflectionToStringBuilder.toString(this);
219      }
220    
221      public static Builder builder() {
222        return new Builder();
223      }
224    
225      public static class Builder {
226        private Collection<String> issueKeys;
227        private Collection<String> severities;
228        private Collection<String> statuses;
229        private Collection<String> resolutions;
230        private Collection<String> components;
231        private Collection<String> componentRoots;
232        private Collection<RuleKey> rules;
233        private Collection<String> actionPlans;
234        private Collection<String> reporters;
235        private Collection<String> assignees;
236        private Boolean assigned = null;
237        private Boolean planned = null;
238        private Boolean resolved = null;
239        private Boolean hideRules = false;
240        private Date createdAt;
241        private Date createdAfter;
242        private Date createdBefore;
243        private String sort;
244        private Boolean asc = false;
245        private Integer pageSize;
246        private Integer pageIndex;
247        private String requiredRole = UserRole.USER;
248    
249        private Builder() {
250        }
251    
252        public Builder issueKeys(@Nullable Collection<String> l) {
253          this.issueKeys = l;
254          return this;
255        }
256    
257        public Builder severities(@Nullable Collection<String> l) {
258          this.severities = l;
259          return this;
260        }
261    
262        public Builder statuses(@Nullable Collection<String> l) {
263          this.statuses = l;
264          return this;
265        }
266    
267        public Builder resolutions(@Nullable Collection<String> l) {
268          this.resolutions = l;
269          return this;
270        }
271    
272        public Builder components(@Nullable Collection<String> l) {
273          this.components = l;
274          return this;
275        }
276    
277        public Builder componentRoots(@Nullable Collection<String> l) {
278          this.componentRoots = l;
279          return this;
280        }
281    
282        public Builder rules(@Nullable Collection<RuleKey> rules) {
283          this.rules = rules;
284          return this;
285        }
286    
287        public Builder actionPlans(@Nullable Collection<String> l) {
288          this.actionPlans = l;
289          return this;
290        }
291    
292        public Builder reporters(@Nullable Collection<String> l) {
293          this.reporters = l;
294          return this;
295        }
296    
297        public Builder assignees(@Nullable Collection<String> l) {
298          this.assignees = l;
299          return this;
300        }
301    
302        /**
303         * If true, it will return all issues assigned to someone
304         * If false, it will return all issues not assigned to someone
305         */
306        public Builder assigned(@Nullable Boolean b) {
307          this.assigned = b;
308          return this;
309        }
310    
311        /**
312         * If true, it will return all issues linked to an action plan
313         * If false, it will return all issues not linked to an action plan
314         */
315        public Builder planned(@Nullable Boolean planned) {
316          this.planned = planned;
317          return this;
318        }
319    
320        /**
321         * If true, it will return all resolved issues
322         * If false, it will return all none resolved issues
323         */
324        public Builder resolved(@Nullable Boolean resolved) {
325          this.resolved = resolved;
326          return this;
327        }
328    
329        /**
330         * If true, rules will not be loaded
331         * If false, rules will be loaded
332         *
333         * @since 4.2
334         *
335         */
336        public Builder hideRules(@Nullable Boolean b) {
337          this.hideRules = b;
338          return this;
339        }
340    
341        public Builder createdAt(@Nullable Date d) {
342          this.createdAt = d == null ? null : new Date(d.getTime());
343          return this;
344        }
345    
346        public Builder createdAfter(@Nullable Date d) {
347          this.createdAfter = d == null ? null : new Date(d.getTime());
348          return this;
349        }
350    
351        public Builder createdBefore(@Nullable Date d) {
352          this.createdBefore = d == null ? null : new Date(d.getTime());
353          return this;
354        }
355    
356        public Builder sort(@Nullable String s) {
357          if (s != null && !SORTS.contains(s)) {
358            throw new IllegalArgumentException("Bad sort field: " + s);
359          }
360          this.sort = s;
361          return this;
362        }
363    
364        public Builder asc(@Nullable Boolean asc) {
365          this.asc = asc;
366          return this;
367        }
368    
369        public Builder pageSize(@Nullable Integer i) {
370          this.pageSize = i;
371          return this;
372        }
373    
374        public Builder pageIndex(@Nullable Integer i) {
375          this.pageIndex = i;
376          return this;
377        }
378    
379        public Builder requiredRole(@Nullable String s) {
380          this.requiredRole = s;
381          return this;
382        }
383    
384        public IssueQuery build() {
385          initPageIndex();
386          initPageSize();
387          if (issueKeys != null) {
388            Preconditions.checkArgument(issueKeys.size() <= MAX_PAGE_SIZE, "Number of issue keys must be less than " + MAX_PAGE_SIZE + " (got " + issueKeys.size() + ")");
389          }
390          return new IssueQuery(this);
391        }
392    
393        private void initPageSize() {
394          if (components != null && components.size() == 1 && pageSize == null) {
395            pageSize = 999999;
396          } else {
397            if (pageSize == null) {
398              pageSize = DEFAULT_PAGE_SIZE;
399            } else if (pageSize <= 0 || pageSize > MAX_PAGE_SIZE) {
400              pageSize = MAX_PAGE_SIZE;
401            }
402          }
403        }
404    
405        private void initPageIndex() {
406          if (pageIndex == null) {
407            pageIndex = DEFAULT_PAGE_INDEX;
408          }
409          Preconditions.checkArgument(pageIndex > 0, "Page index must be greater than 0 (got " + pageIndex + ")");
410        }
411      }
412    
413      private static <T> Collection<T> defaultCollection(@Nullable Collection<T> c) {
414        return c == null ? Collections.<T>emptyList() : Collections.unmodifiableCollection(c);
415      }
416    }