Package net.sourceforge.cruisecontrol.sourcecontrols

Source Code of net.sourceforge.cruisecontrol.sourcecontrols.VssJournal

/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2001, ThoughtWorks, Inc.
* 651 W Washington Ave. Suite 600
* Chicago, IL 60661 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*     + Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*
*     + Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*
*     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol.sourcecontrols;

import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.Modification;
import net.sourceforge.cruisecontrol.SourceControl;
import net.sourceforge.cruisecontrol.util.ValidationHelper;

import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.FileReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
*  This class handles all VSS-related aspects of determining the modifications
*  since the last good build.
*
*  This class uses Source Safe Journal files.  Unlike the history files
*  that are generated by executing <code>ss.exe history</code>, journal
*  files must be setup by the Source Safe administrator before the point
*  that logging of modifications is to occur.
*
*  This code has been tested against Visual Source Safe v6.0 build 8383.
*
@author Eli Tucker
@author <a href="mailto:alden@thoughtworks.com">alden almagro</a>
@author <a href="mailto:jcyip@thoughtworks.com">Jason Yip</a>
@author Arun Aggarwal
@author Jonny Boman
*/
public class VssJournal implements SourceControl {

    private static final Logger LOG = Logger.getLogger(VssJournal.class);

    public static final SimpleDateFormat VSS_OUT_FORMAT =
        new SimpleDateFormat("'Date: 'MM/dd/yy  'Time: 'hh:mma", Locale.US);

    private String ssDir = "$/";
    private String journalFile;

    private Hashtable properties = new Hashtable();
    private String property;
    private String propertyOnDelete;

    private Date lastBuild;

    private ArrayList modifications = new ArrayList();
    private List moListVssJournalDateFormat = new ArrayList();

    public VssJournal() {
        // Add the default date format
        VssJournalDateFormat oVssJournalDateFormat = createVssjournaldateformat();
        oVssJournalDateFormat.setFormat("MM/dd/yy hh:mma");
    }

    /**
     * Add a nested element for date format interpretation from the journal file.
     * The date and time parameters are fed as a "date time" string (a single space separates date from time)
     * @return VssJournalDateFormat
     */
    public VssJournalDateFormat createVssjournaldateformat() {
        VssJournalDateFormat oVssJournalDateFormat = new VssJournalDateFormat();
        moListVssJournalDateFormat.add(oVssJournalDateFormat);
        return oVssJournalDateFormat;
    }

    /**
     *  Set the project to get history from
     *
     *  @param  ssDir
     */
    public void setSsDir(String ssDir) {
        this.ssDir = "$" + ssDir;
    }

    /**
     *  Full path to journal file.  Example: <code>c:/vssdata/journal/journal.txt</code>
     *
     *  @param journalFile
     */
    public void setJournalFile(String journalFile) {
        this.journalFile = journalFile;
    }

    /**
     *  Choose a property to be set if the project has modifications if we have a
     *  change that only requires repackaging, i.e. jsp, we don't need to recompile
     *  everything, just rejar.
     *
     *@param  property
     */
    public void setProperty(String property) {
        this.property = property;
    }

    public void setPropertyOnDelete(String propertyOnDelete) {
        this.propertyOnDelete = propertyOnDelete;
    }

    /**
     *  Sets the _lastBuild date. Protected so it can be used by tests.
     */
    protected void setLastBuildDate(Date lastBuild) {
        this.lastBuild = lastBuild;
    }

    public Hashtable getProperties() {
        return properties;
    }

    public void validate() throws CruiseControlException {
        ValidationHelper.assertIsSet(journalFile, "journalfile", this.getClass());
        ValidationHelper.assertIsSet(ssDir, "ssdir", this.getClass());
    }

