hudson.plugins.clearcase.history.AbstractHistoryAction.java Source code

Java tutorial

Introduction

Here is the source code for hudson.plugins.clearcase.history.AbstractHistoryAction.java

Source

/**
 * The MIT License
 *
 * Copyright (c) 2007-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt,
 *                          Henrik Lynggaard, Peter Liljenberg, Andrew Bayer, Vincent Latombe
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.plugins.clearcase.history;

import static hudson.plugins.clearcase.util.OutputFormat.COMMENT;
import static hudson.plugins.clearcase.util.OutputFormat.LINEEND;
import hudson.plugins.clearcase.AbstractClearCaseScm.ChangeSetLevel;
import hudson.plugins.clearcase.ClearTool;
import hudson.plugins.clearcase.util.ClearToolFormatHandler;
import hudson.scm.ChangeLogSet.Entry;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

/**
 * @author hlyh
 */
public abstract class AbstractHistoryAction implements HistoryAction {

    protected ClearTool cleartool;
    private Filter filter;
    protected String extendedViewPath;
    protected boolean isDynamicView;
    private ChangeSetLevel changeset;

    public AbstractHistoryAction(ClearTool cleartool, boolean isDynamicView, Filter filter,
            ChangeSetLevel changeset) {
        this.cleartool = cleartool;
        this.filter = filter;
        this.isDynamicView = isDynamicView;
        this.changeset = changeset;
    }

    protected abstract List<? extends Entry> buildChangelog(String viewPath, List<HistoryEntry> entries)
            throws IOException, InterruptedException;

    protected List<HistoryEntry> filterEntries(List<HistoryEntry> entries)
            throws IOException, InterruptedException {
        if (filter == null) {
            return entries;
        }
        List<HistoryEntry> filtered = new ArrayList<HistoryEntry>();
        for (HistoryEntry entry : entries) {
            if (filter.accept(entry)) {
                filtered.add(entry);
            }
        }
        return filtered;
    }

    @Override
    public List<Entry> getChanges(Date time, String viewPath, String viewTag, String[] branchNames,
            String[] viewPaths) throws IOException, InterruptedException {
        List<Entry> changelog;
        if (needsLsHistoryForGetChanges(viewTag, viewPaths)) {
            List<HistoryEntry> historyEntries = runAndFilterLsHistory(time, viewPath, viewTag, branchNames,
                    viewPaths);
            changelog = (List<Entry>) buildChangelog(viewPath, historyEntries);
        } else {
            changelog = Collections.emptyList();
        }
        return changelog;
    }

    public ChangeSetLevel getChangeset() {
        return changeset;
    }

    public String getExtendedViewPath() {
        return extendedViewPath;
    }

    protected abstract ClearToolFormatHandler getHistoryFormatHandler();

    @Override
    public boolean hasChanges(Date time, String viewPath, String viewTag, String[] branchNames, String[] viewPaths)
            throws IOException, InterruptedException {
        if (needsLsHistoryForHasChanges(viewTag, viewPaths)) {
            List<HistoryEntry> historyEntries = runAndFilterLsHistory(time, viewPath, viewTag, branchNames,
                    viewPaths);
            return historyEntries.size() > 0;
        } else {
            return false;
        }
    }

    private List<HistoryEntry> runAndFilterLsHistory(Date time, String viewPath, String viewTag,
            String[] branchNames, String[] viewPaths) throws IOException, InterruptedException {
        List<HistoryEntry> historyEntries = runLsHistory(time, viewPath, viewTag, branchNames, viewPaths);
        return filterEntries(historyEntries);
    }

    private String[] normalizeBranches(String[] branchNames) {
        if (ArrayUtils.isEmpty(branchNames)) {
            // If no branch was specified lshistory should be called
            // without branch filtering.
            // This solves [HUDSON-4800] and is required for [HUDSON-7218].
            branchNames = new String[] { StringUtils.EMPTY };
        }
        return branchNames;
    }

    protected abstract HistoryEntry parseEventLine(Matcher matcher, String line)
            throws IOException, InterruptedException, ParseException;

