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) 2017, James Service <jas2701@googlemail.com>. 22 * Portions Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. 23 * Portions Copyright (c) 2017, Chris Fraire <cfraire@me.com>. 24 */ 25 package org.opengrok.indexer.history; 26 27 import java.io.BufferedReader; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.InputStreamReader; 31 import java.text.SimpleDateFormat; 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Set; 35 import java.util.TreeSet; 36 import java.util.logging.Level; 37 import java.util.logging.Logger; 38 39 import org.opengrok.indexer.logger.LoggerFactory; 40 import org.opengrok.indexer.util.Executor; 41 42 /** 43 * BitKeeperHistoryParser handles parsing the output of `bk log` into a history object. 44 * 45 * @author James Service {@literal <jas2701@googlemail.com>} 46 */ 47 class BitKeeperHistoryParser implements Executor.StreamHandler { 48 49 private static final Logger LOGGER = LoggerFactory.getLogger(BitKeeperHistoryParser.class); 50 51 /** 52 * Parses dates. 53 */ 54 private final SimpleDateFormat dateFormat; 55 /** 56 * Store entries processed from executor output. 57 */ 58 private final List<HistoryEntry> entries = new ArrayList<>(); 59 /** 60 * Store renamed files processed from executor output. 61 */ 62 private final Set<String> renamedFiles = new TreeSet<>(); 63 64 /** 65 * Constructor to construct the thing to be constructed. 66 * 67 * @param datePattern a simple date format string 68 */ BitKeeperHistoryParser(String datePattern)69 BitKeeperHistoryParser(String datePattern) { 70 dateFormat = new SimpleDateFormat(datePattern); 71 } 72 73 /** 74 * Returns the history that has been created. 75 * 76 * @return history a history object 77 */ getHistory()78 History getHistory() { 79 return new History(entries, new ArrayList<>(renamedFiles)); 80 } 81 82 /** 83 * Process the output of a `bk log` command. 84 * 85 * Each input line should be in the following format: 86 * either 87 * D FILE\tREVISION\tDATE\tUSER(\tRENAMED_FROM)? 88 * or 89 * C COMMIT_MESSAGE_LINE 90 * 91 * BitKeeper always outputs the file name relative to its root directory, which is nice for us since that's what 92 * History expects. 93 * 94 * @param input the executor input stream 95 * @throws IOException if the stream reader throws an IOException 96 */ 97 @Override processStream(InputStream input)98 public void processStream(InputStream input) throws IOException { 99 HistoryEntry entry = null; 100 101 final BufferedReader in = new BufferedReader(new InputStreamReader(input)); 102 for (String line = in.readLine(); line != null; line = in.readLine()) { 103 if (line.startsWith("D ")) { 104 if (entry != null) { 105 entries.add(entry); 106 entry = null; 107 } 108 109 final String[] fields = line.substring(2).split("\t"); 110 final HistoryEntry newEntry = new HistoryEntry(); 111 try { 112 if (fields[0].equals("ChangeSet")) { 113 continue; 114 } 115 newEntry.addFile(fields[0].intern()); 116 newEntry.setRevision(fields[1]); 117 newEntry.setDate(dateFormat.parse(fields[2])); 118 newEntry.setAuthor(fields[3]); 119 newEntry.setActive(true); 120 } catch (final Exception e) { 121 LOGGER.log(Level.SEVERE, "Error: malformed BitKeeper log output {0}", line); 122 continue; 123 } 124 125 entry = newEntry; 126 if (fields.length == 5) { 127 renamedFiles.add(fields[4]); 128 } 129 } else if (line.startsWith("C ") && (entry != null)) { 130 final String messageLine = line.substring(2); 131 entry.appendMessage(messageLine); 132 } 133 } 134 135 if (entry != null) { 136 entries.add(entry); 137 } 138 } 139 } 140