001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.wc;
013:
014: import java.io.File;
015: import java.util.Collection;
016: import java.util.HashMap;
017: import java.util.Iterator;
018: import java.util.Map;
019: import java.util.TreeSet;
020:
021: import org.tmatesoft.svn.core.ISVNDirEntryHandler;
022: import org.tmatesoft.svn.core.ISVNLogEntryHandler;
023: import org.tmatesoft.svn.core.SVNAnnotationGenerator;
024: import org.tmatesoft.svn.core.SVNDirEntry;
025: import org.tmatesoft.svn.core.SVNErrorCode;
026: import org.tmatesoft.svn.core.SVNErrorMessage;
027: import org.tmatesoft.svn.core.SVNException;
028: import org.tmatesoft.svn.core.SVNLock;
029: import org.tmatesoft.svn.core.SVNLogEntry;
030: import org.tmatesoft.svn.core.SVNNodeKind;
031: import org.tmatesoft.svn.core.SVNURL;
032: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
033: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
034: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
035: import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
036: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
037: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
038: import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
039: import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
040: import org.tmatesoft.svn.core.io.SVNRepository;
041:
042: /**
043: * The <b>SVNLogClient</b> class is intended for such purposes as getting
044: * revisions history, browsing repository entries and annotating file contents.
045: *
046: * <p>
047: * Here's a list of the <b>SVNLogClient</b>'s methods
048: * matched against corresponing commands of the <b>SVN</b> command line
049: * client:
050: *
051: * <table cellpadding="3" cellspacing="1" border="0" width="40%" bgcolor="#999933">
052: * <tr bgcolor="#ADB8D9" align="left">
053: * <td><b>SVNKit</b></td>
054: * <td><b>Subversion</b></td>
055: * </tr>
056: * <tr bgcolor="#EAEAEA" align="left">
057: * <td>doLog()</td><td>'svn log'</td>
058: * </tr>
059: * <tr bgcolor="#EAEAEA" align="left">
060: * <td>doList()</td><td>'svn list'</td>
061: * </tr>
062: * <tr bgcolor="#EAEAEA" align="left">
063: * <td>doAnnotate()</td><td>'svn blame'</td>
064: * </tr>
065: * </table>
066: *
067: * @version 1.1.1
068: * @author TMate Software Ltd.
069: */
070: public class SVNLogClient extends SVNBasicClient {
071:
072: private SVNDiffOptions myDiffOptions;
073:
074: /**
075: * Constructs and initializes an <b>SVNLogClient</b> object
076: * with the specified run-time configuration and authentication
077: * drivers.
078: *
079: * <p>
080: * If <code>options</code> is <span class="javakeyword">null</span>,
081: * then this <b>SVNLogClient</b> will be using a default run-time
082: * configuration driver which takes client-side settings from the
083: * default SVN's run-time configuration area but is not able to
084: * change those settings (read more on {@link ISVNOptions} and {@link SVNWCUtil}).
085: *
086: * <p>
087: * If <code>authManager</code> is <span class="javakeyword">null</span>,
088: * then this <b>SVNLogClient</b> will be using a default authentication
089: * and network layers driver (see {@link SVNWCUtil#createDefaultAuthenticationManager()})
090: * which uses server-side settings and auth storage from the
091: * default SVN's run-time configuration area (or system properties
092: * if that area is not found).
093: *
094: * @param authManager an authentication and network layers driver
095: * @param options a run-time configuration options driver
096: */
097: public SVNLogClient(ISVNAuthenticationManager authManager,
098: ISVNOptions options) {
099: super (authManager, options);
100: }
101:
102: public SVNLogClient(ISVNRepositoryPool repositoryPool,
103: ISVNOptions options) {
104: super (repositoryPool, options);
105: }
106:
107: /**
108: * Sets diff options for this client to use in annotate operations.
109: *
110: * @param diffOptions diff options object
111: */
112: public void setDiffOptions(SVNDiffOptions diffOptions) {
113: myDiffOptions = diffOptions;
114: }
115:
116: /**
117: * Gets the diff options that are used in annotate operations
118: * by this client. Creates a new one if none was used before.
119: *
120: * @return diff options
121: */
122: public SVNDiffOptions getDiffOptions() {
123: if (myDiffOptions == null) {
124: myDiffOptions = new SVNDiffOptions();
125: }
126: return myDiffOptions;
127: }
128:
129: /**
130: * Obtains annotation information for each file text line from a repository
131: * (using a Working Copy path to get a corresponding URL) and passes it to a
132: * provided annotation handler.
133: *
134: * <p>
135: * If <code>startRevision</code> is invalid (for example,
136: * <code>startRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED}) then
137: * it's set to revision 1.
138: *
139: * <p>
140: * Calling this method is equivalent to
141: * <code>doAnnotate(path, pegRevision, startRevision, endRevision, false, handler)</code>.
142: *
143: * @param path a WC file item to be annotated
144: * @param pegRevision a revision in which <code>path</code> is first looked up
145: * in the repository
146: * @param startRevision a revision for an operation to start from
147: * @param endRevision a revision for an operation to stop at
148: * @param handler a caller's handler to process annotation information
149: * @throws SVNException if <code>startRevision > endRevision</code>
150: * @see #doAnnotate(SVNURL, SVNRevision, SVNRevision, SVNRevision, ISVNAnnotateHandler)
151: */
152: public void doAnnotate(File path, SVNRevision pegRevision,
153: SVNRevision startRevision, SVNRevision endRevision,
154: ISVNAnnotateHandler handler) throws SVNException {
155: doAnnotate(path, pegRevision, startRevision, endRevision,
156: false, handler);
157: }
158:
159: /**
160: * Obtains annotation information for each file text line from a repository
161: * (using a Working Copy path to get a corresponding URL) and passes it to a
162: * provided annotation handler.
163: *
164: * <p>
165: * If <code>startRevision</code> is invalid (for example,
166: * <code>startRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED}) then
167: * it's set to revision 1.
168: *
169: * @param path a WC file item to be annotated
170: * @param pegRevision a revision in which <code>path</code> is first looked up
171: * in the repository
172: * @param startRevision a revision for an operation to start from
173: * @param endRevision a revision for an operation to stop at
174: * @param force forces operation to run (all files to be treated as
175: * text, no matter what SVNKit has inferred from the mime-type
176: * property)
177: * @param handler a caller's handler to process annotation information
178: * @throws SVNException
179: * @since 1.1
180: */
181: public void doAnnotate(File path, SVNRevision pegRevision,
182: SVNRevision startRevision, SVNRevision endRevision,
183: boolean force, ISVNAnnotateHandler handler)
184: throws SVNException {
185: if (startRevision == null || !startRevision.isValid()) {
186: startRevision = SVNRevision.create(1);
187: }
188: if (endRevision == null || !endRevision.isValid()) {
189: endRevision = pegRevision;
190: }
191: SVNRepository repos = createRepository(null, path, pegRevision,
192: endRevision);
193: long endRev = getRevisionNumber(endRevision, repos, path);
194: long startRev = getRevisionNumber(startRevision, repos, path);
195: if (endRev < startRev) {
196: SVNErrorManager.error(SVNErrorMessage.create(
197: SVNErrorCode.CLIENT_BAD_REVISION,
198: "Start revision must precede end revision"));
199: }
200: File tmpFile = new File(path.getParentFile(), SVNFileUtil
201: .getAdminDirectoryName());
202: tmpFile = new File(tmpFile, "tmp/text-base");
203: if (!tmpFile.isDirectory()) {
204: tmpFile = SVNFileUtil.createTempDirectory("annotate");
205: }
206: doAnnotate(path.getAbsolutePath(), startRev, tmpFile, repos,
207: endRev, force, handler, null);
208: }
209:
210: /**
211: * Obtains annotation information for each file text line from a repository
212: * and passes it to a provided annotation handler.
213: *
214: * <p>
215: * If <code>startRevision</code> is invalid (for example,
216: * <code>startRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED}) then
217: * it's set to revision 1.
218: *
219: * <p>
220: * Calling this method is equivalent to
221: * <code>doAnnotate(url, pegRevision, startRevision, endRevision, handler, null)</code>.
222: *
223: * @param url a URL of a text file that is to be annotated
224: * @param pegRevision a revision in which <code>path</code> is first looked up
225: * in the repository
226: * @param startRevision a revision for an operation to start from
227: * @param endRevision a revision for an operation to stop at
228: * @param handler a caller's handler to process annotation information
229: * @throws SVNException if <code>startRevision > endRevision</code>
230: * @see #doAnnotate(File, SVNRevision, SVNRevision, SVNRevision, ISVNAnnotateHandler)
231: */
232: public void doAnnotate(SVNURL url, SVNRevision pegRevision,
233: SVNRevision startRevision, SVNRevision endRevision,
234: ISVNAnnotateHandler handler) throws SVNException {
235: doAnnotate(url, pegRevision, startRevision, endRevision,
236: handler, null);
237: }
238:
239: /**
240: * Obtains annotation information for each file text line from a repository
241: * and passes it to a provided annotation handler.
242: *
243: * <p>
244: * If <code>startRevision</code> is invalid (for example,
245: * <code>startRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED}) then
246: * it's set to revision 1.
247: *
248: * <p>
249: * Calling this method is equivalent to
250: * <code>doAnnotate(url, pegRevision, startRevision, endRevision, false, handler, inputEncoding)</code>.
251: *
252: * @param url a URL of a text file that is to be annotated
253: * @param pegRevision a revision in which <code>path</code> is first looked up
254: * in the repository
255: * @param startRevision a revision for an operation to start from
256: * @param endRevision a revision for an operation to stop at
257: * @param handler a caller's handler to process annotation information
258: * @param inputEncoding a desired character set (encoding) of text lines
259: * @throws SVNException
260: */
261: public void doAnnotate(SVNURL url, SVNRevision pegRevision,
262: SVNRevision startRevision, SVNRevision endRevision,
263: ISVNAnnotateHandler handler, String inputEncoding)
264: throws SVNException {
265: doAnnotate(url, pegRevision, startRevision, endRevision, false,
266: handler, inputEncoding);
267: }
268:
269: /**
270: * Obtains annotation information for each file text line from a repository
271: * and passes it to a provided annotation handler.
272: *
273: * <p>
274: * If <code>startRevision</code> is invalid (for example,
275: * <code>startRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED}) then
276: * it's set to revision 1.
277: *
278: * <p>
279: * If <code>inputEncoding</code> is <span class="javakeyword">null</span> then
280: * <span class="javastring">"file.encoding"</span> system property is used.
281: *
282: * @param url a URL of a text file that is to be annotated
283: * @param pegRevision a revision in which <code>path</code> is first looked up
284: * in the repository
285: * @param startRevision a revision for an operation to start from
286: * @param endRevision a revision for an operation to stop at
287: * @param force forces operation to run (all files to be treated as
288: * text, no matter what SVNKit has inferred from the mime-type
289: * property)
290: * @param handler a caller's handler to process annotation information
291: * @param inputEncoding a desired character set (encoding) of text lines
292: * @throws SVNException
293: * @since 1.1
294: */
295: public void doAnnotate(SVNURL url, SVNRevision pegRevision,
296: SVNRevision startRevision, SVNRevision endRevision,
297: boolean force, ISVNAnnotateHandler handler,
298: String inputEncoding) throws SVNException {
299: if (startRevision == null || !startRevision.isValid()) {
300: startRevision = SVNRevision.create(1);
301: }
302: if (endRevision == null || !endRevision.isValid()) {
303: endRevision = pegRevision;
304: }
305: SVNRepository repos = createRepository(url, null, pegRevision,
306: endRevision);
307: long endRev = getRevisionNumber(endRevision, repos, null);
308: long startRev = getRevisionNumber(startRevision, repos, null);
309: if (endRev < startRev) {
310: SVNErrorManager.error(SVNErrorMessage.create(
311: SVNErrorCode.CLIENT_BAD_REVISION,
312: "Start revision must precede end revision"));
313: }
314: File tmpFile = SVNFileUtil.createTempDirectory("annotate");
315: doAnnotate(repos.getLocation().toDecodedString(), startRev,
316: tmpFile, repos, endRev, force, handler, inputEncoding);
317: }
318:
319: private void doAnnotate(String path, long startRev, File tmpFile,
320: SVNRepository repos, long endRev, boolean force,
321: ISVNAnnotateHandler handler, String inputEncoding)
322: throws SVNException {
323: SVNAnnotationGenerator generator = new SVNAnnotationGenerator(
324: path, tmpFile, startRev, force, getDiffOptions(), this );
325: try {
326: repos.getFileRevisions("", startRev > 0 ? startRev - 1
327: : startRev, endRev, generator);
328: generator.reportAnnotations(handler, inputEncoding);
329: } finally {
330: generator.dispose();
331: SVNFileUtil.deleteAll(tmpFile, !"text-base".equals(tmpFile
332: .getName()), null);
333: }
334: }
335:
336: /**
337: * Gets commit log messages with other revision specific
338: * information from a repository (using Working Copy paths to get
339: * corresponding URLs) and passes them to a log entry handler for
340: * processing. Useful for observing the history of affected paths,
341: * author, date and log comments information per revision.
342: *
343: * <p>
344: * If <code>paths</code> is not empty then the result will be restricted
345: * to only those revisions from the specified range [<code>startRevision</code>, <code>endRevision</code>],
346: * where <code>paths</code> were changed in the repository. To cover the
347: * entire range set <code>paths</code> just to an empty array:
348: * <pre class="javacode">
349: * logClient.doLog(<span class="javakeyword">new</span> File[]{<span class="javastring">""</span>},..);</pre><br />
350: * <p>
351: * If <code>startRevision</code> is valid but <code>endRevision</code> is
352: * not (for example, <code>endRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED})
353: * then <code>endRevision</code> is equated to <code>startRevision</code>.
354: *
355: * <p>
356: * If <code>startRevision</code> is invalid (for example, {@link SVNRevision#UNDEFINED UNDEFINED})
357: * then it's equated to {@link SVNRevision#BASE BASE}. In this case if <code>endRevision</code> is
358: * also invalid, then <code>endRevision</code> is set to revision 0.
359: *
360: * <p>
361: * Calling this method is equivalent to
362: * <code>doLog(paths, SVNRevision.UNDEFINED, startRevision, endRevision, stopOnCopy, reportPaths, limit, handler)</code>.
363: *
364: * @param paths an array of Working Copy paths,
365: * should not be <span class="javakeyword">null</span>
366: * @param startRevision a revision for an operation to start from (including
367: * this revision)
368: * @param endRevision a revision for an operation to stop at (including
369: * this revision)
370: * @param stopOnCopy <span class="javakeyword">true</span> not to cross
371: * copies while traversing history, otherwise copies history
372: * will be also included into processing
373: * @param reportPaths <span class="javakeyword">true</span> to report
374: * of all changed paths for every revision being processed
375: * (those paths will be available by calling
376: * {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
377: * @param limit a maximum number of log entries to be processed
378: * @param handler a caller's log entry handler
379: * @throws SVNException if one of the following is true:
380: * <ul>
381: * <li>a path is not under version control
382: * <li>can not obtain a URL of a WC path - there's no such
383: * entry in the Working Copy
384: * <li><code>paths</code> contain entries that belong to
385: * different repositories
386: * </ul>
387: * @see #doLog(SVNURL, String[], SVNRevision, SVNRevision, SVNRevision, boolean, boolean, long, ISVNLogEntryHandler)
388: */
389: public void doLog(File[] paths, SVNRevision startRevision,
390: SVNRevision endRevision, boolean stopOnCopy,
391: boolean reportPaths, long limit,
392: final ISVNLogEntryHandler handler) throws SVNException {
393: doLog(paths, SVNRevision.UNDEFINED, startRevision, endRevision,
394: stopOnCopy, reportPaths, limit, handler);
395: }
396:
397: /**
398: * Gets commit log messages with other revision specific
399: * information from a repository (using Working Copy paths to get
400: * corresponding URLs) and passes them to a log entry handler for
401: * processing. Useful for observing the history of affected paths,
402: * author, date and log comments information per revision.
403: *
404: * <p>
405: * If <code>paths</code> is not empty then the result will be restricted
406: * to only those revisions from the specified range [<code>startRevision</code>, <code>endRevision</code>],
407: * where <code>paths</code> were changed in the repository. To cover the
408: * entire range set <code>paths</code> just to an empty array:
409: * <pre class="javacode">
410: * logClient.doLog(<span class="javakeyword">new</span> File[]{<span class="javastring">""</span>},..);</pre><br />
411: * <p>
412: * If <code>startRevision</code> is valid but <code>endRevision</code> is
413: * not (for example, <code>endRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED})
414: * then <code>endRevision</code> is equated to <code>startRevision</code>.
415: *
416: * <p>
417: * If <code>startRevision</code> is invalid (for example, {@link SVNRevision#UNDEFINED UNDEFINED})
418: * then it's equated to {@link SVNRevision#BASE BASE}. In this case if <code>endRevision</code> is
419: * also invalid, then <code>endRevision</code> is set to revision 0.
420: *
421: * @param paths an array of Working Copy paths,
422: * should not be <span class="javakeyword">null</span>
423: * @param pegRevision a revision in which <code>path</code> is first looked up
424: * in the repository
425: * @param startRevision a revision for an operation to start from (including
426: * this revision)
427: * @param endRevision a revision for an operation to stop at (including
428: * this revision)
429: * @param stopOnCopy <span class="javakeyword">true</span> not to cross
430: * copies while traversing history, otherwise copies history
431: * will be also included into processing
432: * @param reportPaths <span class="javakeyword">true</span> to report
433: * of all changed paths for every revision being processed
434: * (those paths will be available by calling
435: * {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
436: * @param limit a maximum number of log entries to be processed
437: * @param handler a caller's log entry handler
438: * @throws SVNException if one of the following is true:
439: * <ul>
440: * <li>a path is not under version control
441: * <li>can not obtain a URL of a WC path - there's no such
442: * entry in the Working Copy
443: * <li><code>paths</code> contain entries that belong to
444: * different repositories
445: * </ul>
446: */
447: public void doLog(File[] paths, SVNRevision pegRevision,
448: SVNRevision startRevision, SVNRevision endRevision,
449: boolean stopOnCopy, boolean reportPaths, long limit,
450: final ISVNLogEntryHandler handler) throws SVNException {
451: if (paths == null || paths.length == 0) {
452: return;
453: }
454: if (startRevision.isValid() && !endRevision.isValid()) {
455: endRevision = startRevision;
456: } else if (!startRevision.isValid()) {
457: if (!pegRevision.isValid()) {
458: startRevision = SVNRevision.BASE;
459: } else {
460: startRevision = pegRevision;
461: }
462: if (!endRevision.isValid()) {
463: endRevision = SVNRevision.create(0);
464: }
465: }
466: ISVNLogEntryHandler wrappingHandler = new ISVNLogEntryHandler() {
467: public void handleLogEntry(SVNLogEntry logEntry)
468: throws SVNException {
469: checkCancelled();
470: handler.handleLogEntry(logEntry);
471: }
472: };
473: SVNURL[] urls = new SVNURL[paths.length];
474: SVNWCAccess wcAccess = createWCAccess();
475: for (int i = 0; i < paths.length; i++) {
476: checkCancelled();
477: File path = paths[i];
478: wcAccess.probeOpen(path, false, 0);
479: SVNEntry entry = wcAccess.getEntry(path, false);
480: if (entry == null) {
481: SVNErrorMessage err = SVNErrorMessage.create(
482: SVNErrorCode.UNVERSIONED_RESOURCE,
483: "''{0}'' is not under version control", path);
484: SVNErrorManager.error(err);
485: }
486: if (entry.getURL() == null) {
487: SVNErrorMessage err = SVNErrorMessage.create(
488: SVNErrorCode.ENTRY_MISSING_URL,
489: "Entry ''{0}'' has no URL", path);
490: SVNErrorManager.error(err);
491: }
492: urls[i] = entry.getSVNURL();
493: wcAccess.closeAdminArea(path);
494: }
495: if (urls.length == 0) {
496: return;
497: }
498: Collection targets = new TreeSet();
499: SVNURL baseURL = SVNURLUtil.condenceURLs(urls, targets, true);
500: if (baseURL == null) {
501: SVNErrorMessage err = SVNErrorMessage
502: .create(SVNErrorCode.ILLEGAL_TARGET,
503: "target log paths belong to different repositories");
504: SVNErrorManager.error(err);
505: }
506: if (targets.isEmpty()) {
507: targets.add("");
508: }
509: SVNRevision rev = SVNRevision.UNDEFINED;
510: if (startRevision.getNumber() >= 0
511: && endRevision.getNumber() >= 0) {
512: rev = startRevision.getNumber() > endRevision.getNumber() ? startRevision
513: : endRevision;
514: } else if (startRevision.getDate() != null
515: && endRevision.getDate() != null) {
516: rev = startRevision.getDate().compareTo(
517: endRevision.getDate()) > 0 ? startRevision
518: : endRevision;
519: }
520: SVNRepository repos = rev.isValid() ? //!startRevision.isLocal() && !pegRevision.isLocal() ?
521: createRepository(baseURL, null, pegRevision, rev)
522: : createRepository(baseURL, true);
523: String[] targetPaths = (String[]) targets
524: .toArray(new String[targets.size()]);
525: for (int i = 0; i < targetPaths.length; i++) {
526: targetPaths[i] = SVNEncodingUtil.uriDecode(targetPaths[i]);
527: }
528: if (startRevision.isLocal() || endRevision.isLocal()) {
529: for (int i = 0; i < paths.length; i++) {
530: checkCancelled();
531: long startRev = getRevisionNumber(startRevision, repos,
532: paths[i]);
533: long endRev = getRevisionNumber(endRevision, repos,
534: paths[i]);
535: repos.log(targetPaths, startRev, endRev, reportPaths,
536: stopOnCopy, limit, wrappingHandler);
537: }
538: } else {
539: long startRev = getRevisionNumber(startRevision, repos,
540: null);
541: long endRev = getRevisionNumber(endRevision, repos, null);
542: repos.log(targetPaths, startRev, endRev, reportPaths,
543: stopOnCopy, limit, wrappingHandler);
544: }
545: }
546:
547: /**
548: * Gets commit log messages with other revision specific
549: * information from a repository and passes them to a log entry
550: * handler for processing. Useful for observing the history of
551: * affected paths, author, date and log comments information per revision.
552: *
553: * <p>
554: * If <code>paths</code> is <span class="javakeyword">null</span> or empty
555: * then <code>url</code> is the target path that is used to restrict the result
556: * to only those revisions from the specified range [<code>startRevision</code>, <code>endRevision</code>],
557: * where <code>url</code> was changed in the repository. Otherwise if <code>paths</code> is
558: * not empty then <code>url</code> is the root for all those paths (that are
559: * used for restricting the result).
560: *
561: * <p>
562: * If <code>startRevision</code> is valid but <code>endRevision</code> is
563: * not (for example, <code>endRevision = </code>{@link SVNRevision#UNDEFINED UNDEFINED})
564: * then <code>endRevision</code> is equated to <code>startRevision</code>.
565: *
566: * <p>
567: * If <code>startRevision</code> is invalid (for example, {@link SVNRevision#UNDEFINED UNDEFINED})
568: * then it's equated to {@link SVNRevision#HEAD HEAD}. In this case if <code>endRevision</code> is
569: * also invalid, then <code>endRevision</code> is set to revision 0.
570: *
571: *
572: * @param url a target URL
573: * @param paths an array of paths relative to the target
574: * <code>url</code>
575: * @param pegRevision a revision in which <code>url</code> is first looked up
576: * @param startRevision a revision for an operation to start from (including
577: * this revision)
578: * @param endRevision a revision for an operation to stop at (including
579: * this revision)
580: * @param stopOnCopy <span class="javakeyword">true</span> not to cross
581: * copies while traversing history, otherwise copies history
582: * will be also included into processing
583: * @param reportPaths <span class="javakeyword">true</span> to report
584: * of all changed paths for every revision being processed
585: * (those paths will be available by calling
586: * {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
587: * @param limit a maximum number of log entries to be processed
588: * @param handler a caller's log entry handler
589: * @throws SVNException
590: * @see #doLog(File[], SVNRevision, SVNRevision, boolean, boolean, long, ISVNLogEntryHandler)
591: * @since 1.1, new in Subversion 1.4
592: */
593: public void doLog(SVNURL url, String[] paths,
594: SVNRevision pegRevision, SVNRevision startRevision,
595: SVNRevision endRevision, boolean stopOnCopy,
596: boolean reportPaths, long limit,
597: final ISVNLogEntryHandler handler) throws SVNException {
598: if (startRevision.isValid() && !endRevision.isValid()) {
599: endRevision = startRevision;
600: } else if (!startRevision.isValid()) {
601: if (!pegRevision.isValid()) {
602: startRevision = SVNRevision.HEAD;
603: } else {
604: startRevision = pegRevision;
605: }
606: if (!endRevision.isValid()) {
607: endRevision = SVNRevision.create(0);
608: }
609: }
610: paths = paths == null || paths.length == 0 ? new String[] { "" }
611: : paths;
612: ISVNLogEntryHandler wrappingHandler = new ISVNLogEntryHandler() {
613: public void handleLogEntry(SVNLogEntry logEntry)
614: throws SVNException {
615: checkCancelled();
616: handler.handleLogEntry(logEntry);
617: }
618: };
619: SVNRevision rev = SVNRevision.UNDEFINED;
620: if (startRevision.getNumber() >= 0
621: && endRevision.getNumber() >= 0) {
622: rev = startRevision.getNumber() > endRevision.getNumber() ? startRevision
623: : endRevision;
624: } else if (startRevision.getDate() != null
625: && endRevision.getDate() != null) {
626: rev = startRevision.getDate().compareTo(
627: endRevision.getDate()) > 0 ? startRevision
628: : endRevision;
629: }
630: SVNRepository repos = rev.isValid() ? createRepository(url,
631: null, pegRevision, rev) : createRepository(url, true);
632: checkCancelled();
633: long startRev = getRevisionNumber(startRevision, repos, null);
634: checkCancelled();
635: long endRev = getRevisionNumber(endRevision, repos, null);
636: checkCancelled();
637: repos.log(paths, startRev, endRev, reportPaths, stopOnCopy,
638: limit, wrappingHandler);
639: }
640:
641: /**
642: * Browses directory entries from a repository (using Working
643: * Copy paths to get corresponding URLs) and uses the provided dir
644: * entry handler to process them.
645: *
646: * <p>
647: * On every entry that this method stops it gets some useful entry
648: * information which is packed into an {@link org.tmatesoft.svn.core.SVNDirEntry}
649: * object and passed to the <code>handler</code>'s
650: * {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry) handleDirEntry()} method.
651: *
652: * @param path a WC item to get its repository location
653: * @param pegRevision a revision in which the item's URL is first looked up
654: * @param revision a target revision
655: * @param fetchLocks <span class="javakeyword">true</span> to fetch locks
656: * information from a repository
657: * @param recursive <span class="javakeyword">true</span> to
658: * descend recursively (relevant for directories)
659: * @param handler a caller's directory entry handler (to process
660: * info on an entry)
661: * @throws SVNException
662: * @see #doList(SVNURL, SVNRevision, SVNRevision, boolean, ISVNDirEntryHandler)
663: */
664: public void doList(File path, SVNRevision pegRevision,
665: SVNRevision revision, boolean fetchLocks,
666: boolean recursive, ISVNDirEntryHandler handler)
667: throws SVNException {
668: if (revision == null || !revision.isValid()) {
669: revision = SVNRevision.BASE;
670: }
671: SVNRepository repos = createRepository(null, path, pegRevision,
672: revision);
673: long rev = getRevisionNumber(revision, repos, path);
674: doList(repos, rev, handler, fetchLocks, recursive);
675: }
676:
677: /**
678: * Browses directory entries from a repository (using Working
679: * Copy paths to get corresponding URLs) and uses the provided dir
680: * entry handler to process them.
681: *
682: * <p>
683: * On every entry that this method stops it gets some useful entry
684: * information which is packed into an {@link org.tmatesoft.svn.core.SVNDirEntry}
685: * object and passed to the <code>handler</code>'s
686: * {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry) handleDirEntry()} method.
687: *
688: * @param path a WC item to get its repository location
689: * @param pegRevision a revision in which the item's URL is first looked up
690: * @param revision a target revision
691: * @param recursive <span class="javakeyword">true</span> to
692: * descend recursively (relevant for directories)
693: * @param handler a caller's directory entry handler (to process
694: * info on an entry)
695: * @throws SVNException
696: * @see #doList(SVNURL, SVNRevision, SVNRevision, boolean, ISVNDirEntryHandler)
697: */
698: public void doList(File path, SVNRevision pegRevision,
699: SVNRevision revision, boolean recursive,
700: ISVNDirEntryHandler handler) throws SVNException {
701: doList(path, pegRevision, revision, false, recursive, handler);
702:
703: }
704:
705: /**
706: * Browses directory entries from a repository and uses the provided
707: * dir entry handler to process them. This method is
708: * especially useful when having no Working Copy.
709: *
710: * <p>
711: * On every entry that this method stops it gets some useful entry
712: * information which is packed into an {@link org.tmatesoft.svn.core.SVNDirEntry}
713: * object and passed to the <code>handler</code>'s
714: * {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry) handleDirEntry()} method.
715: *
716: * @param url a repository location to be "listed"
717: * @param pegRevision a revision in which the item's URL is first looked up
718: * @param revision a target revision
719: * @param fetchLocks <span class="javakeyword">true</span> to
720: * fetch locks information from repository
721: * @param recursive <span class="javakeyword">true</span> to
722: * descend recursively (relevant for directories)
723: * @param handler a caller's directory entry handler (to process
724: * info on an entry)
725: * @throws SVNException
726: * @see #doList(File, SVNRevision, SVNRevision, boolean, ISVNDirEntryHandler)
727: */
728: public void doList(SVNURL url, SVNRevision pegRevision,
729: SVNRevision revision, boolean fetchLocks,
730: boolean recursive, ISVNDirEntryHandler handler)
731: throws SVNException {
732: long[] pegRev = new long[] { -1 };
733: SVNRepository repos = createRepository(url, null, pegRevision,
734: revision, pegRev);
735: if (pegRev[0] < 0) {
736: pegRev[0] = getRevisionNumber(revision, repos, null);
737: }
738: doList(repos, pegRev[0], handler, fetchLocks, recursive);
739: }
740:
741: /**
742: * Browses directory entries from a repository and uses the provided
743: * dir entry handler to process them. This method is
744: * especially useful when having no Working Copy.
745: *
746: * <p>
747: * On every entry that this method stops it gets some useful entry
748: * information which is packed into an {@link org.tmatesoft.svn.core.SVNDirEntry}
749: * object and passed to the <code>handler</code>'s
750: * {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry) handleDirEntry()} method.
751: *
752: * @param url a repository location to be "listed"
753: * @param pegRevision a revision in which the item's URL is first looked up
754: * @param revision a target revision
755: * @param recursive <span class="javakeyword">true</span> to
756: * descend recursively (relevant for directories)
757: * @param handler a caller's directory entry handler (to process
758: * info on an entry)
759: * @throws SVNException
760: * @see #doList(File, SVNRevision, SVNRevision, boolean, ISVNDirEntryHandler)
761: */
762: public void doList(SVNURL url, SVNRevision pegRevision,
763: SVNRevision revision, boolean recursive,
764: ISVNDirEntryHandler handler) throws SVNException {
765: doList(url, pegRevision, revision, false, recursive, handler);
766: }
767:
768: private void doList(SVNRepository repos, long rev,
769: final ISVNDirEntryHandler handler, boolean fetchLocks,
770: boolean recursive) throws SVNException {
771: final Map locksMap = new HashMap();
772: if (fetchLocks) {
773: SVNLock[] locks = new SVNLock[0];
774: try {
775: locks = repos.getLocks("");
776: } catch (SVNException e) {
777: if (!(e.getErrorMessage() != null && e
778: .getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED)) {
779: throw e;
780: }
781: }
782:
783: if (locks != null && locks.length > 0) {
784: SVNURL root = repos.getRepositoryRoot(true);
785: for (int i = 0; i < locks.length; i++) {
786: String repositoryPath = locks[i].getPath();
787: locksMap.put(
788: root.appendPath(repositoryPath, false),
789: locks[i]);
790: }
791: }
792: }
793: ISVNDirEntryHandler nestedHandler = new ISVNDirEntryHandler() {
794: public void handleDirEntry(SVNDirEntry dirEntry)
795: throws SVNException {
796: dirEntry.setLock((SVNLock) locksMap.get(dirEntry
797: .getURL()));
798: handler.handleDirEntry(dirEntry);
799: }
800: };
801: if (repos.checkPath("", rev) == SVNNodeKind.FILE) {
802: String name = SVNPathUtil.tail(repos.getLocation()
803: .getPath());
804: SVNURL fileULR = repos.getLocation();
805: repos.setLocation(repos.getLocation().removePathTail(),
806: false);
807: Collection dirEntries = repos.getDir("", rev, null,
808: (Collection) null);
809:
810: SVNDirEntry fileEntry = null;
811: for (Iterator ents = dirEntries.iterator(); ents.hasNext();) {
812: SVNDirEntry dirEntry = (SVNDirEntry) ents.next();
813: if (name.equals(dirEntry.getName())) {
814: fileEntry = dirEntry;
815: break;
816: }
817: }
818: if (fileEntry != null) {
819: fileEntry.setRelativePath(name);
820: nestedHandler.handleDirEntry(fileEntry);
821: } else {
822: SVNErrorMessage err = SVNErrorMessage.create(
823: SVNErrorCode.FS_NOT_FOUND,
824: "URL ''{0}'' non-existent in that revision",
825: fileULR);
826: SVNErrorManager.error(err);
827: }
828: } else {
829: list(repos, "", rev, recursive, nestedHandler);
830: }
831: }
832:
833: private static void list(SVNRepository repository, String path,
834: long rev, boolean recursive, ISVNDirEntryHandler handler)
835: throws SVNException {
836: Collection entries = new TreeSet();
837: entries = repository.getDir(path, rev, null, entries);
838:
839: for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
840: SVNDirEntry entry = (SVNDirEntry) iterator.next();
841: String childPath = SVNPathUtil
842: .append(path, entry.getName());
843: entry.setRelativePath(childPath);
844: handler.handleDirEntry(entry);
845: if (entry.getKind() == SVNNodeKind.DIR
846: && entry.getDate() != null && recursive) {
847: list(repository, childPath, rev, recursive, handler);
848: }
849: }
850: }
851:
852: }
|