KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > sourcecontrols > P4


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2001-2003, ThoughtWorks, Inc.
4  * 651 W Washington Ave. Suite 600
5  * Chicago, IL 60661 USA
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * + Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * + Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  *
20  * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21  * names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior
23  * written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  ********************************************************************************/

37 package net.sourceforge.cruisecontrol.sourcecontrols;
38
39 import net.sourceforge.cruisecontrol.CruiseControlException;
40 import net.sourceforge.cruisecontrol.Modification;
41 import net.sourceforge.cruisecontrol.SourceControl;
42 import net.sourceforge.cruisecontrol.util.Commandline;
43 import net.sourceforge.cruisecontrol.util.StreamPumper;
44 import net.sourceforge.cruisecontrol.util.Util;
45 import net.sourceforge.cruisecontrol.util.ValidationHelper;
46 import org.apache.log4j.Logger;
47 import org.jdom.Element;
48
49 import java.io.BufferedReader JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.io.InputStreamReader JavaDoc;
53 import java.io.PrintWriter JavaDoc;
54 import java.text.DateFormat JavaDoc;
55 import java.text.ParseException JavaDoc;
56 import java.text.SimpleDateFormat JavaDoc;
57 import java.util.ArrayList JavaDoc;
58 import java.util.Calendar JavaDoc;
59 import java.util.Date JavaDoc;
60 import java.util.HashMap JavaDoc;
61 import java.util.Hashtable JavaDoc;
62 import java.util.Iterator JavaDoc;
63 import java.util.List JavaDoc;
64 import java.util.Map JavaDoc;
65 import java.util.NoSuchElementException JavaDoc;
66 import java.util.StringTokenizer JavaDoc;
67
68 /**
69  * This class implements the SourceControlElement methods for a P4 depot. The
70  * call to CVS is assumed to work without any setup. This implies that if the
71  * authentication type is pserver the call to cvs login should be done prior to
72  * calling this class.
73  * <p/>
74  * P4Element depends on the optional P4 package delivered with Ant v1.3. But
75  * since it probably doesn't make much sense using the P4Element without other
76  * P4 support it shouldn't be a problem.
77  * <p/>
78  * P4Element sets the property ${p4element.change} with the latest changelist
79  * number or the changelist with the latest date. This should then be passed
80  * into p4sync or other p4 commands.
81  *
82  * @author <a HREF="mailto:niclas.olofsson@ismobile.com">Niclas Olofsson - isMobile.com</a>
83  * @author <a HREF="mailto:jcyip@thoughtworks.com">Jason Yip</a>
84  * @author Tim McCune
85  * @author J D Glanville
86  * @author Patrick Conant Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
87  * @author John Lussmyer
88  */