    /**
     *  Do the work... I'm writing to a file since VSS will start wrapping lines
     * if I read directly from the stream.
     */
    public List getModifications(Date lastBuild, Date now) {
        this.lastBuild = lastBuild;
        modifications.clear();

        try {
            BufferedReader br = new BufferedReader(new FileReader(journalFile));

            String s = br.readLine();
            while (s != null) {
                ArrayList entry = new ArrayList();
                entry.add(s);
                s = br.readLine();
                while (s != null && !s.equals("")) {
                    entry.add(s);
                    s = br.readLine();
                }
                Modification mod = handleEntry(entry);
                if (mod != null) {
                    modifications.add(mod);
                }

                if ("".equals(s)) {
                    s = br.readLine();
                }
            }

            br.close();

        } catch (Exception e) {
            LOG.warn(e);
        }

        if (property != null && modifications.size() > 0) {
            properties.put(property, "true");
        }

        LOG.info("Found " + modifications.size() + " modified files");
        return modifications;
    }

    /**
     *  Parse individual VSS history entry
     *
     *@param  historyEntry
     */
    protected Modification handleEntry(List historyEntry) {
        Modification mod = new Modification("vss");
        String nameAndDateLine = (String) historyEntry.get(2);
        mod.userName = parseUser(nameAndDateLine);
        mod.modifiedTime = parseDate(nameAndDateLine);

        String folderLine = (String) historyEntry.get(0);
        String fileLine = (String) historyEntry.get(3);

        if (!isInSsDir(folderLine)) {
            // We are only interested in modifications to files in the specified ssdir
            return null;
        } else if (isBeforeLastBuild(mod.modifiedTime)) {
            // We are only interested in modifications since the last build
            return null;
        } else if (fileLine.startsWith("Labeled")) {
            // We don't add labels.
            return null;
        } else if (fileLine.startsWith("Checked in")) {

            String fileName = substringFromLastSlash(folderLine);
            String folderName = substringToLastSlash(folderLine);
            Modification.ModifiedFile modfile = mod.createModifiedFile(fileName, folderName);

            modfile.action = "checkin";
            mod.comment = parseComment(historyEntry);
        } else if (fileLine.indexOf(" renamed to ") > -1) {
            // TODO: This is a special case that is really two modifications: deleted and recovered.
            //       For now I'll consider it a deleted to force a clean build.
            //       I should really make this two modifications.
            mod.comment = parseComment(historyEntry);

            String fileName = fileLine.substring(0, fileLine.indexOf(" "));
            String folderName = folderLine;

            Modification.ModifiedFile modfile = mod.createModifiedFile(fileName, folderName);
            modfile.action = "delete";

        } else if (fileLine.indexOf(" moved to ") > -1) {
            // TODO: This is a special case that is really two modifications: deleted and recovered.
            //       For now I'll consider it a deleted to force a clean build.
            //       I should really make this two modifications.
            mod.comment = parseComment(historyEntry);
            String fileName = fileLine.substring(0, fileLine.indexOf(" "));
            String folderName = folderLine;

            Modification.ModifiedFile modfile = mod.createModifiedFile(fileName, folderName);
            modfile.action = "delete";

        } else {
            String folderName = folderLine;
            String fileName = fileLine.substring(0, fileLine.lastIndexOf(" "));
            Modification.ModifiedFile modfile = mod.createModifiedFile(fileName, folderName);

            mod.comment = parseComment(historyEntry);

            if (fileLine.endsWith("added")) {
                modfile.action = "add";
            } else if (fileLine.endsWith("deleted")) {
                modfile.action = "delete";
            } else if (fileLine.endsWith("recovered")) {
                modfile.action = "recover";
            } else if (fileLine.endsWith("shared")) {
                modfile.action = "branch";
            }
        }

        if (propertyOnDelete != null && "delete".equals(mod.type)) {
            properties.put(propertyOnDelete, "true");
        }

        if (property != null) {
            properties.put(property, "true");
        }

        return mod;
    }

    /**
     *  parse comment from vss history (could be multiline)
     *
     *@param  a
     *@return
     */
    private String parseComment(List a) {
        StringBuffer comment = new StringBuffer();
        for (int i = 4; i < a.size(); i++) {
            comment.append(a.get(i) + " ");
        }
        return comment.toString().trim();
    }

