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