1
/*****************************************************************************
2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
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.
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.
42
* Contributor(s): Robert Greig.
43
*****************************************************************************/
44
package org.netbeans.lib.cvsclient.admin;
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
56
public final class Entry {
58
* The dummy timestamp set the conflict information for added or removed
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
64
public static final String MERGE_TIMESTAMP = "Result of merge"; //NOI18N
67
* Indicates a sticky tag.
69
private static final String TAG = "T"; //NOI18N
72
* Indicates a sticky date.
74
private static final String DATE = "D"; //NOI18N
77
* The instance of the date formatter for sticky dates.
79
private static SimpleDateFormat stickyDateFormatter;
82
* Returns the instance of the date formatter for sticky dates.
84
private static SimpleDateFormat getStickyDateFormatter() {
85
if (stickyDateFormatter == null) {
86
stickyDateFormatter = new SimpleDateFormat("yyyy.MM.dd.hh.mm.ss"); //NOI18N
88
return stickyDateFormatter;
92
* Indicates a binary file.
94
private static final String BINARY_FILE = "-kb"; //NOI18N
97
* Indicates that no user file is meant by the version details
99
private static final String NO_USER_FILE = ""; //NOI18N
102
* Indicates that a new user file is meant by the version details
104
private static final String NEW_USER_FILE = "0"; //NOI18N
107
* Indicates that the file is to be removed, in the version details
109
private static final String REMOVE_USER_FILE = "-"; //NOI18N
112
* The date formatter to be used for noting the Last Modified date in a
115
private static SimpleDateFormat lastModifiedDateFormatter;
118
* Returns the instance of the Last-Modified-Date-Formatter.
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());
126
return lastModifiedDateFormatter;
130
* All entries times are by defaulf in Zulu/GMT0
132
public static TimeZone getTimeZone() {
133
return TimeZone.getTimeZone("GMT"); //NOI18N
137
* Indicates that the file had conflicts.
139
public static final char HAD_CONFLICTS = '+';
142
* Indicates that the timestamp matches the file.
144
public static final char TIMESTAMP_MATCHES_FILE = '=';
147
* Indicates that the file had conflicts and timestamp matches.
148
* It likely means unresolved conflict.
150
public static final String HAD_CONFLICTS_AND_TIMESTAMP_MATCHES_FILE = "+=";
153
* Initial letter that indicates a directory entry.
155
private static final String DIRECTORY_PREFIX = "D/";
158
* The name of the file.
163
* The revision. There are constants defined for no user file, new user
164
* file and user file has to be removed.
166
private String revision;
169
* The conflict information. There are constants defined for indicating
170
* that conflicts occurred and that the timestamp matches the file
172
private String conflict;
175
* The last modified date of the file.
177
private Date lastModified;
180
* The options for signifying keyword expansion.
182
private String options;
185
* The tag. May be present in place of the date information.
190
* The date. May be present in place of the tag information.
195
* Indicates whether the entry is for a directory.
197
private boolean directory;
200
* Construct a new Entry from a given entry line.
202
public Entry(String entryLine) {
207
* Construct a new blank Entry.
213
* Initialise the Entry by parsing an entry line.
214
* @param entryLine the entry line in standard CVS format
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
221
if (entryLine.startsWith(DIRECTORY_PREFIX)) {
223
entryLine = entryLine.substring(1);
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];
231
slashPositions[0] = 0;
232
for (int i = 1; i < 5; i++) {
233
slashPositions[i] = entryLine.indexOf('/',
234
slashPositions[i - 1] + 1);
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,
245
revision = entryLine.substring(slashPositions[1] + 1,
247
if ((slashPositions[3] - slashPositions[2]) > 1) {
248
String conflict = entryLine.substring(slashPositions[2] + 1,
250
setConflict(conflict);
252
if ((slashPositions[4] - slashPositions[3]) > 1) {
253
options = entryLine.substring(slashPositions[3] + 1,
256
if (slashPositions[4] != (entryLine.length() - 1)) {
257
String tagOrDate = entryLine.substring(slashPositions[4]
259
if (tagOrDate.startsWith(TAG)) {
260
setTag(tagOrDate.substring(1));
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.
267
String dateString = tagOrDate.substring(DATE.length());
268
Date stickyDate = getStickyDateFormatter().
272
catch (ParseException exc) {
273
System.err.println("We got another inconsistency in the library's date formatting."); //NOI18N
279
catch (Exception e) {
280
System.err.println("Error parsing entry line: " + e); //NOI18N
282
throw new IllegalArgumentException("Invalid entry line: " + //NOI18N
288
* Get the name of the associated file.
289
* @return the file name
291
public String getName() {
297
* @param theName the filename to set
299
public void setName(String name) {
305
* @return the revision
307
public String getRevision() {
313
* @param theVersion the revision to set
315
public void setRevision(String revision) {
316
this.revision = revision;
320
* Get the last modification time.
322
* @return date.getTime() compatible with File.lastModified()
324
public Date getLastModified() {
329
* Get the conflict information.
330
* @return the conflict String
332
public String getConflict() {
337
* Set the conflict information.
338
* @param theConflict the conflict information
340
public void setConflict(String conflict) {
341
this.conflict = conflict;
342
this.lastModified = null;
345
|| conflict.equals(DUMMY_TIMESTAMP)
346
|| conflict.equals(MERGE_TIMESTAMP)
347
|| conflict.equals(DUMMY_TIMESTAMP_NEW_ENTRY)) {
351
String dateString = conflict;
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
358
int timeMatchIndex = dateString.indexOf(TIMESTAMP_MATCHES_FILE);
359
conflictIndex = Math.max(conflictIndex, timeMatchIndex);
362
// At this point the conflict index tells us where the real conflict
364
if (conflictIndex >= 0) {
365
dateString = dateString.substring(conflictIndex + 1);
368
// if we have nothing after the = then don't try to parse it
369
if (dateString.length() == 0) {
374
this.lastModified = getLastModifiedDateFormatter().parse(dateString);
376
catch (Exception ex) {
378
// System.err.println("[Entry] can't parse " + dateString); //NOI18N
383
* Get the options information.
384
* @return the options details
386
public String getOptions() {
391
* Set the options information.
392
* @param theOptions the options
394
public void setOptions(String options) {
395
this.options = options;
399
* Get the sticky information.
400
* It's either a tag, a date or null.
402
public String getStickyInformation() {
406
return getDateFormatted();
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
414
* @return the tag, or null if none is present
416
public String getTag() {
421
* Set the sticky tag information.
422
* Setting this will remove any date information that is set.
423
* @param theTag the tag information
425
public void setTag(String tag) {
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
435
* @return the date, or null if none is present
437
public Date getDate() {
442
* Gets the sticky date information as a string in the appropriate format.
443
* Returns null if there ain't a sticky date assigned.
445
public String getDateFormatted() {
446
if (getDate() == null) {
449
SimpleDateFormat format = getStickyDateFormatter();
450
String dateFormatted = format.format(getDate());
451
return dateFormatted;
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.
459
public void setDate(Date date) {
465
* Determines whether the entry has a date (as opposed to a tag).
466
* @return true if the entry has a date, false otherwise
468
public boolean hasDate() {
469
return (date != null);
473
* Determines whether the entry has a tag (as opposed to a date).
474
* @return true if the entry has a tag, false otherwise
476
public boolean hasTag() {
477
return (tag != null);
481
* Determines whether the file is a binary file.
483
public boolean isBinary() {
484
return options != null
485
&& options.equals(BINARY_FILE);
489
* Determine whether there is no user file of that name.
490
* @return true if there is no user file of that name
492
public boolean isNoUserFile() {
493
return revision == null
494
|| revision.equals(NO_USER_FILE);
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
501
public boolean isNewUserFile() {
502
return revision != null
503
&& revision.startsWith(NEW_USER_FILE);
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
510
public boolean isUserFileToBeRemoved() {
511
return revision != null
512
&& revision.startsWith(REMOVE_USER_FILE);
516
* Determines whether the entry is valid.
517
* A valid entry has at least a name.
519
public boolean isValid() {
520
return getName() != null &&
521
getName().length() > 0;
525
* Determine whether the entry refers to a directory.
527
public boolean isDirectory() {
532
* Set whether the entry refers to a directory.
534
public void setDirectory(boolean directory) {
535
this.directory = directory;
539
* Determine whether there were any conflicts.
540
* @return true if there were conflicts, false otherwise
542
public boolean hadConflicts() {
543
if (conflict != null) {
544
return conflict.indexOf(HAD_CONFLICTS) >= 0;
552
* Determine whether the timestamp matches the file.
553
* @return true if the timpestamp does match the file, false otherwise
555
public boolean timestampMatchesFile() {
556
return (conflict.charAt(1) == TIMESTAMP_MATCHES_FILE);
560
* Create a string representation of the entry line.
561
* Create the standard CVS 1.10 entry line format.
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}.
567
public String toString() {
568
StringBuffer buf = new StringBuffer();
570
buf.append(DIRECTORY_PREFIX);
575
// if name is null, then this is a totally empty entry, so append
580
if (revision != null) {
581
buf.append(revision);
584
if (conflict != null) {
585
buf.append(conflict);
588
if (options != null) {
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) {
597
buf.append(getTag());
600
else if (tag == null && date != null) {
601
String dateString = getDateFormatted();
603
buf.append(dateString);
606
return buf.toString();