~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to javacvs/libsrc/org/netbeans/lib/cvsclient/admin/Entry.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is the CVS Client Library.
 
27
 * The Initial Developer of the Original Software is Robert Greig.
 
28
 * Portions created by Robert Greig are Copyright (C) 2000.
 
29
 * All Rights Reserved.
 
30
 *
 
31
 * If you wish your version of this file to be governed by only the CDDL
 
32
 * or only the GPL Version 2, indicate your decision by adding
 
33
 * "[Contributor] elects to include this software in this distribution
 
34
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
35
 * single choice of license, a recipient has the option to distribute
 
36
 * your version of this file under either the CDDL, the GPL Version 2 or
 
37
 * to extend the choice of license to its licensees as provided above.
 
38
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
39
 * Version 2 license, then the option applies only if the new code is
 
40
 * made subject to such option by the copyright holder.
 
41
 *
 
42
 * Contributor(s): Robert Greig.
 
43
 *****************************************************************************/
 
44
package org.netbeans.lib.cvsclient.admin;
 
45
 
 
46
import java.text.*;
 
47
import java.util.*;
 
48
 
 
49
/**
 
50
 * The class abstracts the CVS concept of an <i>entry line</i>. The entry
 
51
 * line is textually of the form:<p>
 
52
 * / name / version / conflict / options / tag_or_date
 
53
 * <p>These are explained in section 5.1 of the CVS protocol 1.10 document.
 
54
 * @author  Robert Greig
 
55
 */
 
56
public final class Entry {
 
57
    /**
 
58
     * The dummy timestamp set the conflict information for added or removed
 
59
     * files.
 
60
     */
 
61
    public static final String DUMMY_TIMESTAMP = "dummy timestamp"; //NOI18N
 
62
    public static final String DUMMY_TIMESTAMP_NEW_ENTRY = "dummy timestamp from new-entry"; //NOI18N
 
63
 
 
64
    public static final String MERGE_TIMESTAMP = "Result of merge"; //NOI18N
 
65
 
 
66
    /**
 
67
     * Indicates a sticky tag.
 
68
     */
 
69
    private static final String TAG = "T"; //NOI18N
 
70
 
 
71
    /**
 
72
     * Indicates a sticky date.
 
73
     */
 
74
    private static final String DATE = "D"; //NOI18N
 
75
 
 
76
    /**
 
77
     * The instance of the date formatter for sticky dates.
 
78
     */
 
79
    private static SimpleDateFormat stickyDateFormatter;
 
80
 
 
81
    /**
 
82
     * Returns the instance of the date formatter for sticky dates.
 
83
     */
 
84
    private static SimpleDateFormat getStickyDateFormatter() {
 
85
        if (stickyDateFormatter == null) {
 
86
            stickyDateFormatter = new SimpleDateFormat("yyyy.MM.dd.hh.mm.ss"); //NOI18N
 
87
        }
 
88
        return stickyDateFormatter;
 
89
    }
 
90
 
 
91
    /**
 
92
     * Indicates a binary file.
 
93
     */
 
94
    private static final String BINARY_FILE = "-kb"; //NOI18N
 
95
 
 
96
    /**
 
97
     * Indicates that no user file is meant by the version details
 
98
     */
 
99
    private static final String NO_USER_FILE = ""; //NOI18N
 
100
 
 
101
    /**
 
102
     * Indicates that a new user file is meant by the version details
 
103
     */
 
104
    private static final String NEW_USER_FILE = "0"; //NOI18N
 
105
 
 
106
    /**
 
107
     * Indicates that the file is to be removed, in the version details
 
108
     */
 
109
    private static final String REMOVE_USER_FILE = "-"; //NOI18N
 
110
 
 
111
    /**
 
112
     * The date formatter to be used for noting the Last Modified date in a
 
113
     * file.
 
114
     */
 
115
    private static SimpleDateFormat lastModifiedDateFormatter;
 
116
 
 
117
    /**
 
118
     * Returns the instance of the Last-Modified-Date-Formatter.
 
119
     */
 
120
    public static SimpleDateFormat getLastModifiedDateFormatter() {
 
121
        if (lastModifiedDateFormatter == null) {
 
122
            lastModifiedDateFormatter =
 
123
                    new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.US); //NOI18N
 
124
            lastModifiedDateFormatter.setTimeZone(getTimeZone());
 
125
        }
 
126
        return lastModifiedDateFormatter;
 
127
    }
 