    protected void parseLsHistory(BufferedReader reader, Collection<HistoryEntry> history)
            throws IOException, InterruptedException, ParseException {
        HistoryEntry previousEntry = null;

        for (String line = reader.readLine(); line != null; line = reader.readLine()) {
            if (!gotACleartoolError(line)) {
                Matcher matcher = getHistoryFormatHandler().checkLine(line);
                if (startOfLsHistoryEntry(matcher)) {
                    previousEntry = buildHistoryEntry(history, line, matcher);
                } else {
                    tryToAttachLineToPreviousEntry(previousEntry, line);
                }
            } else {
                processError(line);
            }
        }
    }

    private boolean gotACleartoolError(String line) {
        return line.startsWith("cleartool: Error:");
    }

    private boolean startOfLsHistoryEntry(Matcher matcher) {
        return matcher != null;
    }

    private HistoryEntry buildHistoryEntry(Collection<HistoryEntry> history, String line, Matcher matcher)
            throws IOException, InterruptedException, ParseException {
        HistoryEntry currentEntry;
        currentEntry = parseEventLine(matcher, line).normalize(extendedViewPath);
        history.add(currentEntry);
        return currentEntry;
    }

    private void tryToAttachLineToPreviousEntry(HistoryEntry previousEntry, String line) {
        if (previousEntry != null) {
            previousEntry.appendComment(line).appendComment("\n");
        } else {
            Logger.getLogger(AbstractHistoryAction.class.getName())
                    .warning("Got the comment : \"" + line + "\" but couldn't attach it to any entry");
        }
    }

    private void processError(String line) {
        // TODO: better error handling
    }

    protected List<HistoryEntry> runLsHistory(Date time, String viewPath, String viewTag, String[] branchNames,
            String[] viewPaths) throws IOException, InterruptedException {
        Validate.notNull(viewPath);
        List<HistoryEntry> historyEntries;
        prepareViewForHistory(viewTag);
        try {
            historyEntries = retrieveHistoryEntries(time, viewPath, branchNames, viewPaths);
        } catch (ParseException ex) {
            historyEntries = Collections.emptyList();
        }
        return historyEntries;
    }

    private boolean needsLsHistoryForGetChanges(String viewTag, String[] loadRules)
            throws IOException, InterruptedException {
        return !ChangeSetLevel.NONE.equals(changeset) && cleartool.doesViewExist(viewTag)
                && !ArrayUtils.isEmpty(loadRules);
    }

    private boolean needsLsHistoryForHasChanges(String viewTag, String[] loadRules)
            throws IOException, InterruptedException {
        return cleartool.doesViewExist(viewTag) && !ArrayUtils.isEmpty(loadRules);
    }

    private void prepareViewForHistory(String viewTag) throws IOException, InterruptedException {
        if (isDynamicView) {
            cleartool.startView(viewTag);
        }
    }

    private List<HistoryEntry> retrieveHistoryEntries(Date time, String viewPath, String[] branchNames,
            String[] viewPaths) throws IOException, InterruptedException, ParseException {
        List<HistoryEntry> historyEntries = new ArrayList<HistoryEntry>();
        for (String branchName : normalizeBranches(branchNames)) {
            BufferedReader bufferedReader = getLsHistoryBufferedReader(time, viewPath, viewPaths, branchName);
            parseLsHistory(bufferedReader, historyEntries);
            bufferedReader.close();
        }
        return historyEntries;
    }

    private BufferedReader getLsHistoryBufferedReader(Date time, String viewPath, String[] viewPaths,
            String branchName) throws IOException, InterruptedException {
        return new BufferedReader(getLsHistoryReader(time, viewPath, viewPaths, branchName));
    }

    private Reader getLsHistoryReader(Date time, String viewPath, String[] viewPaths, String branchName)
            throws IOException, InterruptedException {
        return cleartool.lshistory(getLsHistoryFormat(), time, viewPath, branchName, viewPaths, needMinorEvents());
    }

    private String getLsHistoryFormat() {
        return MessageFormat.format("{0}{1}{2}", getHistoryFormatHandler().getFormat(), COMMENT, LINEEND);
    }

    private boolean needMinorEvents() {
        return (filter != null) && (filter.requiresMinorEvents());
    }

    /**
     * Sets the extended view path. The extended view path will be removed from
     * file paths in the event. The extended view path is for example the view
     * root + view name; and this path shows up in the history and can be
     * confusing for users.
     * 
     * @param path
     *            the new extended view path.
     */
    public void setExtendedViewPath(String path) {
        this.extendedViewPath = path;
    }
}