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