ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen/*
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * CDDL HEADER START
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen *
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * The contents of this file are subject to the terms of the
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * Common Development and Distribution License (the "License").
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * You may not use this file except in compliance with the License.
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen *
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * See LICENSE.txt included in this distribution for the specific
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * language governing permissions and limitations under the License.
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen *
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * When distributing Covered Code, include this CDDL HEADER in each
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * file and include the License file at LICENSE.txt.
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * If applicable, add the following below this CDDL HEADER, with the
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * fields enclosed by brackets "[]" replaced with your own identifying
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * information: Portions Copyright [yyyy] [name of copyright owner]
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen *
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * CDDL HEADER END
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen */
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen/*
9f5e862909e44377e352d0fed9e5f582ee4e5773Knut Anders Hatlen * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen */
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlenpackage org.opensolaris.opengrok.history;
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.io.BufferedReader;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.io.File;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.io.FileReader;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.io.IOException;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.util.ArrayList;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.util.Iterator;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.util.List;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport java.util.TreeMap;
7a147aeccd50900dc20ead611c17ab5728a1a6a6Jorgen Austvikimport java.util.logging.Level;
6336b638e9afd018de5f6c516eac4775d140fdaeJHKSTimport java.util.logging.Logger;
6336b638e9afd018de5f6c516eac4775d140fdaeJHKST
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport org.apache.commons.jrcs.rcs.Archive;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport org.apache.commons.jrcs.rcs.Node;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport org.apache.commons.jrcs.rcs.ParseException;
f75cb11e9d95b4d9ec959f890eeab3c442215b24Trond Norbyeimport org.apache.commons.jrcs.rcs.Version;
6336b638e9afd018de5f6c516eac4775d140fdaeJHKSTimport org.opensolaris.opengrok.logger.LoggerFactory;
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen/**
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen * Virtualise RCS file as a reader, getting a specified version
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen */
20c666f595e231740b3d6e0cee9348eec5befdd9Knut Anders Hatlenclass RCSHistoryParser {
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
6336b638e9afd018de5f6c516eac4775d140fdaeJHKST private static final Logger LOGGER = LoggerFactory.getLogger(RCSHistoryParser.class);
6336b638e9afd018de5f6c516eac4775d140fdaeJHKST
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik private static File readCVSRoot(File root, File CVSdir, String name) throws IOException {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik String cvsroot = readFirstLine(root);
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik if (cvsroot == null) {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik return null;
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik }
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik if (cvsroot.charAt(0) != '/') {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik return null;
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik }
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik File repository = new File(CVSdir, "Repository");
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik String repo = readFirstLine(repository);
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik String dir = cvsroot + File.separatorChar + repo;
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik String filename = name + ",v";
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik File rcsFile = new File(dir, filename);
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik if (!rcsFile.exists()) {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik File atticFile = new File(dir + File.separatorChar + "Attic", filename);
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik if (atticFile.exists()) {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik rcsFile = atticFile;
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik }
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik }
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik return rcsFile;
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik }
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik
20c666f595e231740b3d6e0cee9348eec5befdd9Knut Anders Hatlen History parse(File file, Repository repos) throws HistoryException {
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen try {
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen return parseFile(file);
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen } catch (IOException ioe) {
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen throw new HistoryException(ioe);
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen }
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen }
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen
4cbb84cb7daca3facdd991121b5a8ec50f1c4b56Knut Anders Hatlen private History parseFile(File file) throws IOException {
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik try {
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik Archive archive = new Archive(getRCSFile(file).getPath());
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik Version ver = archive.getRevisionVersion();
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik Node n = archive.findNode(ver);
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik n = n.root();
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik ArrayList<HistoryEntry> entries = new ArrayList<HistoryEntry>();
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik traverse(n, entries);
445efbdac4156e1cb893b8ed9fe6752d51245a4eKnut Anders Hatlen
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik History history = new History();
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik history.setHistoryEntries(entries);
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik return history;
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik } catch (ParseException pe) {
09d5b31fe4f162365b71c22bad146374c6a7ca0bKnut Anders Hatlen throw RCSRepository.wrapInIOException(
09d5b31fe4f162365b71c22bad146374c6a7ca0bKnut Anders Hatlen "Could not parse file " + file.getPath(), pe);
5bb5351df1d00de040b15f5361d91b4e2c0bbde2Jorgen Austvik }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen private void traverse(Node n, List<HistoryEntry> history) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye if (n == null) {
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen return;
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen traverse(n.getChild(), history);
8ea4b8d9796de43443cdf7b66e3f185aedf7b570Jens Elkner TreeMap<?,?> brt = n.getBranches();
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen if (brt != null) {
8ea4b8d9796de43443cdf7b66e3f185aedf7b570Jens Elkner for (Iterator<?> i = brt.values().iterator(); i.hasNext();) {
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen Node b = (Node) i.next();
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen traverse(b, history);
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye if (!n.isGhost()) {
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen HistoryEntry entry = new HistoryEntry();
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen entry.setRevision(n.getVersion().toString());
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen entry.setDate(n.getDate());
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen entry.setAuthor(n.getAuthor());
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen entry.setMessage(n.getLog());
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen entry.setActive(true);
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen history.add(entry);
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye protected static File getRCSFile(File file) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye return getRCSFile(file.getParent(), file.getName());
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye protected static File getRCSFile(String parent, String name) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye File rcsDir = new File(parent, "RCS");
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye File rcsFile = new File(rcsDir, name + ",v");
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye if (rcsFile.exists()) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye return rcsFile;
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye // not RCS, try CVS instead
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye return getCVSFile(parent, name);
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye protected static File getCVSFile(String parent, String name) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye try {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye File CVSdir = new File(parent, "CVS");
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye if (CVSdir.isDirectory() && CVSdir.canRead()) {
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye File root = new File(CVSdir, "Root");
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye if (root.canRead()) {
29c007c01f8bcf08a390e63fce6724b5638ff97dJorgen Austvik return readCVSRoot(root, CVSdir, name);
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye } catch (Exception e) {
6336b638e9afd018de5f6c516eac4775d140fdaeJHKST LOGGER.log(Level.WARNING,
7a147aeccd50900dc20ead611c17ab5728a1a6a6Jorgen Austvik "Failed to retrieve CVS file of parent: " + parent + ", name: " + name, e);
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye return null;
645502d6bafedc9364583ce7f535d027f43f124dTrond Norbye }
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen /**
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen * Read the first line of a file.
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen * @param file the file from which to read
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen * @return the first line of the file, or {@code null} if the file is empty
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen * @throws IOException if an I/O error occurs while reading the file
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen */
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen private static String readFirstLine(File file) throws IOException {
9f5e862909e44377e352d0fed9e5f582ee4e5773Knut Anders Hatlen try (BufferedReader in = new BufferedReader(new FileReader(file))) {
b558677f077ff78465d3533427595a308923f080Knut Anders Hatlen return in.readLine();
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen }
d9984087ccbd98713a781d164386a358104108d0Knut Anders Hatlen }
ebb9f739bca3bc9382340b628554b484e4837d6aKnut Anders Hatlen}