89 public class P4 implements SourceControl {
90
91     private static final Logger LOG = Logger.getLogger(P4.class);
92
93     private String JavaDoc p4Port;
94     private String JavaDoc p4Client;
95     private String JavaDoc p4User;
96     private String JavaDoc p4View;
97     private String JavaDoc p4Passwd;
98     private boolean correctForServerTime = true;
99     private boolean useP4Email = true;
100
101     private final SimpleDateFormat JavaDoc p4RevisionDateFormatter =
102             new SimpleDateFormat JavaDoc("yyyy/MM/dd:HH:mm:ss");
103
104
105     private static final String JavaDoc SERVER_DATE = "Server date: ";
106     private static final String JavaDoc P4_SERVER_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss";
107
108     private final SimpleDateFormat JavaDoc p4ServerDateFormatter =
109             new SimpleDateFormat JavaDoc(P4_SERVER_DATE_FORMAT);
110
111
112     private Hashtable JavaDoc properties = new Hashtable JavaDoc();
113
114     public void setPort(String JavaDoc p4Port) {
115         this.p4Port = p4Port;
116     }
117
118     public void setClient(String JavaDoc p4Client) {
119         this.p4Client = p4Client;
120     }
121
122     public void setUser(String JavaDoc p4User) {
123         this.p4User = p4User;
124     }
125
126     public void setView(String JavaDoc p4View) {
127         this.p4View = p4View;
128     }
129
130     public void setPasswd(String JavaDoc p4Passwd) {
131         this.p4Passwd = p4Passwd;
132     }
133
134     /**
135      * Indicates whether to correct for time differences between the p4
136      * server and the CruiseControl server. Setting the flag to "true"
137      * will correct for both time zone differences and for non-synchronized
138      * system clocks.
139      */

140     public void setCorrectForServerTime(boolean flag) {
141         this.correctForServerTime = flag;
142     }
143
144     /**
145      * Sets if the Email address for the user should be retrieved from Perforce.
146      *
147      * @param flag true to retrieve email addresses from perforce.
148      */

149     public void setUseP4Email(boolean flag) {
150         useP4Email = flag;
151     }
152
153     public Map JavaDoc getProperties() {
154         return properties;
155     }
156
157     public void validate() throws CruiseControlException {
158         ValidationHelper.assertIsSet(p4Port, "port", this.getClass());
159         ValidationHelper.assertIsSet(p4Client, "client", this.getClass());
160         ValidationHelper.assertIsSet(p4User, "user", this.getClass());
161         ValidationHelper.assertIsSet(p4View, "view", this.getClass());
162         ValidationHelper.assertNotEmpty(p4Passwd, "passwd", this.getClass());
163     }
164
165     /**
166      * Get a List of modifications detailing all the changes between now and
167      * the last build. Return this as an element. It is not neccessary for
168      * sourcecontrols to acctually do anything other than returning a chunch
169      * of XML data back.
170      *
171      * @param lastBuild time of last build
172      * @param now time this build started
173      * @return a list of XML elements that contains data about the modifications
174      * that took place. If no changes, this method returns an empty list.
175      */

176     public List JavaDoc getModifications(Date JavaDoc lastBuild, Date JavaDoc now) {
177         List JavaDoc mods = new ArrayList JavaDoc();
178         try {
179             String JavaDoc[] changelistNumbers = collectChangelistSinceLastBuild(lastBuild, now);
180             if (changelistNumbers.length == 0) {
181                 return mods;
182             }
183             mods = describeAllChangelistsAndBuildOutput(changelistNumbers);
184         } catch (Exception JavaDoc e) {
185             LOG.error("Log command failed to execute succesfully", e);
186         }
187
188         return mods;
189     }
190
191     private List JavaDoc describeAllChangelistsAndBuildOutput(String JavaDoc[] changelistNumbers)
192             throws IOException JavaDoc, InterruptedException JavaDoc {
193
194         Commandline command = buildDescribeCommand(changelistNumbers);
195         LOG.info(command.toString());
196         Process JavaDoc p = Runtime.getRuntime().exec(command.getCommandline());
197
198         logErrorStream(p.getErrorStream());
199         InputStream JavaDoc p4Stream = p.getInputStream();
200         List JavaDoc mods = parseChangeDescriptions(p4Stream);
201         getRidOfLeftoverData(p4Stream);
202
203         // Get the Email address of the user for each changelist
204
if ((mods.size() > 0) && useP4Email) {
205             getEmailAddresses(mods);
206         }
207
208         p.waitFor();
209         p.getInputStream().close();
210         p.getOutputStream().close();
211         p.getErrorStream().close();
212
213         return mods;
214     }
215
216     /**
217      * Get the Email Address of the users who submitted the change lists.
218      *
219      * @param mods List of P4Modification structures
220      * @throws IOException
221      * @throws InterruptedException
222      */

223     private void getEmailAddresses(List JavaDoc mods) throws IOException JavaDoc,
224             InterruptedException JavaDoc {
225         Iterator JavaDoc iter = mods.iterator();
226         Map JavaDoc users = new HashMap JavaDoc();
227
228         while (iter.hasNext()) {
229             P4Modification change = (P4Modification) iter.next();
230
231             if ((change.userName != null) && (change.userName.length() > 0)) {
232                 change.emailAddress = (String JavaDoc) users.get(change.userName);
233
234                 if (change.emailAddress == null) {
235                     change.emailAddress = getUserEmailAddress(change.userName);
236                     users.put(change.userName, change.emailAddress);
237                 }
238             }
239
240         }
241
242     }
243
244     /**
245      * Get the Email Address for the given P4 User
246      *
247      * @param username Perforce user name
248      * @return User Email address if available
249      * @throws IOException
250      * @throws InterruptedException
251      */

252     private String JavaDoc getUserEmailAddress(String JavaDoc username) throws IOException JavaDoc,
253             InterruptedException JavaDoc {
254         String JavaDoc emailaddr = null;
255
256         Commandline command = buildUserCommand(username);
257         LOG.info(command.toString());
258         Process JavaDoc p = Runtime.getRuntime().exec(command.getCommandline());
259
260         logErrorStream(p.getErrorStream());
261         InputStream JavaDoc p4Stream = p.getInputStream();
262         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(
263                 p4Stream));
264
265         // Find first Changelist item if there is one.
266
String JavaDoc line;
267         while ((line = readToNotPast(reader, "info: Email:",
268                 "I really don't care")) != null) {
269             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(line);
270
271             try {
272                 st.nextToken(); // skip 'info:' text
273
st.nextToken(); // skip 'Email:' text
274
emailaddr = st.nextToken();
275             } catch (NoSuchElementException JavaDoc ex) {
276                 // No email address given
277
}
278         }
279
280         getRidOfLeftoverData(p4Stream);
281
282         p.waitFor();
283         p.getInputStream().close();
284         p.getOutputStream().close();
285         p.getErrorStream().close();
286
287         return (emailaddr);
288     }
289
290     private String JavaDoc[] collectChangelistSinceLastBuild(Date JavaDoc lastBuild, Date JavaDoc now)
291             throws IOException JavaDoc, InterruptedException JavaDoc {
292
293         Commandline command = buildChangesCommand(lastBuild, now, Util.isWindows());
294         LOG.debug("Executing: " + command.toString());
295         Process JavaDoc p = Runtime.getRuntime().exec(command.getCommandline());
296
297         logErrorStream(p.getErrorStream());
298         InputStream JavaDoc p4Stream = p.getInputStream();
299
300         String JavaDoc[] changelistNumbers = parseChangelistNumbers(p4Stream);
301
302         getRidOfLeftoverData(p4Stream);
303         p.waitFor();
304         p.getInputStream().close();
305         p.getOutputStream().close();
306         p.getErrorStream().close();
307
308         return changelistNumbers;
309     }
310
311     private void getRidOfLeftoverData(InputStream JavaDoc stream) {
312         StreamPumper outPumper = new StreamPumper(stream, (PrintWriter JavaDoc) null);
313         new Thread JavaDoc(outPumper).start();
314     }
315
316     protected String JavaDoc[] parseChangelistNumbers(InputStream JavaDoc is) throws IOException JavaDoc {
317         ArrayList JavaDoc changelists = new ArrayList JavaDoc();
318
319         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
320         String JavaDoc line;
321         while ((line = reader.readLine()) != null) {
322             if (line.startsWith("error:")) {
323                 throw new IOException JavaDoc("Error reading P4 stream: P4 says: " + line);
324             } else if (line.startsWith("exit: 1")) {
325                 throw new IOException JavaDoc("Error reading P4 stream: P4 says: " + line);
326             } else if (line.startsWith("exit: 0")) {
327                 break;
328             } else if (line.startsWith("info:")) {
329                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(line);
330                 st.nextToken(); // skip 'info:' text
331
st.nextToken(); // skip 'Change' text
332
changelists.add(st.nextToken());
333             }
334         }
335         if (line == null) {
336             throw new IOException JavaDoc("Error reading P4 stream: Unexpected EOF reached");
337         }
338         String JavaDoc[] changelistNumbers = new String JavaDoc[ 0 ];
339         return (String JavaDoc[]) changelists.toArray(changelistNumbers);
340     }
341
342     protected List JavaDoc parseChangeDescriptions(InputStream JavaDoc is) throws IOException JavaDoc {
343
344         ArrayList JavaDoc changelists = new ArrayList JavaDoc();
345
346         BufferedReader JavaDoc reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
347
348         // Find first Changelist item if there is one.
349
String JavaDoc line;
350         while ((line = readToNotPast(reader, "text: Change", "exit:")) != null) {
351
352             P4Modification changelist = new P4Modification();
353             if (line.startsWith("error:")) {
354                 throw new IOException JavaDoc("Error reading P4 stream: P4 says: " + line);
355             } else if (line.startsWith("exit: 1")) {
356                 throw new IOException JavaDoc("Error reading P4 stream: P4 says: " + line);
357             } else if (line.startsWith("exit: 0")) {
358                 return changelists;
359             } else if (line.startsWith("text: Change")) {
360                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(line);
361
362                 st.nextToken(); // skip 'text:' text
363
st.nextToken(); // skip 'Change' text
364
changelist.revision = st.nextToken();
365                 st.nextToken(); // skip 'by' text
366

367                 // split user@client
368
StringTokenizer JavaDoc st2 = new StringTokenizer JavaDoc(st.nextToken(), "@");
369                 changelist.userName = st2.nextToken();
370                 changelist.client = st2.nextToken();
371
372                 st.nextToken(); // skip 'on' text
373
String JavaDoc date = st.nextToken() + ":" + st.nextToken();
374                 try {
375                     changelist.modifiedTime = p4RevisionDateFormatter.parse(date);
376                 } catch (ParseException JavaDoc xcp) {
377                     changelist.modifiedTime = new Date JavaDoc();
378                 }
379             }
380
381             reader.readLine(); // get past a 'text:'
382
StringBuffer JavaDoc descriptionBuffer = new StringBuffer JavaDoc();
383             // Use this since we don't want the final (empty) line
384
String JavaDoc previousLine = null;
385             while ((line = reader.readLine()) != null
386                     && line.startsWith("text:")
387                     && !line.startsWith("text: Affected files ...")) {
388
389                 if (previousLine != null) {
390                     if (descriptionBuffer.length() > 0) {
391                         descriptionBuffer.append('\n');
392                     }
393                     descriptionBuffer.append(previousLine);
394                 }
395                 try {
396                     previousLine = line.substring(5).trim();
397                 } catch (Exception JavaDoc e) {
398                     LOG.error("Error parsing Perforce description, line that caused problem was: ["
399                             + line
400                             + "]");
401                 }
402
403             }
404
405             changelist.comment = descriptionBuffer.toString();
406
407             // Ok, read affected files if there are any.
408
if (line != null) {
409                 reader.readLine(); // read past next 'text:'
410
while ((line = readToNotPast(reader, "info1:", "text:")) != null
411                         && line.startsWith("info1:")) {
412
413                     String JavaDoc fileName = line.substring(7, line.lastIndexOf("#"));
414                     Modification.ModifiedFile affectedFile = changelist.createModifiedFile(fileName, null);
415                     affectedFile.action = line.substring(line.lastIndexOf(" ") + 1);
416                     affectedFile.revision =
417                             line.substring(line.lastIndexOf("#") + 1, line.lastIndexOf(" "));
418                 }
419             }
420             changelists.add(changelist);
421         }
422
423         return changelists;
424     }
425
426     private void logErrorStream(InputStream JavaDoc is) {
427         StreamPumper errorPumper = new StreamPumper(is, new PrintWriter JavaDoc(System.err, true));
428         new Thread JavaDoc(errorPumper).start();
429     }
430
431     /**
432      * p4 -s [-c client] [-p port] [-u user] changes -s submitted [view@lastBuildTime@now]
433      */

