| /******************************************************************************* |
| * Copyright (c) 2005, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.eclipse.jst.jsp.core.taglib; |
| |
| import java.io.File; |
| import java.lang.ref.Reference; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.zip.CRC32; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.jobs.ILock; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jdt.core.ElementChangedEvent; |
| import org.eclipse.jdt.core.IElementChangedListener; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaElementDelta; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jst.jsp.core.internal.JSPCorePlugin; |
| import org.eclipse.jst.jsp.core.internal.Logger; |
| import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * A non-extendable index manager for taglibs similar to the previous J2EE |
| * ITaglibRegistry but lacking any ties to project natures. Each record |
| * returned from the index represents a single tag library descriptor. |
| * |
| * Indexing is only persisted between sessions for entries on the Java Build |
| * Path. New ADD events will be sent to ITaglibIndexListeners during each |
| * workbench session for both cached and newly found records. REMOVE events |
| * are not fired on workbench shutdown. The record's contents should be |
| * examined for any further information. |
| * |
| * @since 1.0 |
| */ |
| public final class TaglibIndex { |
| class ClasspathChangeListener implements IElementChangedListener { |
| List projectsIndexed = new ArrayList(1); |
| |
| public void elementChanged(ElementChangedEvent event) { |
| if (!isIndexAvailable()) |
| return; |
| try { |
| LOCK.acquire(); |
| if (_debugEvents) { |
| Logger.log(Logger.INFO, "TaglibIndex responding to:" + event); //$NON-NLS-1$ |
| } |
| projectsIndexed.clear(); |
| elementChanged(event.getDelta(), true); |
| fireCurrentDelta(event); |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| |
| private void elementChanged(IJavaElementDelta delta, boolean forceUpdate) { |
| if (frameworkIsShuttingDown()) |
| return; |
| |
| IJavaElement element = delta.getElement(); |
| if (element.getElementType() == IJavaElement.JAVA_MODEL) { |
| IJavaElementDelta[] changed = delta.getAffectedChildren(); |
| for (int i = 0; i < changed.length; i++) { |
| elementChanged(changed[i], forceUpdate); |
| } |
| } |
| // Handle any changes at the project level |
| else if (element.getElementType() == IJavaElement.JAVA_PROJECT) { |
| if ((delta.getFlags() & IJavaElementDelta.F_CLASSPATH_CHANGED) != 0) { |
| IJavaElement proj = element; |
| handleClasspathChange((IJavaProject) proj, forceUpdate); |
| } |
| else { |
| IJavaElementDelta[] deltas = delta.getAffectedChildren(); |
| if (deltas.length == 0) { |
| if (delta.getKind() == IJavaElementDelta.REMOVED || (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0) { |
| /* |
| * If the project is being deleted or closed, just |
| * remove the description |
| */ |
| IJavaProject proj = (IJavaProject) element; |
| ProjectDescription description = (ProjectDescription) fProjectDescriptions.remove(proj.getProject()); |
| if (description != null) { |
| if (_debugIndexCreation) { |
| Logger.log(Logger.INFO, "removing index of " + description.fProject.getName()); //$NON-NLS-1$ |
| } |
| // removing the index file ensures that we |
| // don't get stale data if the project is |
| // reopened |
| removeIndex(proj.getProject()); |
| } |
| } |
| } |
| /* |
| * (else) Without the classpath changing, there's nothing |
| * else to do |
| */ |
| else { |
| for (int i = 0; i < deltas.length; i++) { |
| elementChanged(deltas[i], false); |
| } |
| } |
| } |
| } |
| /* |
| * Other modification to the classpath (such as within a classpath |
| * container like "Web App Libraries") go to the description |
| * itself |
| */ |
| else if ((delta.getFlags() & IJavaElementDelta.F_ADDED_TO_CLASSPATH) != 0 || (delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) { |
| IJavaProject affectedProject = element.getJavaProject(); |
| if (affectedProject != null) { |
| /* |
| * If the affected project has an index on-disk, it's |
| * going to be invalid--we need to create/load the |
| * description so it will be up to date [loading now and |
| * updating is usually faster than regenerating the entire |
| * index]. If there is no index on disk, do nothing more. |
| */ |
| File indexFile = new File(computeIndexLocation(affectedProject.getProject().getFullPath())); |
| if (indexFile.exists()) { |
| ProjectDescription affectedDescription = createDescription(affectedProject.getProject()); |
| if (affectedDescription != null) { |
| affectedDescription.handleElementChanged(delta); |
| } |
| } |
| projectsIndexed.add(affectedProject.getProject()); |
| } |
| } |
| } |
| |
| private void handleClasspathChange(IJavaProject project, boolean forceUpdate) { |
| if (frameworkIsShuttingDown()) |
| return; |
| |
| try { |
| /* Handle large changes to this project's build path */ |
| IResource resource = project.getCorrespondingResource(); |
| if (resource.getType() == IResource.PROJECT && !projectsIndexed.contains(resource)) { |
| /* |
| * Use get instead of create since the downstream |
| * (upstream?) project wasn't itself modified. |
| */ |
| ProjectDescription description = null; |
| if (forceUpdate) { |
| description = createDescription((IProject) resource); |
| } |
| else { |
| description = getDescription((IProject) resource); |
| } |
| if (description != null && !frameworkIsShuttingDown()) { |
| projectsIndexed.add(resource); |
| description.setBuildPathIsDirty(); |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| Logger.logException(e); |
| } |
| } |
| } |
| |
| class ResourceChangeListener implements IResourceChangeListener { |
| public void resourceChanged(IResourceChangeEvent event) { |
| if (!isIndexAvailable()) |
| return; |
| try { |
| LOCK.acquire(); |
| if (_debugEvents) { |
| Logger.log(Logger.INFO, "TaglibIndex responding to:" + event + "\n" + event.getDelta()); //$NON-NLS-2$ //$NON-NLS-1$ |
| } |
| switch (event.getType()) { |
| case IResourceChangeEvent.PRE_CLOSE : |
| case IResourceChangeEvent.PRE_DELETE : { |
| try { |
| // pair deltas with projects |
| IResourceDelta[] deltas = new IResourceDelta[]{event.getDelta()}; |
| IProject[] projects = null; |
| |
| if (deltas.length > 0) { |
| IResource resource = null; |
| if (deltas[0] != null) { |
| resource = deltas[0].getResource(); |
| } |
| else { |
| resource = event.getResource(); |
| } |
| |
| if (resource != null) { |
| if (resource.getType() == IResource.ROOT) { |
| deltas = deltas[0].getAffectedChildren(); |
| projects = new IProject[deltas.length]; |
| for (int i = 0; i < deltas.length; i++) { |
| if (deltas[i].getResource().getType() == IResource.PROJECT) { |
| projects[i] = (IProject) deltas[i].getResource(); |
| } |
| } |
| } |
| else { |
| projects = new IProject[1]; |
| if (resource.getType() != IResource.PROJECT) { |
| projects[0] = resource.getProject(); |
| } |
| else { |
| projects[0] = (IProject) resource; |
| } |
| } |
| } |
| for (int i = 0; i < projects.length; i++) { |
| if (_debugIndexCreation) { |
| Logger.log(Logger.INFO, "TaglibIndex noticed " + projects[i].getName() + " is about to be deleted/closed"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| ProjectDescription description = (ProjectDescription) fProjectDescriptions.remove(projects[i]); |
| if (description != null) { |
| if (_debugIndexCreation) { |
| Logger.log(Logger.INFO, "removing index of " + description.fProject.getName()); //$NON-NLS-1$ |
| } |
| description.clear(); |
| } |
| } |
| } |
| } |
| catch (Exception e) { |
| Logger.logException("Exception while processing resource deletion", e); //$NON-NLS-1$ |
| } |
| } |
| case IResourceChangeEvent.POST_CHANGE : { |
| try { |
| // pair deltas with projects |
| IResourceDelta[] deltas = new IResourceDelta[]{event.getDelta()}; |
| IProject[] projects = null; |
| |
| if (deltas.length > 0) { |
| IResource resource = null; |
| if (deltas[0] != null) { |
| resource = deltas[0].getResource(); |
| } |
| else { |
| resource = event.getResource(); |
| } |
| |
| if (resource != null) { |
| if (resource.getType() == IResource.ROOT) { |
| deltas = deltas[0].getAffectedChildren(); |
| projects = new IProject[deltas.length]; |
| for (int i = 0; i < deltas.length; i++) { |
| if (deltas[i].getResource().getType() == IResource.PROJECT) { |
| projects[i] = (IProject) deltas[i].getResource(); |
| } |
| } |
| } |
| else { |
| projects = new IProject[1]; |
| if (resource.getType() != IResource.PROJECT) { |
| projects[0] = resource.getProject(); |
| } |
| else { |
| projects[0] = (IProject) resource; |
| } |
| } |
| } |
| for (int i = 0; i < projects.length; i++) { |
| if (deltas[i].getKind() == IResourceDelta.CHANGED && (deltas[i].getFlags() == IResourceDelta.ENCODING || deltas[i].getFlags() == IResourceDelta.MARKERS)) |
| continue; |
| try { |
| if (deltas[i] != null && deltas[i].getKind() != IResourceDelta.REMOVED && projects[i].isAccessible()) { |
| ProjectDescription description = getDescription(projects[i]); |
| if (description != null && !frameworkIsShuttingDown()) { |
| deltas[i].accept(description.getVisitor()); |
| } |
| } |
| if (!projects[i].isAccessible() || (deltas[i] != null && deltas[i].getKind() == IResourceDelta.REMOVED)) { |
| if (_debugIndexCreation) { |
| Logger.log(Logger.INFO, "TaglibIndex noticed " + projects[i].getName() + " was removed or is no longer accessible"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| ProjectDescription description = (ProjectDescription) fProjectDescriptions.remove(projects[i]); |
| if (description != null) { |
| if (_debugIndexCreation) { |
| Logger.log(Logger.INFO, "removing index of " + description.fProject.getName()); //$NON-NLS-1$ |
| } |
| description.clear(); |
| } |
| } |
| } |
| catch (CoreException e) { |
| Logger.logException(e); |
| } |
| } |
| } |
| } |
| catch (Exception e) { |
| Logger.logException("Exception while processing resource change", e); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| fireCurrentDelta(event); |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| } |
| |
| static final boolean _debugChangeListener = false; |
| |
| static boolean _debugEvents = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jst.jsp.core/taglib/events")); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| static boolean _debugIndexCreation = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jst.jsp.core/taglib/indexcreation")); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| static final boolean _debugResolution = "true".equals(Platform.getDebugOption("org.eclipse.jst.jsp.core/taglib/resolve")); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| static TaglibIndex _instance = new TaglibIndex(); |
| |
| private boolean initialized; |
| |
| private static final CRC32 checksumCalculator = new CRC32(); |
| |
| private static final String CLEAN = "CLEAN"; |
| private static final String DIRTY = "DIRTY"; |
| static boolean ENABLED = false; |
| |
| static ILock LOCK = Job.getJobManager().newLock(); |
| |
| /** |
| * NOT API. |
| * |
| * @param listener |
| * the listener to be added |
| */ |
| public static void addTaglibIndexListener(ITaglibIndexListener listener) { |
| if (getInstance().isInitialized()) |
| getInstance().internalAddTaglibIndexListener(listener); |
| } |
| |
| static void fireTaglibDelta(ITaglibIndexDelta delta) { |
| if (_debugEvents) { |
| Logger.log(Logger.INFO, "TaglibIndex fired delta:" + delta + " [" + delta.getAffectedChildren().length + "]\n" + ((TaglibIndexDelta) delta).trigger); //$NON-NLS-1$ |
| } |
| /* |
| * Flush any shared cache entries, the TaglibControllers should handle |
| * updating their documents as needed. |
| */ |
| ITaglibIndexDelta[] deltas = delta.getAffectedChildren(); |
| for (int i = 0; i < deltas.length; i++) { |
| ITaglibRecord taglibRecord = deltas[i].getTaglibRecord(); |
| if (taglibRecord != null) { |
| Object uniqueIdentifier = TLDCMDocumentManager.getUniqueIdentifier(taglibRecord); |
| if (uniqueIdentifier != null) { |
| TLDCMDocumentManager.getSharedDocumentCache().remove(uniqueIdentifier); |
| } |
| else { |
| Logger.log(Logger.ERROR, "identifier for " + taglibRecord + " was null"); |
| } |
| } |
| } |
| synchronized (TLDCMDocumentManager.getSharedDocumentCache()) { |
| Iterator values = TLDCMDocumentManager.getSharedDocumentCache().values().iterator(); |
| while (values.hasNext()) { |
| Object o = values.next(); |
| if (o instanceof Reference) { |
| values.remove(); |
| } |
| } |
| } |
| |
| if (_instance.isInitialized()) { |
| ITaglibIndexListener[] listeners = _instance.fTaglibIndexListeners; |
| if (listeners != null) { |
| for (int j = 0; j < listeners.length; j++) { |
| try { |
| listeners[j].indexChanged(delta); |
| } |
| catch (Exception e) { |
| Logger.log(Logger.WARNING, e.getMessage()); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Finds all of the visible ITaglibRecords for the given path in the |
| * workspace. Taglib mappings from web.xml files are only visible to paths |
| * within the web.xml's corresponding web content folder. |
| * |
| * @param fullPath - |
| * a path within the workspace |
| * @return All of the visible ITaglibRecords from the given path. |
| */ |
| public static ITaglibRecord[] getAvailableTaglibRecords(IPath fullPath) { |
| if (!_instance.isInitialized()) { |
| return new ITaglibRecord[0]; |
| } |
| ITaglibRecord[] records = null; |
| if (getInstance().isInitialized()) { |
| records = getInstance().internalGetAvailableTaglibRecords(fullPath); |
| } |
| else { |
| records = new ITaglibRecord[0]; |
| } |
| getInstance().fireCurrentDelta("enumerate: " + fullPath); //$NON-NLS-1$ |
| return records; |
| } |
| |
| /** |
| * Returns the IPath considered to be the web-app root for the given path. |
| * All resolution from the given path beginning with '/' will be relative |
| * to the computed web-app root. |
| * |
| * @deprecated - is not correct in flexible projects |
| * @param path - |
| * a path under the web-app root |
| * @return the IPath considered to be the web-app's root for the given |
| * path or null if one could not be determined |
| */ |
| public static IPath getContextRoot(IPath path) { |
| try { |
| LOCK.acquire(); |
| if (getInstance().isInitialized()) { |
| return getInstance().internalGetContextRoot(path); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| return null; |
| } |
| |
| public static TaglibIndex getInstance() { |
| return _instance; |
| } |
| |
| /** |
| * NOT API. |
| * |
| * @param listener |
| * the listener to be removed |
| */ |
| public static void removeTaglibIndexListener(ITaglibIndexListener listener) { |
| if (!getInstance().isInitialized()) |
| return; |
| if (getInstance().isInitialized()) |
| getInstance().internalRemoveTaglibIndexListener(listener); |
| } |
| |
| /** |
| * Finds a matching ITaglibRecord given the reference. Typically the |
| * result will have to be cast to a subiinterface of ITaglibRecord. |
| * |
| * @param basePath - |
| * the workspace-relative path for IResources, full filesystem |
| * path otherwise |
| * @param reference - |
| * the URI to lookup, for example the uri value from a taglib |
| * directive |
| * @param crossProjects - |
| * whether to search across projects (currently ignored) |
| * |
| * @return a visible ITaglibRecord or null if the reference points to no |
| * known tag library descriptor |
| * |
| * @See ITaglibRecord |
| */ |
| public static ITaglibRecord resolve(String basePath, String reference, boolean crossProjects) { |
| ITaglibRecord result = null; |
| if (getInstance().isInitialized()) { |
| result = getInstance().internalResolve(basePath, reference, crossProjects); |
| } |
| getInstance().fireCurrentDelta("resolve: " + reference); //$NON-NLS-1$ |
| if (_debugResolution) { |
| if (result == null) { |
| Logger.log(Logger.INFO, "TaglibIndex could not resolve \"" + reference + "\" from " + basePath); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| else { |
| switch (result.getRecordType()) { |
| case (ITaglibRecord.TLD) : { |
| ITLDRecord record = (ITLDRecord) result; |
| Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| break; |
| case (ITaglibRecord.JAR) : { |
| IJarRecord record = (IJarRecord) result; |
| Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| break; |
| case (ITaglibRecord.TAGDIR) : { |
| ITagDirRecord record = (ITagDirRecord) result; |
| Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| break; |
| case (ITaglibRecord.URL) : { |
| IURLRecord record = (IURLRecord) result; |
| Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| break; |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Instructs the index to stop listening for resource and classpath |
| * changes, and to forget all information about the workspace. |
| */ |
| public static void shutdown() { |
| try { |
| LOCK.acquire(); |
| if (_instance.isInitialized()) { |
| _instance.stop(); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| |
| /** |
| * Instructs the index to begin listening for resource and classpath |
| * changes. |
| */ |
| public static void startup() { |
| boolean shuttingDown = !Platform.isRunning() || Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING; |
| if (!shuttingDown) { |
| try { |
| LOCK.acquire(); |
| ENABLED = !"false".equalsIgnoreCase(System.getProperty(TaglibIndex.class.getName())); //$NON-NLS-1$ |
| getInstance().initializeInstance(); |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| } |
| |
| private ClasspathChangeListener fClasspathChangeListener = null; |
| |
| private TaglibIndexDelta fCurrentTopLevelDelta = null; |
| |
| Map fProjectDescriptions = null; |
| |
| private ResourceChangeListener fResourceChangeListener; |
| |
| private ITaglibIndexListener[] fTaglibIndexListeners = null; |
| |
| /** symbolic name for OSGI framework */ |
| private final static String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$ |
| |
| private TaglibIndex() { |
| super(); |
| } |
| |
| private void initializeInstance() { |
| |
| if (isInitialized()) |
| return; |
| try { |
| LOCK.acquire(); |
| /* |
| * check again, just incase it was initialized on another thread, |
| * while we were waiting for the lock |
| */ |
| if (!isInitialized()) { |
| /* |
| * Only consider a crash if a value exists and is DIRTY (not a |
| * new workspace) |
| */ |
| if (DIRTY.equalsIgnoreCase(getState())) { |
| Logger.log(Logger.ERROR, "A workspace crash was detected. The previous session did not exit normally. Not using saved taglib indexes."); //$NON-NLS-3$ |
| removeIndexes(false); |
| } |
| |
| fProjectDescriptions = new Hashtable(); |
| fResourceChangeListener = new ResourceChangeListener(); |
| fClasspathChangeListener = new ClasspathChangeListener(); |
| if (ENABLED) { |
| ResourcesPlugin.getWorkspace().addResourceChangeListener(fResourceChangeListener, IResourceChangeEvent.POST_CHANGE); |
| JavaCore.addElementChangedListener(fClasspathChangeListener); |
| } |
| setIntialized(true); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| |
| /** |
| * Adds the given delta as a child to an overall delta |
| * |
| * @param delta |
| */ |
| void addDelta(ITaglibIndexDelta delta) { |
| ensureDelta(delta.getProject()).addChildDelta(delta); |
| } |
| |
| /** |
| * Based on org.eclipse.jdt.internal.core.search.indexing.IndexManager |
| * |
| * @param containerPath |
| * @return |
| */ |
| String computeIndexLocation(IPath containerPath) { |
| String fileName = computeIndexName(containerPath); |
| if (_debugIndexCreation) |
| Logger.log(Logger.INFO, "-> index name for " + containerPath + " is " + fileName); //$NON-NLS-1$ //$NON-NLS-2$ |
| String indexLocation = getTaglibIndexStateLocation().append(fileName).toOSString(); |
| return indexLocation; |
| } |
| |
| String computeIndexName(IPath containerPath) { |
| checksumCalculator.reset(); |
| checksumCalculator.update(containerPath.toOSString().getBytes()); |
| // use ".dat" so we're not confused with JDT indexes |
| String fileName = Long.toString(checksumCalculator.getValue()) + ".dat"; //$NON-NLS-1$ |
| return fileName; |
| } |
| |
| /** |
| * @param project |
| * @return |
| */ |
| ProjectDescription createDescription(IProject project) { |
| ProjectDescription description = null; |
| try { |
| LOCK.acquire(); |
| description = (ProjectDescription) fProjectDescriptions.get(project); |
| if (description == null) { |
| // Once we've started indexing, we're dirty again |
| if (fProjectDescriptions.isEmpty()) { |
| setState(DIRTY); |
| } |
| description = new ProjectDescription(project, computeIndexLocation(project.getFullPath())); |
| fProjectDescriptions.put(project, description); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| return description; |
| } |
| |
| /** |
| * Ensures that a delta exists for holding index change information |
| */ |
| private TaglibIndexDelta ensureDelta(IProject project) { |
| /* |
| * The first delta to be added will determine which project the |
| * top-level delta will contain. |
| */ |
| if (fCurrentTopLevelDelta == null) { |
| fCurrentTopLevelDelta = new TaglibIndexDelta(project, null, ITaglibIndexDelta.CHANGED); |
| } |
| return fCurrentTopLevelDelta; |
| } |
| |
| void fireCurrentDelta(Object trigger) { |
| if (fCurrentTopLevelDelta != null) { |
| fCurrentTopLevelDelta.trigger = trigger; |
| ITaglibIndexDelta delta = fCurrentTopLevelDelta; |
| fCurrentTopLevelDelta = null; |
| fireTaglibDelta(delta); |
| } |
| } |
| |
| /** |
| * A check to see if the OSGI framework is shutting down. |
| * |
| * @return true if the System Bundle is stopped (ie. the framework is |
| * shutting down) |
| */ |
| boolean frameworkIsShuttingDown() { |
| // in the Framework class there's a note: |
| // set the state of the System Bundle to STOPPING. |
| // this must be done first according to section 4.19.2 from the OSGi |
| // R3 spec. |
| boolean shuttingDown = !Platform.isRunning() || Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING; |
| return shuttingDown; |
| } |
| |
| ProjectDescription getDescription(IProject project) { |
| ProjectDescription description = null; |
| if (isInitialized()) { |
| description = (ProjectDescription) fProjectDescriptions.get(project); |
| } |
| return description; |
| } |
| |
| private String getState() { |
| String state = JSPCorePlugin.getDefault().getPluginPreferences().getString(TaglibIndex.class.getName()); |
| return state; |
| } |
| |
| private IPath getTaglibIndexStateLocation() { |
| return JSPCorePlugin.getDefault().getStateLocation().append("taglibindex/"); |
| } |
| |
| private void internalAddTaglibIndexListener(ITaglibIndexListener listener) { |
| try { |
| LOCK.acquire(); |
| if (fTaglibIndexListeners == null) { |
| fTaglibIndexListeners = new ITaglibIndexListener[]{listener}; |
| } |
| else { |
| List listeners = new ArrayList(Arrays.asList(fTaglibIndexListeners)); |
| if (!listeners.contains(listener)) { |
| listeners.add(listener); |
| } |
| fTaglibIndexListeners = (ITaglibIndexListener[]) listeners.toArray(new ITaglibIndexListener[0]); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| |
| private ITaglibRecord[] internalGetAvailableTaglibRecords(IPath path) { |
| ITaglibRecord[] records = new ITaglibRecord[0]; |
| if (path.segmentCount() > 0) { |
| IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)); |
| if (project.isAccessible()) { |
| ProjectDescription description = createDescription(project); |
| List availableRecords = description.getAvailableTaglibRecords(path); |
| |
| // ICatalog catalog = |
| // XMLCorePlugin.getDefault().getDefaultXMLCatalog(); |
| // while (catalog != null) { |
| // ICatalogEntry[] entries = catalog.getCatalogEntries(); |
| // for (int i = 0; i < entries.length; i++) { |
| // // System.out.println(entries[i].getURI()); |
| // } |
| // INextCatalog[] nextCatalogs = catalog.getNextCatalogs(); |
| // for (int i = 0; i < nextCatalogs.length; i++) { |
| // ICatalogEntry[] entries2 = |
| // nextCatalogs[i].getReferencedCatalog().getCatalogEntries(); |
| // for (int j = 0; j < entries2.length; j++) { |
| // // System.out.println(entries2[j].getURI()); |
| // } |
| // } |
| // } |
| |
| records = (ITaglibRecord[]) availableRecords.toArray(records); |
| } |
| } |
| return records; |
| } |
| |
| private IPath internalGetContextRoot(IPath path) { |
| IFile baseResource = FileBuffers.getWorkspaceFileAtLocation(path); |
| if (baseResource != null && baseResource.getProject().isAccessible()) { |
| IProject project = baseResource.getProject(); |
| ProjectDescription description = getInstance().createDescription(project); |
| IPath rootPath = description.getLocalRoot(baseResource.getFullPath()); |
| return rootPath; |
| } |
| // try to handle out-of-workspace paths |
| IPath root = path.makeAbsolute(); |
| while (root.segmentCount() > 0 && !root.isRoot()) |
| root = root.removeLastSegments(1); |
| return root; |
| } |
| |
| private void internalRemoveTaglibIndexListener(ITaglibIndexListener listener) { |
| try { |
| LOCK.acquire(); |
| if (fTaglibIndexListeners != null) { |
| List listeners = new ArrayList(Arrays.asList(fTaglibIndexListeners)); |
| listeners.remove(listener); |
| fTaglibIndexListeners = (ITaglibIndexListener[]) listeners.toArray(new ITaglibIndexListener[0]); |
| } |
| } |
| finally { |
| LOCK.release(); |
| } |
| } |
| |
| private ITaglibRecord internalResolve(String basePath, final String reference, boolean crossProjects) { |
| IProject project = null; |
| ITaglibRecord resolved = null; |
| |
| Path baseIPath = new Path(basePath); |
| IResource baseResource = FileBuffers.getWorkspaceFileAtLocation(baseIPath); |
| |
| if (baseResource == null) { |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| // Try the base path as a folder first |
| if (baseIPath.segmentCount() > 1) { |
| baseResource = workspaceRoot.getFolder(baseIPath); |
| } |
| // If not a folder, then try base path as a file |
| if (baseResource != null && !baseResource.exists() && baseIPath.segmentCount() > 1) { |
| baseResource = workspaceRoot.getFile(baseIPath); |
| } |
| if (baseResource == null && baseIPath.segmentCount() == 1) { |
| baseResource = workspaceRoot.getProject(baseIPath.segment(0)); |
| } |
| } |
| |
| if (baseResource == null) { |
| /* |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=116529 |
| * |
| * This method produces a less accurate result, but doesn't |
| * require that the file exist yet. |
| */ |
| IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(baseIPath); |
| if (files.length > 0) |
| baseResource = files[0]; |
| } |
| if (baseResource != null) { |
| project = ResourcesPlugin.getWorkspace().getRoot().getProject(baseIPath.segment(0)); |
| if (project.isAccessible()) { |
| ProjectDescription description = createDescription(project); |
| resolved = description.resolve(basePath, reference); |
| } |
| } |
| |
| return resolved; |
| } |
| |
| boolean isIndexAvailable() { |
| return _instance.isInitialized() && ENABLED; |
| } |
| |
| /** |
| * Removes index file for the given project. |
| */ |
| private void removeIndex(IProject project) { |
| File indexFile = new File(computeIndexLocation(project.getFullPath())); |
| if (indexFile.exists()) { |
| indexFile.delete(); |
| } |
| } |
| |
| /** |
| * Removes index files. Used for maintenance and keeping the index folder |
| * a manageable size. |
| * |
| * @param staleOnly - |
| * if <b>true</b>, removes only the indexes for projects not |
| * open in the workspace, if <b>false</b>, removes all of the |
| * indexes |
| */ |
| private void removeIndexes(boolean staleOnly) { |
| String osPath = getTaglibIndexStateLocation().toOSString(); |
| File folder = new File(osPath); |
| if (!folder.isDirectory()) { |
| try { |
| folder.mkdir(); |
| } |
| catch (SecurityException e) { |
| } |
| } |
| |
| // remove any extraneous index files |
| IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| List indexNames = new ArrayList(projects.length); |
| if (staleOnly) { |
| for (int i = 0; i < projects.length; i++) { |
| if (projects[i].isAccessible()) { |
| indexNames.add(computeIndexName(projects[i].getFullPath())); |
| } |
| } |
| } |
| |
| if (folder.isDirectory()) { |
| File[] files = folder.listFiles(); |
| for (int i = 0; files != null && i < files.length; i++) { |
| if (!indexNames.contains(files[i].getName())) |
| files[i].delete(); |
| } |
| } |
| } |
| |
| private void setState(String state) { |
| if (!state.equals(getState())) { |
| JSPCorePlugin.getDefault().getPluginPreferences().setValue(TaglibIndex.class.getName(), state); |
| JSPCorePlugin.getDefault().savePluginPreferences(); |
| } |
| } |
| |
| private void stop() { |
| if (isInitialized()) { |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(fResourceChangeListener); |
| JavaCore.removeElementChangedListener(fClasspathChangeListener); |
| |
| /* |
| * Clearing the existing saved states helps prune dead data from |
| * the index folder. |
| */ |
| removeIndexes(true); |
| |
| Iterator i = fProjectDescriptions.values().iterator(); |
| while (i.hasNext()) { |
| ProjectDescription description = (ProjectDescription) i.next(); |
| description.saveReferences(); |
| } |
| |
| fProjectDescriptions.clear(); |
| |
| setState(CLEAN); |
| fProjectDescriptions = null; |
| fResourceChangeListener = null; |
| fClasspathChangeListener = null; |
| setIntialized(false); |
| } |
| } |
| |
| private boolean isInitialized() { |
| return initialized; |
| } |
| |
| private void setIntialized(boolean intialized) { |
| this.initialized = intialized; |
| } |
| } |