/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.domain.materials.perforce; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.util.command.ConsoleResult; import com.thoughtworks.go.domain.materials.ModifiedAction; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import static com.thoughtworks.go.util.ExceptionUtils.bomb; public class P4OutputParser { private static final Logger LOG = Logger.getLogger(P4OutputParser.class); private static final String P4_DATE_PATTERN = "yyyy/MM/dd HH:mm:ss"; private static final String DESCRIBE_OUTPUT_PATTERN = "^(.+)\n\\s+((.*\n)*)\\s+"; private static final String FIRST_LINE_PATTERN = "^Change (\\d+) by (\\S+@\\S+) on (\\d+/\\d+/\\d+ \\d+:\\d+:\\d+)$"; private static final String FILE_LINE_PATTERN = "^\\.\\.\\. //.+?/(.+)#(\\d+) (\\w+)$"; private static final String SEPARATOR = "Affected files ...\n\n"; private final P4Client p4Client; public P4OutputParser(P4Client p4Client) { this.p4Client = p4Client; } long revisionFromChange(String changeOutput) { Pattern pattern = Pattern.compile("^Change (\\d+) "); Matcher matcher = pattern.matcher(changeOutput); if (matcher.find()) { String changeNumber = matcher.group(1); return Long.parseLong(changeNumber); } throw bomb("Unable to parse revision from change: '" + changeOutput + "'"); } Modification modificationFromDescription(String output, ConsoleResult result) throws P4OutputParseException { String[] parts = StringUtils.splitByWholeSeparator(output, SEPARATOR); Pattern pattern = Pattern.compile(DESCRIBE_OUTPUT_PATTERN, Pattern.MULTILINE); Matcher matcher = pattern.matcher(parts[0]); if (matcher.find()) { Modification modification = new Modification(); parseFistline(modification, matcher.group(1),result); parseComment(matcher, modification); parseAffectedFiles(parts, modification); return modification; } throw new P4OutputParseException("Could not parse P4 description: " + output); } private void parseAffectedFiles(String[] parts, Modification modification) { if (parts.length > 1) { parseFileLines(modification, parts[1]); } } private void parseComment(Matcher matcher, Modification modification) { modification.setComment(matcher.group(2).replaceAll("\\t", "").trim()); } private void parseFileLines(Modification modification, String lines) { BufferedReader reader = new BufferedReader(new StringReader(lines)); try { for (String line = reader.readLine(); line != null; line = reader.readLine()) { parseFileLine(modification, line); } } catch (IOException e) { bomb(e); } } private void parseFileLine(Modification modification, String line) { Pattern pattern = Pattern.compile(FILE_LINE_PATTERN); Matcher matcher = pattern.matcher(line); if (matcher.find()) { modification.createModifiedFile(matcher.group(1), "", ModifiedAction.parseP4Action(matcher.group(3))); } } void parseFistline(Modification modification, String line, ConsoleResult result) throws P4OutputParseException { Pattern pattern = Pattern.compile(FIRST_LINE_PATTERN); Matcher matcher = pattern.matcher(line); if (matcher.find()) { modification.setRevision(matcher.group(1)); modification.setUserName(matcher.group(2)); try { modification.setModifiedTime(new SimpleDateFormat(P4_DATE_PATTERN).parse(matcher.group(3))); } catch (ParseException e) { throw bomb(e); } } else { LOG.warn("Could not parse P4 describe: " + result.replaceSecretInfo(line)); throw new P4OutputParseException("Could not parse P4 describe: " + result.replaceSecretInfo(line)); } } public List<Modification> modifications(ConsoleResult result) { List<Modification> modifications = new ArrayList<>(); for (String change : result.output()) { if (!StringUtils.isBlank(change)) { String description = ""; try { long revision = revisionFromChange(change); description = p4Client.describe(revision); modifications.add(modificationFromDescription(description,result)); } catch (P4OutputParseException e) { LOG.error("Error parsing changes for " + this); LOG.error("---- change ---------"); LOG.error(result.replaceSecretInfo(change)); LOG.error("---- description ----"); LOG.error(result.replaceSecretInfo(description)); LOG.error("---------------------"); } catch (RuntimeException e) { throw (RuntimeException) result.smudgedException(e); } } } return modifications; } }