blob: 33491adf2ba2e0004cb74651c34fa834388f5f1f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
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.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
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.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;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
import org.eclipse.jst.jsp.core.internal.util.DocumentProvider;
import org.eclipse.jst.jsp.core.internal.util.FacetModuleCoreSupport;
import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
import org.eclipse.wst.sse.core.internal.util.JarUtilities;
import org.eclipse.wst.sse.core.utils.StringUtils;
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.eclipse.wst.xml.core.internal.catalog.provisional.INextCatalog;
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 {
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
if (resource.getType() == IResource.FILE) {
if (delta.getKind() == IResourceDelta.CHANGED && (delta.getFlags() == IResourceDelta.ENCODING || delta.getFlags() == IResourceDelta.MARKERS))
return true;
if (resource.getName().endsWith(".tld")) { //$NON-NLS-1$
if (delta.getKind() == IResourceDelta.REMOVED) {
removeTLD(resource);
}
else {
updateTLD(resource, delta.getKind());
}
}
else if (resource.getName().endsWith(".jar")) { //$NON-NLS-1$
if (delta.getKind() == IResourceDelta.REMOVED) {
removeJAR(resource);
}
else {
updateJAR(resource, delta.getKind());
}
}
else if ("tag".equalsIgnoreCase(resource.getFileExtension()) || "tagx".equalsIgnoreCase(resource.getFileExtension())) { //$NON-NLS-1$ //$NON-NLS-2$
if (delta.getKind() == IResourceDelta.REMOVED) {
removeTag(resource);
}
else {
updateTag(resource, delta.getKind());
}
}
else if (resource.getName().equals(WEB_XML) && resource.getParent().getName().equals(WEB_INF)) {
if (delta.getKind() == IResourceDelta.REMOVED) {
removeWebXML(resource);
}
else {
updateWebXML(resource, delta.getKind());
}
}
}
return resource.getName().length() != 0 && resource.getName().charAt(0) != '.';
}
}
class Indexer implements IResourceProxyVisitor {
public boolean visit(IResourceProxy proxy) throws CoreException {
boolean visitMembers = true;
if (proxy.getType() == IResource.FILE) {
if (proxy.getName().endsWith(".tld")) { //$NON-NLS-1$
updateTLD(proxy.requestResource(), ITaglibIndexDelta.ADDED);
}
else if (proxy.getName().endsWith(".jar")) { //$NON-NLS-1$
updateJAR(proxy.requestResource(), ITaglibIndexDelta.ADDED);
}
else if (proxy.getName().endsWith(".tag") || proxy.getName().endsWith(".tagx")) { //$NON-NLS-1$ //$NON-NLS-2$
updateTagDir(proxy.requestResource().getParent(), ITaglibIndexDelta.ADDED);
// any folder with these files will create a record for
// that folder in one pass
visitMembers = false;
}
else if (proxy.getName().equals(WEB_XML) && proxy.requestResource().getParent().getName().equals(WEB_INF)) {
updateWebXML(proxy.requestResource(), ITaglibIndexDelta.ADDED);
}
}
String name = proxy.getName();
return name.length() != 0 && name.charAt(0) != '.' && visitMembers;
}
}
static class JarRecord implements IJarRecord {
boolean has11TLD;
TaglibInfo info;
boolean isExported = true;
boolean isMappedInWebXML;
IPath location;
List urlRecords;
public boolean equals(Object obj) {
if (!(obj instanceof JarRecord))
return false;
return ((JarRecord) obj).location.equals(location) && ((JarRecord) obj).has11TLD == has11TLD && ((JarRecord) obj).info.equals(info);
}
public ITaglibDescriptor getDescriptor() {
return info != null ? info : new TaglibInfo();
}
/**
* @return Returns the location.
*/
public IPath getLocation() {
return location;
}
public int getRecordType() {
return ITaglibRecord.JAR;
}
/**
* @return Returns the recommended/default prefix if one was given.
*/
public String getShortName() {
if (info == null)
return null;
return info.shortName;
}
/**
* @return Returns the uri.
*/
public String getURI() {
if (info == null)
return null;
return info.uri;
}
public List getURLRecords() {
return urlRecords;
}
public String toString() {
StringBuffer s = new StringBuffer("JarRecord: ");//$NON-NLS-1$
s.append(location);
if (urlRecords.size() > 0) {
s.append('\n');//$NON-NLS-1$
for (int i = 0; i < urlRecords.size(); i++) {
s.append(urlRecords.get(i));
s.append('\n');//$NON-NLS-1$
}
}
return s.toString();
}
}
static class TagDirRecord implements ITagDirRecord {
TaglibInfo info;
IPath path;
// a List holding Strings of .tag and .tagx filenames relative to the
// tagdir's location
List tags = new ArrayList(0);
public boolean equals(Object obj) {
if (!(obj instanceof TagDirRecord))
return false;
return ((TagDirRecord) obj).path.equals(path) && ((TagDirRecord) obj).info.equals(info);
}
public ITaglibDescriptor getDescriptor() {
return info != null ? info : (info = new TaglibInfo());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.jsp.core.taglib.ITagDirRecord#getPath()
*/
public IPath getPath() {
return path;
}
public int getRecordType() {
return ITaglibRecord.TAGDIR;
}
/**
* @return Returns the tags.
*/
public String[] getTagFilenames() {
return (String[]) tags.toArray(new String[tags.size()]);
}
public String toString() {
return "TagdirRecord: " + path + " <-> " + info.shortName; //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* A brief representation of the information in a TLD.
*/
static class TaglibInfo implements ITaglibDescriptor {
// extract only when asked?
String description = "";
String displayName = "";
String jspVersion = "";
String largeIcon = "";
String shortName = "";
String smallIcon = "";
String tlibVersion = "";
String uri = "";
public TaglibInfo() {
super();
}
public boolean equals(Object obj) {
if (!(obj instanceof TaglibInfo))
return false;
return ((TaglibInfo) obj).jspVersion == jspVersion && ((TaglibInfo) obj).description.equals(description) && ((TaglibInfo) obj).largeIcon.equals(largeIcon) && ((TaglibInfo) obj).shortName.equals(shortName) && ((TaglibInfo) obj).smallIcon.equals(smallIcon) && ((TaglibInfo) obj).tlibVersion.equals(tlibVersion) && ((TaglibInfo) obj).uri.equals(uri);
}
public String getDescription() {
return description;
}
public String getDisplayName() {
return displayName;
}
public String getJSPVersion() {
return jspVersion;
}
public String getLargeIcon() {
return largeIcon;
}
public String getShortName() {
return shortName;
}
public String getSmallIcon() {
return smallIcon;
}
public String getTlibVersion() {
return tlibVersion;
}
public String getURI() {
return uri;
}
public String toString() {
return "TaglibInfo|" + shortName + "|" + tlibVersion + "|" + smallIcon + "|" + largeIcon + "|" + jspVersion + "|" + uri + "|" + description; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
}
}
static class TLDRecord implements ITLDRecord {
TaglibInfo info;
IPath path;
public boolean equals(Object obj) {
if (!(obj instanceof TLDRecord))
return false;
return ((TLDRecord) obj).path.equals(path) && ((TLDRecord) obj).getURI().equals(getURI()) && ((TLDRecord) obj).info.equals(info);
}
public ITaglibDescriptor getDescriptor() {
return info != null ? info : new TaglibInfo();
}
public IPath getPath() {
return path;
}
public int getRecordType() {
return ITaglibRecord.TLD;
}
public String getShortName() {
if (info == null)
return null;
return info.shortName;
}
/**
* @return Returns the uri.
*/
public String getURI() {
if (info == null)
return null;
return info.uri;
}
public String toString() {
return "TLDRecord: " + getURI() + " <-> " + path; //$NON-NLS-1$ //$NON-NLS-2$
}
}
static class URLRecord implements IURLRecord {
String baseLocation;
TaglibInfo info;
boolean isExported = true;
URL url;
public URLRecord() {
super();
}
public boolean equals(Object obj) {
if (!(obj instanceof URLRecord))
return false;
return ((URLRecord) obj).baseLocation.equals(baseLocation) && ((URLRecord) obj).url.equals(url) && ((URLRecord) obj).info.equals(info);
}
public String getBaseLocation() {
return baseLocation;
}
public ITaglibDescriptor getDescriptor() {
return info != null ? info : new TaglibInfo();
}
public int getRecordType() {
return ITaglibRecord.URL;
}
/**
* @return Returns the recommended/default prefix if one was given.
*/
public String getShortName() {
if (info == null)
return null;
return info.shortName;
}
/**
* @return Returns the uri.
*/
public String getURI() {
if (info == null)
return ""; //$NON-NLS-1$
return info.uri;
}
/**
* @return Returns the URL.
*/
public URL getURL() {
return url;
}
public String toString() {
return "URLRecord: (exported=" + isExported + ") " + baseLocation + " <-> " + getURI(); //$NON-NLS-1$ //$NON-NLS-2$
}
}
static class WebXMLRecord {
TaglibInfo info;
IPath path;
List tldRecords = new ArrayList(0);
public boolean equals(Object obj) {
if (!(obj instanceof WebXMLRecord))
return false;
return ((WebXMLRecord) obj).path.equals(path) && ((WebXMLRecord) obj).info.equals(info);
}
/**
* @return Returns the recommended/default prefix if one was given.
*/
public String getPrefix() {
if (info == null)
return null;
return info.shortName;
}
/**
*
*/
public List getTLDRecords() {
return tldRecords;
}
/**
* @return Returns the webxml.
*/
public IPath getWebXML() {
return path;
}
public String toString() {
StringBuffer s = new StringBuffer("WebXMLRecord: ");//$NON-NLS-1$
s.append(path);
if (tldRecords.size() > 0) {
s.append('\n');//$NON-NLS-1$
for (int i = 0; i < tldRecords.size(); i++) {
s.append(tldRecords.get(i));
s.append('\n');//$NON-NLS-1$
}
}
return s.toString();
}
}
static boolean _debugIndexCreation = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jst.jsp.core/taglib/indexcreation")); //$NON-NLS-1$ //$NON-NLS-2$
static boolean _debugIndexTime = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jst.jsp.core/taglib/indextime")); //$NON-NLS-1$ //$NON-NLS-2$
private static final String BUILDPATH_DIRTY = "BUILDPATH_DIRTY"; //$NON-NLS-1$
private static final String BUILDPATH_ENTRIES = "BUILDPATH_ENTRIES"; //$NON-NLS-1$
private static final String BUILDPATH_PROJECT = "BUILDPATH_PROJECT"; //$NON-NLS-1$
private static final String SAVE_FORMAT_VERSION = "Tag Library Index 1.0.3"; //$NON-NLS-1$
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$
/**
* Notes that the build path information is stale. Some operations can now
* be skipped until a resolve/getAvailable call is made.
*/
boolean fBuildPathIsDirty = false;
/**
* Count of entries on the build path. Primary use case is for classpath
* containers that add an entry. Without notification (3.3), we can only
* check after-the-fact.
*/
int fBuildPathEntryCount = 0;
/**
* A cached copy of all of the records createable from the XMLCatalog.
*/
private Collection fCatalogRecords;
/*
* Records active JARs on the classpath. Taglib descriptors should be
* usable, but the jars by themselves are not.
*/
Hashtable fClasspathJars;
/**
* A set of the projects that are in this project's build path.
* Lookups/enumerations will be redirected to the corresponding
* ProjectDescription instances
*/
Set fClasspathProjects = null;
// holds references by URI to JARs
Hashtable fClasspathReferences;
/*
* this table is special in that it holds tables of references according
* to local roots
*/
Hashtable fImplicitReferences;
Hashtable fJARReferences;
IProject fProject;
private String fSaveStateFilename;
Hashtable fTagDirReferences;
Hashtable fTLDReferences;
IResourceDeltaVisitor fVisitor;
Hashtable fWebXMLReferences;
ILock LOCK = Job.getJobManager().newLock();
private long time0;
ProjectDescription(IProject project, String saveStateFile) {
super();
fProject = project;
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);
fClasspathProjects = new HashSet();
restoreReferences();
}
private Collection _getJSP11AndWebXMLJarReferences(Collection allJARs) {
List collection = new ArrayList(allJARs.size());
Iterator i = allJARs.iterator();
while (i.hasNext()) {
JarRecord record = (JarRecord) i.next();
if (record.has11TLD || record.isMappedInWebXML) {
collection.add(record);
}
}
return collection;
}
/**
* Adds the list of known references from this project's build path to the
* map, appending any processed projects into the list to avoid
* build-path-cycles.
*
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=142408
*
* @param references -
* the map of references to ITaglibRecords
* @param projectsProcessed -
* the list of projects already considered
* @param exportedOnly -
* Whether to only add references derived from exported build
* path containers. This method calls itself recursively with
* this parameter as false.
*/
void addBuildPathReferences(Map references, List projectsProcessed, boolean exportedOnly) {
ensureUpTodate();
// Add the build path references that are exported from this project
Enumeration keys = fClasspathReferences.keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
URLRecord urlRecord = (URLRecord) fClasspathReferences.get(key);
if (exportedOnly) {
if (urlRecord.isExported) {
references.put(key, urlRecord);
}
}
else {
references.put(key, urlRecord);
}
}
IProject[] buildpathProjects = (IProject[]) fClasspathProjects.toArray(new IProject[fClasspathProjects.size()]);
for (int i = 0; i < buildpathProjects.length; i++) {
if (!projectsProcessed.contains(buildpathProjects[i]) && buildpathProjects[i].isAccessible()) {
projectsProcessed.add(buildpathProjects[i]);
ProjectDescription description = TaglibIndex.getInstance().createDescription(buildpathProjects[i]);
description.addBuildPathReferences(references, projectsProcessed, true);
/*
* 199843 (183756) - JSP Validation Cannot Find Tag Library
* Descriptor in Referenced Projects
*
* Add any TLD records having URI values from projects on the
* build path
*/
Map[] rootReferences = (Map[]) description.fImplicitReferences.values().toArray(new Map[description.fImplicitReferences.size()]);
for (int j = 0; j < rootReferences.length; j++) {
Iterator implicitRecords = rootReferences[j].values().iterator();
while (implicitRecords.hasNext()) {
ITaglibRecord record = (ITaglibRecord) implicitRecords.next();
if (record.getRecordType() == ITaglibRecord.TLD && ((ITLDRecord) record).getURI() != null) {
references.put(((ITLDRecord) record).getURI(), record);
}
}
}
}
}
}
/**
* 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 void closeJarFile(ZipFile file) {
if (file == null)
return;
try {
file.close();
}
catch (IOException ioe) {
// no cleanup can be done
Logger.logException("TaglibIndex: Could not close zip file " + file.getName(), ioe); //$NON-NLS-1$
}
}
/**
* @param catalogEntry
* a XML catalog entry pointing to a .jar or .tld file
* @return a ITaglibRecord describing a TLD contributed to the XMLCatalog
* if one was found at the given location, null otherwise
*/
private ITaglibRecord createCatalogRecord(ICatalogEntry catalogEntry) {
return createCatalogRecord(catalogEntry.getKey(), catalogEntry.getURI());
}
/**
* @param uri -
* the key value that will become the returned record's "URI"
* @param urlString -
* the string indicating where the TLD really is
* @return a ITaglibRecord describing a TLD contributed to the XMLCatalog
* if one was found at the given location, null otherwise
*/
private ITaglibRecord createCatalogRecord(String uri, String urlString) {
ITaglibRecord record = null;
// handle "file:" URLs that point to a .jar file on disk (1.1 mode)
if (urlString.toLowerCase(Locale.US).endsWith((".jar")) && urlString.startsWith("file:")) { //$NON-NLS-1$ //$NON-NLS-2$
String fileLocation = null;
try {
URL url = new URL(urlString);
fileLocation = url.getFile();
}
catch (MalformedURLException e) {
// not worth reporting
}
if (fileLocation != null) {
JarRecord jarRecord = createJARRecord(fileLocation);
String[] entries = JarUtilities.getEntryNames(fileLocation);
for (int jEntry = 0; jEntry < entries.length; jEntry++) {
if (entries[jEntry].endsWith(".tld")) { //$NON-NLS-1$
if (entries[jEntry].equals(JarUtilities.JSP11_TAGLIB)) {
jarRecord.has11TLD = true;
InputStream contents = JarUtilities.getInputStream(fileLocation, entries[jEntry]);
if (contents != null) {
TaglibInfo info = extractInfo(fileLocation, contents);
/*
* the record's reported URI should match the
* catalog entry's "key" so replace the
* detected value
*/
info.uri = uri;
jarRecord.info = info;
}
try {
contents.close();
}
catch (IOException e) {
}
}
}
}
if (jarRecord.has11TLD) {
if (_debugIndexCreation)
Logger.log(Logger.INFO, "created catalog record for " + urlString + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
record = jarRecord;
}
}
}
// The rest are URLs into a plug-in...somewhere
else {
URL url = null;
ByteArrayInputStream cachedContents = null;
InputStream tldStream = null;
try {
url = new URL(urlString);
URLConnection connection = url.openConnection();
connection.setDefaultUseCaches(false);
tldStream = connection.getInputStream();
}
catch (Exception e1) {
Logger.logException("Exception reading TLD contributed to the XML Catalog", e1);
}
if (tldStream != null) {
int c;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
// array dim restriction?
byte bytes[] = new byte[2048];
try {
while ((c = tldStream.read(bytes)) >= 0) {
buffer.write(bytes, 0, c);
}
cachedContents = new ByteArrayInputStream(buffer.toByteArray());
}
catch (IOException ioe) {
// no cleanup can be done
}
finally {
try {
tldStream.close();
}
catch (IOException e) {
}
}
URLRecord urlRecord = null;
TaglibInfo info = extractInfo(urlString, cachedContents);
if (info != null) {
/*
* the record's reported URI should match the catalog
* entry's "key" so replace the detected value
*/
info.uri = uri;
urlRecord = new URLRecord();
urlRecord.info = info;
urlRecord.baseLocation = urlString;
urlRecord.url = url; //$NON-NLS-1$ //$NON-NLS-2$
}
try {
cachedContents.close();
}
catch (IOException e) {
}
record = urlRecord;
}
}
return record;
}
/**
* @param resource
* @return
*/
private JarRecord createJARRecord(IResource jar) {
IPath location = jar.getLocation();
JarRecord jarRecord = null;
if (location != null) {
jarRecord = createJARRecord(location.toString());
}
else if (jar.getLocationURI() != null) {
jarRecord = createJARRecord(jar.getLocationURI().toString());
}
return jarRecord;
}
private JarRecord createJARRecord(String fileLocation) {
JarRecord record = new JarRecord();
record.info = new TaglibInfo();
record.location = new Path(fileLocation);
record.urlRecords = new ArrayList(0);
return record;
}
/**
* @return
*/
private TagDirRecord createTagdirRecord(IFolder tagdir) {
IPath tagdirPath = tagdir.getFullPath();
TagDirRecord record = new TagDirRecord();
record.path = tagdir.getFullPath();
record.info = new TaglibInfo();
// 8.4.3
if (tagdir.getName().equals("tags")) //$NON-NLS-1$
record.info.shortName = "tags"; //$NON-NLS-1$
else {
boolean determined = false;
IPath path = tagdirPath;
String[] segments = path.segments();
for (int i = 1; i < segments.length; i++) {
if (segments[i - 1].equals("WEB-INF") && segments[i].equals("tags")) { //$NON-NLS-1$ //$NON-NLS-2$
IPath tagdirLocalPath = path.removeFirstSegments(i + 1);
record.info.shortName = StringUtils.replace(tagdirLocalPath.toString(), "/", "-");
determined = true;
}
}
if (!determined) {
record.info.shortName = StringUtils.replace(tagdirPath.toString(), "/", "-");
}
}
// 8.4.3
record.info.tlibVersion = "1.0";
record.info.description = "";
record.info.displayName = "";
record.info.smallIcon = "";
record.info.largeIcon = "";
try {
IResource[] tagfiles = tagdir.members();
for (int i = 0; i < tagfiles.length; i++) {
if (tagfiles[i].getType() != IResource.FILE)
continue;
String extension = tagfiles[i].getFileExtension();
if (extension != null && (extension.equals("tag") || extension.equals("tagx"))) {
record.tags.add(tagfiles[i].getName());
}
}
}
catch (CoreException e) {
Logger.logException(e);
}
return record;
}
/**
* @param resource
* @return
*/
private TLDRecord createTLDRecord(IResource tld) {
TLDRecord record = new TLDRecord();
record.path = tld.getFullPath();
InputStream contents = null;
try {
if (tld.isAccessible()) {
contents = ((IFile) tld).getContents(true);
String basePath = tld.getFullPath().toString();
TaglibInfo info = extractInfo(basePath, contents);
if (info != null) {
record.info = info;
}
}
}
catch (CoreException e) {
Logger.logException(e);
}
finally {
try {
if (contents != null) {
contents.close();
}
}
catch (IOException e) {
// ignore
}
}
return record;
}
private void ensureUpTodate() {
LOCK.acquire();
if (!fBuildPathIsDirty) {
/*
* Double-check that the number of build path entries has not
* changed. This should cover most cases such as when a library is
* added into or removed from a container.
*/
try {
IJavaProject jproject = JavaCore.create(fProject);
if (jproject != null && jproject.exists()) {
IClasspathEntry[] entries = jproject.getResolvedClasspath(true);
fBuildPathIsDirty = (fBuildPathEntryCount != entries.length);
}
}
catch (JavaModelException e) {
Logger.logException(e);
}
}
if (fBuildPathIsDirty) {
indexClasspath();
fBuildPathIsDirty = false;
}
LOCK.release();
}
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(basePath);
Node child = provider.getRootElement();
if (child == null || child.getNodeType() != Node.ELEMENT_NODE || !child.getNodeName().equals(JSP12TLDNames.TAGLIB)) {
return null;
}
child = child.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
if (child.getNodeName().equals(JSP12TLDNames.URI)) {
info.uri = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.SHORT_NAME) || child.getNodeName().equals(JSP11TLDNames.SHORTNAME)) {
info.shortName = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.DESCRIPTION) || child.getNodeName().equals(JSP11TLDNames.INFO)) {
info.description = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.DISPLAY_NAME)) {
info.displayName = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.JSP_VERSION) || child.getNodeName().equals(JSP11TLDNames.JSPVERSION)) {
info.jspVersion = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.TLIB_VERSION) || child.getNodeName().equals(JSP11TLDNames.TLIBVERSION)) {
info.tlibVersion = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.SMALL_ICON)) {
info.smallIcon = getTextContents(child);
}
else if (child.getNodeName().equals(JSP12TLDNames.LARGE_ICON)) {
info.largeIcon = getTextContents(child);
}
}
child = child.getNextSibling();
}
}
return info;
}
List getAvailableTaglibRecords(IPath path) {
ensureUpTodate();
float jspVersion = DeploymentDescriptorPropertyCache.getInstance().getJSPVersion(path);
LOCK.acquire();
Collection implicitReferences = new HashSet(getImplicitReferences(path.toString()).values());
Collection records = new ArrayList(fTLDReferences.size() + fTagDirReferences.size() + fJARReferences.size() + fWebXMLReferences.size());
records.addAll(fTLDReferences.values());
if (jspVersion >= 1.1) {
records.addAll(_getJSP11AndWebXMLJarReferences(fJARReferences.values()));
}
if (jspVersion >= 1.2) {
records.addAll(implicitReferences);
Map buildPathReferences = new HashMap();
List projectsProcessed = new ArrayList(fClasspathProjects.size() + 1);
projectsProcessed.add(fProject);
addBuildPathReferences(buildPathReferences, projectsProcessed, false);
records.addAll(buildPathReferences.values());
}
if (jspVersion >= 2.0) {
records.addAll(fTagDirReferences.values());
}
records.addAll(getCatalogRecords());
LOCK.release();
return new ArrayList(records);
}
/**
* Provides a stream to a local copy of the input or null if not possible
*/
private InputStream getCachedInputStream(ZipFile zipFile, ZipEntry zipEntry) {
InputStream cache = null;
if (zipFile != null) {
if (zipEntry != null) {
InputStream entryInputStream = null;
try {
entryInputStream = zipFile.getInputStream(zipEntry);
}
catch (IOException ioExc) {
Logger.logException("Taglib Index: " + zipFile.getName(), ioExc); //$NON-NLS-1$
}
if (entryInputStream != null) {
int c;
ByteArrayOutputStream buffer = null;
if (zipEntry.getSize() > 0) {
buffer = new ByteArrayOutputStream((int) zipEntry.getSize());
}
else {
buffer = new ByteArrayOutputStream();
}
// array dim restriction?
byte bytes[] = new byte[2048];
try {
while ((c = entryInputStream.read(bytes)) >= 0) {
buffer.write(bytes, 0, c);
}
cache = new ByteArrayInputStream(buffer.toByteArray());
}
catch (IOException ioe) {
// no cleanup can be done
}
finally {
try {
entryInputStream.close();
}
catch (IOException e) {
}
}
}
}
}
return cache;
}
private Collection getCatalogRecords() {
if (fCatalogRecords == null) {
List records = new ArrayList();
ICatalog defaultCatalog = XMLCorePlugin.getDefault().getDefaultXMLCatalog();
if (defaultCatalog != null) {
// Process default catalog
ICatalogEntry[] entries = defaultCatalog.getCatalogEntries();
for (int entry = 0; entry < entries.length; entry++) {
ITaglibRecord record = createCatalogRecord(entries[entry]);
records.add(record);
}
// Process declared OASIS nextCatalogs catalog
INextCatalog[] nextCatalogs = defaultCatalog.getNextCatalogs();
for (int nextCatalog = 0; nextCatalog < nextCatalogs.length; nextCatalog++) {
ICatalog catalog = nextCatalogs[nextCatalog].getReferencedCatalog();
ICatalogEntry[] entries2 = catalog.getCatalogEntries();
for (int entry = 0; entry < entries2.length; entry++) {
String uri = entries2[entry].getURI();
if (uri != null) {
uri = uri.toLowerCase(Locale.US);
if (uri.endsWith((".jar")) || uri.endsWith((".tld"))) {
ITaglibRecord record = createCatalogRecord(entries2[entry]);
if (record != null) {
records.add(record);
}
}
}
}
}
}
fCatalogRecords = records;
}
return fCatalogRecords;
}
/**
* @return Returns the implicitReferences for the given path
*/
Hashtable getImplicitReferences(String path) {
String localRoot = getLocalRoot(path);
Hashtable implicitReferences = (Hashtable) fImplicitReferences.get(localRoot);
if (implicitReferences == null) {
implicitReferences = new Hashtable(1);
fImplicitReferences.put(localRoot, implicitReferences);
}
return implicitReferences;
}
/**
* @param basePath
* @return the applicable Web context root path, if one exists
* @deprecated
*/
IPath getLocalRoot(IPath basePath) {
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
// existing workspace resources - this is the 93% case
IResource file = FileBuffers.getWorkspaceFileAtLocation(basePath);
// Try the base path as a folder first
if (file == null && basePath.segmentCount() > 1) {
file = workspaceRoot.getFolder(basePath);
}
// If not a folder, then try base path as a file
if (file != null && !file.exists() && basePath.segmentCount() > 1) {
file = workspaceRoot.getFile(basePath);
}
if (file == null && basePath.segmentCount() == 1) {
file = workspaceRoot.getProject(basePath.segment(0));
}
if (file == 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(basePath);
if (files.length > 0)
file = files[0];
}
while (file != null) {
/**
* Treat any parent folder with a WEB-INF subfolder as a web-app
* root
*/
IContainer folder = null;
if ((file.getType() & IResource.FOLDER) != 0) {
folder = (IContainer) file;
}
else {
folder = file.getParent();
}
// getFolder on a workspace root must use a full path, skip
if (folder != null && (folder.getType() & IResource.ROOT) == 0) {
IFolder webinf = folder.getFolder(WEB_INF_PATH);
if (webinf != null && webinf.exists()) {
return folder.getFullPath();
}
}
file = file.getParent();
}
return fProject.getFullPath();
}
/**
* @param basePath
* @return
*/
private String getLocalRoot(String basePath) {
return getLocalRoot(new Path(basePath)).toString();
}
private String getTextContents(Node parent) {
NodeList children = parent.getChildNodes();
if (children.getLength() == 1) {
return children.item(0).getNodeValue().trim();
}
StringBuffer s = new StringBuffer();
Node child = parent.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
String reference = ((EntityReference) child).getNodeValue();
if (reference == null && child.getNodeName() != null) {
reference = "&" + child.getNodeName() + ";"; //$NON-NLS-1$ //$NON-NLS-2$
}
if (reference != null) {
s.append(reference.trim());
}
}
else {
s.append(child.getNodeValue().trim());
}
child = child.getNextSibling();
}
return s.toString().trim();
}
/**
* @return Returns the visitor.
*/
IResourceDeltaVisitor getVisitor() {
if (fVisitor == null) {
fVisitor = new DeltaVisitor();
}
return fVisitor;
}
void handleElementChanged(IJavaElementDelta delta) {
if (fBuildPathIsDirty) {
return;
}
// Logger.log(Logger.INFO_DEBUG, "IJavaElementDelta: " + delta);
IJavaElement element = delta.getElement();
if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT && ((IPackageFragmentRoot) element).isArchive()) {
time0 = System.currentTimeMillis();
if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT && ((IPackageFragmentRoot) element).isExternal()) {
}
String libLocation = null;
int taglibRecordEventKind = -1;
if ((delta.getFlags() & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0 || (delta.getFlags() & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) > 0 || (delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0) {
taglibRecordEventKind = ITaglibIndexDelta.ADDED;
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(element.getPath());
if (file.isAccessible() && file.getLocation() != null)
libLocation = file.getLocation().toString();
else
libLocation = element.getPath().toString();
}
if (libLocation != null) {
boolean fragmentisExported = true;
try {
IClasspathEntry rawClasspathEntry = ((IPackageFragmentRoot) element).getRawClasspathEntry();
/*
* null may also be returned for deletions depending on
* resource/build path notification order. If it's null,
* it's been deleted and whether it's exported won't
* really matter
*/
if (rawClasspathEntry != null) {
fragmentisExported = rawClasspathEntry.isExported();
}
}
catch (JavaModelException e) {
Logger.logException("Problem handling build path entry for " + element.getPath(), e); //$NON-NLS-1$
}
if ((delta.getFlags() & IJavaElementDelta.F_ADDED_TO_CLASSPATH) > 0) {
fBuildPathEntryCount++;
}
else if ((delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) > 0) {
fBuildPathEntryCount--;
}
updateClasspathLibrary(libLocation, taglibRecordEventKind, fragmentisExported);
}
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);
}
catch (CoreException e) {
Logger.logException(e);
}
if (_debugIndexTime)
Logger.log(Logger.INFO, "indexed " + fProject.getName() + " contents in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
void indexClasspath() {
if (_debugIndexTime)
time0 = System.currentTimeMillis();
fClasspathProjects.clear();
fClasspathReferences.clear();
fClasspathJars.clear();
fBuildPathEntryCount = 0;
IJavaProject javaProject = JavaCore.create(fProject);
/*
* If the Java nature isn't present (or something else is wrong),
* don't check the build path.
*/
if (javaProject != null && javaProject.exists()) {
indexClasspath(javaProject);
}
// else {
// Logger.log(Logger.WARNING, "TaglibIndex was asked to index non-Java
// Project " + fProject.getName()); //$NON-NLS-1$
// }
if (_debugIndexTime)
Logger.log(Logger.INFO, "indexed " + fProject.getName() + " classpath in " + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* @param entry
*/
private void indexClasspath(IClasspathEntry entry) {
switch (entry.getEntryKind()) {
case IClasspathEntry.CPE_CONTAINER : {
IClasspathContainer container = (IClasspathContainer) entry;
IClasspathEntry[] containedEntries = container.getClasspathEntries();
for (int i = 0; i < containedEntries.length; i++) {
indexClasspath(containedEntries[i]);
}
}
break;
case IClasspathEntry.CPE_LIBRARY : {
/*
* Ignore libs in required projects that are not exported
*/
IPath libPath = entry.getPath();
if (!fClasspathJars.containsKey(libPath.toString())) {
if (libPath.toFile().exists()) {
updateClasspathLibrary(libPath.toString(), ITaglibIndexDelta.ADDED, entry.isExported());
}
else {
/*
* Note: .jars on the classpath inside of the project
* will have duplicate entries in the JAR references
* table that will e returned to
* getAvailableTaglibRecords().
*/
IFile libFile = ResourcesPlugin.getWorkspace().getRoot().getFile(libPath);
if (libFile != null && libFile.exists()) {
updateClasspathLibrary(libFile.getLocation().toString(), ITaglibIndexDelta.ADDED, entry.isExported());
}
}
}
}
break;
case IClasspathEntry.CPE_PROJECT : {
/*
* We're currently ignoring whether the project exports all of
* its build path
*/
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(entry.getPath().lastSegment());
if (project != null) {
fClasspathProjects.add(project);
}
}
break;
case IClasspathEntry.CPE_SOURCE :
break;
case IClasspathEntry.CPE_VARIABLE : {
IPath libPath = JavaCore.getResolvedVariablePath(entry.getPath());
if (libPath != null) {
File file = libPath.toFile();
// file in filesystem
if (file.exists() && !file.isDirectory()) {
updateClasspathLibrary(libPath.toString(), ITaglibRecordEvent.ADDED, entry.isExported());
}
else {
// workspace file
IFile jarFile = ResourcesPlugin.getWorkspace().getRoot().getFile(libPath);
if (jarFile.isAccessible() && jarFile.getType() == IResource.FILE && jarFile.getLocation() != null) {
String jarPathString = jarFile.getLocation().toString();
updateClasspathLibrary(jarPathString, ITaglibRecordEvent.ADDED, entry.isExported());
}
}
}
}
break;
}
}
/*
* private void removeClasspathLibrary(String libraryLocation) { JarRecord
* record = (JarRecord) fClasspathJars.remove(libraryLocation); if (record !=
* null) { URLRecord[] records = (URLRecord[])
* record.getURLRecords().toArray(new URLRecord[0]); for (int i = 0; i <
* records.length; i++) {
* fClasspathReferences.remove(records[i].getURI()); }
* TaglibIndex.fireTaglibRecordEvent(new TaglibRecordEvent(record,
* ITaglibIndexDelta.REMOVED)); } }
*/
/**
* @param javaProject
*/
private void indexClasspath(IJavaProject javaProject) {
if (javaProject == null)
return;
IProject project = javaProject.getProject();
if (project.equals(fProject)) {
try {
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
fBuildPathEntryCount = entries.length;
for (int i = 0; i < entries.length; i++) {
indexClasspath(entries[i]);
}
}
catch (JavaModelException e) {
Logger.logException("Error searching Java Build Path + (" + fProject.getName() + ") for tag libraries", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
private String readTextofChild(Node node, String childName) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(childName)) {
return getTextContents(child);
}
}
return ""; //$NON-NLS-1$
}
void removeJAR(IResource jar) {
if (_debugIndexCreation)
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]);
for (int i = 0; i < records.length; i++) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, records[i], ITaglibIndexDelta.REMOVED));
getImplicitReferences(jar.getFullPath().toString()).remove(records[i].getURI());
}
if (record.has11TLD) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.REMOVED));
}
}
}
void removeTag(IResource resource) {
TagDirRecord record = (TagDirRecord) fTagDirReferences.get(resource.getParent().getFullPath().toString());
if (record != null) {
record.tags.remove(resource.getName());
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.CHANGED));
}
}
void removeTagDir(IResource tagdir) {
IPath tagdirPath = tagdir.getFullPath();
if (_debugIndexCreation)
Logger.log(Logger.INFO, "removing record for " + tagdirPath); //$NON-NLS-1$
ITaglibRecord record = (ITaglibRecord) fTagDirReferences.remove(tagdirPath.toString());
if (record != null) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.REMOVED));
}
}
void removeTLD(IResource tld) {
if (_debugIndexCreation)
Logger.log(Logger.INFO, "removing record for " + tld.getFullPath()); //$NON-NLS-1$
TLDRecord record = (TLDRecord) fTLDReferences.remove(tld.getFullPath().toString());
if (record != null) {
if (record.getURI() != null) {
getImplicitReferences(tld.getFullPath().toString()).remove(record.getURI());
}
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.REMOVED));
}
}
void removeWebXML(IResource webxml) {
if (_debugIndexCreation)
Logger.log(Logger.INFO, "removing records for " + webxml.getFullPath()); //$NON-NLS-1$
WebXMLRecord record = (WebXMLRecord) fWebXMLReferences.remove(webxml.getFullPath().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, "removed record for " + records[i].getURI() + "@" + records[i].path); //$NON-NLS-1$ //$NON-NLS-2$
getImplicitReferences(webxml.getFullPath().toString()).remove(records[i].getURI());
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, records[i], ITaglibIndexDelta.REMOVED));
}
}
}
/**
* @param basePath
* @param reference
* @return
*/
ITaglibRecord resolve(String basePath, String reference) {
ensureUpTodate();
ITaglibRecord record = null;
String path = null;
float jspVersion = DeploymentDescriptorPropertyCache.getInstance().getJSPVersion(new Path(basePath));
/**
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=196177
* Support resolution in flexible projects
*/
IPath resourcePath = FacetModuleCoreSupport.resolve(new Path(basePath), reference);
if (resourcePath.segmentCount() > 1) {
if (resourcePath.toString().toLowerCase(Locale.US).endsWith(".tld")) { //$NON-NLS-1$
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath);
if (file.isAccessible()) {
path = resourcePath.toString();
}
}
else if (resourcePath.toString().toLowerCase(Locale.US).endsWith(".jar")) { //$NON-NLS-1$
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath);
if (file.isAccessible()) {
path = resourcePath.toString();
}
}
}
LOCK.acquire();
/**
* Workaround for problem in URIHelper; uris starting with '/' are
* returned as-is.
*/
if (path == null) {
if (reference.startsWith("/")) { //$NON-NLS-1$
path = getLocalRoot(basePath) + reference;
}
else {
path = URIHelper.normalize(reference, basePath, getLocalRoot(basePath));
}
}
// order dictated by JSP spec 2.0 section 7.2.3
record = (ITaglibRecord) fJARReferences.get(path);
// only if 1.1 TLD was found
if (jspVersion < 1.1 || (record instanceof JarRecord && !((JarRecord) record).has11TLD)) {
record = null;
}
if (record == null) {
record = (ITaglibRecord) fTLDReferences.get(path);
}
if (record == null && jspVersion >= 1.2) {
record = (ITaglibRecord) getImplicitReferences(basePath).get(reference);
}
if (record == null && jspVersion >= 2.0) {
record = (ITaglibRecord) fTagDirReferences.get(path);
}
if (record == null && jspVersion >= 1.2) {
record = (ITaglibRecord) fClasspathReferences.get(reference);
}
if (record == null && jspVersion >= 1.2) {
Map buildPathReferences = new HashMap();
List projectsProcessed = new ArrayList(fClasspathProjects.size() + 1);
projectsProcessed.add(fProject);
addBuildPathReferences(buildPathReferences, projectsProcessed, false);
record = (ITaglibRecord) buildPathReferences.get(reference);
}
// Check the XML Catalog
if (record == null) {
ICatalog catalog = XMLCorePlugin.getDefault().getDefaultXMLCatalog();
if (catalog != null) {
String resolvedString = null;
try {
// Check as system reference first
resolvedString = catalog.resolveSystem(reference);
// Check as URI
if (resolvedString == null || resolvedString.trim().length() == 0) {
resolvedString = catalog.resolveURI(reference);
}
// Check as public ID
if (resolvedString == null || resolvedString.trim().length() == 0) {
resolvedString = catalog.resolvePublic(reference, basePath);
}
}
catch (Exception e) {
Logger.logException(e);
}
if (resolvedString != null && resolvedString.trim().length() > 0) {
record = createCatalogRecord(reference, resolvedString);
}
}
}
/*
* If no records were found and no local-root applies, check ALL
* of the web.xml files as a fallback
*/
if (record == null && fProject.getFullPath().toString().equals(getLocalRoot(basePath))) {
WebXMLRecord[] webxmls = (WebXMLRecord[]) fWebXMLReferences.values().toArray(new WebXMLRecord[0]);
for (int i = 0; i < webxmls.length; i++) {
if (record != null)
continue;
record = (ITaglibRecord) getImplicitReferences(webxmls[i].path.toString()).get(reference);
}
}
LOCK.release();
return record;
}
/**
* Restores any saved reference tables
*/
private void restoreReferences() {
final boolean notifyOnRestoration = true;
if (TaglibIndex.ENABLED) {
// resources first
index();
// now build path
// ================ test reload time ========================
boolean restored = false;
File savedState = new File(fSaveStateFilename);
if (savedState.exists()) {
Reader reader = null;
try {
time0 = System.currentTimeMillis();
reader = new InputStreamReader(new BufferedInputStream(new FileInputStream(savedState)), "UTF-16"); //$NON-NLS-1$
// use a string buffer temporarily to reduce string
// creation
StringBuffer buffer = new StringBuffer();
char array[] = new char[2048];
int charsRead = 0;
while ((charsRead = reader.read(array)) != -1) {
if (charsRead > 0) {
buffer.append(array, 0, charsRead);
}
}
IDocument doc = new org.eclipse.jface.text.Document();
doc.set(buffer.toString());
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.trim())) {
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
for (int i = 1; i < lines && !fBuildPathIsDirty; i++) {
line = doc.getLineInformation(i);
lineText = doc.get(line.getOffset(), line.getLength());
StringTokenizer toker = new StringTokenizer(lineText, "|"); //$NON-NLS-1$
if (toker.hasMoreTokens()) {
String tokenType = toker.nextToken();
if ("JAR".equalsIgnoreCase(tokenType)) { //$NON-NLS-1$ //$NON-NLS-2$
boolean has11TLD = Boolean.valueOf(toker.nextToken()).booleanValue();
boolean exported = Boolean.valueOf(toker.nextToken()).booleanValue();
// make the rest the libraryLocation
String libraryLocation = toker.nextToken();
while (toker.hasMoreTokens()) {
libraryLocation = libraryLocation + "|" + toker.nextToken(); //$NON-NLS-1$ //$NON-NLS-2$
}
libraryLocation = libraryLocation.trim();
if (libraryRecord != null && notifyOnRestoration) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, libraryRecord, ITaglibIndexDelta.ADDED));
}
// Create a new JarRecord
libraryRecord = createJARRecord(libraryLocation);
libraryRecord.has11TLD = has11TLD;
libraryRecord.isExported = exported;
// 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 urlRecord = new URLRecord();
urlRecord.info = info;
urlRecord.isExported = exported;
urlRecord.baseLocation = libraryLocation;
try {
urlRecord.url = new URL("jar:file:" + libraryLocation + "!/" + JarUtilities.JSP11_TAGLIB); //$NON-NLS-1$ //$NON-NLS-2$
libraryRecord.urlRecords.add(urlRecord);
fClasspathReferences.put(urlRecord.getURI(), urlRecord);
if (_debugIndexCreation)
Logger.log(Logger.INFO, "created record for " + urlRecord.getURI() + "@" + urlRecord.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(tokenType) && libraryRecord != null) { //$NON-NLS-1$
// relies on a previously declared JAR
// record
boolean exported = Boolean.valueOf(toker.nextToken()).booleanValue();
// make the rest the URL
String urlString = toker.nextToken();
while (toker.hasMoreTokens()) {
urlString = urlString + "|" + toker.nextToken(); //$NON-NLS-1$ //$NON-NLS-2$
}
urlString = urlString.trim();
// Append a URLrecord
URLRecord urlRecord = new URLRecord();
urlRecord.url = new URL(urlString); //$NON-NLS-1$ //$NON-NLS-2$
urlRecord.isExported = exported;
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);
}
else if (BUILDPATH_PROJECT.equalsIgnoreCase(tokenType)) {
String projectName = toker.nextToken();
if (Path.ROOT.isValidSegment(projectName)) {
IProject project = workspaceRoot.getProject(projectName);
/* do not check if "open" here */
if (project != null) {
fClasspathProjects.add(project);
}
}
}
// last since they occur once
else if (BUILDPATH_DIRTY.equalsIgnoreCase(tokenType)) {
fBuildPathIsDirty = Boolean.valueOf(toker.nextToken()).booleanValue();
}
else if (BUILDPATH_ENTRIES.equalsIgnoreCase(tokenType)) {
fBuildPathEntryCount = Integer.valueOf(toker.nextToken()).intValue();
}
}
if (libraryRecord != null && notifyOnRestoration) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, libraryRecord, ITaglibIndexDelta.ADDED));
}
}
restored = true;
}
else {
Logger.log(Logger.INFO, "Tag Library Index: different cache format found, was \"" + lineText + "\", supports \"" + SAVE_FORMAT_VERSION + "\", reindexing build path"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
if (_debugIndexTime)
Logger.log(Logger.INFO, "time spent reloading " + fProject.getName() + " build path: " + (System.currentTimeMillis() - time0)); //$NON-NLS-1$ //$NON-NLS-2$
}
catch (Exception e) {
restored = false;
if (_debugIndexTime)
Logger.log(Logger.INFO, "failure reloading " + fProject.getName() + " build path index", e); //$NON-NLS-1$ //$NON-NLS-2$
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
}
}
}
}
// ================ test reload time (end) ==================
if (!restored) {
setBuildPathIsDirty();
}
}
}
/**
* Saves any storable references to disk. This is only called when the
* description is being cleared and not after every update.
*/
void saveReferences() {
// the build path information is out of date, remember that
time0 = System.currentTimeMillis();
Writer writer = null;
/**
* <pre>
* 1.0.1
* 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), "UTF-16"); //$NON-NLS-1$
writer.write(SAVE_FORMAT_VERSION);
writer.write('\n'); //$NON-NLS-1$
writer.write(BUILDPATH_DIRTY + "|" + fBuildPathIsDirty); //$NON-NLS-1$
writer.write('\n'); //$NON-NLS-1$
writer.write(BUILDPATH_ENTRIES + "|" + fBuildPathEntryCount); //$NON-NLS-1$
writer.write('\n'); //$NON-NLS-1$
IProject[] projects = (IProject[]) fClasspathProjects.toArray(new IProject[0]);
for (int i = 0; i < projects.length; i++) {
writer.write(BUILDPATH_PROJECT);
writer.write("|"); //$NON-NLS-1$
writer.write(projects[i].getName());
writer.write('\n'); //$NON-NLS-1$
}
Enumeration jars = fClasspathJars.keys();
while (jars.hasMoreElements()) {
String jarPath = jars.nextElement().toString();
JarRecord jarRecord = (JarRecord) fClasspathJars.get(jarPath);
writer.write("JAR|"); //$NON-NLS-1$
writer.write(Boolean.toString(jarRecord.has11TLD));
writer.write('|'); //$NON-NLS-1$
writer.write(Boolean.toString(jarRecord.isExported));
writer.write('|'); //$NON-NLS-1$
writer.write(jarPath);
writer.write('\n'); //$NON-NLS-1$
Iterator i = jarRecord.urlRecords.iterator();
while (i.hasNext()) {
URLRecord urlRecord = (URLRecord) i.next();
writer.write("URL|"); //$NON-NLS-1$
writer.write(String.valueOf(urlRecord.isExported));
writer.write("|"); //$NON-NLS-1$
writer.write(urlRecord.getURL().toExternalForm());
writer.write('\n'); //$NON-NLS-1$
}
}
}
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)); //$NON-NLS-1$
}
void setBuildPathIsDirty() {
fBuildPathIsDirty = true;
if (_debugIndexTime)
Logger.log(Logger.INFO, "marking build path information for " + fProject.getName() + " as dirty"); //$NON-NLS-1$
}
void updateClasspathLibrary(String libraryLocation, int deltaKind, boolean isExported) {
JarRecord libraryRecord = null;
if (deltaKind == ITaglibIndexDelta.REMOVED || deltaKind == ITaglibIndexDelta.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++) {
ITaglibRecord record = (ITaglibRecord) fClasspathReferences.remove(urlRecords[i].getURI());
if (record != null) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.REMOVED));
}
}
}
}
if (deltaKind == ITaglibIndexDelta.ADDED || deltaKind == ITaglibIndexDelta.CHANGED) {
libraryRecord = createJARRecord(libraryLocation);
libraryRecord.isExported = isExported;
fClasspathJars.put(libraryLocation, libraryRecord);
ZipFile jarfile = null;
try {
jarfile = new ZipFile(libraryLocation);
Enumeration entries = jarfile.entries();
while (entries.hasMoreElements()) {
ZipEntry z = (ZipEntry) entries.nextElement();
if (!z.isDirectory()) {
if (z.getName().toLowerCase(Locale.US).endsWith(".tld")) { //$NON-NLS-1$
if (z.getName().equals(JarUtilities.JSP11_TAGLIB)) {
libraryRecord.has11TLD = true;
}
InputStream contents = getCachedInputStream(jarfile, z);
if (contents != null) {
TaglibInfo info = extractInfo(libraryLocation, contents);
if (info != null && info.uri != null && info.uri.length() > 0) {
URLRecord urlRecord = new URLRecord();
urlRecord.info = info;
urlRecord.baseLocation = libraryLocation;
try {
urlRecord.isExported = isExported;
urlRecord.url = new URL("jar:file:" + libraryLocation + "!/" + z.getName()); //$NON-NLS-1$ //$NON-NLS-2$
libraryRecord.urlRecords.add(urlRecord);
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, urlRecord, deltaKind));
fClasspathReferences.put(urlRecord.getURI(), urlRecord);
if (_debugIndexCreation)
Logger.log(Logger.INFO, "created record for " + urlRecord.getURI() + "@" + urlRecord.getURL()); //$NON-NLS-1$ //$NON-NLS-2$
}
catch (MalformedURLException e) {
// don't record this URI
Logger.logException(e);
}
}
try {
contents.close();
}
catch (IOException e) {
}
}
}
}
}
}
catch (ZipException zExc) {
Logger.log(Logger.WARNING, "Taglib Index ZipException: " + libraryLocation + " " + zExc.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
catch (IOException ioExc) {
Logger.log(Logger.WARNING, "Taglib Index IOException: " + libraryLocation + " " + ioExc.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
}
finally {
closeJarFile(jarfile);
}
}
if (libraryRecord != null) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, libraryRecord, deltaKind));
}
}
void updateJAR(IResource jar, int deltaKind) {
if (_debugIndexCreation)
Logger.log(Logger.INFO, "creating records for JAR " + jar.getFullPath()); //$NON-NLS-1$
String jarLocationString = null;
if (jar.getLocation() != null)
jarLocationString = jar.getLocation().toString();
else
jarLocationString = jar.getLocationURI().toString();
String[] entries = JarUtilities.getEntryNames(jar);
JarRecord jarRecord = createJARRecord(jar);
fJARReferences.put(jar.getFullPath().toString(), jarRecord);
for (int i = 0; i < entries.length; i++) {
if (entries[i].endsWith(".tld")) { //$NON-NLS-1$
if (entries[i].equals(JarUtilities.JSP11_TAGLIB)) {
jarRecord.has11TLD = true;
}
InputStream contents = JarUtilities.getInputStream(jar, entries[i]);
if (contents != null) {
TaglibInfo info = extractInfo(jarLocationString, contents);
if (info != null && info.uri != null && info.uri.length() > 0) {
URLRecord record = new URLRecord();
record.info = info;
record.baseLocation = jarLocationString;
try {
record.url = new URL("jar:file:" + jarLocationString + "!/" + entries[i]); //$NON-NLS-1$ //$NON-NLS-2$
jarRecord.urlRecords.add(record);
int taglibDeltaKind = ITaglibIndexDelta.ADDED;
Hashtable table = getImplicitReferences(jar.getFullPath().toString());
if (table != null && table.get(record.getURI()) != null) {
taglibDeltaKind = ITaglibIndexDelta.CHANGED;
}
getImplicitReferences(jar.getFullPath().toString()).put(record.getURI(), record);
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, taglibDeltaKind));
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) {
}
}
else {
Logger.log(Logger.ERROR_DEBUG, getClass().getName() + "could not read resource " + jar.getFullPath()); //$NON-NLS-1$
}
}
}
if (jarRecord.has11TLD) {
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, jarRecord, deltaKind));
}
}
void updateTag(IResource resource, int kind) {
TagDirRecord record = (TagDirRecord) fTagDirReferences.get(resource.getParent().getFullPath().toString());
if (record == null) {
record = createTagdirRecord((IFolder) resource.getParent());
fTagDirReferences.put(resource.getParent().getFullPath().toString(), record);
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.ADDED));
}
else {
if (!record.tags.contains(resource.getName())) {
record.tags.add(resource.getName());
}
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, ITaglibIndexDelta.CHANGED));
}
}
void updateTagDir(IResource tagdirResource, int deltaKind) {
/**
* 8.4.1: tag files are loose files under /WEB-INF/tags
*/
if ((tagdirResource.getType() & IResource.FOLDER) != 0) {
if (_debugIndexCreation)
Logger.log(Logger.INFO, "creating record for directory " + tagdirResource.getFullPath()); //$NON-NLS-1$
TagDirRecord record = (TagDirRecord) fTagDirReferences.get(tagdirResource.getFullPath().toString());
if (record == null) {
record = createTagdirRecord((IFolder) tagdirResource);
fTagDirReferences.put(tagdirResource.getFullPath().toString(), record);
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, deltaKind));
}
else {
}
}
/**
* 8.4.1: tag files can also be packaged in the /META-INF/tags folder
* of a jar in /WEB-INF/lib/ (8.4.2: but must be mentioned in a .tld)
*/
else {
// these tags are merely surfaced when the TLD is modelled
}
}
void updateTLD(IResource tld, int deltaKind) {
if (_debugIndexCreation)
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) {
getImplicitReferences(tld.getFullPath().toString()).put(record.getURI(), record);
}
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, deltaKind));
}
void updateWebXML(IResource webxml, int deltaKind) {
if (webxml.getType() != IResource.FILE)
return;
InputStream webxmlContents = null;
Document document = null;
try {
webxmlContents = ((IFile) webxml).getContents(true);
DocumentProvider provider = new DocumentProvider();
provider.setInputStream(webxmlContents);
provider.setValidating(false);
provider.setRootElementName("web-app"); //$NON-NLS-1$
provider.setBaseReference(webxml.getParent().getFullPath().toString());
document = provider.getDocument(false);
}
catch (CoreException e) {
Logger.log(Logger.ERROR_DEBUG, "", e); //$NON-NLS-1$
}
finally {
if (webxmlContents != null)
try {
webxmlContents.close();
}
catch (IOException e1) {
// ignore
}
}
if (document == null)
return;
if (_debugIndexCreation)
Logger.log(Logger.INFO, "creating records for " + webxml.getFullPath()); //$NON-NLS-1$
WebXMLRecord webxmlRecord = new WebXMLRecord();
webxmlRecord.path = webxml.getFullPath();
fWebXMLReferences.put(webxmlRecord.getWebXML().toString(), webxmlRecord);
NodeList taglibs = document.getElementsByTagName(JSP12TLDNames.TAGLIB);
for (int iTaglib = 0; iTaglib < taglibs.getLength(); iTaglib++) {
String taglibUri = readTextofChild(taglibs.item(iTaglib), "taglib-uri").trim(); //$NON-NLS-1$
// specified location is relative to root of the webapp
String taglibLocation = readTextofChild(taglibs.item(iTaglib), "taglib-location").trim(); //$NON-NLS-1$
IPath path = null;
if (taglibLocation.startsWith("/")) { //$NON-NLS-1$
path = new Path(getLocalRoot(webxml.getFullPath().toString()) + taglibLocation);
}
else {
path = new Path(URIHelper.normalize(taglibLocation, webxml.getFullPath().toString(), getLocalRoot(webxml.getFullPath().toString())));
}
if (path.segmentCount() > 1) {
IFile resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
if (resource.isAccessible()) {
ITaglibRecord record = null;
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=125960
*
* Also support mappings to .jar files
*/
if ("jar".equalsIgnoreCase(resource.getFileExtension())) { //$NON-NLS-1$
JarRecord jarRecord = createJARRecord(resource);
String[] entries = JarUtilities.getEntryNames(resource);
for (int jEntry = 0; jEntry < entries.length; jEntry++) {
if (entries[jEntry].endsWith(".tld")) { //$NON-NLS-1$
if (entries[jEntry].equals(JarUtilities.JSP11_TAGLIB)) {
jarRecord.has11TLD = true;
InputStream contents = JarUtilities.getInputStream(resource, entries[jEntry]);
if (contents != null) {
TaglibInfo info = extractInfo(resource.getFullPath().toString(), contents);
jarRecord.info = info;
try {
contents.close();
}
catch (IOException e) {
}
}
}
}
}
record = jarRecord;
// the stored URI should reflect the web.xml's value
if (jarRecord.info == null) {
jarRecord.info = new TaglibInfo();
}
jarRecord.info.uri = taglibUri;
jarRecord.isMappedInWebXML = true;
if (_debugIndexCreation)
Logger.log(Logger.INFO, "created web.xml record for " + taglibUri + "@" + jarRecord.getLocation()); //$NON-NLS-1$ //$NON-NLS-2$
}
else {
TLDRecord tldRecord = createTLDRecord(resource);
record = tldRecord;
// the stored URI should reflect the web.xml's value
tldRecord.info.uri = taglibUri;
if (_debugIndexCreation)
Logger.log(Logger.INFO, "created web.xml record for " + taglibUri + "@" + tldRecord.getPath()); //$NON-NLS-1$ //$NON-NLS-2$
}
if (record != null) {
webxmlRecord.tldRecords.add(record);
getImplicitReferences(webxml.getFullPath().toString()).put(taglibUri, record);
TaglibIndex.getInstance().addDelta(new TaglibIndexDelta(fProject, record, deltaKind));
}
}
}
}
}
}