package org.jenkinsci.plugins.p4.changes; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.scm.ChangeLogParser; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Stack; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.jenkinsci.plugins.p4.PerforceScm; import org.jenkinsci.plugins.p4.client.ConnectionHelper; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; // P4CBDChangeParser was based on P4ChangeParser, but has been edited to suit the CBD workflow. public class P4CBDChangeParser extends ChangeLogParser { /** * Uses the "index.jelly" view to render the changelist details and use the * "digest.jelly" view of to render the summary page. */ @SuppressWarnings("rawtypes") @Override public ChangeLogSet<? extends Entry> parse(AbstractBuild build, File file) throws IOException, SAXException { try { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); ChangeLogHandler handler = new ChangeLogHandler(build); parser.parse(file, handler); P4CBDChangeSet changeSet = handler.getChangeLogSet(); return changeSet; } catch (Exception e) { throw new SAXException("Could not parse perforce changelog: ", e); } } public static class ChangeLogHandler extends DefaultHandler { private Stack<P4ChangeEntry> objects = new Stack<P4ChangeEntry>(); private Stack<P4CBDStructureChangeEntry> structureObjects = new Stack<P4CBDStructureChangeEntry>(); private StringBuffer text = new StringBuffer(); private List<P4ChangeEntry> changeAddedEntries; private List<P4ChangeEntry> changeRemovedEntries; private List<P4CBDStructureChangeEntry> changeStructureEntries; private P4CBDChangeSet changeSet; private AbstractBuild<?, ?> build; private boolean inAddedChanges = false; private boolean inRemovedChanges = false; private boolean inStructureChanges = false; private boolean inStructureAddedSubdirs = false; private boolean inStructureRemovedSubdirs = false; public ChangeLogHandler(AbstractBuild<?, ?> build) { this.build = build; } @Override public void characters(char[] ch, int start, int length) throws SAXException { text.append(ch, start, length); } @Override public void startDocument() throws SAXException { changeAddedEntries = new ArrayList<P4ChangeEntry>(); changeRemovedEntries = new ArrayList<P4ChangeEntry>(); changeStructureEntries = new ArrayList<P4CBDStructureChangeEntry>(); changeSet = new P4CBDChangeSet(build, changeAddedEntries, changeRemovedEntries, changeStructureEntries); } @Override public void endDocument() throws SAXException { } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { text.setLength(0); if (qName.equalsIgnoreCase("changelog")) { // this is the root, so don't do anything return; } if (qName.equalsIgnoreCase("entry")) { objects.push(new P4ChangeEntry(changeSet)); return; } if (qName.equalsIgnoreCase("addedchanges")) { inAddedChanges = true; return; } if (qName.equalsIgnoreCase("removedchanges")) { inRemovedChanges = true; return; } if (qName.equalsIgnoreCase("structurechanges")) { inStructureChanges = true; return; } if ((qName.equalsIgnoreCase("newmodule")) || (qName.equalsIgnoreCase("removedmodule"))) { structureObjects.push(new P4CBDStructureChangeEntry()); P4CBDStructureChangeEntry entry = (P4CBDStructureChangeEntry) structureObjects.peek(); if (qName.equalsIgnoreCase("newmodule")) { entry.setIsnewmodule(); } else if (qName.equalsIgnoreCase("removedmodule")) { entry.setIsremovedmodule(); } int numOfAttributes = attributes.getLength(); for (int i=0; i < numOfAttributes; i++) { String attrName = attributes.getQName(i); String attrValue = attributes.getValue(i); if (attrName.equalsIgnoreCase("depotpath")) { entry.setDepotpath(attrValue); } } return; } if (qName.equalsIgnoreCase("changedmodule")) { structureObjects.push(new P4CBDStructureChangeEntry()); P4CBDStructureChangeEntry entry = (P4CBDStructureChangeEntry) structureObjects.peek(); int numOfAttributes = attributes.getLength(); for (int i=0; i < numOfAttributes; i++) { String attrName = attributes.getQName(i); String attrValue = attributes.getValue(i); if (attrName.equalsIgnoreCase("depotpath")) { entry.setDepotpath(attrValue); } else if (attrName.equalsIgnoreCase("codelinediffers")) { entry.setCodelinediffers(attrValue); } else if (attrName.equalsIgnoreCase("wspathdiffers")) { entry.setWspathdiffers(attrValue); } } return; } if (qName.equalsIgnoreCase("addedsubdirs")) { inStructureAddedSubdirs = true; return; } if (qName.equalsIgnoreCase("removedsubdirs")) { inStructureRemovedSubdirs = true; return; } if (qName.equalsIgnoreCase("subdir")) { P4CBDStructureChangeEntry entry = (P4CBDStructureChangeEntry) structureObjects.peek(); int numOfAttributes = attributes.getLength(); for (int i=0; i < numOfAttributes; i++) { String attrName = attributes.getQName(i); String attrValue = attributes.getValue(i); if (attrName.equalsIgnoreCase("dir")) { if (inStructureAddedSubdirs) { entry.addAddedSubdir(attrValue); } else if (inStructureRemovedSubdirs) { entry.addRemovedSubdir(attrValue); } } } return; } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equalsIgnoreCase("changelog")) { // this is the root, so don't do anything return; } if (qName.equalsIgnoreCase("addedchanges")) { inAddedChanges = false; return; } if (qName.equalsIgnoreCase("removedchanges")) { inRemovedChanges = false; return; } if (qName.equalsIgnoreCase("structurechanges")) { inStructureChanges = false; return; } if ((qName.equalsIgnoreCase("newmodule")) || (qName.equalsIgnoreCase("removedmodule"))) { P4CBDStructureChangeEntry entry = (P4CBDStructureChangeEntry) structureObjects.pop(); if (inStructureChanges) { changeStructureEntries.add(entry); } return; } if (qName.equalsIgnoreCase("changedmodule")) { P4CBDStructureChangeEntry entry = (P4CBDStructureChangeEntry) structureObjects.pop(); if (inStructureChanges) { changeStructureEntries.add(entry); } return; } if (qName.equalsIgnoreCase("addedsubdirs")) { inStructureAddedSubdirs = false; return; } if (qName.equalsIgnoreCase("removedsubdirs")) { inStructureRemovedSubdirs = false; return; } if (qName.equalsIgnoreCase("entry")) { P4ChangeEntry entry = (P4ChangeEntry) objects.pop(); if (inAddedChanges) { changeAddedEntries.add(entry); } else if (inRemovedChanges) { changeRemovedEntries.add(entry); } return; } // Don't assume that we always have some "entry" in the stack at this point if (!(objects.empty())) { if (objects.peek() instanceof P4ChangeEntry) { P4ChangeEntry entry = (P4ChangeEntry) objects.peek(); try { // Find Credential ID and Workspace for this build AbstractProject<?, ?> project = build.getProject(); PerforceScm scm = (PerforceScm) project.getScm(); String credential = scm.getCredential(); // Log in to Perforce and find change-list ConnectionHelper p4 = new ConnectionHelper(credential, null); // Add changelist to entry if (qName.equalsIgnoreCase("changenumber")) { int id = new Integer(text.toString()); entry.setChange(p4, id); } // Add label to entry if (qName.equalsIgnoreCase("label")) { String id = text.toString(); entry.setLabel(p4, id); } // disconnect from Perforce p4.disconnect(); return; } catch (Exception e) { entry = null; } } } } public P4CBDChangeSet getChangeLogSet() { return changeSet; } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 16582 | dawn_foundry | Adding support for subfile elements in the wschanges output. | ||
#2 | 15395 | dawn_foundry | Changelogs also need to display the added and removed module structure changes, which I appeared to have forgotten previously! | ||
#1 | 15235 | dawn_foundry |
Adding a new way to parse the CBD change logs from wschanges, so we can display all the details. Also added a function which creates a fake change log. Useful for testing the xml parsing. |