KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > resources > EclipseFile


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Red Hat Incorporated - is/setExecutable() code
11  *******************************************************************************/

12 package org.eclipse.team.internal.ccvs.core.resources;
13
14 import java.io.File JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.Date JavaDoc;
18 import java.util.List JavaDoc;
19
20 import org.eclipse.core.resources.*;
21 import org.eclipse.core.runtime.*;
22 import org.eclipse.osgi.util.NLS;
23 import org.eclipse.team.core.RepositoryProvider;
24 import org.eclipse.team.core.TeamException;
25 import org.eclipse.team.internal.ccvs.core.*;
26 import org.eclipse.team.internal.ccvs.core.client.Session;
27 import org.eclipse.team.internal.ccvs.core.syncinfo.*;
28
29 /**
30  * Represents handles to CVS resource on the local file system. Synchronization
31  * information is taken from the CVS subdirectories.
32  */

33 public class EclipseFile extends EclipseResource implements ICVSFile {
34
35     private static final String JavaDoc TEMP_FILE_EXTENSION = ".tmp";//$NON-NLS-1$
36
private static final IPath PROJECT_META_DATA_PATH = new Path(".project");//$NON-NLS-1$
37

38     /**
39      * Create a handle based on the given local resource.
40      */

41     protected EclipseFile(IFile file) {
42         super(file);
43     }
44
45     /*
46      * @see ICVSResource#delete()
47      */

48     public void delete() throws CVSException {
49         try {
50             ((IFile)resource).delete(false /*force*/, true /*keepHistory*/, null);
51         } catch(CoreException e) {
52             throw CVSException.wrapException(resource, NLS.bind(CVSMessages.EclipseFile_Problem_deleting_resource, new String JavaDoc[] { resource.getFullPath().toString(), e.getStatus().getMessage() }), e); //
53
}
54     }
55     
56     public long getSize() {
57         return getIOFile().length();
58     }
59
60     public InputStream JavaDoc getContents() throws CVSException {
61         try {
62             return getIFile().getContents();
63         } catch (CoreException e) {
64             throw CVSException.wrapException(resource, NLS.bind(CVSMessages.EclipseFile_Problem_accessing_resource, new String JavaDoc[] { resource.getFullPath().toString(), e.getStatus().getMessage() }), e); //
65
}
66     }
67     
68     /*
69      * @see ICVSFile#getTimeStamp()
70      */

71     public Date JavaDoc getTimeStamp() {
72         long timestamp = getIFile().getLocalTimeStamp();
73         if( timestamp == IResource.NULL_STAMP) {
74             // If there is no file, return the same timestamp as ioFile.lastModified() would
75
return new Date JavaDoc(0L);
76         }
77         return new Date JavaDoc((timestamp/1000)*1000);
78     }
79  
80     /*
81      * @see ICVSFile#setTimeStamp(Date)
82      */

83     public void setTimeStamp(Date JavaDoc date) throws CVSException {
84         long time;
85         if (date == null) {
86             time = System.currentTimeMillis();
87         } else {
88             time = date.getTime();
89         }
90         EclipseSynchronizer.getInstance().setTimeStamp(this, time);
91     }
92
93     /*
94      * @see ICVSResource#isFolder()
95      */

96     public boolean isFolder() {
97         return false;
98     }
99     
100     /*
101      * @see ICVSFile#isModified()
102      */

103     public boolean isModified(IProgressMonitor monitor) throws CVSException {
104         
105         // ignore the monitor, there is no valuable progress to be shown when
106
// calculating the dirty state for files. It is relatively fast.
107

108         if (!exists()) {
109             return getSyncBytes() != null;
110         }
111         int state = EclipseSynchronizer.getInstance().getModificationState(getIFile());
112
113         if (state != UNKNOWN) {
114             boolean dirty = state != CLEAN;
115             // Check to make sure that cached state is the real state.
116
// They can be different if deltas happen in the wrong order.
117
if (dirty == isDirty()) {
118                 return dirty;
119             }
120         }
121         
122         // nothing cached, need to manually check (and record)
123
byte[] syncBytes = getSyncBytes();
124         if (syncBytes == null && isIgnored()) return false;
125         // unmanaged files are reported as modified
126
return EclipseSynchronizer.getInstance().setModified(this, UNKNOWN);
127     }
128     
129     /*
130      * @see ICVSResource#accept(ICVSResourceVisitor)
131      */

132     public void accept(ICVSResourceVisitor visitor) throws CVSException {
133         visitor.visitFile(this);
134     }
135
136     /*
137      * @see ICVSResource#accept(ICVSResourceVisitor, boolean)
138      */

139     public void accept(ICVSResourceVisitor visitor, boolean recurse) throws CVSException {
140         visitor.visitFile(this);
141     }
142     
143     /*
144      * This is to be used by the Copy handler. The filename of the form .#filename
145      */

146     public void copyTo(String JavaDoc filename) throws CVSException {
147         try {
148             IPath targetPath = new Path(null, filename);
149             IFile targetFile = getIFile().getParent().getFile(targetPath);
150             if (targetFile.exists()) {
151                 // There is a file in the target location.
152
// Delete it and keep the history just in case
153
targetFile.delete(false /* force */, true /* keep history */, null);
154             }
155             getIFile().copy(targetPath, true /*force*/, null);
156         } catch(CoreException e) {
157             throw new CVSException(e.getStatus());
158         }
159     }
160
161     /*
162      * @see ICVSResource#getRemoteLocation()
163      */

164     public String JavaDoc getRemoteLocation(ICVSFolder stopSearching) throws CVSException {
165         return getParent().getRemoteLocation(stopSearching) + SEPARATOR + getName();
166     }
167         
168     /*
169      * @see ICVSFile#setReadOnly()
170      */

171     public void setContents(InputStream JavaDoc stream, int responseType, boolean keepLocalHistory, IProgressMonitor monitor) throws CVSException {
172         try {
173             IFile file = getIFile();
174             if (PROJECT_META_DATA_PATH.equals(file.getFullPath().removeFirstSegments(1))) {
175                 responseType = UPDATED;
176             }
177             switch (responseType) {
178                 case UPDATED:
179                     if (resource.exists()) {
180                         file.setContents(stream, false /*force*/, true /*keep history*/, monitor);
181                         break;
182                     }
183                 case CREATED: // creating a new file so it should not exist locally
184
file.create(stream, false /*force*/, monitor);
185                     break;
186                 case MERGED: // merging contents into a file that exists locally
187
// Ensure we don't leave the file in a partially written state
188
IFile tempFile = file.getParent().getFile(new Path(null, file.getName() + TEMP_FILE_EXTENSION));
189                     monitor.beginTask(null, 100);
190                     if (tempFile.exists())
191                         tempFile.delete(true /* force */, Policy.subMonitorFor(monitor, 25));
192                     tempFile.create(stream, true /*force*/, Policy.subMonitorFor(monitor, 25));
193                     file.delete(false /* force */, true /* keep history */, Policy.subMonitorFor(monitor, 25));
194                     tempFile.move(new Path(null, file.getName()), false /*force*/, true /*history*/, Policy.subMonitorFor(monitor, 25));
195                     monitor.done();
196                     break;
197                 case UPDATE_EXISTING: // creating a new file so it should exist locally
198
file.setContents(stream, false /*force*/, true /*keep history*/, monitor);
199                     break;
200             }
201         } catch(CoreException e) {
202             String JavaDoc message = null;
203             if (e.getStatus().getCode() == IResourceStatus.FAILED_READ_LOCAL) {
204                 // This error indicates that Core couldn't read from the server stream
205
// The real reason will be in the message of the wrapped exception
206
Throwable JavaDoc t = e.getStatus().getException();
207                 if (t != null) message = t.getMessage();
208             }
209             if (message == null) message = e.getMessage();
210             throw CVSException.wrapException(resource, NLS.bind(CVSMessages.EclipseFile_Problem_writing_resource, new String JavaDoc[] { resource.getFullPath().toString(), message }), e);
211         }
212     }
213             
214     /*
215      * @see ICVSFile#setReadOnly()
216      */

217     public void setReadOnly(boolean readOnly) throws CVSException {
218         ResourceAttributes attributes = resource.getResourceAttributes();
219         if (attributes != null) {
220             attributes.setReadOnly(readOnly);
221             try {
222                 resource.setResourceAttributes(attributes);
223             } catch (CoreException e) {
224                 throw CVSException.wrapException(e);
225             }
226         }
227     }
228
229     /*
230      * @see ICVSFile#isReadOnly()
231      */

232     public boolean isReadOnly() throws CVSException {
233         return getIFile().isReadOnly();
234     }
235     
236     /*
237      * @see ICVSFile#setExecutable()
238      */

239     public void setExecutable(boolean executable) throws CVSException {
240         ResourceAttributes attributes = resource.getResourceAttributes();
241         if (attributes != null) {
242             attributes.setExecutable(executable);
243             try {
244                 resource.setResourceAttributes(attributes);
245             } catch (CoreException e) {
246                 throw CVSException.wrapException(e);
247             }
248         }
249     }
250
251     /*
252      * @see ICVSFile#isExectuable()
253      */

254     public boolean isExecutable() throws CVSException {
255         ResourceAttributes attributes = resource.getResourceAttributes();
256         if (attributes != null) {
257             return attributes.isExecutable();
258         } else {
259             return false;
260         }
261     }
262     
263     /*
264      * Typecasting helper
265      */

266     public IFile getIFile() {
267         return (IFile)resource;
268     }
269     
270     /*
271      * To allow accessing size and timestamp for the underlying java.io.File
272      */

273     private File JavaDoc getIOFile() {
274         IPath location = resource.getLocation();
275         if(location!=null) {
276             return location.toFile();
277         }
278         return null;
279     }
280     /**
281      * @see ICVSFile#getLogEntries(IProgressMonitor)
282      */

283     public ILogEntry[] getLogEntries(IProgressMonitor monitor) throws TeamException {
284         
285         // try fetching log entries only when the file's project is accessible
286
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=190434
287
if (getIResource() == null
288                 || !getIResource().getProject().isAccessible())
289             return new ILogEntry[0];
290         
291         byte[] syncBytes = getSyncBytes();
292         if(syncBytes != null && !ResourceSyncInfo.isAddition(syncBytes)) {
293             ICVSRemoteResource remoteFile = CVSWorkspaceRoot.getRemoteResourceFor(resource);
294             return ((ICVSRemoteFile)remoteFile).getLogEntries(monitor);
295         }
296         return new ILogEntry[0];
297     }
298     /**
299      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#setNotifyInfo(NotifyInfo)
300      */

301     public void setNotifyInfo(NotifyInfo info) throws CVSException {
302         if (isManaged()) {
303             EclipseSynchronizer.getInstance().setNotifyInfo(resource, info);
304             // On an edit, the base should be cached
305
// On an unedit, the base should be restored (and cleared?)
306
// On a commit, the base should be cleared
307
}
308     }
309
310     /**
311      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#getNotifyInfo()
312      */

313     public NotifyInfo getNotifyInfo() throws CVSException {
314         if (isManaged()) {
315             return EclipseSynchronizer.getInstance().getNotifyInfo(resource);
316         }
317         return null;
318     }
319
320     /**
321      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#setNotifyInfo(NotifyInfo)
322      */

323     public void setBaserevInfo(BaserevInfo info) throws CVSException {
324         if (isManaged()) {
325             if (info == null) {
326                 EclipseSynchronizer.getInstance().deleteBaserevInfo(resource);
327                 EclipseSynchronizer.getInstance().deleteFileFromBaseDirectory(getIFile(), null);
328             } else
329                 EclipseSynchronizer.getInstance().setBaserevInfo(resource, info);
330         }
331     }
332     /**
333      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#getNotifyInfo()
334      */

335     public BaserevInfo getBaserevInfo() throws CVSException {
336         if (isManaged()) {
337             return EclipseSynchronizer.getInstance().getBaserevInfo(resource);
338         }
339         return null;
340     }
341     
342     /**
343      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#checkout(int)
344      */

345     public void edit(final int notifications, boolean notifyForWritable, IProgressMonitor monitor) throws CVSException {
346         if (!notifyForWritable && !isReadOnly()) return;
347         run(new ICVSRunnable() {
348             public void run(IProgressMonitor monitor) throws CVSException {
349                 byte[] syncBytes = getSyncBytes();
350                 if (syncBytes == null || ResourceSyncInfo.isAddition(syncBytes)) return;
351                 
352                 // convert the notifications to internal form
353
char[] internalFormat;
354                 if (notifications == NO_NOTIFICATION) {
355                     internalFormat = null;
356                 } else if (notifications == NOTIFY_ON_ALL) {
357                     internalFormat = NotifyInfo.ALL;
358                 } else {
359                     List JavaDoc notificationCharacters = new ArrayList JavaDoc();
360                     if ((notifications & NOTIFY_ON_EDIT) >0)
361                         notificationCharacters.add(new Character JavaDoc(NotifyInfo.EDIT));
362                     if ((notifications & NOTIFY_ON_UNEDIT) >0)
363                         notificationCharacters.add(new Character JavaDoc(NotifyInfo.UNEDIT));
364                     if ((notifications & NOTIFY_ON_COMMIT) >0)
365                         notificationCharacters.add(new Character JavaDoc(NotifyInfo.COMMIT));
366                     internalFormat = new char[notificationCharacters.size()];
367                     for (int i = 0; i < internalFormat.length; i++) {
368                         internalFormat[i] = ((Character JavaDoc)notificationCharacters.get(i)).charValue();
369                     }
370                 }
371                 
372                 // record the notification
373
NotifyInfo notifyInfo = new NotifyInfo(getName(), NotifyInfo.EDIT, new Date JavaDoc(), internalFormat);
374                 setNotifyInfo(notifyInfo);
375                 
376                 // Only record the base if the file is not modified
377
if (!isModified(null)) {
378                     EclipseSynchronizer.getInstance().copyFileToBaseDirectory(getIFile(), monitor);
379                     setBaserevInfo(new BaserevInfo(getName(), ResourceSyncInfo.getRevision(syncBytes)));
380                 }
381                 
382                 try {
383                     // allow editing
384
setReadOnly(false);
385                 } catch (CVSException e) {
386                     // Just log and keep going
387
CVSProviderPlugin.log(e);
388                 }
389             }
390         }, monitor);
391         
392     }
393
394     /**
395      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#uncheckout()
396      */

397     public void unedit(IProgressMonitor monitor) throws CVSException {
398         if (isReadOnly()) return;
399         run(new ICVSRunnable() {
400             public void run(IProgressMonitor monitor) throws CVSException {
401                 // record the notification
402
NotifyInfo info = getNotifyInfo();
403                 if (info != null && info.getNotificationType() == NotifyInfo.EDIT) {
404                     info = null;
405                 } else {
406                     info = new NotifyInfo(getName(), NotifyInfo.UNEDIT, new Date JavaDoc(), null);
407                 }
408                 setNotifyInfo(info);
409                     
410                 if (isModified(null)) {
411                     ResourceSyncInfo syncInfo = getSyncInfo();
412                     BaserevInfo baserevInfo = getBaserevInfo();
413                     EclipseSynchronizer.getInstance().restoreFileFromBaseDirectory(getIFile(), monitor);
414                     // reset any changes that may have been merged from the server
415
if (!syncInfo.getRevision().equals(baserevInfo.getRevision())) {
416                         MutableResourceSyncInfo newInfo = syncInfo.cloneMutable();
417                         newInfo.setRevision(baserevInfo.getRevision());
418                         newInfo.setTimeStamp(getTimeStamp());
419                         newInfo.setDeleted(false);
420                         setSyncInfo(newInfo, ICVSFile.CLEAN);
421                     } else {
422                         // an unedited file is no longer modified
423
EclipseSynchronizer.getInstance().setModified(EclipseFile.this, CLEAN);
424                     }
425                 } else {
426                     // We still need to report a state change
427
setSyncBytes(getSyncBytes(), ICVSFile.CLEAN);
428                 }
429                 setBaserevInfo(null);
430                     
431                 try {
432                     // prevent editing
433
setReadOnly(true);
434                 } catch (CVSException e) {
435                     // Just log and keep going
436
CVSProviderPlugin.log(e);
437                 }
438             }
439         }, monitor);
440     }
441
442     /**
443      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#notificationCompleted()
444      */

445     public void notificationCompleted() throws CVSException {
446         EclipseSynchronizer.getInstance().deleteNotifyInfo(resource);
447     }
448
449     /**
450      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#getPendingNotification()
451      */

452     public NotifyInfo getPendingNotification() throws CVSException {
453         return getNotifyInfo();
454     }
455     
456     /* (non-Javadoc)
457      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#checkedIn(java.lang.String)
458      */

459     public void checkedIn(String JavaDoc entryLine, boolean commit) throws CVSException {
460         ResourceSyncInfo oldInfo = getSyncInfo();
461         ResourceSyncInfo newInfo = null;
462         int modificationState = ICVSFile.CLEAN;
463         if (entryLine == null) {
464             // cvs commit: the file contents matched the server contents so no entry line was sent
465
if (oldInfo == null) return;
466             // We should never make the timestamp go backwards so we'll set
467
// the entry line timestamp to match that of the file
468
if(! oldInfo.isAdded()) {
469                 MutableResourceSyncInfo mutable = oldInfo.cloneMutable();
470                 mutable.setTimeStamp(getTimeStamp(), true /* clear merged */);
471                 newInfo = mutable;
472             }
473             // (modified = false) the file will be no longer modified
474
} else if (oldInfo == null) {
475             // cvs add: addition of a file
476
newInfo = new ResourceSyncInfo(entryLine, null);
477             // an added file should show up as modified
478
modificationState = ICVSFile.DIRTY;
479         } else {
480             // cvs commit: commit of a changed file
481
// cvs update: update of a file whose contents match the server contents
482
Date JavaDoc timeStamp;
483             if (commit) {
484                 // This is a commit. Put the file timestamp in the entry
485
timeStamp = getTimeStamp();
486             } else {
487                 // This is an update. We need to change the tiemstamp in the
488
// entry file to match the file timestamp returned by Java
489
timeStamp = oldInfo.getTimeStamp();
490                 if (timeStamp == null) {
491                     timeStamp = getTimeStamp();
492                 } else {
493                     // First, set the timestamp of the file to the timestamp from the entry
494
// There is a chance this will do nothing as the call to Java on some
495
// file systems munges the timestamps
496
setTimeStamp(timeStamp);
497                     // To compensate for the above, reset the timestamp in the entry
498
// to match the timestamp in the file
499
timeStamp = getTimeStamp();
500                 }
501             }
502             newInfo = new ResourceSyncInfo(entryLine, timeStamp);
503             
504         }
505         //see bug 106876
506
if (newInfo != null){
507             CVSTag tag = newInfo.getTag();
508             if(tag != null && CVSEntryLineTag.BASE.getName().equals(tag.getName())){
509                 newInfo = newInfo.cloneMutable();
510                 ((MutableResourceSyncInfo)newInfo).setTag(oldInfo.getTag());
511             }
512             setSyncInfo(newInfo, modificationState);
513         }
514         clearCachedBase();
515     }
516     
517     private void clearCachedBase() throws CVSException {
518         BaserevInfo base = getBaserevInfo();
519         if (base != null) {
520             setBaserevInfo(null);
521             try {
522                 setReadOnly(true);
523             } catch (CVSException e) {
524                 // Just log and keep going
525
CVSProviderPlugin.log(e);
526             }
527         } else {
528             // Check to see if watch-edit is enabled for the project
529
CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
530             if (provider != null && provider.isWatchEditEnabled()) {
531                 try {
532                     setReadOnly(true);
533                 } catch (CVSException e) {
534                     // Just log and keep going
535
CVSProviderPlugin.log(e);
536                 }
537             }
538         }
539     }
540
541     /**
542      * @see org.eclipse.team.internal.ccvs.core.ICVSResource#unmanage(org.eclipse.core.runtime.IProgressMonitor)
543      */

544     public void unmanage(IProgressMonitor monitor) throws CVSException {
545         run(new ICVSRunnable() {
546             public void run(IProgressMonitor monitor) throws CVSException {
547                 EclipseFile.super.unmanage(monitor);
548                 clearCachedBase();
549             }
550         }, monitor);
551     }
552     
553     /**
554      * @see org.eclipse.team.internal.ccvs.core.ICVSFile#isEdited()
555      */

556     public boolean isEdited() throws CVSException {
557         return EclipseSynchronizer.getInstance().isEdited(getIFile());
558     }
559
560     /**
561      * @see org.eclipse.team.internal.ccvs.core.ICVSResource#setSyncInfo(org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo)
562      */

563     public void setSyncInfo(ResourceSyncInfo info, int modificationState) throws CVSException {
564         setSyncBytes(info.getBytes(), info, modificationState);
565     }
566     
567     /**
568      * @see org.eclipse.team.internal.ccvs.core.resources.EclipseResource#setSyncBytes(byte[], int)
569      */

570     public void setSyncBytes(byte[] syncBytes, int modificationState) throws CVSException {
571         setSyncBytes(syncBytes, null, modificationState);
572     }
573     
574     /*
575      * @see org.eclipse.team.internal.ccvs.core.resources.EclipseResource#setSyncBytes(byte[], int)
576      */

577     private void setSyncBytes(byte[] syncBytes, ResourceSyncInfo info, int modificationState) throws CVSException {
578         Assert.isNotNull(syncBytes);
579         setSyncBytes(syncBytes);
580         EclipseSynchronizer.getInstance().setModified(this, modificationState);
581     }
582     
583     public void handleModification(boolean forAddition) throws CVSException {
584         if (isIgnored()) {
585             // Special case handling for when a resource passes from the un-managed state
586
// to the ignored state (e.g. ignoring the ignore file). Parent dirty state must be
587
// recalculated but since the resource's end state is ignored there is a lot of code
588
// in the plugin that simply disregards the change to the resource.
589
// There may be a better was of handling resources that transition from un-managed to
590
// ignored but for now this seems like the safest change.
591
if(! resource.isDerived()) {
592                 EclipseSynchronizer.getInstance().setModified(this, CLEAN);
593             }
594             return;
595         }
596         // set the modification state to what it really is and return true if the modification state changed
597
EclipseSynchronizer.getInstance().setModified(this, UNKNOWN);
598     }
599
600     /* (non-Javadoc)
601      * @see org.eclipse.team.internal.ccvs.core.ICVSResource#getRepositoryRelativePath()
602      */

603     public String JavaDoc getRepositoryRelativePath() throws CVSException {
604         if (!isManaged()) return null;
605         String JavaDoc parentPath = getParent().getRepositoryRelativePath();
606         if (parentPath == null) return null;
607         return parentPath + Session.SERVER_SEPARATOR + getName();
608     }
609     
610     protected boolean isDirty() throws CVSException {
611         boolean dirty;
612         byte[] syncBytes = getSyncBytes();
613         if (syncBytes == null) {
614             dirty = exists();
615         } else {
616             // isMerged() must be called because when a file is updated and merged by the cvs server the timestamps
617
// are equal. Merged files should however be reported as dirty because the user should take action and commit
618
// or review the merged contents.
619
if(ResourceSyncInfo.isAddition(syncBytes) || ResourceSyncInfo.isMerge(syncBytes) || !exists()) {
620                 dirty = true;
621             } else {
622                 // TODO: non-optimal as ResourceSyncInfo is created each time
623
ResourceSyncInfo info = new ResourceSyncInfo(syncBytes);
624                 dirty = !getTimeStamp().equals(info.getTimeStamp());
625             }
626         }
627         return dirty;
628     }
629
630 }
631
632
633
Popular Tags