4
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
6
* Permission is hereby granted, free of charge, to any person obtaining a copy
7
* of this software and associated documentation files (the "Software"), to deal
8
* in the Software without restriction, including without limitation the rights
9
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
* copies of the Software, and to permit persons to whom the Software is
11
* furnished to do so, subject to the following conditions:
13
* The above copyright notice and this permission notice shall be included in
14
* all copies or substantial portions of the Software.
16
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
import com.google.common.base.Predicate;
27
import com.google.common.collect.Iterables;
28
import com.google.common.collect.Iterators;
29
import hudson.model.AbstractBuild;
30
import hudson.model.Item;
31
import hudson.model.Job;
32
import hudson.model.Node;
33
import hudson.model.Result;
34
import hudson.model.Run;
35
import hudson.model.View;
36
import hudson.util.Iterators.CountingPredicate;
38
import java.util.AbstractList;
39
import java.util.ArrayList;
40
import java.util.Calendar;
41
import java.util.Collection;
42
import java.util.Collections;
43
import java.util.Comparator;
44
import java.util.GregorianCalendar;
45
import java.util.Iterator;
46
import java.util.List;
47
import java.util.NoSuchElementException;
50
* {@link List} of {@link Run}s, sorted in the descending date order.
52
* @author Kohsuke Kawaguchi
54
public class RunList<R extends Run> extends AbstractList<R> {
56
private Iterable<R> base;
62
base = Collections.emptyList();
65
public RunList(Job j) {
69
public RunList(View view) {// this is a type unsafe operation
70
List<Iterable<R>> jobs = new ArrayList<Iterable<R>>();
71
for (Item item : view.getItems())
72
for (Job<?,?> j : item.getAllJobs())
73
jobs.add(((Job)j).getBuilds());
75
this.base = combine(jobs);
78
public RunList(Collection<? extends Job> jobs) {
79
List<Iterable<R>> src = new ArrayList<Iterable<R>>();
81
src.add(j.getBuilds());
82
this.base = combine(src);
85
private Iterable<R> combine(Iterable<Iterable<R>> jobs) {
86
return Iterables.mergeSorted(jobs, new Comparator<R>() {
87
public int compare(R o1, R o2) {
88
long lhs = o1.getTimeInMillis();
89
long rhs = o2.getTimeInMillis();
90
if (lhs > rhs) return -1;
91
if (lhs < rhs) return 1;
97
private RunList(Iterable<R> c) {
102
public Iterator<R> iterator() {
103
return base.iterator();
107
* @deprecated as of 1.485
108
* {@link RunList}, despite its name, should be really used as {@link Iterable}, not as {@link List}.
124
* @deprecated as of 1.485
125
* {@link RunList}, despite its name, should be really used as {@link Iterable}, not as {@link List}.
128
public R get(int index) {
129
return Iterators.get(iterator(),index);
133
* {@link AbstractList#subList(int, int)} isn't very efficient on our {@link Iterable} based implementation.
134
* In fact the range check alone would require us to iterate all the elements,
135
* so we'd be better off just copying into ArrayList.
138
public List<R> subList(int fromIndex, int toIndex) {
139
List<R> r = new ArrayList<R>();
140
Iterator<R> itr = iterator();
141
Iterators.skip(itr,fromIndex);
142
for (int i=toIndex-fromIndex; i>0; i--) {
149
public int indexOf(Object o) {
160
public int lastIndexOf(Object o) {
172
public boolean isEmpty() {
173
return !iterator().hasNext();
176
public R getFirstBuild() {
181
public R getLastBuild() {
182
Iterator<R> itr = iterator();
183
return itr.hasNext() ? itr.next() : null;
186
public static <R extends Run>
187
RunList<R> fromRuns(Collection<? extends R> runs) {
188
return new RunList<R>((Iterable)runs);
192
* Returns elements that satisfy the given predicate.
194
// for compatibility reasons, this method doesn't create a new list but updates the current one
195
private RunList<R> filter(Predicate<R> predicate) {
198
base = Iterables.filter(base,predicate);
203
* Returns the first streak of the elements that satisfy the given predicate.
205
* For example, {@code filter([1,2,3,4],odd)==[1,3]} but {@code limit([1,2,3,4],odd)==[1]}.
207
private RunList<R> limit(final CountingPredicate<R> predicate) {
210
final Iterable<R> nested = base;
211
base = new Iterable<R>() {
212
public Iterator<R> iterator() {
213
return hudson.util.Iterators.limit(nested.iterator(),predicate);
217
public String toString() {
218
return Iterables.toString(this);
224
public RunList<R> limit(final int n) {
225
return limit(new CountingPredicate<R>() {
226
public boolean apply(int index, R input) {
233
* Filter the list to non-successful builds only.
235
public RunList<R> failureOnly() {
236
return filter(new Predicate<R>() {
237
public boolean apply(R r) {
238
return r.getResult()!=Result.SUCCESS;
244
* Filter the list to builds on a single node only
246
public RunList<R> node(final Node node) {
247
return filter(new Predicate<R>() {
248
public boolean apply(R r) {
249
return (r instanceof AbstractBuild) && ((AbstractBuild)r).getBuiltOn()==node;
255
* Filter the list to regression builds only.
257
public RunList<R> regressionOnly() {
258
return filter(new Predicate<R>() {
259
public boolean apply(R r) {
260
return r.getBuildStatusSummary().isWorse;
266
* Filter the list by timestamp.
270
public RunList<R> byTimestamp(final long start, final long end) {
272
limit(new CountingPredicate<R>() {
273
public boolean apply(int index,R r) {
274
return r.getTimeInMillis()<end;
276
}).filter(new Predicate<R>() {
277
public boolean apply(R r) {
278
return start<=r.getTimeInMillis();
284
* Reduce the size of the list by only leaving relatively new ones.
285
* This also removes on-going builds, as RSS cannot be used to publish information
288
public RunList<R> newBuilds() {
289
GregorianCalendar cal = new GregorianCalendar();
290
cal.add(Calendar.DAY_OF_YEAR, -7);
291
final long t = cal.getTimeInMillis();
293
// can't publish on-going builds
294
return filter(new Predicate<R>() {
295
public boolean apply(R r) {
296
return !r.isBuilding();
299
// put at least 10 builds, but otherwise ignore old builds
300
.limit(new CountingPredicate<R>() {
301
public boolean apply(int index, R r) {
302
return index < 10 || r.getTimeInMillis() >= t;