    /**
     * Parse date/time from VSS file history
     *
     * The nameAndDateLine will look like
     * User: Etucker      Date:  6/26/01   Time: 11:53a
     * Sometimes also this
     * User: Aaggarwa     Date:  6/29/:1   Time:  3:40p
     * Note the ":" instead of a "0"
     *
     * May give additional DateFormats through the vssjournaldateformat tag.
     * E.g. <code><vssjournaldateformat format="yy-MM-dd hh:mm"/></code>
     * @return Date
     * @param nameAndDateLine
     */
    public Date parseDate(String nameAndDateLine) {
        // Extract date and time into one string with just one space separating the date from the time
        String dateAndTime = nameAndDateLine.substring(nameAndDateLine.indexOf("Date: ")).trim();
        // Fixup for weird format
        int indexOfColon = dateAndTime.indexOf("/:");
        if (indexOfColon != -1) {
            dateAndTime =
                dateAndTime.substring(0, indexOfColon)
                    + dateAndTime.substring(
                        indexOfColon,
                        indexOfColon + 2).replace(
                        ':',
                        '0')
                    + dateAndTime.substring(indexOfColon + 2);
        }
        try {
            Date lastModifiedDate = VSS_OUT_FORMAT.parse(dateAndTime + "m");

            return lastModifiedDate;
        } catch (ParseException pe) {
            // The standard parsing failed so we see if there are any suggestions
            // on how to interpret the date, but first we extract date and time into one
            // string with just one space separating the date from the time
            dateAndTime = dateAndTime.substring(5);
            String sDate = dateAndTime.substring(0, dateAndTime.indexOf("Time:")).trim();
            String sTime =
                dateAndTime.substring(dateAndTime.indexOf("Time:") + 5).trim();
            dateAndTime = sDate + " " + sTime;
            Date oDate = null;
            for (Iterator oIterator = moListVssJournalDateFormat.iterator();
                oIterator.hasNext();
                ) {
                VssJournalDateFormat oVssJournalDateFormat =
                    (VssJournalDateFormat) oIterator.next();
                try {
                    oDate = oVssJournalDateFormat.getDateFormat().parse(dateAndTime);
                } catch (ParseException e) {
                    // No luck with this one
                }
            }
            if (oDate == null) {
                LOG.error("Could not parse date in VssJournal file");
            }
            return oDate;
        }
    }

    /**
     *  Parse username from VSS file history
     *
     *@param  userLine
     *@return the user name who made the modification
     */
    public String parseUser(String userLine) {
        final int startOfUserName = 6;
       
        try {
            String userName = userLine.substring(
            startOfUserName, userLine.indexOf("Date: ") - 1).trim();

            return userName;
        } catch (StringIndexOutOfBoundsException e) {
            LOG.error("Unparsable string was: " + userLine);
            throw e;
        }

    }

    /**
     *  Returns the substring of the given string from the last "/" character.
     *  UNLESS the last slash character is the last character or the string
     *  does not contain a slash.  In that case, return the whole string.
     */
    public String substringFromLastSlash(String input) {
        int lastSlashPos = input.lastIndexOf("/");
        if (lastSlashPos > 0 && lastSlashPos + 1 <= input.length()) {
            return input.substring(lastSlashPos + 1);
        } else {
            return input;
        }
    }

    /**
     *  Returns the substring of the given string from the beginning to the last "/" character
     *  or till the end of the string if no slash character exists.
     */
    public String substringToLastSlash(String input) {
        int lastSlashPos = input.lastIndexOf("/");
        if (lastSlashPos > 0) {
            return input.substring(0, lastSlashPos);
        } else {
            return input;
        }
    }

    /**
     *  Determines if the given folder is in the ssdir specified for this VssJournalElement.
     */
    protected boolean isInSsDir(String path) {
        return (path.toLowerCase().indexOf(ssDir.toLowerCase()) != -1);
    }

    /**
     *  Determines if the date given is before the last build for this VssJournalElement.
     */
    protected boolean isBeforeLastBuild(Date date) {
        return date.before(lastBuild);
    }


    public static class VssJournalDateFormat {
        private DateFormat moDateFormat;
        private String msFormat;
       
        public void setFormat(String psFormat) {
            moDateFormat = new SimpleDateFormat(psFormat);
            msFormat = psFormat;
        }
       
        public String getFormat() {
            return msFormat;
        }
       
        public final DateFormat getDateFormat() {
            return moDateFormat;
        }
       
    }
   
}
TOP

Related Classes of net.sourceforge.cruisecontrol.sourcecontrols.VssJournal

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.