1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2020, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.history; 25 26 import java.io.BufferedReader; 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.InputStreamReader; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.logging.Level; 34 import java.util.logging.Logger; 35 import java.util.regex.Matcher; 36 import java.util.regex.Pattern; 37 import org.opengrok.indexer.logger.LoggerFactory; 38 import org.opengrok.indexer.util.Executor; 39 40 /** 41 * handles parsing the output of the {@code p4 annotate} command 42 * into an annotation object. 43 */ 44 public class PerforceAnnotationParser implements Executor.StreamHandler { 45 46 private static final Logger LOGGER = LoggerFactory.getLogger(PerforceAnnotationParser.class); 47 48 /** 49 * Store annotation created by processStream. 50 */ 51 private final Annotation annotation; 52 53 private final PerforceRepository repo; 54 55 private final File file; 56 57 private static final Pattern ANNOTATION_PATTERN = Pattern.compile("^(\\d+): .*"); 58 59 /** 60 * @param repo defined instance 61 * @param file the file being annotated 62 */ PerforceAnnotationParser(PerforceRepository repo, File file)63 public PerforceAnnotationParser(PerforceRepository repo, File file) { 64 annotation = new Annotation(file.getName()); 65 this.repo = repo; 66 this.file = file; 67 } 68 69 /** 70 * Returns the annotation that has been created. 71 * 72 * @return annotation an annotation object 73 */ getAnnotation()74 public Annotation getAnnotation() { 75 return annotation; 76 } 77 78 @Override processStream(InputStream input)79 public void processStream(InputStream input) throws IOException { 80 // Pass null for revision to get all history for the file. 81 PerforceHistoryParser parser = new PerforceHistoryParser(repo); 82 List<HistoryEntry> revisions = parser.getRevisions(file, null).getHistoryEntries(); 83 HashMap<String, String> revAuthor = new HashMap<>(); 84 for (HistoryEntry entry : revisions) { 85 revAuthor.put(entry.getRevision(), entry.getAuthor()); 86 } 87 88 String line; 89 int lineno = 0; 90 try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) { 91 while ((line = reader.readLine()) != null) { 92 ++lineno; 93 if (line.equals("(... files differ ...)")) { 94 // Perforce knows as a binary file? 95 continue; 96 } 97 98 Matcher matcher = ANNOTATION_PATTERN.matcher(line); 99 if (matcher.find()) { 100 String revision = matcher.group(1); 101 String author = revAuthor.get(revision); 102 annotation.addLine(revision, author, true); 103 } else { 104 LOGGER.log(Level.SEVERE, 105 "Error: did not find annotation in line {0}: [{1}]", 106 new Object[]{lineno, line}); 107 } 108 } 109 } catch (IOException e) { 110 LOGGER.log(Level.SEVERE, 111 "Error: Could not read annotations for " + annotation.getFilename(), e); 112 } 113 } 114 } 115