434     public Commandline buildChangesCommand(Date JavaDoc lastBuildTime, Date JavaDoc now, boolean isWindows) {
435
436         //If the Perforce server time is different from the CruiseControl
437
// server time, correct the parameter dates for the difference.
438
if (this.correctForServerTime) {
439             try {
440                 int offset = (int) calculateServerTimeOffset();
441                 Calendar JavaDoc cal = Calendar.getInstance();
442
443                 cal.setTime(lastBuildTime);
444                 cal.add(Calendar.MILLISECOND, offset);
445                 lastBuildTime = cal.getTime();
446
447                 cal.setTime(now);
448                 cal.add(Calendar.MILLISECOND, offset);
449                 now = cal.getTime();
450
451             } catch (IOException JavaDoc ioe) {
452                 LOG.error("Unable to execute \'p4 info\' to get server time: "
453                         + ioe.getMessage()
454                         + "\nProceeding without time offset value.");
455             }
456         } else {
457             LOG.debug("No server time offset determined.");
458         }
459
460
461         Commandline commandLine = buildBaseP4Command();
462
463         commandLine.createArgument().setValue("changes");
464         commandLine.createArgument().setValue("-s");
465         commandLine.createArgument().setValue("submitted");
466         commandLine.createArgument().setValue(p4View
467                 + "@"
468                 + p4RevisionDateFormatter.format(lastBuildTime)
469                 + ",@"
470                 + p4RevisionDateFormatter.format(now));
471
472         return commandLine;
473     }
474
475     /**
476      * p4 -s [-c client] [-p port] [-u user] describe -s [change number]
477      */

