1 11 package org.eclipse.core.internal.resources; 12 13 import java.io.*; 14 import java.util.ArrayList ; 15 import java.util.List ; 16 import org.eclipse.core.filesystem.EFS; 17 import org.eclipse.core.filesystem.IFileStore; 18 import org.eclipse.core.internal.events.ILifecycleListener; 19 import org.eclipse.core.internal.events.LifecycleEvent; 20 import org.eclipse.core.internal.utils.*; 21 import org.eclipse.core.internal.watson.*; 22 import org.eclipse.core.resources.*; 23 import org.eclipse.core.runtime.*; 24 import org.eclipse.core.runtime.content.*; 25 import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; 26 import org.eclipse.core.runtime.jobs.ISchedulingRule; 27 import org.eclipse.osgi.util.NLS; 28 import org.osgi.framework.Bundle; 29 30 36 public class ContentDescriptionManager implements IManager, IRegistryChangeListener, IContentTypeManager.IContentTypeChangeListener, ILifecycleListener { 37 41 private class FlushJob extends WorkspaceJob { 42 private final List toFlush; 43 private boolean fullFlush; 44 45 public FlushJob() { 46 super(Messages.resources_flushingContentDescriptionCache); 47 setSystem(true); 48 setUser(false); 49 setPriority(LONG); 50 setRule(workspace.getRoot()); 51 toFlush = new ArrayList (5); 52 } 53 54 57 public boolean belongsTo(Object family) { 58 return FAMILY_DESCRIPTION_CACHE_FLUSH.equals(family); 59 } 60 61 64 public IStatus runInWorkspace(final IProgressMonitor monitor) { 65 if (monitor.isCanceled()) 66 return Status.CANCEL_STATUS; 67 try { 68 monitor.beginTask("", Policy.opWork); final ISchedulingRule rule = workspace.getRoot(); 72 try { 73 workspace.prepareOperation(rule, monitor); 74 workspace.beginOperation(true); 75 if (systemBundle.getState() != Bundle.STOPPING) 78 doFlushCache(monitor, getPathsToFlush()); 79 } finally { 80 workspace.endOperation(rule, false, Policy.subMonitorFor(monitor, Policy.endOpWork)); 81 } 82 } catch (OperationCanceledException e) { 83 return Status.CANCEL_STATUS; 84 } catch (CoreException e) { 85 return e.getStatus(); 86 } finally { 87 monitor.done(); 88 } 89 return Status.OK_STATUS; 90 } 91 92 private IPath[] getPathsToFlush() { 93 synchronized (toFlush) { 94 try { 95 if (fullFlush) 96 return null; 97 int size = toFlush.size(); 98 return (size == 0) ? null : (IPath[]) toFlush.toArray(new IPath[size]); 99 } finally { 100 fullFlush = false; 101 toFlush.clear(); 102 } 103 } 104 } 105 106 109 void flush(IProject project) { 110 if (Policy.DEBUG_CONTENT_TYPE_CACHE) 111 Policy.debug("Scheduling flushing of content type cache for " + (project == null ? Path.ROOT : project.getFullPath())); synchronized (toFlush) { 113 if (!fullFlush) 114 if (project == null) 115 fullFlush = true; 116 else 117 toFlush.add(project.getFullPath()); 118 } 119 schedule(1000); 120 } 121 122 } 123 124 128 class LazyFileInputStream extends InputStream { 129 private InputStream actual; 130 private IFileStore target; 131 132 LazyFileInputStream(IFileStore target) { 133 this.target = target; 134 } 135 136 public int available() throws IOException { 137 if (actual == null) 138 return 0; 139 return actual.available(); 140 } 141 142 public void close() throws IOException { 143 if (actual == null) 144 return; 145 actual.close(); 146 } 147 148 private void ensureOpened() throws IOException { 149 if (actual != null) 150 return; 151 if (target == null) 152 throw new FileNotFoundException(); 153 try { 154 actual = target.openInputStream(EFS.NONE, null); 155 } catch (CoreException e) { 156 throw new IOException(e.getMessage()); 157 } 158 } 159 160 public int read() throws IOException { 161 ensureOpened(); 162 return actual.read(); 163 } 164 165 public int read(byte[] b, int off, int len) throws IOException { 166 ensureOpened(); 167 return actual.read(b, off, len); 168 } 169 170 public long skip(long n) throws IOException { 171 ensureOpened(); 172 return actual.skip(n); 173 } 174 } 175 176 private static final QualifiedName CACHE_STATE = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "contentCacheState"); private static final QualifiedName CACHE_TIMESTAMP = new QualifiedName(ResourcesPlugin.PI_RESOURCES, "contentCacheTimestamp"); 179 public static final String FAMILY_DESCRIPTION_CACHE_FLUSH = ResourcesPlugin.PI_RESOURCES + ".contentDescriptionCacheFamily"; 181 public static final byte EMPTY_CACHE = 1; 183 public static final byte USED_CACHE = 2; 184 public static final byte INVALID_CACHE = 3; 185 public static final byte FLUSHING_CACHE = 4; 186 187 private static final String PT_CONTENTTYPES = "contentTypes"; 189 private Cache cache; 190 191 private byte cacheState; 192 193 private FlushJob flushJob; 194 private ProjectContentTypes projectContentTypes; 195 196 Workspace workspace; 197 protected final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); 199 202 public void contentTypeChanged(ContentTypeChangeEvent event) { 203 if (Policy.DEBUG_CONTENT_TYPE) 204 Policy.debug("Content type settings changed for " + event.getContentType()); invalidateCache(true, null); 206 } 207 208 synchronized void doFlushCache(final IProgressMonitor monitor, IPath[] toClean) throws CoreException { 209 if (getCacheState() != INVALID_CACHE) { 211 if (Policy.DEBUG_CONTENT_TYPE_CACHE) 212 Policy.debug("Content type cache flush not performed"); return; 214 } 215 try { 216 setCacheState(FLUSHING_CACHE); 217 cache.discardAll(); 219 if (toClean == null || toClean.length == 0) 220 clearContentFlags(Path.ROOT, monitor); 222 else { 223 for (int i = 0; i < toClean.length; i++) 225 clearContentFlags(toClean[i], monitor); 226 } 227 } catch (CoreException ce) { 228 setCacheState(INVALID_CACHE); 229 throw ce; 230 } 231 setCacheState(EMPTY_CACHE); 233 } 234 235 238 private void clearContentFlags(IPath root, final IProgressMonitor monitor) { 239 long flushStart = System.currentTimeMillis(); 240 if (Policy.DEBUG_CONTENT_TYPE_CACHE) 241 Policy.debug("Flushing content type cache for " + root); IElementContentVisitor visitor = new IElementContentVisitor() { 244 public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) { 245 if (monitor.isCanceled()) 246 throw new OperationCanceledException(); 247 if (elementContents == null) 248 return false; 249 ResourceInfo info = (ResourceInfo) elementContents; 250 if (info.getType() != IResource.FILE) 251 return true; 252 info = workspace.getResourceInfo(requestor.requestPath(), false, true); 253 if (info == null) 254 return false; 255 info.clear(ICoreConstants.M_CONTENT_CACHE); 256 return true; 257 } 258 }; 259 new ElementTreeIterator(workspace.getElementTree(), root).iterate(visitor); 260 if (Policy.DEBUG_CONTENT_TYPE_CACHE) 261 Policy.debug("Content type cache for " + root + " flushed in " + (System.currentTimeMillis() - flushStart) + " ms"); } 263 264 Cache getCache() { 265 return cache; 266 } 267 268 269 public synchronized byte getCacheState() { 270 if (cacheState != 0) 271 return cacheState; 273 String persisted; 274 try { 275 persisted = workspace.getRoot().getPersistentProperty(CACHE_STATE); 276 cacheState = persisted != null ? Byte.parseByte(persisted) : INVALID_CACHE; 277 } catch (NumberFormatException e) { 278 cacheState = INVALID_CACHE; 279 } catch (CoreException e) { 280 Policy.log(e.getStatus()); 281 cacheState = INVALID_CACHE; 282 } 283 return cacheState; 284 } 285 286 public long getCacheTimestamp() throws CoreException { 287 try { 288 return Long.parseLong(workspace.getRoot().getPersistentProperty(CACHE_TIMESTAMP)); 289 } catch (NumberFormatException e) { 290 return 0; 291 } 292 } 293 294 public IContentTypeMatcher getContentTypeMatcher(Project project) throws CoreException { 295 return projectContentTypes.getMatcherFor(project); 296 } 297 298 public IContentDescription getDescriptionFor(File file, ResourceInfo info) throws CoreException { 299 if (ProjectContentTypes.usesContentTypePreferences(file.getFullPath().segment(0))) 300 return readDescription(file); 302 switch (getCacheState()) { 303 case INVALID_CACHE : 304 flushJob.schedule(1000); 306 case FLUSHING_CACHE : 308 return readDescription(file); 310 } 311 if (info == null) 314 return null; 315 if (info.isSet(ICoreConstants.M_NO_CONTENT_DESCRIPTION)) 316 return null; 318 if (info.isSet(ICoreConstants.M_DEFAULT_CONTENT_DESCRIPTION)) { 319 IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); 321 IContentType type = contentTypeManager.findContentTypeFor(file.getName()); 323 if (type != null) 324 return type.getDefaultDescription(); 326 info.clear(ICoreConstants.M_CONTENT_CACHE); 329 } 330 synchronized (this) { 331 Cache.Entry entry = cache.getEntry(file.getFullPath()); 333 if (entry != null && entry.getTimestamp() == info.getContentId()) 334 return (IContentDescription) entry.getCached(); 336 setCacheState(USED_CACHE); 338 IContentDescription newDescription = readDescription(file); 340 if (newDescription == null) { 341 info.set(ICoreConstants.M_NO_CONTENT_DESCRIPTION); 343 return null; 344 } 345 if (newDescription.getContentType().getDefaultDescription().equals(newDescription)) { 346 IContentType defaultForName = Platform.getContentTypeManager().findContentTypeFor(file.getName()); 348 if (newDescription.getContentType().equals(defaultForName)) { 349 info.set(ICoreConstants.M_DEFAULT_CONTENT_DESCRIPTION); 351 return newDescription; 352 } 353 } 354 if (entry == null) 356 entry = cache.addEntry(file.getFullPath(), newDescription, info.getContentId()); 358 else { 359 entry.setTimestamp(info.getContentId()); 361 entry.setCached(newDescription); 362 } 363 return newDescription; 364 } 365 } 366 367 374 public synchronized void invalidateCache(boolean flush, IProject project) { 375 if (getCacheState() == EMPTY_CACHE) 376 return; 378 try { 380 setCacheState(INVALID_CACHE); 381 } catch (CoreException e) { 382 Policy.log(e.getStatus()); 383 } 384 if (Policy.DEBUG_CONTENT_TYPE_CACHE) 385 Policy.debug("Invalidated cache for " + (project == null ? Path.ROOT : project.getFullPath())); if (flush) 387 flushJob.flush(project); 388 } 389 390 393 private IContentDescription readDescription(File file) throws CoreException { 394 if (Policy.DEBUG_CONTENT_TYPE) 395 Policy.debug("reading contents of " + file); InputStream contents = new LazyFileInputStream(file.getStore()); 398 try { 399 IContentTypeMatcher matcher = getContentTypeMatcher((Project) file.getProject()); 400 return matcher.getDescriptionFor(contents, file.getName(), IContentDescription.ALL); 401 } catch (IOException e) { 402 String message = NLS.bind(Messages.resources_errorContentDescription, file.getFullPath()); 403 throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, file.getFullPath(), message, e); 404 } finally { 405 file.ensureClosed(contents); 406 } 407 } 408 409 412 public void registryChanged(IRegistryChangeEvent event) { 413 if (event.getExtensionDeltas(Platform.PI_RUNTIME, PT_CONTENTTYPES).length == 0) 415 return; 416 invalidateCache(true, null); 417 } 418 419 422 public void handleEvent(LifecycleEvent event) { 423 switch (event.kind) { 425 case LifecycleEvent.PRE_PROJECT_CHANGE : 426 case LifecycleEvent.PRE_PROJECT_DELETE : 428 case LifecycleEvent.PRE_PROJECT_MOVE : 430 invalidateCache(true, (IProject) event.resource); 432 } 433 } 434 435 synchronized void setCacheState(byte newCacheState) throws CoreException { 436 if (cacheState == newCacheState) 437 return; 438 workspace.getRoot().setPersistentProperty(CACHE_STATE, Byte.toString(newCacheState)); 439 cacheState = newCacheState; 440 } 441 442 private void setCacheTimeStamp(long timeStamp) throws CoreException { 443 workspace.getRoot().setPersistentProperty(CACHE_TIMESTAMP, Long.toString(timeStamp)); 444 } 445 446 public void shutdown(IProgressMonitor monitor) throws CoreException { 447 if (getCacheState() != INVALID_CACHE) 448 setCacheTimeStamp(Platform.getStateStamp()); 450 Platform.getContentTypeManager().removeContentTypeChangeListener(this); 451 Platform.getExtensionRegistry().removeRegistryChangeListener(this); 452 cache.dispose(); 453 cache = null; 454 flushJob.cancel(); 455 flushJob = null; 456 projectContentTypes = null; 457 } 458 459 public void startup(IProgressMonitor monitor) throws CoreException { 460 workspace = (Workspace) ResourcesPlugin.getWorkspace(); 461 cache = new Cache(100, 1000, 0.1); 462 projectContentTypes = new ProjectContentTypes(workspace); 463 getCacheState(); 464 if (cacheState == FLUSHING_CACHE) 465 setCacheState(INVALID_CACHE); 467 flushJob = new FlushJob(); 468 if (getCacheTimestamp() != Platform.getStateStamp()) 470 invalidateCache(false, null); 471 workspace.addLifecycleListener(this); 473 Platform.getContentTypeManager().addContentTypeChangeListener(this); 475 Platform.getExtensionRegistry().addRegistryChangeListener(this, Platform.PI_RUNTIME); 477 } 478 479 public void projectPreferencesChanged(IProject project) { 480 if (Policy.DEBUG_CONTENT_TYPE) 481 Policy.debug("Project preferences changed for " + project); projectContentTypes.contentTypePreferencesChanged(project); 483 } 484 } 485 | Popular Tags |