128
 
 
129
    /**
 
130
     * All entries times are by defaulf in Zulu/GMT0
 
131
     */
 
132
    public static TimeZone getTimeZone() {
 
133
        return TimeZone.getTimeZone("GMT"); //NOI18N
 
134
    }
 
135
    
 
136
    /**
 
137
     * Indicates that the file had conflicts.
 
138
     */
 
139
    public static final char HAD_CONFLICTS = '+';
 
140
 
 
141
    /**
 
142
     * Indicates that the timestamp matches the file.
 
143
     */
 
144
    public static final char TIMESTAMP_MATCHES_FILE = '=';
 
145
 
 
146
    /**
 
147
     * Indicates that the file had conflicts and timestamp matches.
 
148
     * It likely means unresolved conflict.
 
149
     */
 
150
    public static final String HAD_CONFLICTS_AND_TIMESTAMP_MATCHES_FILE = "+=";
 
151
 
 
152
    /**
 
153
     * Initial letter that indicates a directory entry.
 
154
     */
 
155
    private static final String DIRECTORY_PREFIX = "D/";
 
156
 
 
157
    /**
 
158
     * The name of the file.
 
159
     */
 
160
    private String name;
 
161
 
 
162
    /**
 
163
     * The revision. There are constants defined for no user file, new user
 
164
     * file and user file has to be removed.
 
165
     */
 
166
    private String revision;
 
167
 
 
168
    /**
 
169
     * The conflict information. There are constants defined for indicating
 
170
     * that conflicts occurred and that the timestamp matches the file
 
171
     */
 
172
    private String conflict;
 
173
 
 
174
    /**
 
175
     * The last modified date of the file.
 
176
     */
 
177
    private Date lastModified;
 
178
 
 
179
    /**
 
180
     * The options for signifying keyword expansion.
 
181
     */
 
182
    private String options;
 
183
 
 
184
    /**
 
185
     * The tag. May be present in place of the date information.
 
186
     */
 
187
    private String tag;
 
188
 
 
189
    /**
 
190
     * The date. May be present in place of the tag information.
 
191
     */
 
192
    private Date date;
 
193
 
 
194
    /**
 
195
     * Indicates whether the entry is for a directory.
 
196
     */
 
197
    private boolean directory;
 
198
 
 
199
    /**
 
200
     * Construct a new Entry from a given entry line.
 
201
     */
 
202
    public Entry(String entryLine) {
 
203
        init(entryLine);
 
204
    }
 
205
 
 
206
    /**
 
207
     * Construct a new blank Entry.
 
208
     */
 
209
    public Entry() {
 
210
    }
 
211
 
 
212
    /**
 
213
     * Initialise the Entry by parsing an entry line.
 
214
     * @param entryLine the entry line in standard CVS format
 
215
     */
 
