[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 "JAR:"+ has11TLD + jar path
+ * URLRecords as "URL:"+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);
}
}