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 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 }