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 */
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.Collections;
033import java.util.Date;
034import java.util.Set;
035
036/**
037 * @since 3.6
038 */
039public 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}