216
    protected void init(String entryLine) {
 
217
        //System.err.println("Constructing an entry line from: " + entryLine);
 
218
        // try to parse the entry line, if we get stuck just
 
219
        // throw an illegal argument exception
 
220
 
 
221
        if (entryLine.startsWith(DIRECTORY_PREFIX)) {
 
222
            directory = true;
 
223
            entryLine = entryLine.substring(1);
 
224
        }
 
225
 
 
226
        // first character is a slash, so name is read from position 1
 
227
        // up to the next slash
 
228
        final int[] slashPositions = new int[5];
 
229
 
 
230
        try {
 
231
            slashPositions[0] = 0;
 
232
            for (int i = 1; i < 5; i++) {
 
233
                slashPositions[i] = entryLine.indexOf('/',
 
234
                                                      slashPositions[i - 1] + 1);
 
235
            }
 
236
 
 
237
            // Test if this is a D on its own, a special case indicating that
 
238
            // directories are understood and there are no subdirectories
 
239
            // in the current folder
 
240
            if (slashPositions[1] > 0) {
 
241
                // note that the parameters to substring are treated as follows:
 
242
                // (inclusive, exclusive)
 
243
                name = entryLine.substring(slashPositions[0] + 1,
 
244
                                           slashPositions[1]);
 
245
                revision = entryLine.substring(slashPositions[1] + 1,
 
246
                                               slashPositions[2]);
 
247
                if ((slashPositions[3] - slashPositions[2]) > 1) {
 
248
                    String conflict = entryLine.substring(slashPositions[2] + 1,
 
249
                                                          slashPositions[3]);
 
250
                    setConflict(conflict);
 
251
                }
 
252
                if ((slashPositions[4] - slashPositions[3]) > 1) {
 
253
                    options = entryLine.substring(slashPositions[3] + 1,
 
254
                                                  slashPositions[4]);
 
255
                }
 
256
                if (slashPositions[4] != (entryLine.length() - 1)) {
 
257
                    String tagOrDate = entryLine.substring(slashPositions[4]
 
258
                                                           + 1);
 
259
                    if (tagOrDate.startsWith(TAG)) {
 
260
                        setTag(tagOrDate.substring(1));
 
261
                    }
 
262
                    else if (tagOrDate.startsWith(DATE)) {
 
263
                        //TODO process date into something useful
 
264
                        // MK - I didn't notice any time conversions (according to timezone)
 
265
                        // So I just convert it from String to Date and back.
 
266
                        try {
 
267
                            String dateString = tagOrDate.substring(DATE.length());
 
268
                            Date stickyDate = getStickyDateFormatter().
 
269
                                    parse(dateString);
 
270
                            setDate(stickyDate);
 
271
                        }
 
272
                        catch (ParseException exc) {
 
273
                            System.err.println("We got another inconsistency in the library's date formatting."); //NOI18N
 
274
                        }
 
275
                    }
 
276
                }
 
277
            }
 
278
        }
 
279
        catch (Exception e) {
 
280
            System.err.println("Error parsing entry line: " + e); //NOI18N
 
281
            e.printStackTrace();
 
282
            throw new IllegalArgumentException("Invalid entry line: " + //NOI18N
 
283
                                               entryLine);
 
284
        }
 
285
    }
 
286
 
 
287
    /**
 
288
     * Get the name of the associated file.
 
289
     * @return the file name
 
290
     */
 
291
    public String getName() {
 
292
        return name;
 
293
    }
 
294
 
 
295
    /**
 
296
     * Set the name.
 
297
     * @param theName the filename to set
 
298
     */
 
299
    public void setName(String name) {
 
300
        this.name = name;
 
301
    }
 
302
 
 
303
    /**
 
304
     * Get the revision.
 
305
     * @return the revision
 
306
     */
 
307
    public String getRevision() {
 
308
        return revision;
 
309
    }
 
310
 
 
311
    /**
 
312
     * Set the revision.
 
313
     * @param theVersion the revision to set
 
314
     */
 
315
    public void setRevision(String revision) {
 
316
        this.revision = revision;
 
317
    }
 
318
 
 
319
    /**
 
320
     * Get the last modification time.
 
321
     *
 
322
     * @return date.getTime() compatible with File.lastModified() 
 
323
     */
 
324
    public Date getLastModified() {
 
325
        return lastModified;
 
326
    }
 
327
 
 
328
    /**
 
329
     * Get the conflict information.
 
330
     * @return the conflict String
 
331
     */
 
332
    public String getConflict() {
 
333
        return conflict;
 
334
    }
 
335
 
 
336
    /**
 
337
     * Set the conflict information.
 
338
     * @param theConflict the conflict information
 
339
     */
 
