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 }