package org.tmatesoft.svn.core.wc2;
import java.util.ArrayList;
import java.util.Collection;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNRevisionRange;
import org.tmatesoft.svn.util.SVNLogType;
/**
* Represents log operation.
* Gets commit log messages with other revision specific information for <code>target</code>' paths(<code>targetPaths</code>)
* from a repository and returns them as list of {@link SVNLogEntry} items.
* Useful for observing the history of affected paths, author, date and log comments information
* per revision.
* <code>Target</code> can represent one repository URL with list of relative paths - <code>targetPaths</code>
* or many targets representing working copy paths can be provided, and <code>targetPaths</code> should not be set.
* Operation finds root URL of the working copy paths, and calculates relative<code>targetPaths</code> for them.
*
* Operation creates {@link SVNLogEntry} item on each log message from
* <code>startRevision</code> to <code>endRevision</code> in turn, inclusive
* (but never creates item on a given log message more than once).
*
* <p/>
* Log entries are created only on messages whose revisions involved
* a change to some path in <code>targetPaths</code>. <code>Targets</code>' <code>pegRevisions</code>
* indicates in which revision <code>targetPaths</code>' paths are valid. If <code>target</code>'s
* <code>pegRevision</code> is {@link SVNRevision#isValid() invalid}, it
* defaults to {@link SVNRevision#WORKING} if <code>target</code> is working copy path, or
* {@link SVNRevision#HEAD} if <code>target</code> is URL.
*
* <p/>
* If <code>limit</code> is non-zero, only creates first <code>limit</code> log entries.
*
* <p/>
* If <code>discoverChangedPaths</code> is set, then the changed paths
* <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry} on each invocation of <code>handler</code>.
*
* <p/>
* If <code>stopOnCopy</code> is set, copy history (if any exists) will not
* be traversed while harvesting revision logs for each <code>targetPath</code>.
*
* <p/>
* If <code>useMergedHistory</code> is set, log information for
* revisions which have been merged to <code>targetPaths</code> will also be
* returned.
*
* <p/>
* Refer to {@link org.tmatesoft.svn.core.SVNLogEntry#hasChildren()} for
* additional information on how to handle mergeinfo information during a
* log operation.
*
* <p/>
* If
* <code>revisionProperties</code> is <code>null</code>, retrieves all revision properties;
* else, retrieves only the revision properties named in the array (i.e. retrieves none if the array is empty).
*
* <p/>
* For every {@link SvnRevisionRange} in <code>revisionRanges</code>: <b/>
* If <code>startRevision</code> is {@link SVNRevision#isValid() valid} but
* <code>endRevision</code> is not, then <code>endRevision</code> defaults
* to <code>startRevision</code>. If both <code>startRevision</code> and
* <code>endRevision</code> are invalid, then <code>endRevision</code>
* defaults to revision <code>0</code>, and <code>startRevision</code>
* defaults either to <code>target</code>'s <code>pegRevision</code> in case the latter one is
* valid, or to {@link SVNRevision#BASE}, if it is not.
*
* <p/>
* Important: to avoid an exception with the
* {@link SVNErrorCode#FS_NO_SUCH_REVISION} error code when invoked against
* an empty repository (i.e. one not containing a revision 1), callers
* should specify the range {@link SVNRevision#HEAD}:<code>0</code>.
*
* <p/>
* If the caller has provided a non-<code>null</code>
* {@link ISVNEventHandler}, it will be called with the
* {@link SVNEventAction#SKIP} event action on any unversioned paths.
*
* <p/>
* Note: this routine requires repository access.
*
* {@link #run()} method throws {@link SVNException} in if one of the following is true:
* <ul>
* <li>a path is not under version control
* <li>can not obtain a URL of a working copy path
* <li><code>paths</code> contain entries that belong to different repositories
* </ul>
* @see SVNLogEntry
*/
public class SvnLog extends SvnReceivingOperation<SVNLogEntry> {
private long limit;
private boolean useMergeHistory;
private boolean stopOnCopy;
private boolean discoverChangedPaths;
private String[] targetPaths;
private String[] revisionProperties;
private Collection<SvnRevisionRange> revisionRanges;
protected SvnLog(SvnOperationFactory factory) {
super(factory);
}
/**
* Returns a maximum number of log entries to be processed
*
* @return maximum number of entries
*/
public long getLimit() {
return limit;
}
/**
* Sets a maximum number of log entries to be processed
*
* @param limit maximum number of entries
*/
public void setLimit(long limit) {
this.limit = limit;
}
/**
* Returns whether the log information for revisions which have
* been merged to <code>targetPaths</code> will also be returned.
*
* @return <code>true</code> if merged revisions should be also reported, otherwise <code>false</code>
*/
public boolean isUseMergeHistory() {
return useMergeHistory;
}
/**
* Sets whether the log information for revisions which have
* been merged to <code>targetPaths</code> will also be returned.
*
* @param useMergeHistory <code>true</code> if merged revisions should be also reported, otherwise <code>false</code>
*/
public void setUseMergeHistory(boolean useMergeHistory) {
this.useMergeHistory = useMergeHistory;
}
/**
* Returns whether to report of all changed paths for every revision being processed
* If <code>true</code> then the changed paths <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry}.
*
* @return <code>true</code> if all changed paths for every revision being processed should be reported, otherwise <code>false</code>
*/
public boolean isDiscoverChangedPaths() {
return discoverChangedPaths;
}
/**
* Sets whether to report of all changed paths for every revision being processed
* If <code>true</code> then the changed paths <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry}.
*
* @param discoverChangedPaths <code>true</code> if all changed paths for every revision being processed should be reported, otherwise <code>false</code>
*/
public void setDiscoverChangedPaths(boolean discoverChangedPaths) {
this.discoverChangedPaths = discoverChangedPaths;
}
/**
* Returns whether to copy history (if any exists) should be traversed while harvesting revision logs for each <code>targetPath</code>.
*
* @return <code>true</code> if not to cross copies while traversing history, otherwise copies history will be also included into processing
*/
public boolean isStopOnCopy() {
return stopOnCopy;
}
/**
* Sets whether to copy history (if any exists) should be traversed while harvesting revision logs for each <code>targetPath</code>.
*
* @param stopOnCopy <code>true</code> if not to cross copies while traversing history, otherwise copies history will be also included into processing
*/
public void setStopOnCopy(boolean stopOnCopy) {
this.stopOnCopy = stopOnCopy;
}
/**
* Returns all revision ranges for those log should be reported.
*
* @return collection of {@link SVNRevisionRange} objects
*/
public Collection<SvnRevisionRange> getRevisionRanges()
{
return revisionRanges;
}
/**
* Sets all revision ranges for those log should be reported.
*
* @param revisionRanges collection of {@link SVNRevisionRange} objects
*/
public void setRevisionRanges(Collection<SvnRevisionRange> revisionRanges)
{
this.revisionRanges = revisionRanges;
}
/**
* Returns all relative paths what should be reported for each <code>target</code>.
*
* @return relative paths of the <code>target</code>
*/
public String[] getTargetPaths() {
return targetPaths;
}
/**
* Sets all relative paths what should be reported for each <code>target</code>.
*
* @param targetPaths relative paths of the <code>target</code>
*/
public void setTargetPaths(String[] targetPaths) {
this.targetPaths = targetPaths;
}
/**
* Returns what properties should be retrieved.
* If <code>revisionProperties</code> is <code>null</code>, retrieves all revision properties;
* else retrieves only the revision properties named in the array (i.e. retrieves none if the array is empty).
*
* @return array of names of the properties
*/
public String[] getRevisionProperties() {
return revisionProperties;
}
/**
* Sets what properties should be retrieved.
* If <code>revisionProperties</code> is <code>null</code>, retrieves all revision properties;
* else retrieves only the revision properties named in the array (i.e. retrieves none if the array is empty).
*
* @param revisionProperties array of names of the properties
*/
public void setRevisionProperties(String[] revisionProperties) {
this.revisionProperties = revisionProperties;
}
@Override
protected void ensureArgumentsAreValid() throws SVNException {
super.ensureArgumentsAreValid();
if (getLimit() > Long.MAX_VALUE) {
setLimit(Long.MAX_VALUE);
}
if (getRevisionRanges() == null || getRevisionRanges().size() == 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Missing required revision specification");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (hasRemoteTargets() && getTargets().size() > 1) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "When specifying URL, only one target may be given.");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
}
/**
* Adds the revision range to the operation's revision ranges.
*
* @param range revision range
*/
public void addRange(SvnRevisionRange range) {
if (range != null) {
if (getRevisionRanges() == null) {
this.revisionRanges = new ArrayList<SvnRevisionRange>();
}
this.revisionRanges.add(range);
}
}
@Override
protected int getMaximumTargetsCount() {
return Integer.MAX_VALUE;
}
/**
* Gets whether the operation changes working copy
* @return <code>true</code> if the operation changes the working copy, otherwise <code>false</code>
*/
@Override
public boolean isChangesWorkingCopy() {
return false;
}
}