Coverage Report - net.sf.statsvn.input.SvnXmlLogFileHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
SvnXmlLogFileHandler
85%
121/142
68%
45/66
2.421
 
 1  
 package net.sf.statsvn.input;
 2  
 
 3  
 import java.text.ParseException;
 4  
 import java.util.ArrayList;
 5  
 import java.util.Date;
 6  
 import java.util.HashMap;
 7  
 
 8  
 import net.sf.statsvn.output.SvnConfigurationOptions;
 9  
 import net.sf.statsvn.util.XMLUtil;
 10  
 
 11  
 import org.xml.sax.Attributes;
 12  
 import org.xml.sax.SAXException;
 13  
 import org.xml.sax.SAXParseException;
 14  
 import org.xml.sax.helpers.DefaultHandler;
 15  
 
 16  
 /**
 17  
  * This is the SAX parser for the svn log in xml format. It feeds information to
 18  
  * the (@link net.sf.statsvn.input.SvnLogBuilder).
 19  
  * 
 20  
  * @author Jason Kealey <jkealey@shade.ca>
 21  
  * @author Gunter Mussbacher <gunterm@site.uottawa.ca>
 22  
  * 
 23  
  * @version $Id: SvnXmlLogFileHandler.java 351 2008-03-28 18:46:26Z benoitx $
 24  
  */
 25  
 public class SvnXmlLogFileHandler extends DefaultHandler {
 26  
 
 27  
         private static final String INVALID_SVN_LOG_FILE = "Invalid SVN log file.";
 28  
 
 29  
         private static final String AUTHOR = "author";
 30  
 
 31  
         private static final String DATE = "date";
 32  
 
 33  
         private static final String FATAL_ERROR_MESSAGE = INVALID_SVN_LOG_FILE;
 34  
 
 35  
         private static final String LOG = "log";
 36  
 
 37  
         private static final String LOGENTRY = "logentry";
 38  
 
 39  
         private static final String MSG = "msg";
 40  
 
 41  
         private static final String PATH = "path";
 42  
 
 43  
         private static final String PATHS = "paths";
 44  
 
 45  
         private final SvnLogBuilder builder;
 46  
 
 47  
         private ArrayList currentFilenames;
 48  
 
 49  
         private RevisionData currentRevisionData;
 50  
 
 51  
         private ArrayList currentRevisions;
 52  
 
 53  30
         private String lastElement = "";
 54  
 
 55  30
         private String pathAction = "";
 56  
 
 57  30
         private String stringData = "";
 58  
 
 59  30
         private String copyfromRev = "";
 60  
 
 61  30
         private String copyfromPath = "";
 62  
 
 63  
         private final RepositoryFileManager repositoryFileManager;
 64  
 
 65  30
         private final HashMap tagsMap = new HashMap();
 66  
 
 67  30
         private final HashMap tagsDateMap = new HashMap();
 68  
 
 69  
         /**
 70  
          * Default constructor.
 71  
          * 
 72  
          * @param builder
 73  
          *            where to send the information
 74  
          * @param repositoryFileManager
 75  
          *            the repository file manager needed to obtain some information.
 76  
          */
 77  30
         public SvnXmlLogFileHandler(final SvnLogBuilder builder, final RepositoryFileManager repositoryFileManager) {
 78  30
                 this.builder = builder;
 79  30
                 this.repositoryFileManager = repositoryFileManager;
 80  30
         }
 81  
 
 82  
         /**
 83  
          * Builds the string that was read; default implementation can invoke this
 84  
          * function multiple times while reading the data.
 85  
          */
 86  
         public void characters(final char[] ch, final int start, final int length) throws SAXException {
 87  538500
                 super.characters(ch, start, length);
 88  538500
                 stringData += new String(ch, start, length);
 89  538500
         }
 90  
 
 91  
         /**
 92  
          * Makes sure the last element received is appropriate.
 93  
          * 
 94  
          * @param last
 95  
          *            the expected last element.
 96  
          * @throws SAXException
 97  
          *             unexpected event.
 98  
          */
 99  
         private void checkLastElement(final String last) throws SAXException {
 100  506340
                 if (!lastElement.equals(last)) {
 101  0
                         fatalError(FATAL_ERROR_MESSAGE);
 102  
                 }
 103  506340
         }
 104  
 
 105  
         /**
 106  
          * End of author element. Saves author to the current revision.
 107  
          * 
 108  
          * @throws SAXException
 109  
          *             unexpected event.
 110  
          */
 111  
         private void endAuthor() throws SAXException {
 112  18120
                 checkLastElement(LOGENTRY);
 113  18120
                 currentRevisionData.setLoginName(stringData);
 114  18120
         }
 115  
 
 116  
         /**
 117  
          * End of date element. See (@link XMLUtil#parseXsdDateTime(String)) for
 118  
          * parsing of the particular datetime format.
 119  
          * 
 120  
          * Saves date to the current revision.
 121  
          * 
 122  
          * @throws SAXException
 123  
          *             unexpected event.
 124  
          */
 125  
         private void endDate() throws SAXException {
 126  18120
                 checkLastElement(LOGENTRY);
 127  
                 Date dt;
 128  
                 try {
 129  18120
                         dt = XMLUtil.parseXsdDateTime(stringData);
 130  18120
                         currentRevisionData.setDate(dt);
 131  0
                 } catch (final ParseException e) {
 132  0
                         warning("Invalid date specified.");
 133  18120
                 }
 134  18120
         }
 135  
 
 136  
         /**
 137  
          * Handles the end of an xml element and redirects to the appropriate end*
 138  
          * method.
 139  
          * 
 140  
          * @throws SAXException
 141  
          *             unexpected event.
 142  
          */
 143  
         public void endElement(final String uri, final String localName, final String qName) throws SAXException {
 144  253170
                 super.endElement(uri, localName, qName);
 145  253170
                 String eName = localName; // element name
 146  253170
                 if ("".equals(eName)) {
 147  253170
                         eName = qName; // namespaceAware = false
 148  
                 }
 149  253170
                 if (eName.equals(LOG)) {
 150  30
                         endLog();
 151  253140
                 } else if (eName.equals(LOGENTRY)) {
 152  18120
                         endLogEntry();
 153  235020
                 } else if (eName.equals(AUTHOR)) {
 154  18120
                         endAuthor();
 155  216900
                 } else if (eName.equals(DATE)) {
 156  18120
                         endDate();
 157  198780
                 } else if (eName.equals(MSG)) {
 158  18120
                         endMsg();
 159  180660
                 } else if (eName.equals(PATHS)) {
 160  18120
                         endPaths();
 161  162540
                 } else if (eName.equals(PATH)) {
 162  162540
                         endPath();
 163  
                 } else {
 164  0
                         fatalError(INVALID_SVN_LOG_FILE);
 165  
                 }
 166  253170
         }
 167  
 
 168  
         /**
 169  
          * End of log element.
 170  
          * 
 171  
          * @throws SAXException
 172  
          *             unexpected event.
 173  
          */
 174  
         private void endLog() throws SAXException {
 175  30
                 checkLastElement(LOG);
 176  30
                 lastElement = "";
 177  30
         }
 178  
 
 179  
         /**
 180  
          * End of log entry element. For each file that was found, builds the file
 181  
          * and revision in (@link SvnLogBuilder).
 182  
          * 
 183  
          * @throws SAXException
 184  
          *             unexpected event.
 185  
          */
 186  
         private void endLogEntry() throws SAXException {
 187  18120
                 checkLastElement(LOGENTRY);
 188  18120
                 lastElement = LOG;
 189  
 
 190  180660
                 for (int i = 0; i < currentFilenames.size(); i++) {
 191  162540
                         if (currentFilenames.get(i) == null) {
 192  0
                                 continue; // skip files that are not on this branch
 193  
                         }
 194  162540
                         final RevisionData revisionData = (RevisionData) currentRevisions.get(i);
 195  162540
                         revisionData.setComment(currentRevisionData.getComment());
 196  162540
                         revisionData.setDate(currentRevisionData.getDate());
 197  162540
                         revisionData.setLoginName(currentRevisionData.getLoginName());
 198  162540
                         final String currentFilename = currentFilenames.get(i).toString();
 199  
 
 200  162540
                         final boolean isBinary = repositoryFileManager.isBinary(currentFilename);
 201  162540
                         builder.buildFile(currentFilename, isBinary, revisionData.isDeletion(), tagsMap, tagsDateMap);
 202  162540
                         builder.buildRevision(revisionData);
 203  
                 }
 204  18120
         }
 205  
 
 206  
         /**
 207  
          * End of msg element. Saves comment to the current revision.
 208  
          * 
 209  
          * @throws SAXException
 210  
          *             unexpected event.
 211  
          */
 212  
         private void endMsg() throws SAXException {
 213  18120
                 checkLastElement(LOGENTRY);
 214  18120
                 currentRevisionData.setComment(stringData);
 215  18120
         }
 216  
 
 217  
         /**
 218  
          * End of path element. Builds a revision data for this element using the
 219  
          * information that is known to date; rest is done in (@link #endLogEntry())
 220  
          * 
 221  
          * @throws SAXException
 222  
          *             unexpected event.
 223  
          */
 224  
         private void endPath() throws SAXException {
 225  162540
                 checkLastElement(PATHS);
 226  
 
 227  
                 // relies on the fact that absoluteToRelativePath returns null for paths
 228  
                 // that are not on the branch.
 229  162540
                 final String filename = repositoryFileManager.absoluteToRelativePath(stringData);
 230  162540
                 final RevisionData data = currentRevisionData.createCopy();
 231  162540
                 if (!pathAction.equals("D")) {
 232  152490
                         data.setStateExp(true);
 233  152490
                         if (pathAction.equals("A") || pathAction.equals("R")) {
 234  38100
                                 data.setStateAdded(true);
 235  
                         }
 236  
                 } else {
 237  10050
                         data.setStateDead(true);
 238  
                 }
 239  
 
 240  162540
                 final String tagsStr = SvnConfigurationOptions.getTagsDirectory();
 241  162540
                 if (copyfromRev != null && filename == null && stringData != null && stringData.indexOf(tagsStr) >= 0) {
 242  0
                         String tag = stringData.substring(stringData.indexOf(tagsStr) + tagsStr.length());
 243  0
                         if (tag.indexOf("/") >= 0) {
 244  0
                                 tag = tag.substring(0, tag.indexOf("/"));
 245  
                         }
 246  
 
 247  0
                         if (!tagsMap.containsKey(tag) && builder.matchesTagPatterns(tag)) {
 248  0
                                 SvnConfigurationOptions.getTaskLogger().info("= TAG " + tag + " rev:" + copyfromRev + " stringData [" + stringData + "]");
 249  0
                                 tagsMap.put(tag, copyfromRev);
 250  0
                                 tagsDateMap.put(tag, currentRevisionData.getDate());
 251  
                         }
 252  
                 }
 253  
 
 254  162540
                 data.setCopyfromPath(copyfromPath);
 255  162540
                 data.setCopyfromRevision(copyfromRev);
 256  
 
 257  162540
                 currentRevisions.add(data);
 258  162540
                 currentFilenames.add(filename);
 259  162540
         }
 260  
 
 261  
         /**
 262  
          * End of paths element.
 263  
          * 
 264  
          * @throws SAXException
 265  
          *             unexpected event.
 266  
          */
 267  
         private void endPaths() throws SAXException {
 268  18120
                 checkLastElement(PATHS);
 269  18120
                 lastElement = LOGENTRY;
 270  18120
         }
 271  
 
 272  
         /**
 273  
          * Throws a fatal error with the specified message.
 274  
          * 
 275  
          * @param message
 276  
          *            the reason for the error
 277  
          * @throws SAXException
 278  
          *             the error
 279  
          */
 280  
         private void fatalError(final String message) throws SAXException {
 281  0
                 fatalError(new SAXParseException(message, null));
 282  0
         }
 283  
 
 284  
         /**
 285  
          * Start of author, date or message.
 286  
          * 
 287  
          * @throws SAXException
 288  
          *             unexpected event.
 289  
          */
 290  
         private void startAuthorDateMsg() throws SAXException {
 291  54360
                 checkLastElement(LOGENTRY);
 292  54360
         }
 293  
 
 294  
         /**
 295  
          * Handles the start of an xml element and redirects to the appropriate
 296  
          * start* method.
 297  
          * 
 298  
          * @throws SAXException
 299  
          *             unexpected event.
 300  
          */
 301  
         public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
 302  253170
                 super.startElement(uri, localName, qName, attributes);
 303  253170
                 stringData = "";
 304  253170
                 String eName = localName; // element name
 305  253170
                 if ("".equals(eName)) {
 306  253170
                         eName = qName; // namespaceAware = false
 307  
                 }
 308  253170
                 if (eName.equals(LOG)) {
 309  30
                         startLog();
 310  253140
                 } else if (eName.equals(LOGENTRY)) {
 311  18120
                         startLogEntry(attributes);
 312  235020
                 } else if (eName.equals(AUTHOR) || eName.equals(DATE) || eName.equals(MSG)) {
 313  54360
                         startAuthorDateMsg();
 314  180660
                 } else if (eName.equals(PATHS)) {
 315  18120
                         startPaths();
 316  162540
                 } else if (eName.equals(PATH)) {
 317  162540
                         startPath(attributes);
 318  
                 } else {
 319  0
                         fatalError(INVALID_SVN_LOG_FILE);
 320  
                 }
 321  253170
         }
 322  
 
 323  
         /**
 324  
          * Start of the log element.
 325  
          * 
 326  
          * @throws SAXException
 327  
          *             unexpected event.
 328  
          */
 329  
         private void startLog() throws SAXException {
 330  30
                 checkLastElement("");
 331  30
                 lastElement = LOG;
 332  
 
 333  
                 try {
 334  30
                         repositoryFileManager.loadInfo();
 335  30
                         builder.buildModule(repositoryFileManager.getModuleName());
 336  0
                 } catch (final Exception e) {
 337  0
                         throw new SAXException(e);
 338  30
                 }
 339  
 
 340  30
         }
 341  
 
 342  
         /**
 343  
          * Start of the log entry element. Initializes information, to be filled
 344  
          * during this log entry and used in (@link #endLogEntry())
 345  
          * 
 346  
          * @throws SAXException
 347  
          *             unexpected event.
 348  
          */
 349  
         private void startLogEntry(final Attributes attributes) throws SAXException {
 350  18120
                 checkLastElement(LOG);
 351  18120
                 lastElement = LOGENTRY;
 352  18120
                 currentRevisionData = new RevisionData();
 353  18120
                 currentRevisions = new ArrayList();
 354  18120
                 currentFilenames = new ArrayList();
 355  18120
                 if (attributes != null && attributes.getValue("revision") != null) {
 356  18120
                         currentRevisionData.setRevisionNumber(attributes.getValue("revision"));
 357  
                 } else {
 358  0
                         fatalError(INVALID_SVN_LOG_FILE);
 359  
                 }
 360  18120
         }
 361  
 
 362  
         /**
 363  
          * Start of the path element. Saves the path action.
 364  
          * 
 365  
          * @throws SAXException
 366  
          *             unexpected event.
 367  
          */
 368  
         private void startPath(final Attributes attributes) throws SAXException {
 369  162540
                 checkLastElement(PATHS);
 370  162540
                 if (attributes != null && attributes.getValue("action") != null) {
 371  162540
                         pathAction = attributes.getValue("action");
 372  
                 } else {
 373  0
                         fatalError(INVALID_SVN_LOG_FILE);
 374  
                 }
 375  
 
 376  162540
                 copyfromPath = attributes.getValue("copyfrom-path");
 377  162540
                 copyfromRev = attributes.getValue("copyfrom-rev");
 378  
 
 379  162540
         }
 380  
 
 381  
         /**
 382  
          * Start of the paths element.
 383  
          * 
 384  
          * @throws SAXException
 385  
          *             unexpected event.
 386  
          */
 387  
         private void startPaths() throws SAXException {
 388  18120
                 checkLastElement(LOGENTRY);
 389  18120
                 lastElement = PATHS;
 390  18120
         }
 391  
 
 392  
         /**
 393  
          * Logs a warning.
 394  
          * 
 395  
          * @param message
 396  
          *            the reason for the error
 397  
          * @throws SAXException
 398  
          *             the error
 399  
          */
 400  
         private void warning(final String message) throws SAXException {
 401  0
                 SvnConfigurationOptions.getTaskLogger().info(message);
 402  0
         }
 403  
 }