[148649] ProjectDescription.indexClasspath() is expensive
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/ProjectDescription.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/ProjectDescription.java
index 5d9b103..7c3edbb 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/ProjectDescription.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/ProjectDescription.java
@@ -13,13 +13,18 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
@@ -27,6 +32,8 @@
 import java.util.Stack;
 
 import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
@@ -40,13 +47,19 @@
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jdt.core.IClasspathContainer;
 import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaElementDelta;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jst.jsp.core.internal.Logger;
 import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP11TLDNames;
 import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.JSP12TLDNames;
@@ -55,11 +68,14 @@
 import org.eclipse.wst.sse.core.internal.util.JarUtilities;
 import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
 import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalog;
+import org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEntry;
 import org.w3c.dom.Document;
 import org.w3c.dom.EntityReference;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import com.ibm.icu.util.StringTokenizer;
+
 class ProjectDescription {
 
 	class DeltaVisitor implements IResourceDeltaVisitor {
@@ -222,15 +238,26 @@
 		}
 	}
 
+	/**
+	 * A brief representation of the information in a TLD.
+	 */
 	static class TaglibInfo {
-		String description;
+		public TaglibInfo() {
+			super();
+		}
+
 		// extract only when asked?
+		String description;
 		float jspVersion;
 		String largeIcon;
 		String shortName;
 		String smallIcon;
 		String tlibVersion;
 		String uri;
+
+		public String toString() {
+			return "TaglibInfo|" + shortName + "|" + tlibVersion + "|" + smallIcon + "|" + largeIcon + "|" + jspVersion + "|" + uri + "|" + description;
+		}
 	}
 
 	class TaglibRecordEvent implements ITaglibRecordEvent {
@@ -254,16 +281,16 @@
 			String string = fTaglibRecord.toString();
 			switch (fType) {
 				case ITaglibRecordEvent.ADDED :
-					string += " ADDED (" + TaglibRecordEvent.class.getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+					string = " ADDED (" + string + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 					break;
 				case ITaglibRecordEvent.CHANGED :
-					string += " CHANGED (" + TaglibRecordEvent.class.getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+					string = " CHANGED (" + string + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 					break;
 				case ITaglibRecordEvent.REMOVED :
-					string += " REMOVED (" + TaglibRecordEvent.class.getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+					string = " REMOVED (" + string + ")"; //$NON-NLS-1$ //$NON-NLS-2$
 					break;
 				default :
-					string += " other:" + fType + " (" + TaglibRecordEvent.class.getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					string = " other:" + fType + " (" + string + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 					break;
 			}
 			return string;
@@ -406,6 +433,7 @@
 	private static final String WEB_INF = "WEB-INF"; //$NON-NLS-1$
 	private static final IPath WEB_INF_PATH = new Path(WEB_INF);
 	private static final String WEB_XML = "web.xml"; //$NON-NLS-1$
+	private static final String SAVE_FORMAT_VERSION = "1.0";
 
 	/*
 	 * Records active JARs on the classpath. Taglib descriptors should be
@@ -435,17 +463,22 @@
 	Hashtable fWebXMLReferences;
 
 	private long time0;
+	private String fSaveStateFilename;
 
-	ProjectDescription(IProject project) {
+	ProjectDescription(IProject project, String saveStateFile) {
 		super();
 		fProject = project;
-		fClasspathReferences = new Hashtable(0);
+		fSaveStateFilename = saveStateFile;
+
 		fClasspathJars = new Hashtable(0);
 		fJARReferences = new Hashtable(0);
 		fTagDirReferences = new Hashtable(0);
 		fTLDReferences = new Hashtable(0);
 		fWebXMLReferences = new Hashtable(0);
 		fImplicitReferences = new Hashtable(0);
+		fClasspathReferences = new Hashtable(0);
+
+		restoreReferences();
 	}
 
 	private Collection _getJSP11AndWebXMLJarReferences(Collection allJARs) {
@@ -460,7 +493,17 @@
 		return collection;
 	}
 
+	/**
+	 * Erases all known tables
+	 */
 	void clear() {
+		fClasspathJars = new Hashtable(0);
+		fJARReferences = new Hashtable(0);
+		fTagDirReferences = new Hashtable(0);
+		fTLDReferences = new Hashtable(0);
+		fWebXMLReferences = new Hashtable(0);
+		fImplicitReferences = new Hashtable(0);
+		fClasspathReferences = new Hashtable(0);
 	}
 
 	private ITaglibRecord createCatalogRecord(String urlString) {
@@ -496,7 +539,7 @@
 				}
 				if (jarRecord.has11TLD) {
 					if (_debugIndexCreation)
-						Logger.log(Logger.INFO_DEBUG, "created catalog record for " + urlString + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
+						Logger.log(Logger.INFO, "created catalog record for " + urlString + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
 					record = jarRecord;
 				}
 
@@ -609,8 +652,8 @@
 		try {
 			if (tld.getLocation() != null) {
 				contents = ((IFile) tld).getContents(true);
-				String baseLocation = tld.getLocation().toString();
-				TaglibInfo info = extractInfo(baseLocation, contents);
+				String basePath = tld.getFullPath().toString();
+				TaglibInfo info = extractInfo(basePath, contents);
 				if (info != null) {
 					record.info = info;
 				}
@@ -632,14 +675,14 @@
 		return record;
 	}
 
-	private TaglibInfo extractInfo(String baseLocation, InputStream tldContents) {
+	private TaglibInfo extractInfo(String basePath, InputStream tldContents) {
 		TaglibInfo info = new TaglibInfo();
 		if (tldContents != null) {
 			DocumentProvider provider = new DocumentProvider();
 			provider.setInputStream(tldContents);
 			provider.setValidating(false);
 			provider.setRootElementName(JSP12TLDNames.TAGLIB);
-			provider.setBaseReference(baseLocation);
+			provider.setBaseReference(basePath);
 			Node child = provider.getRootElement();
 			if (child == null || child.getNodeType() != Node.ELEMENT_NODE || !child.getNodeName().equals(JSP12TLDNames.TAGLIB)) {
 				return null;
@@ -688,6 +731,16 @@
 		records.addAll(_getJSP11AndWebXMLJarReferences(fJARReferences.values()));
 		records.addAll(fClasspathReferences.values());
 		records.addAll(implicitReferences);
+
+		ICatalog catalog = XMLCorePlugin.getDefault().getDefaultXMLCatalog();
+		if (catalog != null) {
+			ICatalogEntry[] entries = catalog.getCatalogEntries();
+			for (int i = 0; i < entries.length; i++) {
+				ITaglibRecord record = createCatalogRecord(entries[i].getURI());
+				records.add(record);
+			}
+		}
+
 		return records;
 	}
 
@@ -807,12 +860,53 @@
 		return fVisitor;
 	}
 
+	void handleElementChanged(IJavaElementDelta delta) {
+		// Logger.log(Logger.INFO_DEBUG, "IJavaElementDelta: " + delta);
+		IJavaElement element = delta.getElement();
+		if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT && ((IPackageFragmentRoot) element).isArchive()) {
+			time0 = System.currentTimeMillis();
+			String libPath = null;
+			int taglibRecordEventKind = -1;
+			if ((delta.getFlags() & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0) {
+				taglibRecordEventKind = ITaglibRecordEvent.ADDED;
+				IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(element.getPath());
+				if (file.exists())
+					libPath = file.getLocation().toString();
+				else
+					libPath = element.getPath().toString();
+			}
+			else if ((delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0) {
+				taglibRecordEventKind = ITaglibRecordEvent.REMOVED;
+				IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(element.getPath());
+				if (file.getLocation() != null)
+					libPath = file.getLocation().toString();
+				else
+					libPath = element.getPath().toString();
+			}
+			else if ((delta.getFlags() & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) > 0) {
+				taglibRecordEventKind = ITaglibRecordEvent.CHANGED;
+				IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(element.getPath());
+				if (file.exists())
+					libPath = file.getLocation().toString();
+				else
+					libPath = element.getPath().toString();
+			}
+			if (libPath != null) {
+				updateClasspathLibrary(libPath, taglibRecordEventKind);
+			}
+			if (_debugIndexTime)
+				Logger.log(Logger.INFO, "processed build path delta for " + fProject.getName() + "(" + element.getPath() + ") in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+	}
+
 	void index() {
 		time0 = System.currentTimeMillis();
+
 		fTLDReferences.clear();
 		fJARReferences.clear();
 		fTagDirReferences.clear();
 		fWebXMLReferences.clear();
+
 		try {
 			fProject.accept(new Indexer(), 0);
 		}
@@ -821,18 +915,20 @@
 		}
 
 		if (_debugIndexTime)
-			Logger.log(Logger.INFO_DEBUG, "indexed " + fProject.getName() + " contents in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			Logger.log(Logger.INFO, "indexed " + fProject.getName() + " contents in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 	}
 
 	void indexClasspath() {
 		time0 = System.currentTimeMillis();
 		fClasspathProjects = new Stack();
+
 		fClasspathReferences.clear();
 		fClasspathJars.clear();
+
 		IJavaProject javaProject = JavaCore.create(fProject);
 		indexClasspath(javaProject);
 		if (_debugIndexTime)
-			Logger.log(Logger.INFO_DEBUG, "indexed " + fProject.getName() + " classpath in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			Logger.log(Logger.INFO, "indexed " + fProject.getName() + " classpath in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 	}
 
 	/**
@@ -856,7 +952,7 @@
 					IPath libPath = entry.getPath();
 					if (!fClasspathJars.containsKey(libPath.toString())) {
 						if (libPath.toFile().exists()) {
-							updateClasspathLibrary(libPath.toString(), ITaglibRecordEvent.CHANGED);
+							updateClasspathLibrary(libPath.toString(), ITaglibRecordEvent.ADDED);
 						}
 						else {
 							/*
@@ -867,7 +963,7 @@
 							 */
 							IFile libFile = ResourcesPlugin.getWorkspace().getRoot().getFile(libPath);
 							if (libFile != null && libFile.exists()) {
-								updateClasspathLibrary(libFile.getLocation().toString(), ITaglibRecordEvent.CHANGED);
+								updateClasspathLibrary(libFile.getLocation().toString(), ITaglibRecordEvent.ADDED);
 							}
 						}
 					}
@@ -937,7 +1033,7 @@
 
 	void removeJAR(IResource jar) {
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "removing records for JAR " + jar.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "removing records for JAR " + jar.getFullPath()); //$NON-NLS-1$
 		JarRecord record = (JarRecord) fJARReferences.remove(jar.getFullPath().toString());
 		if (record != null) {
 			URLRecord[] records = (URLRecord[]) record.getURLRecords().toArray(new URLRecord[0]);
@@ -959,7 +1055,7 @@
 
 	void removeTLD(IResource tld) {
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "removing record for " + tld.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "removing record for " + tld.getFullPath()); //$NON-NLS-1$
 		TLDRecord record = (TLDRecord) fTLDReferences.remove(tld.getFullPath());
 		if (record != null) {
 			if (record.getURI() != null) {
@@ -971,13 +1067,13 @@
 
 	void removeWebXML(IResource webxml) {
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "removing records for " + webxml.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "removing records for " + webxml.getFullPath()); //$NON-NLS-1$
 		WebXMLRecord record = (WebXMLRecord) fWebXMLReferences.remove(webxml.getLocation().toString());
 		if (record != null) {
 			TLDRecord[] records = (TLDRecord[]) record.getTLDRecords().toArray(new TLDRecord[0]);
 			for (int i = 0; i < records.length; i++) {
 				if (_debugIndexCreation)
-					Logger.log(Logger.INFO_DEBUG, "removed record for " + records[i].getURI() + "@" + records[i].path); //$NON-NLS-1$ //$NON-NLS-2$
+					Logger.log(Logger.INFO, "removed record for " + records[i].getURI() + "@" + records[i].path); //$NON-NLS-1$ //$NON-NLS-2$
 				getImplicitReferences(webxml.getFullPath().toString()).remove(records[i].getURI());
 				TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(records[i], ITaglibRecordEvent.REMOVED));
 			}
@@ -1071,49 +1167,288 @@
 		return record;
 	}
 
-	void updateClasspathLibrary(String libraryLocation, int deltaKind) {
-		String[] entries = JarUtilities.getEntryNames(libraryLocation);
-		JarRecord libraryRecord = createJARRecord(libraryLocation);
-		fClasspathJars.put(libraryLocation, libraryRecord);
-		for (int i = 0; i < entries.length; i++) {
-			if (entries[i].equals(JarUtilities.JSP11_TAGLIB)) {
-				libraryRecord.has11TLD = true;
-			}
-			if (entries[i].endsWith(".tld")) { //$NON-NLS-1$
-				InputStream contents = JarUtilities.getInputStream(libraryLocation, entries[i]);
-				if (contents != null) {
-					TaglibInfo info = extractInfo(libraryLocation, contents);
+	/**
+	 * Restores any saved reference tables
+	 */
+	private void restoreReferences() {
+		if (TaglibIndex.ENABLED) {
+			index();
 
-					if (info != null && info.uri != null && info.uri.length() > 0) {
-						URLRecord record = new URLRecord();
-						record.info = info;
-						record.baseLocation = libraryLocation;
-						try {
-							record.url = new URL("jar:file:" + libraryLocation + "!/" + entries[i]); //$NON-NLS-1$ //$NON-NLS-2$
-							libraryRecord.urlRecords.add(record);
-							fClasspathReferences.put(record.getURI(), record);
-							if (_debugIndexCreation)
-								Logger.log(Logger.INFO_DEBUG, "created record for " + record.getURI() + "@" + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
-						}
-						catch (MalformedURLException e) {
-							// don't record this URI
-							Logger.logException(e);
+			// ================ test reload time ========================
+			boolean restored = false;
+			File savedState = new File(fSaveStateFilename);
+			if (savedState.exists()) {
+				ITextFileBufferManager textFileBufferManager = FileBuffers.getTextFileBufferManager();
+				Path savedStatePath = new Path(fSaveStateFilename);
+				try {
+					time0 = System.currentTimeMillis();
+					textFileBufferManager.connect(savedStatePath, new NullProgressMonitor());
+					ITextFileBuffer buffer = textFileBufferManager.getTextFileBuffer(savedStatePath);
+					IDocument doc = buffer.getDocument();
+					int lines = doc.getNumberOfLines();
+					if (lines > 0) {
+						IRegion line = doc.getLineInformation(0);
+						String lineText = doc.get(line.getOffset(), line.getLength());
+						JarRecord libraryRecord = null;
+						if (SAVE_FORMAT_VERSION.equals(lineText)) {
+							for (int i = 1; i < lines; i++) {
+								line = doc.getLineInformation(i);
+								lineText = doc.get(line.getOffset(), line.getLength());
+								StringTokenizer toker = new StringTokenizer(lineText, "|");
+								if (toker.hasMoreTokens()) {
+									String referenceType = toker.nextToken();
+									if ("JAR".equalsIgnoreCase(referenceType)) {
+										boolean has11TLD = Boolean.valueOf(toker.nextToken()).booleanValue();
+										// make the rest the libraryLocation
+										String libraryLocation = toker.nextToken();
+										while (toker.hasMoreTokens()) {
+											libraryLocation = libraryLocation + "|" + toker.nextToken();
+										}
+										libraryLocation = libraryLocation.trim();
+										if (libraryRecord != null) {
+											TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(libraryRecord, ITaglibRecordEvent.ADDED));
+										}
+										// Create a new JarRecord
+										libraryRecord = createJARRecord(libraryLocation);
+										libraryRecord.has11TLD = has11TLD;
+
+										// Add a URLRecord for the 1.1 TLD
+										if (has11TLD) {
+											InputStream contents = JarUtilities.getInputStream(libraryLocation, JarUtilities.JSP11_TAGLIB);
+											if (contents != null) {
+												TaglibInfo info = extractInfo(libraryLocation, contents);
+
+												if (info != null && info.uri != null && info.uri.length() > 0) {
+													URLRecord record = new URLRecord();
+													record.info = info;
+													record.baseLocation = libraryLocation;
+													try {
+														record.url = new URL("jar:file:" + libraryLocation + "!/" + JarUtilities.JSP11_TAGLIB); //$NON-NLS-1$ //$NON-NLS-2$
+														libraryRecord.urlRecords.add(record);
+														fClasspathReferences.put(record.getURI(), record);
+														if (_debugIndexCreation)
+															Logger.log(Logger.INFO, "created record for " + record.getURI() + "@" + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
+													}
+													catch (MalformedURLException e) {
+														// don't record this
+														// URI
+														Logger.logException(e);
+													}
+												}
+												try {
+													contents.close();
+												}
+												catch (IOException e) {
+												}
+											}
+										}
+
+										fClasspathJars.put(libraryLocation, libraryRecord);
+									}
+									else if ("URL".equalsIgnoreCase(referenceType)) {
+										// make the rest the URL
+										String urlString = toker.nextToken();
+										while (toker.hasMoreTokens()) {
+											urlString = urlString + "|" + toker.nextToken();
+										}
+										urlString = urlString.trim();
+										// Append a URLrecord
+										URLRecord urlRecord = new URLRecord();
+										urlRecord.url = new URL(urlString); //$NON-NLS-1$ //$NON-NLS-2$
+										urlRecord.baseLocation = libraryRecord.location.toString();
+										libraryRecord.urlRecords.add(urlRecord);
+
+										ByteArrayInputStream cachedContents = null;
+										InputStream tldStream = null;
+										try {
+											URLConnection connection = urlRecord.url.openConnection();
+											connection.setDefaultUseCaches(false);
+											tldStream = connection.getInputStream();
+										}
+										catch (IOException e1) {
+											Logger.logException(e1);
+										}
+
+										int c;
+										ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+										// array dim restriction?
+										byte bytes[] = new byte[2048];
+										try {
+											while ((c = tldStream.read(bytes)) >= 0) {
+												byteArrayOutput.write(bytes, 0, c);
+											}
+											cachedContents = new ByteArrayInputStream(byteArrayOutput.toByteArray());
+										}
+										catch (IOException ioe) {
+											// no cleanup can be done
+										}
+										finally {
+											try {
+												tldStream.close();
+											}
+											catch (IOException e) {
+											}
+										}
+
+										TaglibInfo info = extractInfo(urlRecord.url.toString(), cachedContents);
+										if (info != null) {
+											urlRecord.info = info;
+										}
+										try {
+											cachedContents.close();
+										}
+										catch (IOException e) {
+										}
+										fClasspathReferences.put(urlRecord.getURI(), urlRecord);
+									}
+								}
+							}
+							if (libraryRecord != null) {
+								TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(libraryRecord, ITaglibRecordEvent.ADDED));
+							}
 						}
 					}
+					restored = true;
+					if (_debugIndexTime)
+						Logger.log(Logger.INFO, "time spent reloading " + fProject.getName() + " build path: " + (System.currentTimeMillis() - time0));
+				}
+				catch (Exception e) {
+					restored = false;
+					if (_debugIndexTime)
+						Logger.log(Logger.INFO, "failure reloading " + fProject.getName() + " build path index", e);
+				}
+				finally {
 					try {
-						contents.close();
+						textFileBufferManager.disconnect(savedStatePath, new NullProgressMonitor());
 					}
-					catch (IOException e) {
+					catch (CoreException e) {
+						Logger.logException(e);
+					}
+				}
+			}
+
+			// ================ test reload time (end) ==================
+
+
+			if (!restored) {
+				indexClasspath();
+			}
+		}
+	}
+
+	/**
+	 * Saves any storable references to disk. This is only called when the
+	 * description is being cleared and not after every update.
+	 */
+	void saveReferences() {
+		time0 = System.currentTimeMillis();
+		Writer writer = null;
+
+		/**
+		 * <pre>
+		 *                                 1.0
+		 *                                 Save classpath information (! is field delimiter)
+		 *                                 Jars are saved as &quot;JAR:&quot;+ has11TLD + jar path 
+		 *                                 URLRecords as &quot;URL:&quot;+URL
+		 * </pre>
+		 */
+		try {
+			writer = new OutputStreamWriter(new FileOutputStream(fSaveStateFilename), "utf8");
+			writer.write(SAVE_FORMAT_VERSION);
+			writer.write('\n');
+
+			Enumeration jars = fClasspathJars.keys();
+			while (jars.hasMoreElements()) {
+				String jarPath = jars.nextElement().toString();
+				JarRecord jarRecord = (JarRecord) fClasspathJars.get(jarPath);
+				writer.write("JAR|");
+				writer.write(Boolean.toString(jarRecord.has11TLD));
+				writer.write('|');
+				writer.write(jarPath);
+				writer.write('\n');
+				Iterator i = jarRecord.urlRecords.iterator();
+				while (i.hasNext()) {
+					IURLRecord urlRecord = (IURLRecord) i.next();
+					writer.write("URL|");
+					writer.write(urlRecord.getURL().toExternalForm());
+					writer.write('\n');
+				}
+			}
+		}
+		catch (IOException e) {
+		}
+		finally {
+			try {
+				if (writer != null) {
+					writer.close();
+				}
+			}
+			catch (Exception e) {
+			}
+		}
+
+		if (_debugIndexTime)
+			Logger.log(Logger.INFO, "time spent saving index for " + fProject.getName() + ": " + (System.currentTimeMillis() - time0));
+	}
+
+	void updateClasspathLibrary(String libraryLocation, int deltaKind) {
+		JarRecord libraryRecord = null;
+		if (deltaKind == ITaglibRecordEvent.REMOVED || deltaKind == ITaglibRecordEvent.CHANGED) {
+			libraryRecord = (JarRecord) fClasspathJars.remove(libraryLocation);
+			if (libraryRecord != null) {
+				IURLRecord[] urlRecords = (IURLRecord[]) libraryRecord.urlRecords.toArray(new IURLRecord[0]);
+				for (int i = 0; i < urlRecords.length; i++) {
+					fClasspathReferences.remove(urlRecords[i].getURI());
+				}
+			}
+		}
+		if (deltaKind == ITaglibRecordEvent.ADDED || deltaKind == ITaglibRecordEvent.CHANGED) {
+			String[] entries = JarUtilities.getEntryNames(libraryLocation);
+
+			libraryRecord = createJARRecord(libraryLocation);
+			fClasspathJars.put(libraryLocation, libraryRecord);
+			for (int i = 0; i < entries.length; i++) {
+				if (entries[i].endsWith(".tld")) { //$NON-NLS-1$
+					if (entries[i].equals(JarUtilities.JSP11_TAGLIB)) {
+						libraryRecord.has11TLD = true;
+					}
+					InputStream contents = JarUtilities.getInputStream(libraryLocation, entries[i]);
+					if (contents != null) {
+						TaglibInfo info = extractInfo(libraryLocation, contents);
+
+						if (info != null && info.uri != null && info.uri.length() > 0) {
+							URLRecord record = new URLRecord();
+							record.info = info;
+							record.baseLocation = libraryLocation;
+							try {
+								record.url = new URL("jar:file:" + libraryLocation + "!/" + entries[i]); //$NON-NLS-1$ //$NON-NLS-2$
+								libraryRecord.urlRecords.add(record);
+								fClasspathReferences.put(record.getURI(), record);
+								if (_debugIndexCreation)
+									Logger.log(Logger.INFO, "created record for " + record.getURI() + "@" + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
+							}
+							catch (MalformedURLException e) {
+								// don't record this URI
+								Logger.logException(e);
+							}
+						}
+						try {
+							contents.close();
+						}
+						catch (IOException e) {
+						}
 					}
 				}
 			}
 		}
-		TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(libraryRecord, deltaKind));
+		if (libraryRecord != null) {
+			TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(libraryRecord, deltaKind));
+		}
 	}
 
 	void updateJAR(IResource jar, int deltaKind) {
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "creating records for JAR " + jar.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "creating records for JAR " + jar.getFullPath()); //$NON-NLS-1$
 
 		String jarLocationString = jar.getLocation().toString();
 		String[] entries = JarUtilities.getEntryNames(jar);
@@ -1138,7 +1473,7 @@
 							getImplicitReferences(jar.getFullPath().toString()).put(record.getURI(), record);
 							TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(record, ITaglibRecordEvent.ADDED));
 							if (_debugIndexCreation)
-								Logger.log(Logger.INFO_DEBUG, "created record for " + record.getURI() + "@" + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
+								Logger.log(Logger.INFO, "created record for " + record.getURI() + "@" + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
 						}
 						catch (MalformedURLException e) {
 							// don't record this URI
@@ -1152,7 +1487,7 @@
 					}
 				}
 				else {
-					Logger.log(Logger.ERROR, getClass().getName() + "could not read resource " + jar.getFullPath()); //$NON-NLS-1$
+					Logger.log(Logger.ERROR_DEBUG, getClass().getName() + "could not read resource " + jar.getFullPath()); //$NON-NLS-1$
 				}
 			}
 		}
@@ -1178,7 +1513,7 @@
 
 	void updateTLD(IResource tld, int deltaKind) {
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "creating record for " + tld.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "creating record for " + tld.getFullPath()); //$NON-NLS-1$
 		TLDRecord record = createTLDRecord(tld);
 		fTLDReferences.put(tld.getFullPath().toString(), record);
 		if (record.getURI() != null) {
@@ -1198,11 +1533,11 @@
 			provider.setInputStream(webxmlContents);
 			provider.setValidating(false);
 			provider.setRootElementName("web-app"); //$NON-NLS-1$
-			provider.setBaseReference(webxml.getParent().getLocation().toString());
+			provider.setBaseReference(webxml.getParent().getFullPath().toString());
 			document = provider.getDocument(false);
 		}
 		catch (CoreException e) {
-			Logger.logException(e);
+			Logger.log(Logger.ERROR_DEBUG, "", e); //$NON-NLS-1$
 		}
 		finally {
 			if (webxmlContents != null)
@@ -1216,7 +1551,7 @@
 		if (document == null)
 			return;
 		if (_debugIndexCreation)
-			Logger.log(Logger.INFO_DEBUG, "creating records for " + webxml.getFullPath()); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "creating records for " + webxml.getFullPath()); //$NON-NLS-1$
 
 		WebXMLRecord webxmlRecord = new WebXMLRecord();
 		webxmlRecord.path = webxml.getFullPath();
@@ -1231,7 +1566,7 @@
 				path = new Path(getLocalRoot(webxml.getFullPath().toString()) + taglibLocation);
 			}
 			else {
-				path = new Path(URIHelper.normalize(taglibLocation, webxml.getFullPath().toString(), getLocalRoot(webxml.getLocation().toString())));
+				path = new Path(URIHelper.normalize(taglibLocation, webxml.getFullPath().toString(), getLocalRoot(webxml.getFullPath().toString())));
 			}
 			if (path.segmentCount() > 1) {
 				IFile resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
@@ -1251,7 +1586,7 @@
 									jarRecord.has11TLD = true;
 									InputStream contents = JarUtilities.getInputStream(resource, entries[jEntry]);
 									if (contents != null) {
-										TaglibInfo info = extractInfo(resource.getLocation().toString(), contents);
+										TaglibInfo info = extractInfo(resource.getFullPath().toString(), contents);
 										jarRecord.info = info;
 									}
 									try {
@@ -1267,7 +1602,7 @@
 						jarRecord.info.uri = taglibUri;
 						jarRecord.isMappedInWebXML = true;
 						if (_debugIndexCreation)
-							Logger.log(Logger.INFO_DEBUG, "created web.xml record for " + taglibUri + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
+							Logger.log(Logger.INFO, "created web.xml record for " + taglibUri + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
 					}
 					else {
 						TLDRecord tldRecord = createTLDRecord(resource);
@@ -1275,7 +1610,7 @@
 						// the stored URI should reflect the web.xml's value
 						tldRecord.info.uri = taglibUri;
 						if (_debugIndexCreation)
-							Logger.log(Logger.INFO_DEBUG, "created web.xml record for " + taglibUri + "@" + tldRecord.getPath()); //$NON-NLS-1$ //$NON-NLS-2$
+							Logger.log(Logger.INFO, "created web.xml record for " + taglibUri + "@" + tldRecord.getPath()); //$NON-NLS-1$ //$NON-NLS-2$
 					}
 					webxmlRecord.tldRecords.add(record);
 					getImplicitReferences(webxml.getFullPath().toString()).put(taglibUri, record);
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/TaglibIndex.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/TaglibIndex.java
index ab486bd..3c5a5f3 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/TaglibIndex.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/taglib/TaglibIndex.java
@@ -11,12 +11,15 @@
  *******************************************************************************/
 package org.eclipse.jst.jsp.core.taglib;
 
+import java.io.File;
 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.Stack;
+import java.util.zip.CRC32;
 
 import org.eclipse.core.filebuffers.FileBuffers;
 import org.eclipse.core.resources.IFile;
@@ -39,6 +42,7 @@
 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.eclipse.wst.sse.core.utils.StringUtils;
@@ -49,10 +53,11 @@
  * ITaglibRegistry but lacking any ties to project natures. Each record
  * returned from the index represents a single tag library descriptor.
  * 
- * Indexing is not persisted between sessions, so new ADD events will be sent
- * to ITaglibIndexListeners during each workbench session. REMOVE events are
- * not fired on workbench shutdown. The record's contents should be examined
- * for any further information.
+ * 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
  */
@@ -79,17 +84,51 @@
 			if (frameworkIsShuttingDown())
 				return;
 
-			if (delta.getElement().getElementType() == IJavaElement.JAVA_MODEL) {
-				IJavaElementDelta[] changed = delta.getChangedChildren();
+			Logger.log(Logger.INFO_DEBUG, "IJavaElementDelta: " + delta);
+
+			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]);
 				}
 			}
-			else if (delta.getElement().getElementType() == IJavaElement.JAVA_PROJECT) {
+			// Handle any changes at the project level
+			else if (element.getElementType() == IJavaElement.JAVA_PROJECT) {
 				if ((delta.getFlags() & IJavaElementDelta.F_CLASSPATH_CHANGED) != 0) {
-					IJavaElement proj = delta.getElement();
+					IJavaElement proj = element;
 					handleClasspathChange((IJavaProject) proj);
 				}
+				else {
+					IJavaElementDelta[] deltas = delta.getAffectedChildren();
+					for (int i = 0; i < deltas.length; i++) {
+						elementChanged(deltas[i]);
+					}
+				}
+			}
+			/*
+			 * 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);
+						}
+					}
+				}
 			}
 		}
 
@@ -182,12 +221,12 @@
 								}
 								for (int i = 0; i < projects.length; i++) {
 									if (_debugIndexCreation) {
-										Logger.log(Logger.INFO_DEBUG, "TaglibIndex noticed " + projects[i].getName() + " is about to be deleted/closed"); //$NON-NLS-1$ //$NON-NLS-2$
+										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_DEBUG, "removing index of " + description.fProject.getName()); //$NON-NLS-1$
+											Logger.log(Logger.INFO, "removing index of " + description.fProject.getName()); //$NON-NLS-1$
 										}
 										description.clear();
 									}
@@ -243,12 +282,12 @@
 										}
 										if (!projects[i].isAccessible() || (deltas[i] != null && deltas[i].getKind() == IResourceDelta.REMOVED)) {
 											if (_debugIndexCreation) {
-												Logger.log(Logger.INFO_DEBUG, "TaglibIndex noticed " + projects[i].getName() + " was removed or is no longer accessible"); //$NON-NLS-1$ //$NON-NLS-2$
+												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_DEBUG, "removing index of " + description.fProject.getName()); //$NON-NLS-1$
+													Logger.log(Logger.INFO, "removing index of " + description.fProject.getName()); //$NON-NLS-1$
 												}
 												description.clear();
 											}
@@ -282,6 +321,10 @@
 
 	static TaglibIndex _instance;
 
+	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 = null;
 
@@ -303,7 +346,7 @@
 
 	static void fireTaglibRecordEvent(ITaglibRecordEvent event) {
 		if (_debugEvents) {
-			Logger.log(Logger.INFO_DEBUG, "TaglibIndex fired event:" + event); //$NON-NLS-1$
+			Logger.log(Logger.INFO, "TaglibIndex fired event:" + event); //$NON-NLS-1$
 		}
 		/*
 		 * Flush any shared cache entries, the TaglibControllers should handle
@@ -362,6 +405,14 @@
 		}
 	}
 
+	private String getState() {
+		String state = JSPCorePlugin.getDefault().getPluginPreferences().getString(TaglibIndex.class.getName());
+		if (state == null || state.length() == 0) {
+			state = DIRTY;
+		}
+		return state;
+	}
+
 	/**
 	 * NOT API.
 	 * 
@@ -407,18 +458,18 @@
 		}
 		if (_debugResolution) {
 			if (result == null) {
-				Logger.log(Logger.INFO_DEBUG, "TaglibIndex could not resolve \"" + reference + "\" from " + basePath); //$NON-NLS-1$ //$NON-NLS-2$
+				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_DEBUG, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getPath()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						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_DEBUG, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 					}
 						break;
 					case (ITaglibRecord.TAGDIR) : {
@@ -426,7 +477,7 @@
 						break;
 					case (ITaglibRecord.URL) : {
 						IURLRecord record = (IURLRecord) result;
-						Logger.log(Logger.INFO_DEBUG, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						Logger.log(Logger.INFO, "TaglibIndex resolved " + basePath + ":" + reference + " = " + record.getURL()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 					}
 						break;
 				}
@@ -436,6 +487,46 @@
 	}
 
 	/**
+	 * 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();
+			}
+		}
+	}
+
+	/**
 	 * Instructs the index to stop listening for resource and classpath
 	 * changes, and to forget all information about the workspace.
 	 */
@@ -467,14 +558,46 @@
 
 	private TaglibIndex() {
 		super();
+
+		/*
+		 * Only consider a crash if a value exists and is DIRTY (not a new
+		 * workspace)
+		 */
+		if (DIRTY.equalsIgnoreCase(getState())) {
+			Logger.log(Logger.ERROR_DEBUG, "workspace crash detected, not using saved taglib indexes");
+			removeIndexes(false);
+		}
+
+		LOCK = Platform.getJobManager().newLock();
+		fProjectDescriptions = new Hashtable();
 		fResourceChangeListener = new ResourceChangeListener();
 		fClasspathChangeListener = new ClasspathChangeListener();
 		if (ENABLED) {
 			ResourcesPlugin.getWorkspace().addResourceChangeListener(fResourceChangeListener, IResourceChangeEvent.POST_CHANGE);
 			JavaCore.addElementChangedListener(fClasspathChangeListener);
 		}
-		LOCK = Platform.getJobManager().newLock();
-		fProjectDescriptions = new Hashtable();
+	}
+
+	/**
+	 * 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_DEBUG, "-> 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;
 	}
 
 	/**
@@ -485,11 +608,11 @@
 		ProjectDescription description = null;
 		description = (ProjectDescription) fProjectDescriptions.get(project);
 		if (description == null) {
-			description = new ProjectDescription(project);
-			if (ENABLED) {
-				description.index();
-				description.indexClasspath();
+			// 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);
 		}
 		return description;
@@ -516,6 +639,10 @@
 		return description;
 	}
 
+	private IPath getTaglibIndexStateLocation() {
+		return JSPCorePlugin.getDefault().getStateLocation().append("taglibindex/");
+	}
+
 	private void internalAddTaglibIndexListener(ITaglibIndexListener listener) {
 		if (fTaglibIndexListeners == null) {
 			fTaglibIndexListeners = new ITaglibIndexListener[]{listener};
@@ -533,6 +660,22 @@
 			IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0));
 			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;
@@ -601,6 +744,7 @@
 			ProjectDescription description = createDescription(project);
 			resolved = description.resolve(basePath, reference);
 		}
+
 		return resolved;
 	}
 
@@ -608,9 +752,31 @@
 		return _instance != null && ENABLED;
 	}
 
+	private void setState(String state) {
+		if (!state.equals(getState())) {
+			JSPCorePlugin.getDefault().getPluginPreferences().setValue(TaglibIndex.class.getName(), state);
+			JSPCorePlugin.getDefault().savePluginPreferences();
+		}
+	}
+
 	private void stop() {
 		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);
 	}
 }