/* * ==================================================================== * 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.util.Iterator; import java.util.LinkedList; import org.tmatesoft.svn.core.ISVNLogEntryHandler; 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.SVNLogEntryPath; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.util.ISVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; /** * This log handler implementation writes xml formatted information * about the log entries it's passed to a specified <b>ContentHandler</b>. * * @version 1.3 * @author TMate Software Ltd. * @since 1.2 */ public class SVNXMLLogHandler extends AbstractXMLHandler implements ISVNLogEntryHandler { /** * <code>'copyfrom-rev'</code> attribute. */ public static final String COPYFROM_REV_ATTR = "copyfrom-rev"; /** * <code>'copyfrom-path'</code> attribute. */ public static final String COPYFROM_PATH_ATTR = "copyfrom-path"; /** * <code>'action'</code> attribute. */ public static final String ACTION_ATTR = "action"; /** * <code>'revision'</code> attribute. */ public static final String REVISION_ATTR = "revision"; /** * <code>'msg'</code> tag. */ public static final String MSG_TAG = "msg"; /** * <code>'path'</code> tag. */ public static final String PATH_TAG = "path"; /** * <code>'paths'</code> tag. */ public static final String PATHS_TAG = "paths"; /** * <code>'date'</code> tag. */ public static final String DATE_TAG = "date"; /** * <code>'author'</code> tag. */ public static final String AUTHOR_TAG = "author"; /** * <code>'logentry'</code> tag. */ public static final String LOGENTRY_TAG = "logentry"; /** * <code>'log'</code> tag. */ public static final String LOG_TAG = "log"; private boolean myIsOmitLogMessage; private LinkedList myMergeStack; /** * Creates a new log handler. * * @param contentHandler a <b>ContentHandler</b> to form * an XML tree */ public SVNXMLLogHandler(ContentHandler contentHandler) { this(contentHandler, null); } /** * Creates a new log handler. * * @param contentHandler a <b>ContentHandler</b> to form * an XML tree * @param log a debug logger */ public SVNXMLLogHandler(ContentHandler contentHandler, ISVNDebugLog log) { super(contentHandler, log); } /** * Returns the header name specific for a log handler. * * @return {@link #LOG_TAG} string */ public String getHeaderName() { return LOG_TAG; } /** * Handles a next log entry producing corresponding xml. * * @param logEntry log entry * @throws SVNException */ public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { try { sendToHandler(logEntry); } catch (SAXException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.XML_MALFORMED, e.getLocalizedMessage()); SVNErrorManager.error(err, e, SVNLogType.DEFAULT); } } /** * Sets whether log messages must be omitted or not. * * @param omitLogMessage <span class="javakeyword">true</span> to omit; * otherwise <span class="javakeyword">false</span> */ public void setOmitLogMessage(boolean omitLogMessage) { myIsOmitLogMessage = omitLogMessage; } private void sendToHandler(SVNLogEntry logEntry) throws SAXException { if (logEntry.getRevision() == 0 && logEntry.getMessage() == null) { return; } addAttribute(REVISION_ATTR, logEntry.getRevision() + ""); openTag(LOGENTRY_TAG); if (logEntry.getAuthor() != null) { addTag(AUTHOR_TAG, logEntry.getAuthor()); } if (logEntry.getDate() != null && logEntry.getDate().getTime() != 0) { addTag(DATE_TAG, SVNDate.formatDate(logEntry.getDate())); } if (logEntry.getChangedPaths() != null && !logEntry.getChangedPaths().isEmpty()) { openTag(PATHS_TAG); for (Iterator paths = logEntry.getChangedPaths().keySet().iterator(); paths.hasNext();) { String key = (String) paths.next(); SVNLogEntryPath path = (SVNLogEntryPath) logEntry.getChangedPaths().get(key); addAttribute(ACTION_ATTR, path.getType() + ""); if (path.getCopyPath() != null) { addAttribute(COPYFROM_PATH_ATTR, path.getCopyPath()); addAttribute(COPYFROM_REV_ATTR, path.getCopyRevision() + ""); } addTag(PATH_TAG, path.getPath()); } closeTag(PATHS_TAG); } if (!myIsOmitLogMessage) { String message = logEntry.getMessage(); message = message == null ? "" : message; addTag(MSG_TAG, message); } if (myMergeStack != null && !myMergeStack.isEmpty()) { MergeFrame frame = (MergeFrame) myMergeStack.getLast(); frame.myNumberOfChildrenRemaining--; } //TODO: FIXME if (logEntry.hasChildren()) { MergeFrame frame = new MergeFrame(); //frame.myNumberOfChildrenRemaining = logEntry.getNumberOfChildren(); if (myMergeStack == null) { myMergeStack = new LinkedList(); } myMergeStack.addLast(frame); } else { while(myMergeStack != null && !myMergeStack.isEmpty()) { MergeFrame frame = (MergeFrame) myMergeStack.getLast(); if (frame.myNumberOfChildrenRemaining == 0) { closeTag(LOGENTRY_TAG); myMergeStack.removeLast(); } else { break; } } closeTag(LOGENTRY_TAG); } } private class MergeFrame { private long myNumberOfChildrenRemaining; } }