340
    public void setConflict(String conflict) {
 
341
        this.conflict = conflict;
 
342
        this.lastModified = null;
 
343
 
 
344
        if (conflict == null
 
345
                || conflict.equals(DUMMY_TIMESTAMP)
 
346
                || conflict.equals(MERGE_TIMESTAMP)
 
347
                || conflict.equals(DUMMY_TIMESTAMP_NEW_ENTRY)) {
 
348
            return;
 
349
        }
 
350
 
 
351
        String dateString = conflict;
 
352
 
 
353
        // Look for the position of + which indicates a conflict
 
354
        int conflictIndex = dateString.indexOf(HAD_CONFLICTS);
 
355
        if (conflictIndex >= 0) {
 
356
            // if the timestamp matches the file, there will be an = following
 
357
            // the +
 
358
            int timeMatchIndex = dateString.indexOf(TIMESTAMP_MATCHES_FILE);
 
359
            conflictIndex = Math.max(conflictIndex, timeMatchIndex);
 
360
        }
 
361
 
 
362
        // At this point the conflict index tells us where the real conflict
 
363
        // string starts
 
364
        if (conflictIndex >= 0) {
 
365
            dateString = dateString.substring(conflictIndex + 1);
 
366
        }
 
367
 
 
368
        // if we have nothing after the = then don't try to parse it
 
369
        if (dateString.length() == 0) {
 
370
            return;
 
371
        }
 
372
 
 
373
        try {
 
374
            this.lastModified = getLastModifiedDateFormatter().parse(dateString);
 
375
        }
 
376
        catch (Exception ex) {
 
377
            lastModified = null;
 
378
//            System.err.println("[Entry] can't parse " + dateString); //NOI18N
 
379
        }
 
380
    }
 
381
 
 
382
    /**
 
383
     * Get the options information.
 
384
     * @return the options details
 
385
     */
 
386
    public String getOptions() {
 
387
        return options;
 
388
    }
 
389
 
 
390
    /**
 
391
     * Set the options information.
 
392
     * @param theOptions the options
 
393
     */
 
394
    public void setOptions(String options) {
 
395
        this.options = options;
 
396
    }
 
397
 
 
398
    /**
 
399
     * Get the sticky information.
 
400
     * It's either a tag, a date or null.
 
401
     */
 
402
    public String getStickyInformation() {
 
403
        if (tag != null) {
 
404
            return tag;
 
405
        }
 
406
        return getDateFormatted();
 
407
    }
 
408
 
 
409
    /**
 
410
     * Get the sticky tag information.
 
411
     * May return null if no tag information was present. If so, you should
 
412
     * check for date information. Note that tag and date information cannot
 
413
     * both be present.
 
414
     * @return the tag, or null if none is present
 
415
     */
 
416
    public String getTag() {
 
417
        return tag;
 
418
    }
 
419
 
 
420
    /**
 
421
     * Set the sticky tag information.
 
422
     * Setting this will remove any date information that is set.
 
423
     * @param theTag the tag information
 
424
     */
 
425
    public void setTag(String tag) {
 
426
        this.tag = tag;
 
427
        date = null;
 
428
    }
 
429
 
 
430
    /**
 
431
     * Get sticky date information.
 
432
     * May return null if no date information is available. If so, you should
 
433
     * check for tag informaton. Note that tag and date information cannot both
 
434
     * be present.
 
435
     * @return the date, or null if none is present
 
436
     */
 
437
    public Date getDate() {
 
438
        return date;
 
439
    }
 
440
 
 
441
    /**
 
442
     * Gets the sticky date information as a string in the appropriate format.
 
443
     * Returns null if there ain't a sticky date assigned.
 
444
     */
 
445
    public String getDateFormatted() {
 
446
        if (getDate() == null) {
 
447
            return null;
 
448
        }
 
449
        SimpleDateFormat format = getStickyDateFormatter();
 
450
        String dateFormatted = format.format(getDate());
 
451
        return dateFormatted;
 
452
    }
 
453
 
 
454
    /**
 
455
     * Set the sticky date information.
 
456
     * Note that setting this will remove any tag information that is currently set.
 
457
     * @param theDate the date to use.
 
458
     */
 
459
    public void setDate(Date date) {
 
460
        this.date = date;
 
461
        tag = null;
 
462
    }
 
463
 
 
464
    /**
 
465
     * Determines whether the entry has a date (as opposed to a tag).
 
466
     * @return true if the entry has a date, false otherwise
 
467
     */
 
468
    public boolean hasDate() {
 
469
        return (date != null);
 
470
    }
 
471
 
 
472
    /**
 
473
     * Determines whether the entry has a tag (as opposed to a date).
 
474
     * @return true if the entry has a tag, false otherwise
 
475
     */
 
476
    public boolean hasTag() {
 
477
        return (tag != null);
 
478
    }
 
479
 
 
480
    /**
 
481
     * Determines whether the file is a binary file.
 
482
     */
 
483
    public boolean isBinary() {
 
484
        return options != null
 
485
                && options.equals(BINARY_FILE);
 
486
    }
 
