All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dstadler.commons.svn.SVNLogFileParser Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
package org.dstadler.commons.svn;

import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;
import org.dstadler.commons.xml.AbstractSimpleContentHandler;
import org.xml.sax.Attributes;


/**
 * An XML SAX Parser which converts an SVN XML-Log into a Map<Long, LogEntry>
 * where the key is the subversion revision number and the value provides information
 * about the SVN log entry.
 */
public class SVNLogFileParser extends AbstractSimpleContentHandler {
	// number of paths to store with the LogEntry, if set too high, applications will go OOM...
	private static final int PATH_LIMIT = 3;

	private final static String TAG_LOG_ENTRY = "logentry";
	private final static String TAG_AUTHOR = "author";
	private final static String TAG_DATE = "date";
	private final static String TAG_MSG = "msg";
	private final static String TAG_REVISION = "revision";
	private final static String TAG_PATH = "path";
	private final static String TAG_PATH_ACTION = "action";

	private final String[] branches;
	private final LogEntryRunnable runnable;
	private final int pathLimit;

	private String lastAction = "";

	/* Structure in the XML:


dominik.stadler
2012-07-17T07:59:00.313490Z
branch for 4.3 development and bugfixing

/jloadtrace/trunk-classic/non-prod/test/integration/osgi.integrationtest.test/src/com/dynatrace/diagnostics/integrationtest/remote/performance/dotnet/iis/DotNetWithAgent50UsersOverheadPerformanceTest.java

...
*/

	/**
	 * Construct the parser with the list of branches that we are looking at.
	 *
	 * The resulting log-entries can be queried after parsing via getConfigs().
	 *
	 * @param branches An array of branch-names.
	 */
	public SVNLogFileParser(String[] branches) {
		this(branches, null, PATH_LIMIT);
	}

	/**
	 * Construct the parser with the list of branches and a runnable that is
	 * invoked for each log-entry.
	 *
	 * This can be used for streaming parsing of log-entries, e.g. for potentially
	 * large results which would consume a large amount of memory otherwise.
	 *
	 * The found log-entries are not stored and thus getConfigs() will return
	 * an empty map after parsing in this case.
	 *
	 * @param branches An array of branch-names.
	 * @param runnable A {@link LogEntryRunnable} runnable which is invoked
	 *                    once for each log-entry.
	 * @param pathLimit The maximum number of path-elements that are stored.
	 *                     This can be used to limit the amount of memory
	 *                  that is used if the actual affected path names are
	 *                  not important.
     */
	public SVNLogFileParser(String[] branches, LogEntryRunnable runnable, int pathLimit) {
		this.branches = Arrays.copyOf(branches, branches.length);
		this.runnable = runnable;
		this.pathLimit = pathLimit;
	}

	/**
	 * Internal method used for XML parsing
     */
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) {
		if(localName.equals(TAG_LOG_ENTRY)) {
			if (currentTags != null) {
				throw new IllegalStateException("Should not have tags when a config starts in the XML, but had: " + currentTags);
			}
			currentTags = new LogEntry();
			if(attributes.getValue(TAG_REVISION) != null) {
				currentTags.revision = Long.parseLong(attributes.getValue(TAG_REVISION));
			}
		} else if (localName.equals(TAG_PATH)) {
			lastAction = attributes.getValue(TAG_PATH_ACTION);
		}
	}

	/**
	 * Internal method used for XML parsing
	 */
	@Override
	public void endElement(String uri, String localName, String qName) {
		if(localName.equals(TAG_LOG_ENTRY)) {
			if (currentTags.revision == 0) {
				throw new IllegalStateException(
						"Expected to have tag 'revision' for svn-logentry in the XML, but did not find it in: " + currentTags);
			}

			if(runnable != null) {
				runnable.run(currentTags);
			} else {
				configs.put(currentTags.revision, currentTags);
			}
			currentTags = null;
		} else {
			String value = characters.toString().trim();
			switch (localName) {
				case TAG_AUTHOR:
					currentTags.author = value.toLowerCase();
					break;
				case TAG_DATE:
					currentTags.date = value;
					break;
				case TAG_MSG:
					currentTags.msg = value;
					break;
				case TAG_PATH:
					// remove the initial branchname to reduce screen and memory size of the paths
					for (String branch : branches) {
						value = StringUtils.removeStart(value, branch);
					}

					// only store a few paths to not go OOM with too many paths stored
					int size = currentTags.paths == null ? 0 : currentTags.paths.size();
					if (size == pathLimit) {
						currentTags.addPath(LogEntry.MORE, "");
					} else if (size < pathLimit) {
						currentTags.addPath(value, lastAction);
					}
					lastAction = "";
					break;
			}
			characters.setLength(0);
		}
	}

	/**
	 * Callback which is invoked for each log-entry to allow
	 * streaming handling of large svn-log queries.
	 */
	public interface LogEntryRunnable {
		void run(LogEntry entry);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy