/******************************************************************************* * Copyright (c) 2004, 2015 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.internal.taglib; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.jsp.tagext.FunctionInfo; import javax.servlet.jsp.tagext.TagAttributeInfo; import javax.servlet.jsp.tagext.TagData; import javax.servlet.jsp.tagext.TagExtraInfo; import javax.servlet.jsp.tagext.TagFileInfo; import javax.servlet.jsp.tagext.TagInfo; import javax.servlet.jsp.tagext.TagLibraryInfo; import javax.servlet.jsp.tagext.ValidationMessage; import javax.servlet.jsp.tagext.VariableInfo; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jface.text.IDocument; import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; import org.eclipse.jst.jsp.core.internal.Logger; import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TaglibTracker; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDAttributeDeclaration; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDDocument; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDVariable; import org.eclipse.jst.jsp.core.internal.java.IJSPProblem; import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.NotImplementedException; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; import org.eclipse.wst.sse.core.internal.util.AbstractMemoryListener; import org.eclipse.wst.sse.core.utils.StringUtils; import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument; import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMNodeWrapper; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; import org.osgi.service.event.Event; import com.ibm.icu.text.MessageFormat; /** * This class helps find TaglibVariables in a JSP file. */ public class TaglibHelper { private static final String ITERATION_QUALIFIER = "javax.servlet.jsp.tagext"; //$NON-NLS-1$ private static final String ITERATION_NAME = "IterationTag"; //$NON-NLS-1$ // for debugging private static final boolean DEBUG; static { String value = Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/taglibvars"); //$NON-NLS-1$ DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$ } private IProject fProject = null; private ClassLoader fLoader = null; private IJavaProject fJavaProject; /** * A cache of class names that the class loader could not find. * Because the TaglibHelper is destroyed and recreated whenever * the classpath changes this cache will not become stale */ Set fNotFoundClasses = null; Map fClassMap = null; /** * Used to keep the {@link #fNotFoundClasses} cache clean when memory is low */ private MemoryListener fMemoryListener; public TaglibHelper(IProject project) { super(); setProject(project); fMemoryListener = new MemoryListener(); fMemoryListener.connect(); fNotFoundClasses = new HashSet(); fClassMap = Collections.synchronizedMap(new HashMap()); } /** * Checks that <code>type</code> implements an IterationTag * * @param type * @return true if <code>type</code> implements IterationTag * @throws JavaModelException * @throws ClassNotFoundException thrown when the <code>type</code> is null */ private boolean isIterationTag(IType type) throws JavaModelException, ClassNotFoundException { if (type == null) { throw new ClassNotFoundException(); } synchronized (fClassMap) { if (fClassMap.containsKey(type.getFullyQualifiedName())) return ((Boolean) fClassMap.get(type.getFullyQualifiedName())).booleanValue(); } String signature; String qualifier; String name; String[] interfaces = type.getSuperInterfaceTypeSignatures(); boolean isIteration = false; // Check any super interfaces for the iteration tag for (int i = 0; i < interfaces.length; i++) { // For source files, the interface may need to be resolved String erasureSig = Signature.getTypeErasure(interfaces[i]); qualifier = Signature.getSignatureQualifier(erasureSig); name = Signature.getSignatureSimpleName(erasureSig); // Interface type is unresolved if (erasureSig.charAt(0) == Signature.C_UNRESOLVED) { String[][] types = type.resolveType(getQualifiedType(qualifier, name)); // Type was resolved if (types != null && types.length > 0) { isIteration = handleInterface(type, types[0][0], types[0][1]); } } else { isIteration = handleInterface(type, qualifier, name); } if (isIteration) return true; } signature = type.getSuperclassTypeSignature(); if (signature != null) { String erasureSig = Signature.getTypeErasure(signature); qualifier = Signature.getSignatureQualifier(erasureSig); name = Signature.getSignatureSimpleName(erasureSig); // superclass was unresolved if (erasureSig.charAt(0) == Signature.C_UNRESOLVED) { String[][] types = type.resolveType(getQualifiedType(qualifier, name)); // Type was resolved if (types != null && types.length > 0) { isIteration = isIterationTag(fJavaProject.findType(types[0][0], types[0][1])); } } else { isIteration = isIterationTag(fJavaProject.findType(qualifier, name)); } } fClassMap.put(type.getFullyQualifiedName(), Boolean.valueOf(isIteration)); return isIteration; } private boolean handleInterface(IType type, String qualifier, String name) throws JavaModelException, ClassNotFoundException { boolean isIteration = false; // Qualified interface is an iteration tag if (ITERATION_QUALIFIER.equals(qualifier) && ITERATION_NAME.equals(name)) isIteration = true; // Check ancestors of this interface else isIteration = isIterationTag(fJavaProject.findType(qualifier, name)); fClassMap.put(type.getFullyQualifiedName(), Boolean.valueOf(isIteration)); return isIteration; } private String getQualifiedType(String qualifier, String name) { StringBuffer qual = new StringBuffer(qualifier); if (qual.length() > 0) qual.append('.'); qual.append(name); return qual.toString(); } private boolean isIterationTag(TLDElementDeclaration elementDecl, IStructuredDocument document, ITextRegionCollection customTag, List problems) { String className = elementDecl.getTagclass(); if (className == null || className.length() == 0 || fProject == null || fNotFoundClasses.contains(className)) return false; try { synchronized (fClassMap) { if (fClassMap.containsKey(className)) return ((Boolean) fClassMap.get(className)).booleanValue(); } return isIterationTag(fJavaProject.findType(className)); } catch (ClassNotFoundException e) { //the class could not be found so add it to the cache fNotFoundClasses.add(className); Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TagClassNotFound, JSPCoreMessages.TaglibHelper_3, className, true); if (createdProblem != null) problems.add(createdProblem); if (DEBUG) Logger.logException(className, e); } catch (JavaModelException e) { if (DEBUG) Logger.logException(className, e); } catch (Exception e) { // this is 3rd party code, need to catch all errors if (DEBUG) Logger.logException(className, e); } catch (Error e) { // this is 3rd party code, need to catch all errors if (DEBUG) Logger.logException(className, e); } return false; } public CustomTag getCustomTag(String tagToAdd, IStructuredDocument structuredDoc, ITextRegionCollection customTag, List problems) { List results = new ArrayList(); boolean isIterationTag = false; String tagClass = null; String teiClass = null; if (problems == null) problems = new ArrayList(); ModelQuery mq = getModelQuery(structuredDoc); if (mq != null) { TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(structuredDoc); if (mgr != null) { List trackers = mgr.getCMDocumentTrackers(-1); Iterator taglibs = trackers.iterator(); CMDocument doc = null; CMNamedNodeMap elements = null; while (taglibs.hasNext()) { doc = (CMDocument) taglibs.next(); CMNode node = null; if ((elements = doc.getElements()) != null && (node = elements.getNamedItem(tagToAdd)) != null && node.getNodeType() == CMNode.ELEMENT_DECLARATION) { if (node instanceof CMNodeWrapper) { node = ((CMNodeWrapper) node).getOriginNode(); } TLDElementDeclaration tldElementDecl = (TLDElementDeclaration) node; tagClass = tldElementDecl.getTagclass(); teiClass = tldElementDecl.getTeiclass(); isIterationTag = isIterationTag(tldElementDecl, structuredDoc, customTag, problems); /* * Although clearly not the right place to add validation * design-wise, this is the first time we have the * necessary information to validate the tag class. */ validateTagClass(structuredDoc, customTag, tldElementDecl, problems); // 1.2+ taglib style addVariables(results, node, customTag); // for 1.1 need more info from taglib tracker if (doc instanceof TaglibTracker) { String uri = ((TaglibTracker) doc).getURI(); String prefix = ((TaglibTracker) doc).getPrefix(); // only for 1.1 taglibs addTEIVariables(structuredDoc, customTag, results, tldElementDecl, prefix, uri, problems); } break; } } } } return new CustomTag(tagToAdd, tagClass, teiClass, (TaglibVariable[]) results.toArray(new TaglibVariable[results.size()]), isIterationTag); } /** * @param tagToAdd * is the name of the tag whose variables we want * @param structuredDoc * is the IStructuredDocument where the tag is found * @param customTag * is the IStructuredDocumentRegion opening tag for the custom * tag * @param problems problems that are generated while creating variables are added to this collection */ public TaglibVariable[] getTaglibVariables(String tagToAdd, IStructuredDocument structuredDoc, ITextRegionCollection customTag, List problems) { List results = new ArrayList(); if (problems == null) problems = new ArrayList(); ModelQuery mq = getModelQuery(structuredDoc); if (mq != null) { TLDCMDocumentManager mgr = TaglibController.getTLDCMDocumentManager(structuredDoc); // TaglibSupport support = ((TaglibModelQuery) // mq).getTaglibSupport(); if (mgr == null) return new TaglibVariable[0]; List trackers = mgr.getCMDocumentTrackers(-1); Iterator taglibs = trackers.iterator(); // TaglibSupport support = ((TaglibModelQuery) // mq).getTaglibSupport(); // if (support == null) // return new TaglibVariable[0]; // // Iterator taglibs = // support.getCMDocuments(customTag.getStartOffset()).iterator(); CMDocument doc = null; CMNamedNodeMap elements = null; while (taglibs.hasNext()) { doc = (CMDocument) taglibs.next(); CMNode node = null; if ((elements = doc.getElements()) != null && (node = elements.getNamedItem(tagToAdd)) != null && node.getNodeType() == CMNode.ELEMENT_DECLARATION) { if (node instanceof CMNodeWrapper) { node = ((CMNodeWrapper) node).getOriginNode(); } TLDElementDeclaration tldElementDecl = (TLDElementDeclaration) node; /* * Although clearly not the right place to add validation * design-wise, this is the first time we have the * necessary information to validate the tag class. */ boolean tagClassFound = validateTagClass(structuredDoc, customTag, tldElementDecl, problems); // 1.2+ taglib style addVariables(results, node, customTag); // for 1.1 need more info from taglib tracker if (tagClassFound && doc instanceof TaglibTracker) { String uri = ((TaglibTracker) doc).getURI(); String prefix = ((TaglibTracker) doc).getPrefix(); // only for 1.1 taglibs addTEIVariables(structuredDoc, customTag, results, tldElementDecl, prefix, uri, problems); } } } } return (TaglibVariable[]) results.toArray(new TaglibVariable[results.size()]); } /** * Adds 1.2 style TaglibVariables to the results list. * * @param results * list where the <code>TaglibVariable</code> s are added * @param node */ private void addVariables(List results, CMNode node, ITextRegionCollection customTag) { List list = ((TLDElementDeclaration) node).getVariables(); Iterator it = list.iterator(); while (it.hasNext()) { TLDVariable var = (TLDVariable) it.next(); if (!var.getDeclare()) continue; String varName = var.getNameGiven(); if (varName == null) { // 2.0 varName = var.getAlias(); } if (varName == null) { String attrName = var.getNameFromAttribute(); /* * Iterate through the document region to find the * corresponding attribute name, and then use its value */ ITextRegionList regions = customTag.getRegions(); boolean attrNameFound = false; for (int i = 2; i < regions.size(); i++) { ITextRegion region = regions.get(i); if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(region.getType())) { attrNameFound = attrName.equals(customTag.getText(region)); } if (attrNameFound && DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(region.getType())) { varName = StringUtils.strip(customTag.getText(region)); } } } if (varName != null) { String varClass = "java.lang.String"; // the default // class...//$NON-NLS-1$ if (var.getVariableClass() != null) { varClass = getVariableClass(var.getVariableClass()); } results.add(new TaglibVariable(varClass, varName, var.getScope(), var.getDescription())); } } } /** * Returns the class name, and if the class has parameterized types in an environment that supports them, the parameter signature * @param varClass the class of the variable * @return the class name, and a parameterized signature if necessary */ private String getVariableClass(String varClass) { String result = varClass; if (compilerSupportsParameterizedTypes()) { final ClassLoader loader = getClassloader(); try { try { // Check if the class has any parameterized types. For each one, add a type argument final int length = ((Object[]) Class.class.getMethod("getTypeParameters", null).invoke(Class.forName(varClass, true, loader), null)).length; //$NON-NLS-1$ if (length > 0) { final StringBuffer buffer = new StringBuffer("<"); //$NON-NLS-1$ for (int i = 0; i < length; i++) { if (i > 0) { buffer.append(','); } buffer.append('?'); } buffer.append('>'); result = result + buffer.toString(); } } catch (NoSuchMethodException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } } catch (ClassNotFoundException e) { } catch (NoClassDefFoundError e) { } } return result; } private boolean compilerSupportsParameterizedTypes() { String compliance = fJavaProject.getOption(JavaCore.COMPILER_SOURCE, true); try { return Float.parseFloat(compliance) >= 1.5; } catch (NumberFormatException e) { } return false; } /** * Adds 1.1 style TaglibVariables (defined in a TagExtraInfo class) to the * results list. Also reports problems with the tag and tei classes in * fTranslatorProblems. * * @param customTag * @param results * list where the <code>TaglibVariable</code> s are added * @param decl * TLDElementDeclaration for the custom tag * @param prefix * custom tag prefix * @param uri * URI where the tld can be found */ private void addTEIVariables(IStructuredDocument document, ITextRegionCollection customTag, List results, TLDElementDeclaration decl, String prefix, String uri, List problems) { if (TLDElementDeclaration.SOURCE_TAG_FILE.equals(decl.getProperty(TLDElementDeclaration.TAG_SOURCE)) || fJavaProject == null) return; String teiClassname = decl.getTeiclass(); if (teiClassname == null || teiClassname.length() == 0 || fJavaProject == null || fNotFoundClasses.contains(teiClassname)) return; ClassLoader loader = getClassloader(); Class teiClass = null; try { /* * JDT could tell us about it, but loading and calling it would * still take time */ teiClass = Class.forName(teiClassname, true, loader); if (teiClass != null) { Object teiObject = teiClass.newInstance(); if (TagExtraInfo.class.isInstance(teiObject)) { TagExtraInfo tei = (TagExtraInfo) teiObject; Hashtable tagDataTable = extractTagData(customTag); TagInfo info = getTagInfo(decl, tei, prefix, uri); if (info != null) { tei.setTagInfo(info); // add to results TagData td = new TagData(tagDataTable); VariableInfo[] vInfos = tei.getVariableInfo(td); if (vInfos != null) { for (int i = 0; i < vInfos.length; i++) { String className = vInfos[i].getClassName(); if (className != null) { className = getVariableClass(className); } results.add(new TaglibVariable(className, vInfos[i].getVarName(), vInfos[i].getScope(), decl.getDescription())); } } ValidationMessage[] messages = tei.validate(td); if (messages != null && messages.length > 0) { for (int i = 0; i < messages.length; i++) { Object createdProblem = createValidationMessageProblem(document, customTag, messages[i].getMessage()); if (createdProblem != null) { problems.add(createdProblem); } } } } } else { Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TEIClassMisc, JSPCoreMessages.TaglibHelper_2, teiClassname, true); if (createdProblem != null) { problems.add(createdProblem); } // this is 3rd party code, need to catch all exceptions if (DEBUG) { Logger.log(Logger.WARNING, teiClassname + " is not a subclass of TaxExtraInfo"); //$NON-NLS-1$ } } } } catch (ClassNotFoundException e) { //the class could not be found so add it to the cache fNotFoundClasses.add(teiClassname); Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TEIClassNotFound, JSPCoreMessages.TaglibHelper_0, teiClassname, true); if (createdProblem != null) { problems.add(createdProblem); } // TEI class wasn't on build path if (DEBUG) logException(teiClassname, e); } catch (InstantiationException e) { Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TEIClassNotInstantiated, JSPCoreMessages.TaglibHelper_1, teiClassname, true); if (createdProblem != null) { problems.add(createdProblem); } // TEI class couldn't be instantiated if (DEBUG) logException(teiClassname, e); } catch (IllegalAccessException e) { if (DEBUG) logException(teiClassname, e); } // catch (ClassCastException e) { // // TEI class wasn't really a subclass of TagExtraInfo // if (DEBUG) // logException(teiClassname, e); // } catch (Exception e) { Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TEIClassMisc, JSPCoreMessages.TaglibHelper_2, teiClassname, true); if (createdProblem != null) { problems.add(createdProblem); } // this is 3rd party code, need to catch all exceptions if (DEBUG) logException(teiClassname, e); } catch (Error e) { // this is 3rd party code, need to catch all errors Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TEIClassNotInstantiated, JSPCoreMessages.TaglibHelper_1, teiClassname, true); if (createdProblem != null) { problems.add(createdProblem); } if (DEBUG) logException(teiClassname, e); } finally { // Thread.currentThread().setContextClassLoader(oldLoader); } } /** * @param customTag * @param teiClass * @return */ private Object createJSPProblem(final IStructuredDocument document, final ITextRegionCollection customTag, final int problemID, final String messageKey, final String argument, boolean preferVars) { final String tagname = customTag.getText(customTag.getRegions().get(1)); final int start; if (customTag.getNumberOfRegions() > 1) { start = customTag.getStartOffset(customTag.getRegions().get(1)); } else { start = customTag.getStartOffset(); } final int end; if (customTag.getNumberOfRegions() > 1) { end = customTag.getTextEndOffset(customTag.getRegions().get(1)) - 1; } else { end = customTag.getTextEndOffset() - 1; } final int line = document.getLineOfOffset(start); final char[] name; IPath location = TaglibController.getLocation(document); if (location == null) { name = new char[0]; } else { name = location.toString().toCharArray(); } /* * Note: these problems would result in translation errors on the * server, so the severity is not meant to be controllable */ return new IJSPProblem() { public void setSourceStart(int sourceStart) { } public void setSourceLineNumber(int lineNumber) { } public void setSourceEnd(int sourceEnd) { } public boolean isWarning() { return false; } public boolean isInfo() { return false; } public boolean isError() { return true; } public int getSourceStart() { return start; } public int getSourceLineNumber() { return line; } public int getSourceEnd() { return end; } public char[] getOriginatingFileName() { return name; } public String getMessage() { return MessageFormat.format(messageKey, new String[]{tagname, argument}); } public int getID() { return problemID; } public String[] getArguments() { return new String[0]; } public int getEID() { return problemID; } }; } /** * @param customTag * @param validationMessage * @return */ private Object createValidationMessageProblem(final IStructuredDocument document, final ITextRegionCollection customTag, final String validationMessage) { final int start; if (customTag.getNumberOfRegions() > 3) { start = customTag.getStartOffset(customTag.getRegions().get(2)); } else if (customTag.getNumberOfRegions() > 1) { start = customTag.getStartOffset(customTag.getRegions().get(1)); } else { start = customTag.getStartOffset(); } final int end; if (customTag.getNumberOfRegions() > 3) { end = customTag.getTextEndOffset(customTag.getRegions().get(customTag.getNumberOfRegions() - 2)) - 1; } else if (customTag.getNumberOfRegions() > 1) { end = customTag.getTextEndOffset(customTag.getRegions().get(1)) - 1; } else { end = customTag.getTextEndOffset(); } final int line = document.getLineOfOffset(start); final char[] name; IPath location = TaglibController.getLocation(document); if (location == null) { name = new char[0]; } else { name = location.toString().toCharArray(); } return new IJSPProblem() { public void setSourceStart(int sourceStart) { } public void setSourceLineNumber(int lineNumber) { } public void setSourceEnd(int sourceEnd) { } public boolean isInfo() { return false; } public boolean isWarning() { return true; } public boolean isError() { return false; } public int getSourceStart() { return start; } public int getSourceLineNumber() { return line; } public int getSourceEnd() { return end; } public char[] getOriginatingFileName() { return name; } public String getMessage() { return validationMessage; } public int getID() { return getEID(); } public String[] getArguments() { return new String[0]; } public int getEID() { return IJSPProblem.TEIValidationMessage; } }; } /** * @param decl * @return the TagInfo for the TLDELementDeclaration if the declaration is * valid, otherwise null */ private TagInfo getTagInfo(final TLDElementDeclaration decl, TagExtraInfo tei, String prefix, String uri) { TagLibraryInfo libInfo = new TagLibraryInfoImpl(prefix, uri, decl); CMNamedNodeMap attrs = decl.getAttributes(); TagAttributeInfo[] attrInfos = new TagAttributeInfo[attrs.getLength()]; TLDAttributeDeclaration attr = null; String type = ""; //$NON-NLS-1$ // get tag attribute infos for (int i = 0; i < attrs.getLength(); i++) { attr = (TLDAttributeDeclaration) attrs.item(i); type = attr.getType(); // default value for type is String if (attr.getType() == null || attr.getType().equals("")) //$NON-NLS-1$ type = "java.lang.String"; //$NON-NLS-1$ attrInfos[i] = new TagAttributeInfo(attr.getAttrName(), attr.isRequired(), type, false); } String tagName = decl.getNodeName(); String tagClass = decl.getTagclass(); String bodyContent = decl.getBodycontent(); if (tagName != null && tagClass != null && bodyContent != null) return new TagInfo(tagName, tagClass, bodyContent, decl.getInfo(), libInfo, tei, attrInfos); return null; } /** * @param e */ private void logException(String teiClassname, Throwable e) { String message = "teiClassname: ["; //$NON-NLS-1$ if (teiClassname != null) message += teiClassname; message += "]"; //$NON-NLS-1$ Logger.logException(message, e); } /** * Returns all attribute -> value pairs for the tag in a Hashtable. * * @param customTag * @return */ private Hashtable extractTagData(ITextRegionCollection customTag) { Hashtable tagDataTable = new Hashtable(); ITextRegionList regions = customTag.getRegions(); ITextRegion r = null; String attrName = ""; //$NON-NLS-1$ String attrValue = ""; //$NON-NLS-1$ final int size = regions.size(); for (int i = 2; i < size; i++) { r = regions.get(i); // check if attr name if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { attrName = customTag.getText(r); // check equals is next region if (size > ++i) { r = regions.get(i); if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS && size > ++i) { // get attr value r = regions.get(i); final String type = r.getType(); if (type == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { // attributes in our document have quotes, so we // need to strip them attrValue = StringUtils.stripQuotes(customTag.getText(r)); tagDataTable.put(attrName, attrValue); } else if (type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_DQUOTE || type == DOMJSPRegionContexts.JSP_TAG_ATTRIBUTE_VALUE_SQUOTE) { final StringBuffer buffer = new StringBuffer(); while (size > ++i && (r = regions.get(i)).getType() != type) { buffer.append(customTag.getText(r)); } tagDataTable.put(attrName, buffer.toString()); } } } } } tagDataTable.put("jsp:id", customTag.getText(regions.get(1)) + "_" + customTag.getStartOffset()); //$NON-NLS-1$ return tagDataTable; } private ClassLoader getClassloader() { if (fLoader == null) { fLoader = new BuildPathClassLoader(this.getClass().getClassLoader(), fJavaProject); } return fLoader; } /** * @return Returns the fModelQuery. */ public ModelQuery getModelQuery(IDocument doc) { IStructuredModel model = null; ModelQuery mq = null; try { model = StructuredModelManager.getModelManager().getExistingModelForRead(doc); mq = ModelQueryUtil.getModelQuery(model); } finally { if (model != null) model.releaseFromRead(); } return mq; } /** * @return Returns the fFile. */ public IProject getProject() { return fProject; } /** * @param file * The fFile to set. */ public void setProject(IProject p) { fProject = p; IJavaProject javaProject = JavaCore.create(p); if (javaProject.exists()) { fJavaProject = javaProject; } } private boolean isTagFile(TLDElementDeclaration decl) { final String path = decl.getPath(); return TLDElementDeclaration.SOURCE_TAG_FILE.equals(decl.getProperty(TLDElementDeclaration.TAG_SOURCE)) || (path != null && path.startsWith("/META-INF/tags")); //$NON-NLS-1$ } private boolean validateTagClass(IStructuredDocument document, ITextRegionCollection customTag, TLDElementDeclaration decl, List problems) { // skip if from a tag file if (isTagFile(decl) || fJavaProject == null) { return false; } String tagClassname = decl.getTagclass(); Object tagClass = null; if (tagClassname != null && tagClassname.length() > 0 && fJavaProject.exists()) { try { tagClass = fJavaProject.findType(tagClassname); } catch (JavaModelException e) { Logger.logException(e); } } if (tagClass == null) { Object createdProblem = createJSPProblem(document, customTag, IJSPProblem.TagClassNotFound, JSPCoreMessages.TaglibHelper_3, tagClassname, false); if (createdProblem != null) { problems.add(createdProblem); } } return tagClass != null; } /** * */ public void dispose() { fLoader = null; fJavaProject = null; fProject = null; fNotFoundClasses = null; fClassMap = null; fMemoryListener.disconnect(); fMemoryListener = null; } public void invalidateClass(String className) { fClassMap.remove(className); } /** * <p>A {@link AbstractMemoryListener} that clears the {@link #fNotFoundClasses} cache * whenever specific memory events are received.</p> * * <p>Events: * <ul> * <li>{@link AbstractMemoryListener#SEV_NORMAL}</li> * <li>{@link AbstractMemoryListener#SEV_SERIOUS}</li> * <li>{@link AbstractMemoryListener#SEV_CRITICAL}</li> * </ul> * </p> */ private class MemoryListener extends AbstractMemoryListener { /** * <p>Constructor causes this listener to listen for specific memory events.</p> * <p>Events: * <ul> * <li>{@link AbstractMemoryListener#SEV_NORMAL}</li> * <li>{@link AbstractMemoryListener#SEV_SERIOUS}</li> * <li>{@link AbstractMemoryListener#SEV_CRITICAL}</li> * </ul> * </p> */ MemoryListener() { super(new String[] { SEV_NORMAL, SEV_SERIOUS, SEV_CRITICAL }); } /** * On any memory event we handle clear out the project descriptions * * @see org.eclipse.jst.jsp.core.internal.util.AbstractMemoryListener#handleMemoryEvent(org.osgi.service.event.Event) */ protected void handleMemoryEvent(Event event) { /* if running low on memory then this cache can be cleared * and rebuilt at the expense of processing time */ fNotFoundClasses.clear(); fClassMap.clear(); } } class TagLibraryInfoImpl extends TagLibraryInfo { TLDElementDeclaration decl; TagLibraryInfoImpl(String prefix, String uri, TLDElementDeclaration decl){ super(prefix, uri); this.decl = decl; } public String getURI() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getURI(); } public String getPrefixString() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getPrefixString(); } public String getShortName() { return ((TLDDocument)decl.getOwnerDocument()).getShortname(); } public String getReliableURN() { return ((TLDDocument)decl.getOwnerDocument()).getUri(); } public String getInfoString() { return ((TLDDocument)decl.getOwnerDocument()).getInfo(); } public String getRequiredVersion() { return ((TLDDocument)decl.getOwnerDocument()).getJspversion(); } public TagInfo[] getTags() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getTags(); } public TagFileInfo[] getTagFiles() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getTagFiles(); } public TagInfo getTag(String shortname) { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getTag(shortname); } public TagFileInfo getTagFile(String shortname) { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getTagFile(shortname); } public FunctionInfo[] getFunctions() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return super.getFunctions(); } public FunctionInfo getFunction(String name) { new NotImplementedException(name).printStackTrace(); return super.getFunction(name); } public TagLibraryInfo[] getTagLibraryInfos() { if (Platform.inDebugMode()) new NotImplementedException().printStackTrace(); return new TagLibraryInfo[] { this }; } } }