/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.wc.xml;
import java.io.File;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.util.ISVNDebugLog;
import org.tmatesoft.svn.util.SVNLogType;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* This is an implementation of the <b>ISVNStatusHandler</b> interface
* that writes XML formatted status information to a specified
* <b>ContentHandler</b>.
*
* @version 1.3
* @author TMate Software Ltd.
* @since 1.2
*/
public class SVNXMLStatusHandler extends AbstractXMLHandler implements ISVNStatusHandler {
private static final String AGAINST_TAG = "against";
private static final String TARGET_TAG = "target";
/**
* <code>'expires'</code> tag.
*/
public static final String EXPIRES_TAG = "expires";
/**
* <code>'created'</code> tag.
*/
public static final String CREATED_TAG = "created";
/**
* <code>'comment'</code> tag.
*/
public static final String COMMENT_TAG = "comment";
/**
* <code>'owner'</code> tag.
*/
public static final String OWNER_TAG = "owner";
/**
* <code>'token'</code> tag.
*/
public static final String TOKEN_TAG = "token";
/**
* <code>'date'</code> tag.
*/
public static final String DATE_TAG = "date";
/**
* <code>'author'</code> tag.
*/
public static final String AUTHOR_TAG = "author";
/**
* <code>'repos-status'</code> tag.
*/
public static final String REMOTE_STATUS_TAG = "repos-status";
/**
* <code>'lock'</code> tag.
*/
public static final String LOCK_TAG = "lock";
/**
* <code>'commit'</code> tag.
*/
public static final String COMMIT_TAG = "commit";
/**
* <code>'wc-status'</code> tag.
*/
public static final String WC_STATUS_TAG = "wc-status";
/**
* <code>'entry'</code> tag.
*/
public static final String ENTRY_TAG = "entry";
/**
* <code>'status'</code> tag.
*/
public static final String STATUS_TAG = "status";
/**
* <code>'revision'</code> attribute.
*/
public static final String REVISION_ATTR = "revision";
/**
* <code>'switched'</code> attribute.
*/
public static final String SWITCHED_ATTR = "switched";
/**
* <code>'copied'</code> attribute.
*/
public static final String COPIED_ATTR = "copied";
/**
* <code>'wc-locked'</code> attribute.
*/
public static final String WC_LOCKED_ATTR = "wc-locked";
/**
* <code>'props'</code> attribute.
*/
public static final String PROPS_ATTR = "props";
/**
* <code>'item'</code> attribute.
*/
public static final String ITEM_ATTR = "item";
/**
* <code>'path'</code> attribute.
*/
public static final String PATH_ATTR = "path";
private static final String TRUE = "true";
private File myTargetPath;
/**
* Creates a new status handler.
*
* @param saxHandler a <b>ContentHandler</b> to form
* an XML tree
*/
public SVNXMLStatusHandler(ContentHandler saxHandler) {
this(saxHandler, null);
}
/**
* Creates a new status handler.
*
* @param saxHandler a <b>ContentHandler</b> to form
* an XML tree
* @param log a debug logger
*/
public SVNXMLStatusHandler(ContentHandler saxHandler, ISVNDebugLog log) {
super(saxHandler, log);
}
/**
* Begins an XML tree with the target path for which the
* status is run.
*
* @param path a WC target path
*/
public void startTarget(File path) {
try {
myTargetPath = path;
addAttribute(PATH_ATTR, path.getPath());
openTag(TARGET_TAG);
} catch (SAXException e) {
getDebugLog().logSevere(SVNLogType.DEFAULT, e);
}
}
/**
* Handles a next <code>status</code> object producing corresponding xml.
*
* @param status
* @throws SVNException
*/
public void handleStatus(SVNStatus status) throws SVNException {
try {
sendToHandler(status);
} catch (SAXException th) {
getDebugLog().logSevere(SVNLogType.DEFAULT, th);
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.XML_MALFORMED, th.getLocalizedMessage());
SVNErrorManager.error(err, th, SVNLogType.DEFAULT);
}
}
/**
* Closes the formatted XML with the revision against which
* the status is run.
*
* @param revision a revision against which the status is run
*/
public void endTarget(long revision) {
try {
myTargetPath = null;
if (revision >= 0) {
addAttribute(REVISION_ATTR, revision + "");
openTag(AGAINST_TAG);
closeTag(AGAINST_TAG);
}
closeTag(TARGET_TAG);
} catch (SAXException e) {
getDebugLog().logSevere(SVNLogType.DEFAULT, e);
}
}
private void sendToHandler(SVNStatus status) throws SAXException {
addAttribute(PATH_ATTR, getRelativePath(status.getFile()));
openTag(ENTRY_TAG);
addAttribute(PROPS_ATTR, status.getPropertiesStatus().toString());
addAttribute(ITEM_ATTR, status.getContentsStatus().toString());
if (status.isLocked()) {
addAttribute(WC_LOCKED_ATTR, TRUE);
}
if (status.isCopied()) {
addAttribute(COPIED_ATTR, TRUE);
}
if (status.isSwitched()) {
addAttribute(SWITCHED_ATTR, TRUE);
}
if (!status.isCopied() && status.getRevision() != null && status.getRevision().getNumber() >= 0) {
addAttribute(REVISION_ATTR, status.getRevision().toString());
}
openTag(WC_STATUS_TAG);
if (status.getCommittedRevision() != null && status.getCommittedRevision().getNumber() >= 0) {
addAttribute(REVISION_ATTR, status.getCommittedRevision().toString());
openTag(COMMIT_TAG);
addTag(AUTHOR_TAG, status.getAuthor());
if (status.getCommittedDate() != null) {
addTag(DATE_TAG, SVNDate.formatDate(status.getCommittedDate()));
}
closeTag(COMMIT_TAG);
}
if (status.getLocalLock() != null) {
openTag(LOCK_TAG);
addTag(TOKEN_TAG, status.getLocalLock().getID());
addTag(OWNER_TAG, status.getLocalLock().getOwner());
addTag(COMMENT_TAG, status.getLocalLock().getComment());
addTag(CREATED_TAG, SVNDate.formatDate(status.getLocalLock().getCreationDate()));
closeTag(LOCK_TAG);
}
closeTag(WC_STATUS_TAG);
if (status.getRemoteContentsStatus() != SVNStatusType.STATUS_NONE || status.getRemotePropertiesStatus() != SVNStatusType.STATUS_NONE ||
status.getRemoteLock() != null) {
addAttribute(PROPS_ATTR, status.getRemotePropertiesStatus().toString());
addAttribute(ITEM_ATTR, status.getRemoteContentsStatus().toString());
openTag(REMOTE_STATUS_TAG);
if (status.getRemoteLock() != null) {
openTag(LOCK_TAG);
addTag(TOKEN_TAG, status.getRemoteLock().getID());
addTag(OWNER_TAG, status.getRemoteLock().getOwner());
addTag(COMMENT_TAG, status.getRemoteLock().getComment());
addTag(CREATED_TAG, SVNDate.formatDate(status.getRemoteLock().getCreationDate()));
if (status.getRemoteLock().getExpirationDate() != null) {
addTag(EXPIRES_TAG, SVNDate.formatDate(status.getRemoteLock().getExpirationDate()));
}
closeTag(LOCK_TAG);
}
closeTag(REMOTE_STATUS_TAG);
}
closeTag(ENTRY_TAG);
}
protected String getRelativePath(File path) {
String fullPath = path.getAbsoluteFile().getAbsolutePath();
if (myTargetPath == null) {
return fullPath;
}
StringBuffer relativePath = new StringBuffer();
// collect path till target is met, then prepend target.
char pathSeparator = File.separatorChar;
boolean targetMeet = false;
if (!path.getAbsoluteFile().equals(myTargetPath.getAbsoluteFile())) {
do {
if (relativePath.length() > 0) {
relativePath.insert(0, pathSeparator);
}
relativePath = relativePath.insert(0, path.getName());
path = path.getParentFile();
if (path != null) {
targetMeet = path.getAbsoluteFile().equals(myTargetPath.getAbsoluteFile());
}
} while(path !=null && !targetMeet);
} else {
return myTargetPath.getPath();
}
if (path != null) {
if (relativePath.length() > 0) {
relativePath.insert(0, pathSeparator);
}
relativePath = relativePath.insert(0, myTargetPath.getPath());
} else {
return fullPath;
}
return relativePath.toString();
}
protected String getTargetPath() {
return getRelativePath(myTargetPath);
}
protected String getHeaderName() {
return STATUS_TAG;
}
}