Source Code Cross Referenced for ConcurrentVersionsSystem.java in  » Build » cruisecontrol » net » sourceforge » cruisecontrol » sourcecontrols » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Build » cruisecontrol » net.sourceforge.cruisecontrol.sourcecontrols 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /********************************************************************************
002:         * CruiseControl, a Continuous Integration Toolkit
003:         * Copyright (c) 2001-2003, ThoughtWorks, Inc.
004:         * 200 E. Randolph, 25th Floor
005:         * Chicago, IL 60601 USA
006:         * All rights reserved.
007:         *
008:         * Redistribution and use in source and binary forms, with or without
009:         * modification, are permitted provided that the following conditions
010:         * are met:
011:         *
012:         *     + Redistributions of source code must retain the above copyright
013:         *       notice, this list of conditions and the following disclaimer.
014:         *
015:         *     + Redistributions in binary form must reproduce the above
016:         *       copyright notice, this list of conditions and the following
017:         *       disclaimer in the documentation and/or other materials provided
018:         *       with the distribution.
019:         *
020:         *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021:         *       names of its contributors may be used to endorse or promote
022:         *       products derived from this software without specific prior
023:         *       written permission.
024:         *
025:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026:         * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027:         * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028:         * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032:         * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035:         * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036:         ********************************************************************************/package net.sourceforge.cruisecontrol.sourcecontrols;
037:
038:        import java.io.BufferedReader;
039:        import java.io.File;
040:        import java.io.IOException;
041:        import java.io.InputStream;
042:        import java.io.InputStreamReader;
043:        import java.io.Serializable;
044:        import java.text.ParseException;
045:        import java.text.SimpleDateFormat;
046:        import java.util.ArrayList;
047:        import java.util.Date;
048:        import java.util.Hashtable;
049:        import java.util.List;
050:        import java.util.Map;
051:        import java.util.StringTokenizer;
052:
053:        import net.sourceforge.cruisecontrol.CruiseControlException;
054:        import net.sourceforge.cruisecontrol.Modification;
055:        import net.sourceforge.cruisecontrol.SourceControl;
056:        import net.sourceforge.cruisecontrol.util.CVSDateUtil;
057:        import net.sourceforge.cruisecontrol.util.Commandline;
058:        import net.sourceforge.cruisecontrol.util.DiscardConsumer;
059:        import net.sourceforge.cruisecontrol.util.OSEnvironment;
060:        import net.sourceforge.cruisecontrol.util.StreamLogger;
061:        import net.sourceforge.cruisecontrol.util.StreamPumper;
062:        import net.sourceforge.cruisecontrol.util.ValidationHelper;
063:        import net.sourceforge.cruisecontrol.util.IO;
064:
065:        import org.apache.log4j.Logger;
066:
067:        /**
068:         * This class implements the SourceControlElement methods for a CVS repository. The call to CVS is assumed to work
069:         * without any setup. This implies that if the authentication type is pserver the call to cvs login should be done prior
070:         * to calling this class. <p/> There are also differing CVS client/server implementations (e.g. the <i>official</i> CVS
071:         * and the CVSNT fork). <p/> Note that the log formats of the official CVS have changed starting from version 1.12.9.
072:         * This class currently knows of 2 different outputs referred to as the 'old' and the 'new' output formats.
073:         * 
074:         * @author <a href="mailto:pj@thoughtworks.com">Paul Julius</a>
075:         * @author Robert Watkins
076:         * @author Frederic Lavigne
077:         * @author <a href="mailto:jcyip@thoughtworks.com">Jason Yip</a>
078:         * @author Marc Paquette
079:         * @author <a href="mailto:johnny.cass@epiuse.com">Johnny Cass</a>
080:         * @author <a href="mailto:m@loonsoft.com">McClain Looney</a>
081:         */
082:        public class ConcurrentVersionsSystem implements  SourceControl {
083:            private static final long serialVersionUID = -3714548093682602092L;
084:            /**
085:             * name of the official cvs as returned as part of the 'cvs version' command output
086:             */
087:            static final String OFFICIAL_CVS_NAME = "CVS";
088:            static final Version DEFAULT_CVS_SERVER_VERSION = new Version(
089:                    OFFICIAL_CVS_NAME, "1.11");
090:            public static final String LOG_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss z";
091:            private boolean reallyQuiet;
092:            private String compression;
093:
094:            /**
095:             * Represents the version of a CVS client or server
096:             */
097:            static class Version implements  Serializable {
098:
099:                private static final long serialVersionUID = -2433230091640056090L;
100:
101:                private final String cvsName;
102:                private final String cvsVersion;
103:
104:                public Version(String name, String version) {
105:                    if (name == null) {
106:                        throw new IllegalArgumentException("name can't be null");
107:                    }
108:                    if (version == null) {
109:                        throw new IllegalArgumentException(
110:                                "version can't be null");
111:                    }
112:                    this .cvsName = name;
113:                    this .cvsVersion = version;
114:                }
115:
116:                public String getCvsName() {
117:                    return cvsName;
118:                }
119:
120:                public String getCvsVersion() {
121:                    return cvsVersion;
122:                }
123:
124:                public boolean equals(Object o) {
125:                    if (this  == o) {
126:                        return true;
127:                    } else if (!(o instanceof  Version)) {
128:                        return false;
129:                    }
130:
131:                    final Version version = (Version) o;
132:
133:                    if (!cvsName.equals(version.cvsName)) {
134:                        return false;
135:                    } else if (!cvsVersion.equals(version.cvsVersion)) {
136:                        return false;
137:                    }
138:
139:                    return true;
140:                }
141:
142:                public int hashCode() {
143:                    int result;
144:                    result = cvsName.hashCode();
145:                    result = 29 * result + cvsVersion.hashCode();
146:                    return result;
147:                }
148:
149:                public String toString() {
150:                    return cvsName + " " + cvsVersion;
151:                }
152:            }
153:
154:            private SourceControlProperties properties = new SourceControlProperties();
155:
156:            /**
157:             * CVS allows for mapping user names to email addresses. If CVSROOT/users exists, it's contents will be parsed and
158:             * stored in this hashtable.
159:             */
160:            private Hashtable mailAliases;
161:
162:            /**
163:             * The caller can provide the CVSROOT to use when calling CVS, or the CVSROOT environment variable will be used.
164:             */
165:            private String cvsroot;
166:
167:            /**
168:             * The caller must indicate where the local copy of the repository exists.
169:             */
170:            private String local;
171:
172:            /**
173:             * The CVS tag we are dealing with.
174:             */
175:            private String tag;
176:
177:            /**
178:             * The CVS module we are dealing with.
179:             */
180:            private String module;
181:
182:            /**
183:             * The version of the cvs server
184:             */
185:            private Version cvsServerVersion;
186:
187:            /**
188:             * enable logging for this class
189:             */
190:            private static Logger log = Logger
191:                    .getLogger(ConcurrentVersionsSystem.class);
192:
193:            /**
194:             * This line delimits separate files in the CVS log information.
195:             */
196:            private static final String CVS_FILE_DELIM = "==================================================================="
197:                    + "==========";
198:
199:            /**
200:             * This is the keyword that precedes the name of the RCS filename in the CVS log information.
201:             */
202:            private static final String CVS_RCSFILE_LINE = "RCS file: ";
203:
204:            /**
205:             * This is the keyword that precedes the name of the working filename in the CVS log information.
206:             */
207:            private static final String CVS_WORKINGFILE_LINE = "Working file: ";
208:
209:            /**
210:             * This line delimits the different revisions of a file in the CVS log information.
211:             */
212:            private static final String CVS_REVISION_DELIM = "----------------------------";
213:
214:            /**
215:             * This is the keyword that precedes the timestamp of a file revision in the CVS log information.
216:             */
217:            private static final String CVS_REVISION_DATE = "date:";
218:
219:            /**
220:             * This is the name of the tip of the main branch, which needs special handling with the log entry parser
221:             */
222:            private static final String CVS_HEAD_TAG = "HEAD";
223:
224:            /**
225:             * This is the keyword that tells us when we have reached the end of the header as found in the CVS log information.
226:             */
227:            private static final String CVS_DESCRIPTION = "description:";
228:
229:            /**
230:             * This is a state keyword which indicates that a revision to a file was not relevant to the current branch, or the
231:             * revision consisted of a deletion of the file (removal from branch..).
232:             */
233:            private static final String CVS_REVISION_DEAD = "dead";
234:
235:            /**
236:             * System dependent new line separator.
237:             */
238:            private static final String NEW_LINE = System
239:                    .getProperty("line.separator");
240:
241:            /**
242:             * This is the date format returned in the log information from CVS.
243:             */
244:            private final SimpleDateFormat logDateFormatter = new SimpleDateFormat(
245:                    LOG_DATE_FORMAT);
246:
247:            /**
248:             * Sets the CVSROOT for all calls to CVS.
249:             * 
250:             * @param cvsroot
251:             *            CVSROOT to use.
252:             */
253:            public void setCvsRoot(String cvsroot) {
254:                this .cvsroot = cvsroot;
255:            }
256:
257:            /**
258:             * Sets the local working copy to use when making calls to CVS.
259:             * 
260:             * @param local
261:             *            String indicating the relative or absolute path to the local working copy of the module of which to
262:             *            find the log history.
263:             */
264:            public void setLocalWorkingCopy(String local) {
265:                this .local = local;
266:            }
267:
268:            /**
269:             * Set the cvs tag. Note this should work with names, numbers, and anything else you can put on log -rTAG
270:             * 
271:             * @param tag
272:             *            the cvs tag
273:             */
274:            public void setTag(String tag) {
275:                this .tag = tag;
276:            }
277:
278:            /**
279:             * Set the cvs module name. Note that this is only used when localworkingcopy is not set.
280:             * 
281:             * @param module
282:             *            the cvs module
283:             */
284:            public void setModule(String module) {
285:                this .module = module;
286:            }
287:
288:            public void setProperty(String property) {
289:                properties.assignPropertyName(property);
290:            }
291:
292:            public void setPropertyOnDelete(String propertyOnDelete) {
293:                properties.assignPropertyOnDeleteName(propertyOnDelete);
294:            }
295:
296:            /**
297:             * @param reallyQuiet When true, this class should use the -Q cvs option instead of -q for the log command.
298:             */
299:            public void setReallyQuiet(boolean reallyQuiet) {
300:                this .reallyQuiet = reallyQuiet;
301:            }
302:
303:            /**
304:             * Sets the compression level used for the call to cvs, corresponding to the "-z" command line parameter. When not
305:             * set, the command line parameter is NOT included.
306:             *
307:             * @param level Valid levels are 1 (high speed, low compression) to 9 (low speed, high compression), or 0
308:             * to disable compression.
309:             */
310:            public void setCompression(String level) {
311:                compression = level;
312:            }
313:
314:            protected Version getCvsServerVersion() {
315:                if (cvsServerVersion == null) {
316:
317:                    Commandline commandLine = getCommandline();
318:                    commandLine.setExecutable("cvs");
319:
320:                    if (cvsroot != null) {
321:                        commandLine.createArguments("-d", cvsroot);
322:                    }
323:
324:                    commandLine.createArgument().setLine("version");
325:
326:                    Process p = null;
327:                    try {
328:                        if (local != null) {
329:                            commandLine.setWorkingDirectory(local);
330:                        }
331:
332:                        p = commandLine.execute();
333:                        Thread stderr = logErrorStream(p);
334:                        InputStream is = p.getInputStream();
335:                        BufferedReader in = new BufferedReader(
336:                                new InputStreamReader(is));
337:
338:                        cvsServerVersion = extractCVSServerVersionFromCVSVersionCommandOutput(in);
339:
340:                        log.debug("cvs server version: " + cvsServerVersion);
341:
342:                        p.waitFor();
343:                        stderr.join();
344:                        IO.close(p);
345:                    } catch (IOException e) {
346:                        log.error("Failed reading cvs server version", e);
347:                    } catch (CruiseControlException e) {
348:                        log.error("Failed reading cvs server version", e);
349:                    } catch (InterruptedException e) {
350:                        log.error("Failed reading cvs server version", e);
351:                    }
352:
353:                    if (p == null || p.exitValue() != 0
354:                            || cvsServerVersion == null) {
355:                        if (p == null) {
356:                            log
357:                                    .debug("Process p was null in CVS.getCvsServerVersion()");
358:                        } else {
359:                            log.debug("Process exit value = " + p.exitValue());
360:                        }
361:                        cvsServerVersion = DEFAULT_CVS_SERVER_VERSION;
362:                        log.warn("problem getting cvs server version; using "
363:                                + cvsServerVersion);
364:                    }
365:                }
366:                return cvsServerVersion;
367:            }
368:
369:            /**
370:             * This method retrieves the cvs server version from the specified output. The line it parses will have the
371:             * following format:
372:             * 
373:             * <pre>
374:             *    Server: Concurrent Versions System (CVS) 1.11.16 (client/server)
375:             * </pre>
376:             * 
377:             * @param in
378:             * @return the version of null if the version couldn't be extracted
379:             * @throws IOException
380:             */
381:            private Version extractCVSServerVersionFromCVSVersionCommandOutput(
382:                    BufferedReader in) throws IOException {
383:                String line = in.readLine();
384:                if (line == null) {
385:                    return null;
386:                }
387:                if (line.startsWith("Client:")) {
388:                    line = in.readLine();
389:                    if (line == null) {
390:                        return null;
391:                    }
392:                    if (!line.startsWith("Server:")) {
393:                        log
394:                                .warn("Warning expected a line starting with \"Server:\" but got "
395:                                        + line);
396:                        // we try anyway
397:                    }
398:                }
399:                log.debug("server version line: " + line);
400:                int nameBegin = line.indexOf(" (");
401:                int nameEnd = line.indexOf(") ", nameBegin);
402:                final String name;
403:                final String version;
404:                if (nameBegin == -1 || nameEnd < nameBegin
405:                        || nameBegin + 2 >= line.length()) {
406:                    log.warn("cvs server version name couldn't be parsed from "
407:                            + line);
408:                    return null;
409:                }
410:                name = line.substring(nameBegin + 2, nameEnd);
411:                int verEnd = line.indexOf(" ", nameEnd + 2);
412:                if (verEnd < nameEnd + 2) {
413:                    log
414:                            .warn("cvs server version number couldn't be parsed from "
415:                                    + line);
416:                    return null;
417:                }
418:                version = line.substring(nameEnd + 2, verEnd);
419:
420:                return new Version(name, version);
421:            }
422:
423:            public boolean isCvsNewOutputFormat() {
424:                Version version = getCvsServerVersion();
425:                if (OFFICIAL_CVS_NAME.equals(version.getCvsName())) {
426:                    String csv = version.getCvsVersion();
427:                    StringTokenizer st = new StringTokenizer(csv, ".");
428:                    try {
429:                        st.nextToken();
430:                        int subversion = Integer.parseInt(st.nextToken());
431:                        if (subversion > 11) {
432:                            if (subversion == 12) {
433:                                if (Integer.parseInt(st.nextToken()) < 9) {
434:                                    return false;
435:                                }
436:                            }
437:                            return true;
438:                        }
439:                    } catch (Throwable e) {
440:                        log
441:                                .warn("problem identifying cvs server. Assuming output is of 'old' type");
442:                    }
443:                }
444:                return false;
445:            }
446:
447:            public Map getProperties() {
448:                return properties.getPropertiesAndReset();
449:            }
450:
451:            /**
452:             * for mocking *
453:             */
454:            protected OSEnvironment getOSEnvironment() {
455:                return new OSEnvironment();
456:            }
457:
458:            public void validate() throws CruiseControlException {
459:                ValidationHelper
460:                        .assertFalse(local == null
461:                                && (cvsroot == null || module == null),
462:                                "must specify either 'localWorkingCopy' or 'cvsroot' and 'module' on CVS");
463:                ValidationHelper
464:                        .assertFalse(
465:                                local != null
466:                                        && (cvsroot != null || module != null),
467:                                "if 'localWorkingCopy' is specified then cvsroot and module are not allowed on CVS");
468:                ValidationHelper.assertFalse(local != null
469:                        && !new File(local).exists(), "Local working copy \""
470:                        + local + "\" does not exist!");
471:
472:                if (compression != null) {
473:                    ValidationHelper
474:                            .assertIntegerInRange(compression, 0, 9,
475:                                    "'compression' must be an integer between 0 and 9, inclusive.");
476:                }
477:            }
478:
479:            /**
480:             * Returns a List of Modifications detailing all the changes between the last build and the latest revision at the
481:             * repository
482:             * 
483:             * @param lastBuild
484:             *            last build time
485:             * @return maybe empty, never null.
486:             */
487:            public List getModifications(Date lastBuild, Date now) {
488:                mailAliases = getMailAliases();
489:
490:                List mods = null;
491:                try {
492:                    mods = execHistoryCommand(buildHistoryCommand(lastBuild,
493:                            now));
494:                } catch (Exception e) {
495:                    log.error("Log command failed to execute successfully", e);
496:                }
497:
498:                if (mods == null) {
499:                    return new ArrayList();
500:                }
501:                return mods;
502:            }
503:
504:            /**
505:             * Get CVS's idea of user/address mapping. Only runs once per class instance. Won't run if the mailAlias was already
506:             * set.
507:             * 
508:             * @return a Hashtable containing the mapping defined in CVSROOT/users. If CVSROOT/users doesn't exist, an empty
509:             *         Hashtable is returned.
510:             */
511:            private Hashtable getMailAliases() {
512:                if (mailAliases == null) {
513:                    mailAliases = new Hashtable();
514:                    Commandline commandLine = getCommandline();
515:                    commandLine.setExecutable("cvs");
516:
517:                    if (cvsroot != null) {
518:                        commandLine.createArguments("-d", cvsroot);
519:                    }
520:
521:                    commandLine.createArgument().setLine(
522:                            "-q co -p CVSROOT/users");
523:
524:                    Process p = null;
525:                    try {
526:                        if (local != null) {
527:                            commandLine.setWorkingDirectory(local);
528:                        }
529:
530:                        p = commandLine.execute();
531:                        Thread stderr = logErrorStream(p);
532:                        InputStream is = p.getInputStream();
533:                        BufferedReader in = new BufferedReader(
534:                                new InputStreamReader(is));
535:
536:                        String line;
537:                        while ((line = in.readLine()) != null) {
538:                            addAliasToMap(line);
539:                        }
540:
541:                        p.waitFor();
542:                        stderr.join();
543:                        IO.close(p);
544:                    } catch (Exception e) {
545:                        log.error("Failed reading mail aliases", e);
546:                    }
547:
548:                    if (p == null || p.exitValue() != 0) {
549:                        if (p == null) {
550:                            log
551:                                    .debug("Process p was null in CVS.getMailAliases()");
552:                        } else {
553:                            log.debug("Process exit value = " + p.exitValue());
554:                        }
555:                        log
556:                                .warn("problem getting CVSROOT/users; using empty email map");
557:                        mailAliases = new Hashtable();
558:                    }
559:                }
560:
561:                return mailAliases;
562:            }
563:
564:            void addAliasToMap(String line) {
565:                log.debug("Mapping " + line);
566:                int colon = line.indexOf(':');
567:
568:                if (colon >= 0) {
569:                    String user = line.substring(0, colon);
570:                    String address = line.substring(colon + 1);
571:                    mailAliases.put(user, address);
572:
573:                }
574:            }
575:
576:            /**
577:             * @param lastBuildTime
578:             * @param checkTime
579:             * @return CommandLine for "cvs -d CVSROOT -q log -N -dlastbuildtime<checktime "
580:             */
581:            public Commandline buildHistoryCommand(Date lastBuildTime,
582:                    Date checkTime) throws CruiseControlException {
583:                Commandline commandLine = getCommandline();
584:                commandLine.setExecutable("cvs");
585:
586:                if (compression != null) {
587:                    commandLine.createArgument("-z" + compression);
588:                }
589:                if (cvsroot != null) {
590:                    commandLine.createArguments("-d", cvsroot);
591:                }
592:                commandLine.createArgument(reallyQuiet ? "-Q" : "-q");
593:
594:                if (local != null) {
595:                    commandLine.setWorkingDirectory(local);
596:                    commandLine.createArgument("log");
597:                } else {
598:                    commandLine.createArgument("rlog");
599:                }
600:
601:                if (useHead()) {
602:                    commandLine.createArgument("-N");
603:                }
604:                String dateRange = formatCVSDate(lastBuildTime) + "<"
605:                        + formatCVSDate(checkTime);
606:                commandLine.createArgument("-d" + dateRange);
607:
608:                if (!useHead()) {
609:                    // add -b and -rTAG to list changes relative to the current branch,
610:                    // not relative to the default branch, which is HEAD
611:
612:                    // note: -r cannot have a space between itself and the tag spec.
613:                    commandLine.createArgument("-r" + tag);
614:                } else {
615:                    // This is used to include the head only if a Tag is not specified.
616:                    commandLine.createArgument("-b");
617:                }
618:
619:                if (local == null) {
620:                    commandLine.createArgument(module);
621:                }
622:
623:                return commandLine;
624:            }
625:
626:            // factory method for mock...
627:            protected Commandline getCommandline() {
628:                return new Commandline();
629:            }
630:
631:            static String formatCVSDate(Date date) {
632:                return CVSDateUtil.formatCVSDate(date);
633:            }
634:
635:            /**
636:             * Parses the input stream, which should be from the cvs log command. This method will format the data found in the
637:             * input stream into a List of Modification instances.
638:             * 
639:             * @param input
640:             *            InputStream to get log data from.
641:             * @return List of Modification elements, maybe empty never null.
642:             * @throws IOException
643:             */
644:            protected List parseStream(InputStream input) throws IOException {
645:                BufferedReader reader = new BufferedReader(
646:                        new InputStreamReader(input));
647:
648:                // Read to the first RCS file name. The first entry in the log
649:                // information will begin with this line. A CVS_FILE_DELIMITER is NOT
650:                // present. If no RCS file lines are found then there is nothing to do.
651:
652:                String line = readToNotPast(reader, CVS_RCSFILE_LINE, null);
653:                ArrayList mods = new ArrayList();
654:
655:                while (line != null) {
656:                    // Parse the single file entry, which may include several
657:                    // modifications.
658:                    List returnList = parseEntry(reader, line);
659:
660:                    // Add all the modifications to the local list.
661:                    mods.addAll(returnList);
662:
663:                    // Read to the next RCS file line. The CVS_FILE_DELIMITER may have
664:                    // been consumed by the parseEntry method, so we cannot read to it.
665:                    line = readToNotPast(reader, CVS_RCSFILE_LINE, null);
666:                }
667:
668:                return mods;
669:            }
670:
671:            private void getRidOfLeftoverData(InputStream stream) {
672:                new StreamPumper(stream, new DiscardConsumer()).run();
673:            }
674:
675:            private List execHistoryCommand(Commandline command)
676:                    throws Exception {
677:                Process p = command.execute();
678:
679:                Thread stderr = logErrorStream(p);
680:                InputStream cvsLogStream = p.getInputStream();
681:                List mods = parseStream(cvsLogStream);
682:
683:                getRidOfLeftoverData(cvsLogStream);
684:                p.waitFor();
685:                stderr.join();
686:                IO.close(p);
687:
688:                return mods;
689:            }
690:
691:            protected void setMailAliases(Hashtable mailAliases) {
692:                this .mailAliases = mailAliases;
693:            }
694:
695:            private static Thread logErrorStream(Process p) {
696:                return logErrorStream(p.getErrorStream());
697:            }
698:
699:            static Thread logErrorStream(InputStream error) {
700:                Thread stderr = new Thread(StreamLogger.getWarnPumper(log,
701:                        error));
702:                stderr.start();
703:                return stderr;
704:            }
705:
706:            // (PENDING) Extract CVSEntryParser class
707:
708:            /**
709:             * Parses a single file entry from the reader. This entry may contain zero or more revisions. This method may
710:             * consume the next CVS_FILE_DELIMITER line from the reader, but no further. <p/> When the log is related to a non
711:             * branch tag, only the last modification for each file will be listed.
712:             * 
713:             * @param reader
714:             *            Reader to parse data from.
715:             * @return modifications found in this entry; maybe empty, never null.
716:             * @throws IOException
717:             */
718:            private List parseEntry(BufferedReader reader, String rcsLine)
719:                    throws IOException {
720:                ArrayList mods = new ArrayList();
721:
722:                String nextLine = "";
723:
724:                // Read to the working file name line to get the filename.
725:                // If working file name line isn't found we'll extract is from the RCS file line
726:                String workingFileName;
727:                if (module != null && cvsroot != null) {
728:                    final String repositoryRoot = cvsroot.substring(cvsroot
729:                            .lastIndexOf(":") + 1);
730:                    final int startAt = "RCS file: ".length()
731:                            + repositoryRoot.length();
732:                    workingFileName = rcsLine.substring(startAt, rcsLine
733:                            .length() - 2);
734:                } else {
735:                    String workingFileLine = readToNotPast(reader,
736:                            CVS_WORKINGFILE_LINE, null);
737:                    workingFileName = workingFileLine
738:                            .substring(CVS_WORKINGFILE_LINE.length());
739:                }
740:
741:                String branchRevisionName = parseBranchRevisionName(reader);
742:                boolean newCVSVersion = isCvsNewOutputFormat();
743:                while (nextLine != null && !nextLine.startsWith(CVS_FILE_DELIM)) {
744:                    nextLine = readToNotPast(reader, "revision", CVS_FILE_DELIM);
745:                    if (nextLine == null) {
746:                        // No more revisions for this file.
747:                        break;
748:                    }
749:
750:                    StringTokenizer tokens = new StringTokenizer(nextLine, " ");
751:                    tokens.nextToken();
752:                    String revision = tokens.nextToken();
753:                    if (!useHead()) {
754:                        if (!revision.equals(branchRevisionName)) {
755:                            // Indeed this is a branch, not just a regular tag
756:                            String itsBranchRevisionName = revision.substring(
757:                                    0, revision.lastIndexOf('.'));
758:                            if (!itsBranchRevisionName
759:                                    .equals(branchRevisionName)) {
760:                                break;
761:                            }
762:                        }
763:                    }
764:
765:                    // Read to the revision date. It is ASSUMED that each revision
766:                    // section will include this date information line.
767:                    nextLine = readToNotPast(reader, CVS_REVISION_DATE,
768:                            CVS_FILE_DELIM);
769:                    if (nextLine == null) {
770:                        break;
771:                    }
772:
773:                    tokens = new StringTokenizer(nextLine, " \t\n\r\f;");
774:                    // First token is the keyword for date, then the next two should be
775:                    // the date and time stamps.
776:                    tokens.nextToken();
777:                    String dateStamp = tokens.nextToken();
778:                    String timeStamp = tokens.nextToken();
779:
780:                    // New format sometimes has a +0000 in it. This skips it if we don't see
781:                    // the start of the author: section
782:                    String isThisTimeOffset = tokens.nextToken();
783:                    if (!isThisTimeOffset.equals("author:")) {
784:                        tokens.nextToken();
785:                    }
786:                    // The next token should be the author keyword, then the author name.
787:                    String authorName = tokens.nextToken();
788:
789:                    // The next token should be the state keyword, then the state name.
790:                    tokens.nextToken();
791:                    String stateKeyword = tokens.nextToken();
792:
793:                    // if no lines keyword then file is added
794:                    boolean isAdded = !tokens.hasMoreTokens();
795:
796:                    // All the text from now to the next revision delimiter or working
797:                    // file delimiter constitutes the message.
798:                    String message = "";
799:                    nextLine = reader.readLine();
800:                    boolean multiLine = false;
801:
802:                    while (nextLine != null
803:                            && !nextLine.startsWith(CVS_FILE_DELIM)
804:                            && !nextLine.startsWith(CVS_REVISION_DELIM)) {
805:
806:                        if (multiLine) {
807:                            message += NEW_LINE;
808:                        } else {
809:                            multiLine = true;
810:                        }
811:                        message += nextLine;
812:
813:                        // Go to the next line.
814:                        nextLine = reader.readLine();
815:                    }
816:
817:                    Modification nextModification = new Modification("cvs");
818:                    nextModification.revision = revision;
819:
820:                    int lastSlashIndex = workingFileName.lastIndexOf("/");
821:
822:                    String fileName, folderName = null;
823:                    fileName = workingFileName.substring(lastSlashIndex + 1);
824:                    if (lastSlashIndex != -1) {
825:                        folderName = workingFileName.substring(0,
826:                                lastSlashIndex);
827:                    }
828:                    Modification.ModifiedFile modfile = nextModification
829:                            .createModifiedFile(fileName, folderName);
830:                    modfile.revision = nextModification.revision;
831:
832:                    try {
833:                        if (newCVSVersion) {
834:                            nextModification.modifiedTime = CVSDateUtil
835:                                    .parseCVSDate(dateStamp + " " + timeStamp
836:                                            + " GMT");
837:                        } else {
838:                            nextModification.modifiedTime = logDateFormatter
839:                                    .parse(dateStamp + " " + timeStamp + " GMT");
840:                        }
841:                    } catch (ParseException pe) {
842:                        log
843:                                .error(
844:                                        "Error parsing cvs log for date and time",
845:                                        pe);
846:                        return null;
847:                    }
848:
849:                    nextModification.userName = authorName;
850:
851:                    String address = (String) mailAliases.get(authorName);
852:                    if (address != null) {
853:                        nextModification.emailAddress = address;
854:                    }
855:
856:                    nextModification.comment = message;
857:
858:                    if (stateKeyword.equalsIgnoreCase(CVS_REVISION_DEAD)
859:                            && message.indexOf("was initially added on branch") != -1) {
860:                        log.debug("skipping branch addition activity for "
861:                                + nextModification);
862:                        // this prevents additions to a branch from showing up as action "deleted" from head
863:                        continue;
864:                    }
865:
866:                    if (stateKeyword.equalsIgnoreCase(CVS_REVISION_DEAD)) {
867:                        modfile.action = "deleted";
868:                        properties.deletionFound();
869:                    } else if (isAdded) {
870:                        modfile.action = "added";
871:                    } else {
872:                        modfile.action = "modified";
873:                    }
874:                    properties.modificationFound();
875:                    mods.add(nextModification);
876:                }
877:                return mods;
878:            }
879:
880:            /**
881:             * Find the CVS branch revision name, when the tag is not HEAD The reader will consume all lines up to the next
882:             * description.
883:             * 
884:             * @return the branch revision name, or <code>null</code> if not applicable or none was found.
885:             */
886:            private String parseBranchRevisionName(BufferedReader reader)
887:                    throws IOException {
888:                String branchRevisionName = null;
889:
890:                if (!useHead()) {
891:                    // Look for the revision of the form "tag: *.(0.)y ". this doesn't work for HEAD
892:                    // get line with branch revision on it.
893:
894:                    String branchRevisionLine = readToNotPast(reader, "\t"
895:                            + tag + ": ", CVS_DESCRIPTION);
896:
897:                    if (branchRevisionLine != null) {
898:                        // Look for the revision of the form "tag: *.(0.)y ", return "*.y"
899:                        branchRevisionName = branchRevisionLine.substring(tag
900:                                .length() + 3);
901:                        if (branchRevisionName.charAt(branchRevisionName
902:                                .lastIndexOf(".") - 1) == '0') {
903:                            branchRevisionName = branchRevisionName.substring(
904:                                    0, branchRevisionName.lastIndexOf(".") - 2)
905:                                    + branchRevisionName
906:                                            .substring(branchRevisionName
907:                                                    .lastIndexOf("."));
908:                        }
909:                    }
910:                }
911:                return branchRevisionName;
912:            }
913:
914:            /**
915:             * This method will consume lines from the reader up to the line that begins with the String specified but not past
916:             * a line that begins with the notPast String. If the line that begins with the beginsWith String is found then it
917:             * will be returned. Otherwise null is returned.
918:             * 
919:             * @param reader
920:             *            Reader to read lines from.
921:             * @param beginsWith
922:             *            String to match to the beginning of a line.
923:             * @param notPast
924:             *            String which indicates that lines should stop being consumed, even if the begins with match has not
925:             *            been found. Pass null to this method to ignore this string.
926:             * @return String that begin as indicated, or null if none matched to the end of the reader or the notPast line was
927:             *         found.
928:             * @throws IOException
929:             */
930:            private static String readToNotPast(BufferedReader reader,
931:                    String beginsWith, String notPast) throws IOException {
932:                boolean checkingNotPast = notPast != null;
933:
934:                String nextLine = reader.readLine();
935:                while (nextLine != null && !nextLine.startsWith(beginsWith)) {
936:                    if (checkingNotPast && nextLine.startsWith(notPast)) {
937:                        return null;
938:                    }
939:                    nextLine = reader.readLine();
940:                }
941:
942:                return nextLine;
943:            }
944:
945:            boolean useHead() {
946:                return tag == null || tag.equals(CVS_HEAD_TAG)
947:                        || tag.equals("");
948:            }
949:
950:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.