20
20
import java .io .FileNotFoundException ;
21
21
import java .io .IOException ;
22
22
import java .io .InputStream ;
23
+ import java .nio .charset .StandardCharsets ;
24
+ import java .security .MessageDigest ;
25
+ import java .security .NoSuchAlgorithmException ;
26
+ import java .util .Arrays ;
23
27
import java .util .Collections ;
28
+ import java .util .HexFormat ;
24
29
import java .util .LinkedHashSet ;
30
+ import java .util .Objects ;
25
31
import java .util .Set ;
32
+ import java .util .stream .Collectors ;
26
33
import org .eclipse .core .filesystem .EFS ;
27
34
import org .eclipse .core .filesystem .IFileStore ;
28
35
import org .eclipse .core .internal .events .ILifecycleListener ;
57
64
import org .eclipse .core .runtime .jobs .ISchedulingRule ;
58
65
import org .eclipse .osgi .util .NLS ;
59
66
import org .osgi .framework .Bundle ;
67
+ import org .osgi .framework .BundleContext ;
60
68
61
69
/**
62
70
* Keeps a cache of recently read content descriptions.
65
73
* @see IFile#getContentDescription()
66
74
*/
67
75
public class ContentDescriptionManager implements IManager , IRegistryChangeListener , IContentTypeManager .IContentTypeChangeListener , ILifecycleListener {
76
+
68
77
/**
69
78
* This job causes the content description cache and the related flags
70
79
* in the resource tree to be flushed.
@@ -337,14 +346,6 @@ public byte getCacheState() {
337
346
return cacheState ;
338
347
}
339
348
340
- public long getCacheTimestamp () throws CoreException {
341
- try {
342
- return Long .parseLong (workspace .getRoot ().getPersistentProperty (CACHE_TIMESTAMP ));
343
- } catch (NumberFormatException e ) {
344
- return 0 ;
345
- }
346
- }
347
-
348
349
public IContentTypeMatcher getContentTypeMatcher (Project project ) throws CoreException {
349
350
return projectContentTypes .getMatcherFor (project );
350
351
}
@@ -370,9 +371,7 @@ public IContentDescription getDescriptionFor(File file, ResourceInfo info, boole
370
371
if (inSync && getCacheState () != ABOUT_TO_FLUSH ) {
371
372
// first look for the flags in the resource info to avoid looking in the cache
372
373
// don't need to copy the info because the modified bits are not in the deltas
373
- if (info == null )
374
- return null ;
375
- if (info .isSet (ICoreConstants .M_NO_CONTENT_DESCRIPTION ))
374
+ if ((info == null ) || info .isSet (ICoreConstants .M_NO_CONTENT_DESCRIPTION ))
376
375
// presumably, this file has no known content type
377
376
return null ;
378
377
if (info .isSet (ICoreConstants .M_DEFAULT_CONTENT_DESCRIPTION )) {
@@ -480,6 +479,38 @@ public synchronized void invalidateCache(boolean flush, IProject project) {
480
479
}
481
480
}
482
481
482
+ private static String getCurrentPlatformState () {
483
+ ResourcesPlugin plugin = ResourcesPlugin .getPlugin ();
484
+ if (plugin == null ) {
485
+ return "" ; //$NON-NLS-1$
486
+ }
487
+ BundleContext bundleContext = plugin .getBundle ().getBundleContext ();
488
+ if (bundleContext == null ) {
489
+ return "" ; //$NON-NLS-1$
490
+ }
491
+ String ID = Arrays .stream (bundleContext .getBundles ())
492
+ .map (bundle -> String .format ("%d %s %s" , bundle .getBundleId (), bundle .getSymbolicName (), //$NON-NLS-1$
493
+ bundle .getVersion ()))
494
+ .sorted ().collect (Collectors .joining (System .lineSeparator ()));
495
+ try {
496
+ MessageDigest digest = MessageDigest .getInstance ("SHA-256" ); //$NON-NLS-1$
497
+ return HexFormat .of ().formatHex (digest .digest (ID .getBytes (StandardCharsets .UTF_8 )));
498
+ } catch (NoSuchAlgorithmException e ) {
499
+ return Integer .toHexString (ID .hashCode ());
500
+ }
501
+ }
502
+
503
+ /**
504
+ * @return the cached platform state, only public for testing purpose!
505
+ */
506
+ private String getCachedPlatformState () {
507
+ try {
508
+ return Objects .requireNonNullElse (workspace .getRoot ().getPersistentProperty (CACHE_TIMESTAMP ), "" ); //$NON-NLS-1$
509
+ } catch (CoreException e ) {
510
+ return e .toString ();
511
+ }
512
+ }
513
+
483
514
/**
484
515
* Tries to obtain a content description for the given file.
485
516
*/
@@ -536,15 +567,11 @@ synchronized void setCacheState(byte newCacheState) throws CoreException {
536
567
cacheState = newCacheState ;
537
568
}
538
569
539
- private void setCacheTimeStamp (long timeStamp ) throws CoreException {
540
- workspace .getRoot ().setPersistentProperty (CACHE_TIMESTAMP , Long .toString (timeStamp ));
541
- }
542
-
543
570
@ Override
544
571
public void shutdown (IProgressMonitor monitor ) throws CoreException {
545
- if (getCacheState () != INVALID_CACHE )
546
- // remember the platform timestamp for which we have a valid cache
547
- setCacheTimeStamp ( Platform . getStateStamp ());
572
+ if (getCacheState () != INVALID_CACHE ) {
573
+ workspace . getRoot (). setPersistentProperty ( CACHE_TIMESTAMP , getCurrentPlatformState ());
574
+ }
548
575
IContentTypeManager contentTypeManager = Platform .getContentTypeManager ();
549
576
//tolerate missing services during shutdown because they might be already gone
550
577
if (contentTypeManager != null )
@@ -569,8 +596,9 @@ public void startup(IProgressMonitor monitor) throws CoreException {
569
596
setCacheState (INVALID_CACHE );
570
597
flushJob = new FlushJob (workspace );
571
598
// the cache is stale (plug-ins that might be contributing content types were added/removed)
572
- if (getCacheTimestamp () != Platform . getStateStamp ())
599
+ if (! Objects . equals ( getCachedPlatformState (), getCurrentPlatformState ())) {
573
600
invalidateCache (false , null );
601
+ }
574
602
// register a lifecycle listener
575
603
workspace .addLifecycleListener (this );
576
604
// register a content type change listener
0 commit comments