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