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