478     public Commandline buildDescribeCommand(String JavaDoc[] changelistNumbers) {
479         Commandline commandLine = buildBaseP4Command();
480
481         // execP4Command("describe -s " + changeNumber.toString(),
482

483         commandLine.createArgument().setValue("describe");
484         commandLine.createArgument().setValue("-s");
485
486         for (int i = 0; i < changelistNumbers.length; i++) {
487             commandLine.createArgument().setValue(changelistNumbers[i]);
488         }
489
490         return commandLine;
491     }
492
493     /**
494      * p4 -s [-c client] [-p port] [-u user] user -o [username]
495      */

496     public Commandline buildUserCommand(String JavaDoc username) {
497         Commandline commandLine = buildBaseP4Command();
498         commandLine.createArgument().setValue("user");
499         commandLine.createArgument().setValue("-o");
500         commandLine.createArgument().setValue(username);
501
502         return commandLine;
503     }
504
505     /**
506      * Calculate the difference in time between the Perforce server and the
507      * CruiseControl server. A negative time difference indicates that the
508      * Perforce server time is later than CruiseControl server (e.g. Perforce
509      * in New York, CruiseControl in San Francisco). A negative offset
510      * indicates that the Perforce server time is before the CruiseControl
511      * server.
512      */

513     protected long calculateServerTimeOffset() throws IOException JavaDoc {
514         Commandline command = new Commandline();
515         command.setExecutable("p4");
516         command.createArgument().setValue("info");
517
518         LOG.debug("Executing: " + command.toString());
519         LOG.info(command.toString());
520         Process JavaDoc p = Runtime.getRuntime().exec(command.getCommandline());
521         logErrorStream(p.getErrorStream());
522
523         return parseServerInfo(p.getInputStream());
524     }
525
526     protected long parseServerInfo(InputStream JavaDoc is) throws IOException JavaDoc {
527
528         BufferedReader JavaDoc p4reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
529
530         Date JavaDoc ccServerTime = new Date JavaDoc();
531         Date JavaDoc p4ServerTime;
532
533         String JavaDoc line;
534         long offset = 0;
535         while ((line = p4reader.readLine()) != null && offset == 0) {
536             if (line.startsWith(SERVER_DATE)) {
537                 try {
538                     String JavaDoc dateString = line.substring(SERVER_DATE.length(),
539                             SERVER_DATE.length() + P4_SERVER_DATE_FORMAT.length());
540                     p4ServerTime = p4ServerDateFormatter.parse(dateString);
541                     offset = p4ServerTime.getTime() - ccServerTime.getTime();
542                 } catch (ParseException JavaDoc pe) {
543                     LOG.error("Unable to parse p4 server time from line \'"
544                             + line
545                             + "\'. " + pe.getMessage()
546                             + "; Proceeding without time offset.");
547                 }
548             }
549         }
550
551         LOG.info("Perforce server time offset: " + offset + " ms");
552         return offset;
553     }
554
555
556     private Commandline buildBaseP4Command() {
557         Commandline commandLine = new Commandline();
558         commandLine.setExecutable("p4");
559         commandLine.createArgument().setValue("-s");
560
561         if (p4Client != null) {
562             commandLine.createArgument().setValue("-c");
563             commandLine.createArgument().setValue(p4Client);
564         }
565
566         if (p4Port != null) {
567             commandLine.createArgument().setValue("-p");
568             commandLine.createArgument().setValue(p4Port);
569         }
570
571         if (p4User != null) {
572             commandLine.createArgument().setValue("-u");
573             commandLine.createArgument().setValue(p4User);
574         }
575
576         if (p4Passwd != null) {
577             commandLine.createArgument().setValue("-P");
578             commandLine.createArgument().setValue(p4Passwd);
579         }
580         return commandLine;
581     }
582
583     /**
584      * This is a modified version of the one in the CVS element. I found it far
585      * more useful if you acctually return either or, because otherwise it would
586      * be darn hard to use in places where I acctually need the notPast line.
587      * Or did I missunderatnd something?
588      */

