Coverage Report - net.sf.statsvn.util.SvnDiffUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
SvnDiffUtils
0%
0/140
0%
0/110
3.222
 
 1  
 package net.sf.statsvn.util;
 2  
 
 3  
 import java.io.IOException;
 4  
 import java.io.InputStream;
 5  
 import java.io.InputStreamReader;
 6  
 import java.io.StringReader;
 7  
 import java.util.Vector;
 8  
 
 9  
 import net.sf.statcvs.util.LookaheadReader;
 10  
 import net.sf.statsvn.output.SvnConfigurationOptions;
 11  
 
 12  
 /**
 13  
  * Utilities class that manages calls to svn diff.
 14  
  * 
 15  
  * @author Jason Kealey <jkealey@shade.ca>
 16  
  * @author Gunter Mussbacher <gunterm@site.uottawa.ca>
 17  
  * 
 18  
  * @version $Id: SvnDiffUtils.java 368 2008-06-25 21:23:46Z benoitx $
 19  
  */
 20  
 public final class SvnDiffUtils {
 21  
         public static final int RESULT_SIZE = 3;
 22  
 
 23  
     private static final int PROPERTY_NAME_LINE = 4;
 24  
 
 25  
         private static final String PROPERTY_CHANGE = "Property changes on:";
 26  
 
 27  
         private static final String PROPERTY_NAME = "Name:";
 28  
 
 29  
         private static final String BINARY_TYPE = "Cannot display: file marked as a binary type.";
 30  
 
 31  
         private static final String INDEX_MARKER = "Index: ";
 32  
 
 33  
         /**
 34  
          * A utility class (only static methods) should be final and have a private
 35  0
          * constructor.
 36  0
          */
 37  0
         private SvnDiffUtils() {
 38  0
         }
 39  
 
 40  
         /**
 41  
          * Calls svn diff for the filename and revisions given. Will use URL
 42  
          * invocation, to ensure that we get diffs even for deleted files.
 43  
          * 
 44  
          * @param oldRevNr
 45  
          *            old revision number
 46  
          * @param newRevNr
 47  
          *            new revision number
 48  
          * @param filename
 49  
          *            filename.
 50  
          * @return the InputStream related to the call. If the error steam is
 51  
          *         non-empty, will return the error stream instead of the default
 52  
          *         input stream.
 53  0
          */
 54  0
         private static synchronized ProcessUtils callSvnDiff(final String oldRevNr, final String newRevNr, String filename) throws IOException {
 55  0
                 String svnDiffCommand = null;
 56  0
                 filename = SvnInfoUtils.relativePathToUrl(filename);
 57  0
                 filename = SvnInfoUtils.replace(" ", "%20", filename);
 58  0
                 svnDiffCommand = "svn diff --old " + filename + "@" + oldRevNr + "  --new " + filename + "@" + newRevNr + "" + SvnCommandHelper.getAuthString();
 59  0
                 SvnConfigurationOptions.getTaskLogger().log(Thread.currentThread().getName() + " FIRING command line:\n[" + svnDiffCommand + "]");
 60  0
                 return ProcessUtils.call(svnDiffCommand);
 61  
         }
 62  
 
 63  
         /**
 64  
          * Calls svn diff on all files for given revision and revision-1.  
 65  
          * 
 66  
          * @param newRevNr
 67  
          *            revision number
 68  
          * @return the InputStream related to the call. If the error steam is
 69  
          *         non-empty, will return the error stream instead of the default
 70  
          *         input stream.
 71  0
          */
 72  0
         private static synchronized ProcessUtils callSvnDiff(final String newRevNr) throws IOException {
 73  0
                 String svnDiffCommand = null;
 74  0
                 svnDiffCommand = "svn diff -c " + newRevNr + " " + SvnInfoUtils.getRootUrl() + " " + SvnCommandHelper.getAuthString();
 75  0
                 SvnConfigurationOptions.getTaskLogger().log(Thread.currentThread().getName() + " FIRING command line:\n[" + svnDiffCommand + "]");
 76  0
                 return ProcessUtils.call(svnDiffCommand);
 77  
         }
 78  
 
 79  
         /**
 80  
          * Returns line count differences between two revisions of a file.
 81  
          * 
 82  
          * @param oldRevNr
 83  
          *            old revision number
 84  
          * @param newRevNr
 85  
          *            new revision number
 86  
          * @param filename
 87  
          *            the filename
 88  
          * @return A int[2] array of [lines added, lines removed] is returned.
 89  
          * @throws IOException
 90  
          *             problem parsing the stream
 91  
          * @throws BinaryDiffException
 92  
          *             if the error message is due to trying to diff binary files.
 93  
          */
 94  0
         public static int[] getLineDiff(final String oldRevNr, final String newRevNr, final String filename) throws IOException, BinaryDiffException {
 95  
                 int[] lineDiff;
 96  0
                 ProcessUtils pUtils = null;
 97  0
                 try {
 98  0
                         pUtils = callSvnDiff(oldRevNr, newRevNr, filename);
 99  0
                         final InputStream diffStream = pUtils.getInputStream();
 100  0
 
 101  0
                         final LookaheadReader diffReader = new LookaheadReader(new InputStreamReader(diffStream));
 102  0
                         lineDiff = parseDiff(diffReader);
 103  
 
 104  0
                         verifyOutput(pUtils);
 105  0
                 } finally {
 106  0
                         if (pUtils != null) {
 107  0
                                 pUtils.close();
 108  
                         }
 109  0
                 }
 110  
 
 111  0
                 return lineDiff;
 112  
         }
 113  
 
 114  
         /**
 115  
          * Verifies the process error stream. 
 116  
          * @param pUtils the process call
 117  
          * @throws IOException problem parsing the stream
 118  
          * @throws BinaryDiffException if the error message is due to trying to diff binary files.
 119  0
          */
 120  
         private static void verifyOutput(final ProcessUtils pUtils) throws IOException, BinaryDiffException {
 121  0
                 if (pUtils.hasErrorOccured()) {
 122  0
                         // The binary checking code here might be useless... as it may
 123  0
                         // be output on the standard out.
 124  0
                         final String msg = pUtils.getErrorMessage();
 125  0
                         if (isBinaryErrorMessage(msg)) {
 126  0
                                 throw new BinaryDiffException();
 127  
                         } else {
 128  0
                                 throw new IOException(msg);
 129  0
                         }
 130  
                 }
 131  0
         }
 132  
 
 133  
         /**
 134  
         * Returns line count differences for all files in a particular revision.
 135  
         * 
 136  
         * @param newRevNr
 137  
         *            new revision number
 138  
         * @return A vector of object[3] array of [filename, int[2](lines added, lines removed), isBinary] is returned.
 139  
         * @throws IOException
 140  
         *             problem parsing the stream
 141  
         * @throws BinaryDiffException
 142  
         *             if the error message is due to trying to diff binary files.
 143  0
         */
 144  
         public static Vector getLineDiff(final String newRevNr) throws IOException, BinaryDiffException {
 145  0
                 final Vector answer = new Vector();
 146  
 
 147  0
                 ProcessUtils pUtils = null;
 148  0
                 try {
 149  0
                         pUtils = callSvnDiff(newRevNr);
 150  0
                         final InputStream diffStream = pUtils.getInputStream();
 151  0
                         final LookaheadReader diffReader = new LookaheadReader(new InputStreamReader(diffStream));
 152  0
                         String currFile = null;
 153  0
                         StringBuffer sb = new StringBuffer();
 154  0
                         while (diffReader.hasNextLine()) {
 155  0
                                 final String currLine = diffReader.nextLine();
 156  0
 
 157  0
                                 if (currFile == null && currLine.startsWith(INDEX_MARKER)) {
 158  0
                                         currFile = currLine.substring(INDEX_MARKER.length());
 159  0
                                 } else if (currFile != null && currLine.startsWith(INDEX_MARKER)) {
 160  0
                                         appendResults(answer, currFile, sb);
 161  0
                                         sb = new StringBuffer();
 162  0
                                         currFile = currLine.substring(INDEX_MARKER.length());
 163  0
                                 }
 164  0
 
 165  0
                                 sb.append(currLine);
 166  0
                                 sb.append(System.getProperty("line.separator"));
 167  0
                         }
 168  0
 
 169  0
             // last file
 170  0
             if (currFile!=null) {
 171  0
                 appendResults(answer, currFile, sb);
 172  0
             }
 173  0
 
 174  0
                         verifyOutput(pUtils);
 175  
                 } finally {
 176  0
                         if (pUtils != null) {
 177  0
                                 pUtils.close();
 178  0
                         }
 179  
                 }
 180  
 
 181  0
                 return answer;
 182  
         }
 183  
 
 184  
         /**
 185  
          * Append results to answer vector.  
 186  
          * @param answer the current answers
 187  
          * @param currFile the current file being added. 
 188  
          * @param sb the diff for this individual file. 
 189  
          * @throws IOException
 190  
          *             problem parsing the stream
 191  
          * @throws BinaryDiffException
 192  0
          *             if the error message is due to trying to diff binary files.
 193  0
          */
 194  0
         private static void appendResults(final Vector answer, final String currFile, final StringBuffer sb) throws IOException {
 195  0
                 int[] lineDiff;
 196  0
                 Boolean isBinary = Boolean.FALSE;
 197  0
 
 198  0
                 final LookaheadReader individualDiffReader = new LookaheadReader(new StringReader(sb.toString()));
 199  0
                 try {
 200  0
                         lineDiff = parseDiff(individualDiffReader);
 201  0
                 } catch (final BinaryDiffException e) {
 202  0
                         lineDiff = new int[2];
 203  0
                         lineDiff[0] = 0;
 204  0
                         lineDiff[1] = 0;
 205  0
                         isBinary = Boolean.TRUE;
 206  0
 
 207  0
                 }
 208  0
                 final Object[] results = new Object[RESULT_SIZE];
 209  0
                 results[0] = currFile;
 210  0
                 results[1] = lineDiff;
 211  0
                 results[2] = isBinary;
 212  0
                 answer.add(results);
 213  0
         }
 214  
 
 215  
         /**
 216  
          * Returns true if msg is an error message display that the file is binary.
 217  
          * 
 218  
          * @param msg
 219  
          *            the error message given by ProcessUtils.getErrorMessage();
 220  
          * @return true if the file is binary
 221  
          */
 222  
         private static boolean isBinaryErrorMessage(final String msg) {
 223  
                 /*
 224  
                  * Index: junit.jar
 225  
                  * ===================================================================
 226  0
                  * Cannot display: file marked as a binary type.
 227  0
                  * 
 228  
                  * svn:mime-type = application/octet-stream
 229  
                  */
 230  0
                 return (msg.indexOf(BINARY_TYPE) >= 0);
 231  
         }
 232  
 
 233  
         /**
 234  
          * Returns line count differences between two revisions of a file.
 235  
          * 
 236  
          * @param diffReader
 237  
          *            the stream reader for svn diff
 238  
          * 
 239  
          * @return A int[2] array of [lines added, lines removed] is returned.
 240  0
          * @throws IOException
 241  0
          *             problem parsing the stream
 242  0
          */
 243  0
         private static int[] parseDiff(final LookaheadReader diffReader) throws IOException, BinaryDiffException {
 244  0
                 final int[] lineDiff = { -1, -1 };
 245  0
                 boolean propertyChange = false;
 246  0
                 if (!diffReader.hasNextLine()) {
 247  0
                         // diff has no output because we modified properties or the changes
 248  0
                         // are auto-generated ($id$ $author$ kind of thing)
 249  0
                         // http://svnbook.red-bean.com/nightly/en/svn.advanced.props.html#svn.advanced.props.special.keywords
 250  0
                         lineDiff[0] = 0;
 251  0
                         lineDiff[1] = 0;
 252  0
                 }
 253  0
                 while (diffReader.hasNextLine()) {
 254  0
                         diffReader.nextLine();
 255  0
                         final String currentLine = diffReader.getCurrentLine();
 256  0
 
 257  0
                         SvnConfigurationOptions.getTaskLogger().log(Thread.currentThread().getName() + " Diff Line: [" + currentLine + "]");
 258  0
 
 259  0
                         if (currentLine.length() == 0) {
 260  0
                                 continue;
 261  0
                         }
 262  0
                         final char firstChar = currentLine.charAt(0);
 263  0
                         // very simple algorithm
 264  0
                         if (firstChar == '+') {
 265  0
                                 lineDiff[0]++;
 266  0
                         } else if (firstChar == '-') {
 267  0
                                 lineDiff[1]++;
 268  0
                         } else if (currentLine.indexOf(PROPERTY_CHANGE) == 0
 269  0
                                 || (currentLine.indexOf(PROPERTY_NAME) == 0 && diffReader.getLineNumber() == PROPERTY_NAME_LINE)) {
 270  0
                                 propertyChange = true;
 271  0
                         } else if (currentLine.indexOf(BINARY_TYPE) == 0) {
 272  0
                                 throw new BinaryDiffException();
 273  0
                         }
 274  0
                 }
 275  0
                 if (propertyChange && (lineDiff[0] == -1 || lineDiff[1] == -1)) {
 276  0
                         lineDiff[0] = 0;
 277  0
                         lineDiff[1] = 0;
 278  
                 }
 279  
 
 280  0
                 return lineDiff;
 281  
         }
 282  
 
 283  
 }