View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.revwalk;
45  
46  import java.io.IOException;
47  
48  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
49  import org.eclipse.jgit.errors.MissingObjectException;
50  import org.eclipse.jgit.revwalk.filter.RevFilter;
51  
52  /**
53   * An ordered list of {@link org.eclipse.jgit.revwalk.RevCommit} subclasses.
54   *
55   * @param <E>
56   *            type of subclass of RevCommit the list is storing.
57   */
58  public class RevCommitList<E extends RevCommit> extends RevObjectList<E> {
59  	private RevWalk walker;
60  
61  	/** {@inheritDoc} */
62  	@Override
63  	public void clear() {
64  		super.clear();
65  		walker = null;
66  	}
67  
68  	/**
69  	 * Apply a flag to all commits matching the specified filter.
70  	 * <p>
71  	 * Same as <code>applyFlag(matching, flag, 0, size())</code>, but without
72  	 * the incremental behavior.
73  	 *
74  	 * @param matching
75  	 *            the filter to test commits with. If the filter includes a
76  	 *            commit it will have the flag set; if the filter does not
77  	 *            include the commit the flag will be unset.
78  	 * @param flag
79  	 *            the flag to apply (or remove). Applications are responsible
80  	 *            for allocating this flag from the source RevWalk.
81  	 * @throws java.io.IOException
82  	 *             revision filter needed to read additional objects, but an
83  	 *             error occurred while reading the pack files or loose objects
84  	 *             of the repository.
85  	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
86  	 *             revision filter needed to read additional objects, but an
87  	 *             object was not of the correct type. Repository corruption may
88  	 *             have occurred.
89  	 * @throws org.eclipse.jgit.errors.MissingObjectException
90  	 *             revision filter needed to read additional objects, but an
91  	 *             object that should be present was not found. Repository
92  	 *             corruption may have occurred.
93  	 */
94  	public void applyFlag(RevFilter matching, RevFlag flag)
95  			throws MissingObjectException, IncorrectObjectTypeException,
96  			IOException {
97  		applyFlag(matching, flag, 0, size());
98  	}
99  
100 	/**
101 	 * Apply a flag to all commits matching the specified filter.
102 	 * <p>
103 	 * This version allows incremental testing and application, such as from a
104 	 * background thread that needs to periodically halt processing and send
105 	 * updates to the UI.
106 	 *
107 	 * @param matching
108 	 *            the filter to test commits with. If the filter includes a
109 	 *            commit it will have the flag set; if the filter does not
110 	 *            include the commit the flag will be unset.
111 	 * @param flag
112 	 *            the flag to apply (or remove). Applications are responsible
113 	 *            for allocating this flag from the source RevWalk.
114 	 * @param rangeBegin
115 	 *            first commit within the list to begin testing at, inclusive.
116 	 *            Must not be negative, but may be beyond the end of the list.
117 	 * @param rangeEnd
118 	 *            last commit within the list to end testing at, exclusive. If
119 	 *            smaller than or equal to <code>rangeBegin</code> then no
120 	 *            commits will be tested.
121 	 * @throws java.io.IOException
122 	 *             revision filter needed to read additional objects, but an
123 	 *             error occurred while reading the pack files or loose objects
124 	 *             of the repository.
125 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
126 	 *             revision filter needed to read additional objects, but an
127 	 *             object was not of the correct type. Repository corruption may
128 	 *             have occurred.
129 	 * @throws org.eclipse.jgit.errors.MissingObjectException
130 	 *             revision filter needed to read additional objects, but an
131 	 *             object that should be present was not found. Repository
132 	 *             corruption may have occurred.
133 	 */
134 	public void applyFlag(final RevFilter matching, final RevFlag flag,
135 			int rangeBegin, int rangeEnd) throws MissingObjectException,
136 			IncorrectObjectTypeException, IOException {
137 		final RevWalk w = flag.getRevWalk();
138 		rangeEnd = Math.min(rangeEnd, size());
139 		while (rangeBegin < rangeEnd) {
140 			int index = rangeBegin;
141 			Block s = contents;
142 			while (s.shift > 0) {
143 				final int i = index >> s.shift;
144 				index -= i << s.shift;
145 				s = (Block) s.contents[i];
146 			}
147 
148 			while (rangeBegin++ < rangeEnd && index < BLOCK_SIZE) {
149 				final RevCommit c = (RevCommit) s.contents[index++];
150 				if (matching.include(w, c))
151 					c.add(flag);
152 				else
153 					c.remove(flag);
154 			}
155 		}
156 	}
157 
158 	/**
159 	 * Remove the given flag from all commits.
160 	 * <p>
161 	 * Same as <code>clearFlag(flag, 0, size())</code>, but without the
162 	 * incremental behavior.
163 	 *
164 	 * @param flag
165 	 *            the flag to remove. Applications are responsible for
166 	 *            allocating this flag from the source RevWalk.
167 	 */
168 	public void clearFlag(RevFlag flag) {
169 		clearFlag(flag, 0, size());
170 	}
171 
172 	/**
173 	 * Remove the given flag from all commits.
174 	 * <p>
175 	 * This method is actually implemented in terms of:
176 	 * <code>applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd)</code>.
177 	 *
178 	 * @param flag
179 	 *            the flag to remove. Applications are responsible for
180 	 *            allocating this flag from the source RevWalk.
181 	 * @param rangeBegin
182 	 *            first commit within the list to begin testing at, inclusive.
183 	 *            Must not be negative, but may be beyond the end of the list.
184 	 * @param rangeEnd
185 	 *            last commit within the list to end testing at, exclusive. If
186 	 *            smaller than or equal to <code>rangeBegin</code> then no
187 	 *            commits will be tested.
188 	 */
189 	public void clearFlag(final RevFlag flag, final int rangeBegin,
190 			final int rangeEnd) {
191 		try {
192 			applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd);
193 		} catch (IOException e) {
194 			// Never happen. The filter we use does not throw any
195 			// exceptions, for any reason.
196 		}
197 	}
198 
199 	/**
200 	 * Find the next commit that has the given flag set.
201 	 *
202 	 * @param flag
203 	 *            the flag to test commits against.
204 	 * @param begin
205 	 *            first commit index to test at. Applications may wish to begin
206 	 *            at 0, to test the first commit in the list.
207 	 * @return index of the first commit at or after index <code>begin</code>
208 	 *         that has the specified flag set on it; -1 if no match is found.
209 	 */
210 	public int indexOf(RevFlag flag, int begin) {
211 		while (begin < size()) {
212 			int index = begin;
213 			Block s = contents;
214 			while (s.shift > 0) {
215 				final int i = index >> s.shift;
216 				index -= i << s.shift;
217 				s = (Block) s.contents[i];
218 			}
219 
220 			while (begin++ < size() && index < BLOCK_SIZE) {
221 				final RevCommit c = (RevCommit) s.contents[index++];
222 				if (c.has(flag))
223 					return begin;
224 			}
225 		}
226 		return -1;
227 	}
228 
229 	/**
230 	 * Find the next commit that has the given flag set.
231 	 *
232 	 * @param flag
233 	 *            the flag to test commits against.
234 	 * @param begin
235 	 *            first commit index to test at. Applications may wish to begin
236 	 *            at <code>size()-1</code>, to test the last commit in the
237 	 *            list.
238 	 * @return index of the first commit at or before index <code>begin</code>
239 	 *         that has the specified flag set on it; -1 if no match is found.
240 	 */
241 	public int lastIndexOf(RevFlag flag, int begin) {
242 		begin = Math.min(begin, size() - 1);
243 		while (begin >= 0) {
244 			int index = begin;
245 			Block s = contents;
246 			while (s.shift > 0) {
247 				final int i = index >> s.shift;
248 				index -= i << s.shift;
249 				s = (Block) s.contents[i];
250 			}
251 
252 			while (begin-- >= 0 && index >= 0) {
253 				final RevCommit c = (RevCommit) s.contents[index--];
254 				if (c.has(flag))
255 					return begin;
256 			}
257 		}
258 		return -1;
259 	}
260 
261 	/**
262 	 * Set the revision walker this list populates itself from.
263 	 *
264 	 * @param w
265 	 *            the walker to populate from.
266 	 * @see #fillTo(int)
267 	 */
268 	public void source(RevWalk w) {
269 		walker = w;
270 	}
271 
272 	/**
273 	 * Is this list still pending more items?
274 	 *
275 	 * @return true if {@link #fillTo(int)} might be able to extend the list
276 	 *         size when called.
277 	 */
278 	public boolean isPending() {
279 		return walker != null;
280 	}
281 
282 	/**
283 	 * Ensure this list contains at least a specified number of commits.
284 	 * <p>
285 	 * The revision walker specified by {@link #source(RevWalk)} is pumped until
286 	 * the given number of commits are contained in this list. If there are
287 	 * fewer total commits available from the walk then the method will return
288 	 * early. Callers can test the final size of the list by {@link #size()} to
289 	 * determine if the high water mark specified was met.
290 	 *
291 	 * @param highMark
292 	 *            number of commits the caller wants this list to contain when
293 	 *            the fill operation is complete.
294 	 * @throws java.io.IOException
295 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
296 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
297 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
298 	 * @throws org.eclipse.jgit.errors.MissingObjectException
299 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
300 	 */
301 	@SuppressWarnings("unchecked")
302 	public void fillTo(int highMark) throws MissingObjectException,
303 			IncorrectObjectTypeException, IOException {
304 		if (walker == null || size > highMark)
305 			return;
306 
307 		RevCommit c = walker.next();
308 		if (c == null) {
309 			walker = null;
310 			return;
311 		}
312 		enter(size, (E) c);
313 		add((E) c);
314 
315 		while (size <= highMark) {
316 			int index = size;
317 			Block s = contents;
318 			while (index >> s.shift >= BLOCK_SIZE) {
319 				s = new Block(s.shift + BLOCK_SHIFT);
320 				s.contents[0] = contents;
321 				contents = s;
322 			}
323 			while (s.shift > 0) {
324 				final int i = index >> s.shift;
325 				index -= i << s.shift;
326 				if (s.contents[i] == null)
327 					s.contents[i] = new Block(s.shift - BLOCK_SHIFT);
328 				s = (Block) s.contents[i];
329 			}
330 
331 			final Object[] dst = s.contents;
332 			while (size <= highMark && index < BLOCK_SIZE) {
333 				c = walker.next();
334 				if (c == null) {
335 					walker = null;
336 					return;
337 				}
338 				enter(size++, (E) c);
339 				dst[index++] = c;
340 			}
341 		}
342 	}
343 
344 	/**
345 	 * Ensures all commits until the given commit are loaded. The revision
346 	 * walker specified by {@link #source(RevWalk)} is pumped until the
347 	 * specified commit is loaded. Callers can test the final size of the list
348 	 * by {@link #size()} to determine if the high water mark specified was met.
349 	 * <p>
350 	 *
351 	 * @param commitToLoad
352 	 *            commit the caller wants this list to contain when the fill
353 	 *            operation is complete.
354 	 * @param highMark
355 	 *            maximum number of commits the caller wants this list to
356 	 *            contain when the fill operation is complete. If highMark is 0
357 	 *            the walk is pumped until the specified commit or the end of
358 	 *            the walk is reached.
359 	 * @throws java.io.IOException
360 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
361 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
362 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
363 	 * @throws org.eclipse.jgit.errors.MissingObjectException
364 	 *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
365 	 */
366 	@SuppressWarnings("unchecked")
367 	public void fillTo(RevCommit commitToLoad, int highMark)
368 			throws MissingObjectException, IncorrectObjectTypeException,
369 			IOException {
370 		if (walker == null || commitToLoad == null
371 				|| (highMark > 0 && size > highMark))
372 			return;
373 
374 		RevCommit c = walker.next();
375 		if (c == null) {
376 			walker = null;
377 			return;
378 		}
379 		enter(size, (E) c);
380 		add((E) c);
381 
382 		while ((highMark == 0 || size <= highMark) && !c.equals(commitToLoad)) {
383 			int index = size;
384 			Block s = contents;
385 			while (index >> s.shift >= BLOCK_SIZE) {
386 				s = new Block(s.shift + BLOCK_SHIFT);
387 				s.contents[0] = contents;
388 				contents = s;
389 			}
390 			while (s.shift > 0) {
391 				final int i = index >> s.shift;
392 				index -= i << s.shift;
393 				if (s.contents[i] == null)
394 					s.contents[i] = new Block(s.shift - BLOCK_SHIFT);
395 				s = (Block) s.contents[i];
396 			}
397 
398 			final Object[] dst = s.contents;
399 			while ((highMark == 0 || size <= highMark) && index < BLOCK_SIZE
400 					&& !c.equals(commitToLoad)) {
401 				c = walker.next();
402 				if (c == null) {
403 					walker = null;
404 					return;
405 				}
406 				enter(size++, (E) c);
407 				dst[index++] = c;
408 			}
409 		}
410 	}
411 
412 	/**
413 	 * Optional callback invoked when commits enter the list by fillTo.
414 	 * <p>
415 	 * This method is only called during {@link #fillTo(int)}.
416 	 *
417 	 * @param index
418 	 *            the list position this object will appear at.
419 	 * @param e
420 	 *            the object being added (or set) into the list.
421 	 */
422 	protected void enter(int index, E e) {
423 		// Do nothing by default.
424 	}
425 }