487
 
 
488
    /**
 
489
     * Determine whether there is no user file of that name.
 
490
     * @return true if there is no user file of that name
 
491
     */
 
492
    public boolean isNoUserFile() {
 
493
        return revision == null
 
494
                || revision.equals(NO_USER_FILE);
 
495
    }
 
496
 
 
497
    /**
 
498
     * Determine whether there is a new user file of that name.
 
499
     * @return true if there is a new user file with that name
 
500
     */
 
501
    public boolean isNewUserFile() {
 
502
        return revision != null
 
503
                && revision.startsWith(NEW_USER_FILE);
 
504
    }
 
505
 
 
506
    /**
 
507
     * Determine whether the user file of that name is to be removed.
 
508
     * @return true if the user file with this name is to be removed
 
509
     */
 
510
    public boolean isUserFileToBeRemoved() {
 
511
        return revision != null
 
512
                && revision.startsWith(REMOVE_USER_FILE);
 
513
    }
 
514
 
 
515
    /**
 
516
     * Determines whether the entry is valid.
 
517
     * A valid entry has at least a name.
 
518
     */
 
519
    public boolean isValid() {
 
520
        return getName() != null &&
 
521
                getName().length() > 0;
 
522
    }
 
523
 
 
524
    /**
 
525
     * Determine whether the entry refers to a directory.
 
526
     */
 
527
    public boolean isDirectory() {
 
528
        return directory;
 
529
    }
 
530
 
 
531
    /**
 
532
     * Set whether the entry refers to a directory.
 
533
     */
 
534
    public void setDirectory(boolean directory) {
 
535
        this.directory = directory;
 
536
    }
 
537
 
 
538
    /**
 
539
     * Determine whether there were any conflicts.
 
540
     * @return true if there were conflicts, false otherwise
 
541
     */
 
542
    public boolean hadConflicts() {
 
543
        if (conflict != null) {
 
544
            return conflict.indexOf(HAD_CONFLICTS) >= 0;
 
545
        }
 
546
        else {
 
547
            return false;
 
548
        }
 
549
    }
 
550
 
 
551
    /**
 
552
     * Determine whether the timestamp matches the file.
 
553
     * @return true if the timpestamp does match the file, false otherwise
 
554
     */
 
555
    public boolean timestampMatchesFile() {
 
556
        return (conflict.charAt(1) == TIMESTAMP_MATCHES_FILE);
 
557
    }
 
558
 
 
559
    /**
 
560
     * Create a string representation of the entry line.
 
561
     * Create the standard CVS 1.10 entry line format.
 
562
     * <p>
 
563
     * Th eline format is suitable for writing into <tt>CVS/Entries</tt> file.
 
564
     * Conflict one must be transformed before sending to wire
 
565
     * {@link org.netbeans.lib.cvsclient.command.BasicCommand#sendEntryAndModifiedRequests}.
 
566
     */
 
567
    public String toString() {
 
568
        StringBuffer buf = new StringBuffer();
 
569
        if (directory) {
 
570
            buf.append(DIRECTORY_PREFIX);
 
571
        }
 
572
        else {
 
573
            buf.append('/');
 
574
        }
 
575
        // if name is null, then this is a totally empty entry, so append
 
576
        // nothing further
 
577
        if (name != null) {
 
578
            buf.append(name);
 
579
            buf.append('/');
 
580
            if (revision != null) {
 
581
                buf.append(revision);
 
582
            }
 
583
            buf.append('/');
 
584
            if (conflict != null) {
 
585
                buf.append(conflict);
 
586
            }
 
587
            buf.append('/');
 
588
            if (options != null) {
 
589
                buf.append(options);
 
590
            }
 
591
            buf.append('/');
 
592
            // TODO: put in tag_or_date section!!!
 
593
            // MK - Added. Based on assumption "There can be only one"
 
594
            if (tag != null && date == null) {
 
595
                if ("HEAD".equals(tag) == false) {
 
596
                    buf.append(TAG);
 
597
                    buf.append(getTag());
 
598
                }
 
599
            }
 
600
            else if (tag == null && date != null) {
 
601
                String dateString = getDateFormatted();
 
602
                buf.append(DATE);
 
603
                buf.append(dateString);
 
604
            }
 
605
        }
 
606
        return buf.toString();
 
607
    }
 
608
}