589     private String JavaDoc readToNotPast(BufferedReader JavaDoc reader, String JavaDoc beginsWith, String JavaDoc notPast)
590             throws IOException JavaDoc {
591
592         String JavaDoc nextLine = reader.readLine();
593
594         // (!A && !B) || (!A && !C) || (!B && !C)
595
// !A || !B || !C
596
while (!(nextLine == null
597                 || nextLine.startsWith(beginsWith)
598                 || nextLine.startsWith(notPast))) {
599             nextLine = reader.readLine();
600         }
601         return nextLine;
602     }
603
604     private static class P4Modification extends Modification {
605         public String JavaDoc client;
606
607         public int compareTo(Object JavaDoc o) {
608             P4Modification modification = (P4Modification) o;
609             return getChangelistNumber() - modification.getChangelistNumber();
610         }
611
612         public boolean equals(Object JavaDoc o) {
613             if (o == null || !(o instanceof P4Modification)) {
614                 return false;
615             }
616
617             P4Modification modification = (P4Modification) o;
618             return getChangelistNumber() == modification.getChangelistNumber();
619         }
620
621         public int hashCode() {
622             return getChangelistNumber();
623         }
624
625         private int getChangelistNumber() {
626             return Integer.parseInt(revision);
627         }
628
629         P4Modification() {
630             super("p4");
631         }
632
633         public Element toElement(DateFormat JavaDoc format) {
634
635             Element element = super.toElement(format);
636             LOG.debug("client = " + client);
637
638             Element clientElement = new Element("client");
639             clientElement.addContent(client);
640             element.addContent(clientElement);
641
642             return element;
643         }
644     }
645
646     static String JavaDoc getQuoteChar(boolean isWindows) {
647         return isWindows ? "\"" : "'";
648     }
649 }
650
Popular Tags