/*******************************************************************************
* Copyright (c) 2013, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.team.build.internal.hjplugin;
import hudson.model.Run;
import hudson.scm.ChangeLogParser;
import hudson.scm.RepositoryBrowser;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.util.Digester2;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xml.sax.SAXException;
public class RTCChangeLogParser extends ChangeLogParser {
private static final Logger LOGGER = Logger.getLogger(RTCChangeLogParser.class.getName());
@Override
public ChangeLogSet<? extends Entry> parse(Run build, RepositoryBrowser<?> browser,
File changelogFile) throws IOException, SAXException {
LOGGER.finest("RTCChangeLogParser.parse with changelogFile: Begin"); //$NON-NLS$1
if (LOGGER.isLoggable(Level.FINER)) {
try {
if (changelogFile != null) {
LOGGER.finer("Parsing changelog for file " + changelogFile.getAbsolutePath()); //$NON-NLS$1
}
}
catch (SecurityException exp) {
LOGGER.finer("Error getting changelog file path for filename" + changelogFile.getName()); //$NON-NLS$1
}
}
FileInputStream inputStream = new FileInputStream(changelogFile);
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT); //$NON-NLS-1$
Reader reader = new InputStreamReader(inputStream, decoder);
return parse(build, browser, reader);
}
public ChangeLogSet<? extends Entry> parse(Run build, RepositoryBrowser<?> browser,
Reader changelogReader) throws IOException, SAXException {
LOGGER.finest("RTCChangeLogParser.parse with Reader : Begin"); //$NON-NLS$1
try {
RTCChangeLogSet result = new RTCChangeLogSet(build, browser);
Digester2 digester = getDigester();
digester.push(result);
digester.addSetProperties("changelog"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/baselineSetItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/baselineSetName"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/workspaceItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/workspaceName"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/buildDefinitionName"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/buildDefinitionItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/streamName"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/streamItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/isPersonalBuild"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/previousBuildUrl"); //$NON-NLS-1$
// When digester reads a {{<changeset>}} node it will create a {{RTCChangeLogChangeSetEntry}} object
digester.addObjectCreate("*/changeset", RTCChangeLogChangeSetEntry.class); //$NON-NLS-1$
// Reads all attributes in the {{<changeset>}} node and uses setter method in class to set the values
digester.addSetProperties("*/changeset"); //$NON-NLS-1$
// Reads the child node {{<action>}} and uses {{RTCChangeLogChangeSetEntry.setAction()}} to set the value
digester.addBeanPropertySetter("*/changeset/action"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changeSetItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/componentItemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/componentName"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/owner"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/comment"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/additionalChanges"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/date"); //$NON-NLS-1$
// The digested node/changeset is added to the change log through {{RTCChangeLogSet.add()}}
digester.addSetNext("*/changeset", "add"); //$NON-NLS-1$ //$NON-NLS-2$
// When digester reads a {{<changes>}} child node of {{<changeset}} it will create a {{RTCChangeLogChangeSetEntry.ChangeDesc}} object
digester.addObjectCreate("*/changeset/changes/change", RTCChangeLogChangeSetEntry.ChangeDesc.class); //$NON-NLS-1$
digester.addSetProperties("*/changeset/changes/change"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changes/change/kind"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changes/change/name"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changes/change/itemType"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changes/change/itemId"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/changes/change/stateId"); //$NON-NLS-1$
// The digested node/change is added to the change set through {{RTCChangeLogChangeSetEntry.addChange()}}
digester.addSetNext("*/changeset/changes/change", "addChange"); //$NON-NLS-1$ //$NON-NLS-2$
// When digester reads a {{<workitems>}} child node of {{<changeset}} it will create a {{RTCChangeLogChangeSetEntry.WorkItemDesc}} object
digester.addObjectCreate("*/changeset/workItems/workItem", RTCChangeLogChangeSetEntry.WorkItemDesc.class); //$NON-NLS-1$
digester.addSetProperties("*/changeset/workItems/workItem"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/workItems/workItem/number"); //$NON-NLS-1$
digester.addBeanPropertySetter("*/changeset/workItems/workItem/summary"); //$NON-NLS-1$
// The digested node/workItem is added to the change set through {{RTCChangeLogChangeSetEntry.addWorkItem()}}
digester.addSetNext("*/changeset/workItems/workItem", "addWorkItem"); //$NON-NLS-1$ //$NON-NLS-2$
// components that were added/dropped
// When digester reads a {{<component>}} node it will create a {{RTCChangeLogComponentEntry}} object
digester.addObjectCreate("*/component", RTCChangeLogComponentEntry.class); //$NON-NLS-1$
// Reads all attributes in the {{<component>}} node and uses setter method in class to set the values
digester.addSetProperties("*/component"); //$NON-NLS-1$
// Reads the child node {{<action>}} and uses {{RTCChangeLogComponentEntry.setAction()}} to set the value
digester.addBeanPropertySetter("*/component/action"); //$NON-NLS-1$
// Reads the child node {{<name>}} and uses {{RTCChangeLogComponentEntry.setName()}} to set the value
digester.addBeanPropertySetter("*/component/name"); //$NON-NLS-1$
// Reads the child node {{<uuid>}} and uses {{RTCChangeLogComponentEntry.setUuid()}} to set the value
digester.addBeanPropertySetter("*/component/uuid"); //$NON-NLS-1$
// The digested node/change set is added to the list through {{RTCChangeLogSet.add()}}
digester.addSetNext("*/component", "add"); //$NON-NLS-1$ //$NON-NLS-2$
// Do the actual parsing
digester.parse(changelogReader);
return result;
} finally {
changelogReader.close();
}
}
private Digester2 getDigester() {
LOGGER.finest("RTCChangeLogParser.getDigester : Begin");
Digester2 digester;
try {
digester = new Digester2();
return digester;
} catch (Error e) {
LOGGER.log(Level.FINER, "Failed to get Digest2 error: " + e.getMessage(), e);
dumpClassLoader("getDigester()");
// Switch out the context class loader because in some configurations, the
// LogFactory class used by Log used by the Digester can't be loaded.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
digester = new Digester2();
return digester;
} finally {
Thread.currentThread().setContextClassLoader(classLoader);
}
}
}
private void dumpClassLoader(String string) {
LOGGER.finest("RTCChangeLogParser.dumpClassLoader : Begin");
String message = string + "\n";
String indent = " ";
ClassLoader classLoader = this.getClass().getClassLoader();
message += indent + "ClassLoader: " + classLoader.getClass().getName() + "\n";
message += appendClassLoaderParents(classLoader.getParent(), indent);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
message += indent + "ContextClassLoader: " + contextClassLoader.getClass().getName() + "\n";
message += appendClassLoaderParents(contextClassLoader.getParent(), indent);
LOGGER.log(Level.FINER, message);
LOGGER.finest("RTCChangeLogParser.dumpClassLoader : End");
}
private static String appendClassLoaderParents(ClassLoader parent,
String indent) {
if (parent == null) {
return "\n";
} else {
indent += " ";
String result = indent + parent.getClass().getName() + "\n";
result += appendClassLoaderParents(parent.getParent(), indent);
return result;
}
}
}