From e6ce9991c039aa68f4b38c5e7e0b6e4481fb77e7 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 16 Jul 2010 17:59:20 +0400 Subject: [PATCH] support loading taskdefs and typedefs from antlibs (both explicit and auto-discover modes) --- .../intellij/lang/ant/dom/AntDomAntlib.java | 12 + .../intellij/lang/ant/dom/AntDomExtender.java | 15 +- .../lang/ant/dom/AntDomFileDescription.java | 13 +- .../lang/ant/dom/AntFileDescription.java | 33 ++ .../ant/dom/AntlibDomFileDescription.java | 49 +++ .../ant/dom/CustomAntElementsRegistry.java | 309 ++++++++++-------- 6 files changed, 281 insertions(+), 150 deletions(-) create mode 100644 plugins/ant/src/com/intellij/lang/ant/dom/AntFileDescription.java create mode 100644 plugins/ant/src/com/intellij/lang/ant/dom/AntlibDomFileDescription.java diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomAntlib.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomAntlib.java index 60d59c70ab64..23ed607df9ec 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomAntlib.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomAntlib.java @@ -16,6 +16,7 @@ package com.intellij.lang.ant.dom; import com.intellij.util.xml.SubTagList; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -25,6 +26,9 @@ import java.util.List; */ public abstract class AntDomAntlib extends AntDomElement{ + public static final String ANTLIB_DEFAULT_FILENAME = "antlib.xml"; + public static final String ANTLIB_URI_PREFIX = "antlib:"; + @SubTagList("typedef") public abstract List getTypedefs(); @@ -39,4 +43,12 @@ public abstract class AntDomAntlib extends AntDomElement{ @SubTagList("scriptdef") public abstract List getScriptdefs(); + + @Nullable + public static String toAntlibResource(String antlibUri) { + if (!antlibUri.startsWith(ANTLIB_URI_PREFIX)) { + return null; + } + return antlibUri.substring(ANTLIB_URI_PREFIX.length()).replace('.', '/') + "/" + ANTLIB_DEFAULT_FILENAME; + } } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java index 26888f508b99..80f4ac830c6d 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java @@ -59,6 +59,9 @@ public class AntDomExtender extends DomExtender{ final String tagName = xmlTag.getName(); // todo: support namespaces final AntDomProject antProject = antDomElement.getAntProject(); + if (antProject == null) { + return; + } final ReflectedProject reflected = ReflectedProject.getProject(antProject.getClassLoader()); final DomGenericInfo genericInfo = antDomElement.getGenericInfo(); @@ -218,7 +221,11 @@ public class AntDomExtender extends DomExtender{ return Collections.emptySet(); } final AntDomElement element = (AntDomElement)parent; - final CustomAntElementsRegistry registry = CustomAntElementsRegistry.getInstance(element.getAntProject()); + final AntDomProject antDomProject = element.getAntProject(); + if (antDomProject == null) { + return Collections.emptySet(); + } + final CustomAntElementsRegistry registry = CustomAntElementsRegistry.getInstance(antDomProject); final Set result = new HashSet(); for (XmlName variant : registry.getCompletionVariants(element)) { final String ns = variant.getNamespaceKey(); @@ -245,7 +252,11 @@ public class AntDomExtender extends DomExtender{ return null; } final AntDomElement parentElement = (AntDomElement)parent; - final CustomAntElementsRegistry registry = CustomAntElementsRegistry.getInstance(parentElement.getAntProject()); + final AntDomProject antDomProject = parentElement.getAntProject(); + if (antDomProject == null) { + return null; + } + final CustomAntElementsRegistry registry = CustomAntElementsRegistry.getInstance(antDomProject); final AntDomElement declaringElement = registry.findDeclaringElement(parentElement, xmlName); if (declaringElement == null) { return null; diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java index 8c16c85b56eb..fc6e097ca891 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java @@ -21,7 +21,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; -import com.intellij.util.xml.DomFileDescription; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,15 +28,11 @@ import org.jetbrains.annotations.Nullable; * @author Eugene Zhuravlev * Date: Apr 6, 2010 */ -public class AntDomFileDescription extends DomFileDescription { - private static final String PROJECT_TAG_NAME = "project"; +public class AntDomFileDescription extends AntFileDescription { + private static final String ROOT_TAG_NAME = "project"; public AntDomFileDescription() { - super(AntDomProject.class, PROJECT_TAG_NAME); - } - - protected void initializeFileDescription() { - registerReferenceInjector(new AntReferenceInjector()); + super(AntDomProject.class, ROOT_TAG_NAME); } public boolean isMyFile(@NotNull XmlFile file, @Nullable Module module) { @@ -48,7 +43,7 @@ public class AntDomFileDescription extends DomFileDescription { final XmlDocument document = xmlFile.getDocument(); if (document != null) { final XmlTag tag = document.getRootTag(); - if (tag != null && PROJECT_TAG_NAME.equals(tag.getName()) && tag.getContext() instanceof XmlDocument) { + if (tag != null && ROOT_TAG_NAME.equals(tag.getName()) && tag.getContext() instanceof XmlDocument) { return true; } final VirtualFile vFile = xmlFile.getOriginalFile().getVirtualFile(); diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntFileDescription.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntFileDescription.java new file mode 100644 index 000000000000..34ebba9cb9ee --- /dev/null +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntFileDescription.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.ant.dom; + +import com.intellij.util.xml.DomFileDescription; + +/** + * @author Eugene Zhuravlev + * Date: Apr 6, 2010 + */ +public class AntFileDescription extends DomFileDescription { + + public AntFileDescription(final Class rootElementClass, final String rootTagName) { + super(rootElementClass, rootTagName); + } + + protected void initializeFileDescription() { + registerReferenceInjector(new AntReferenceInjector()); + } +} diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntlibDomFileDescription.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntlibDomFileDescription.java new file mode 100644 index 000000000000..9a8be78a1186 --- /dev/null +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntlibDomFileDescription.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.ant.dom; + +import com.intellij.openapi.module.Module; +import com.intellij.psi.xml.XmlDocument; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Eugene Zhuravlev + * Date: Apr 6, 2010 + */ +public class AntlibDomFileDescription extends AntFileDescription { + private static final String ROOT_TAG_NAME = "antlib"; + + public AntlibDomFileDescription() { + super(AntDomAntlib.class, ROOT_TAG_NAME); + } + + public boolean isMyFile(@NotNull XmlFile file, @Nullable Module module) { + return super.isMyFile(file, module) && isAntLibFile(file); + } + + public static boolean isAntLibFile(final XmlFile xmlFile) { + final XmlDocument document = xmlFile.getDocument(); + if (document != null) { + final XmlTag tag = document.getRootTag(); + return tag != null && ROOT_TAG_NAME.equals(tag.getName()) && tag.getContext() instanceof XmlDocument; + } + return false; + } + +} diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java b/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java index dcdfe8a7e1e6..c2709fee1903 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java @@ -32,8 +32,8 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlFile; import com.intellij.util.LocalTimeCounter; -import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.xml.XmlName; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -63,13 +63,7 @@ public class CustomAntElementsRegistry { private final Map myNamedLoaders = new HashMap(); private CustomAntElementsRegistry(final AntDomProject antProject) { - antProject.accept(new BaseVisitor() { - public void visitTypeDef(AntDomTypeDef typedef) { - // if loaderRef attribute is specified, make sure the loader is built and stored - rememberNamedClassLoader(typedef, antProject); - defineCustomElements(typedef, antProject); - } - }); + antProject.accept(new CustomTagDefinitionFinder(antProject)); } public static CustomAntElementsRegistry getInstance(AntDomProject antProject) { @@ -108,69 +102,6 @@ public class CustomAntElementsRegistry { return myErrors.get(xmlName); } - private static class BaseVisitor extends AntDomRecursiveVisitor { - public void visitInclude(AntDomInclude includeTag) { - processInclude(includeTag); - } - - public void visitImport(AntDomImport importTag) { - processInclude(importTag); - } - - private void processInclude(AntDomIncludingDirective directive) { - final PsiFileSystemItem item = directive.getFile().getValue(); - if (item instanceof PsiFile) { - final AntDomProject slaveProject = AntSupport.getAntDomProject((PsiFile)item); - if (slaveProject != null) { - slaveProject.accept(this); - } - } - } - } - - private void defineCustomElements(AntDomTypeDef typedef, final AntDomProject antProject) { - final String uri = typedef.getUri().getStringValue(); - - final String customTagName = typedef.getName().getStringValue(); - final String classname = typedef.getClassName().getStringValue(); - if (classname != null && customTagName != null) { - registerElement(typedef, customTagName, uri, classname, getClassLoader(typedef, antProject)); - } - else { - final XmlElement xmlElement = antProject.getXmlElement(); - final Project project = xmlElement != null? xmlElement.getProject() : null; - if (project != null) { - final String resource = typedef.getResource().getStringValue(); - if (resource != null) { - final ClassLoader loader = getClassLoader(typedef, antProject); - if (loader != null) { - final InputStream stream = loader.getResourceAsStream(resource); - if (stream != null) { - loadFromStream(typedef, uri, resource, loader, stream, project); - } - } - } - else { - final PsiFileSystemItem file = typedef.getFile().getValue(); - if (file instanceof PsiFile) { - final VirtualFile vf = file.getVirtualFile(); - if (vf != null) { - try { - final InputStream stream = vf.getInputStream(); - if (stream != null) { - loadFromStream(typedef, uri, file.getName(), getClassLoader(typedef, antProject), stream, project); - } - } - catch (IOException e) { - LOG.info(e); - } - } - } - } - } - } - } - private void rememberNamedClassLoader(AntDomTypeDef typedef, AntDomProject antProject) { final String loaderRef = typedef.getLoaderRef().getStringValue(); if (loaderRef != null) { @@ -189,82 +120,35 @@ public class CustomAntElementsRegistry { return createClassLoader(collectUrls(typedef), antProject); } - private void loadFromStream(AntDomTypeDef typedef, String nsPrefix, String resource, ClassLoader loader, InputStream stream, final Project project) { - if (isXmlFormat(typedef, resource)) { - // todo - //loadAntlibStream(stream, nsPrefix, project); + @Nullable + private static PsiFile loadContentAsFile(PsiFile originalFile, LanguageFileType fileType) { + final VirtualFile vFile = originalFile.getVirtualFile(); + if (vFile == null) { + return null; } - else { - final StringBuilder builder = StringBuilderSpinAllocator.alloc(); - try { - int nextByte; - try { - while ((nextByte = stream.read()) >= 0) { - builder.append((char)nextByte); - } - } - finally { - stream.close(); - } - final PropertiesFile propFile = (PropertiesFile)createDummyFile("dummy.properties", StdFileTypes.PROPERTIES, builder, project); - for (final Property property : propFile.getProperties()) { - registerElement(typedef, property.getUnescapedKey(), nsPrefix, property.getValue(), loader); - } - } - catch (IOException e) { - LOG.info(e); - } - finally { - StringBuilderSpinAllocator.dispose(builder); - } - } - } - - /* - static void loadAntlibStream(@NotNull final InputStream antlibStream, final String nsPrefix, Project project) { - final StringBuilder builder = StringBuilderSpinAllocator.alloc(); try { - int nextByte; - while ((nextByte = antlibStream.read()) >= 0) { - builder.append((char)nextByte); - } - antlibStream.close(); - final XmlFile xmlFile = (XmlFile)createDummyFile("dummy.xml", StdFileTypes.XML, builder, project); - final XmlDocument document = xmlFile.getDocument(); - if (document != null) { - final XmlTag rootTag = document.getRootTag(); - if (rootTag == null) return; - for (final XmlTag tag : rootTag.getSubTags()) { - if (nsPrefix != null && nsPrefix.length() > 0) { - try { - tag.setName(nsPrefix + ':' + tag.getLocalName()); - } - catch (IncorrectOperationException e) { - continue; - } - } - final AntElement newElement = AntElementFactory.createAntElement(element, tag); - if (newElement instanceof AntTypeDef) { - for (final AntTypeDefinition def : ((AntTypeDef)newElement).getDefinitions()) { - if (element instanceof AntTypeDefImpl) { - final AntTypeDefImpl td = ((AntTypeDefImpl)element); - final AntTypeDefinition[] defs = td.myNewDefinitions != null ? td.myNewDefinitions : AntTypeDefinition.EMPTY_ARRAY; - td.myNewDefinitions = ArrayUtil.append(defs, def); - } - } - } - } - } + return loadContentAsFile(originalFile.getProject(), vFile.getInputStream(), fileType); } catch (IOException e) { LOG.info(e); } - finally { - StringBuilderSpinAllocator.dispose(builder); - } + return null; } - */ + private static PsiFile loadContentAsFile(Project project, InputStream stream, LanguageFileType fileType) throws IOException { + final StringBuilder builder = new StringBuilder(); + try { + int nextByte; + while ((nextByte = stream.read()) >= 0) { + builder.append((char)nextByte); + } + } + finally { + stream.close(); + } + final PsiFileFactory factory = PsiFileFactory.getInstance(project); + return factory.createFileFromText("_ant_dummy__." + fileType.getDefaultExtension(), fileType, builder, LocalTimeCounter.currentTime(), false, false); + } private void registerElement(AntDomTypeDef typedef, String customTagName, String nsUri, String classname, ClassLoader loader) { Class clazz = null; @@ -365,4 +249,151 @@ public class CustomAntElementsRegistry { } + private class CustomTagDefinitionFinder extends AntDomRecursiveVisitor { + + private final Set processedAntlibs = new HashSet(); + private final AntDomProject myAntProject; + + public CustomTagDefinitionFinder(AntDomProject antProject) { + myAntProject = antProject; + } + + public void visitAntDomElement(AntDomElement element) { + final String uri = element.getXmlElementNamespace(); + if (!processedAntlibs.contains(uri)) { + processedAntlibs.add(uri); + final String antLibResource = AntDomAntlib.toAntlibResource(uri); + if (antLibResource != null) { + final XmlElement xmlElement = element.getXmlElement(); + if (xmlElement != null) { + final ClassLoader loader = myAntProject.getClassLoader(); + final InputStream stream = loader.getResourceAsStream(antLibResource); + if (stream != null) { + try { + final XmlFile xmlFile = (XmlFile)loadContentAsFile(xmlElement.getProject(), stream, StdFileTypes.XML); + if (xmlFile != null) { + loadDefinitionsFromAntlib(xmlFile, uri, loader, null); + } + } + catch (IOException e) { + LOG.info(e); + } + } + } + } + } + super.visitAntDomElement(element); + } + + public void visitTypeDef(AntDomTypeDef typedef) { + // if loaderRef attribute is specified, make sure the loader is built and stored + rememberNamedClassLoader(typedef, myAntProject); + defineCustomElements(typedef, myAntProject); + } + + public void visitInclude(AntDomInclude includeTag) { + processInclude(includeTag); + } + + public void visitImport(AntDomImport importTag) { + processInclude(importTag); + } + + private void processInclude(AntDomIncludingDirective directive) { + final PsiFileSystemItem item = directive.getFile().getValue(); + if (item instanceof PsiFile) { + final AntDomProject slaveProject = AntSupport.getAntDomProject((PsiFile)item); + if (slaveProject != null) { + slaveProject.accept(this); + } + } + } + + private void defineCustomElements(AntDomTypeDef typedef, final AntDomProject antProject) { + final String uri = typedef.getUri().getStringValue(); + final String customTagName = typedef.getName().getStringValue(); + final String classname = typedef.getClassName().getStringValue(); + + if (classname != null && customTagName != null) { + registerElement(typedef, customTagName, uri, classname, getClassLoader(typedef, antProject)); + } + else { + XmlFile xmlFile = null; + PropertiesFile propFile = null; + ClassLoader loader = null; + + final XmlElement xmlElement = antProject.getXmlElement(); + final Project project = xmlElement != null? xmlElement.getProject() : null; + if (project != null) { + final String resource = typedef.getResource().getStringValue(); + if (resource != null) { + loader = getClassLoader(typedef, antProject); + if (loader != null) { + final InputStream stream = loader.getResourceAsStream(resource); + if (stream != null) { + try { + if (isXmlFormat(typedef, resource)) { + xmlFile = (XmlFile)loadContentAsFile(project, stream, StdFileTypes.XML); + } + else { + propFile = (PropertiesFile)loadContentAsFile(project, stream, StdFileTypes.PROPERTIES); + } + } + catch (IOException e) { + LOG.info(e); + } + } + } + } + else { + final PsiFileSystemItem file = typedef.getFile().getValue(); + if (file instanceof PsiFile) { + if (isXmlFormat(typedef, file.getName())) { + xmlFile = file instanceof XmlFile ? (XmlFile)file : (XmlFile)loadContentAsFile((PsiFile)file, StdFileTypes.XML); + } + else { // assume properties format + propFile = file instanceof PropertiesFile ? (PropertiesFile)file : (PropertiesFile)loadContentAsFile((PsiFile)file, StdFileTypes.PROPERTIES); + } + } + } + + if (propFile != null) { + if (loader == null) { // if not initialized yet + loader = getClassLoader(typedef, antProject); + } + for (final Property property : propFile.getProperties()) { + registerElement(typedef, property.getUnescapedKey(), uri, property.getValue(), loader); + } + } + + if (xmlFile != null) { + loadDefinitionsFromAntlib(xmlFile, uri, loader != null? loader : getClassLoader(typedef, antProject), typedef); + } + } + } + } + + private void loadDefinitionsFromAntlib(XmlFile xmlFile, String uri, ClassLoader loader, @Nullable AntDomTypeDef typedef) { + final AntDomAntlib antLib = AntSupport.getAntLib(xmlFile); + if (antLib != null) { + final List defs = new ArrayList(); + defs.addAll(antLib.getTaskdefs()); + defs.addAll(antLib.getTypedefs()); + if (!defs.isEmpty()) { + for (AntDomTypeDef def : defs) { + final String tagName = def.getName().getStringValue(); + if (tagName == null) { + continue; + } + final String className = def.getClassName().getStringValue(); + if (className == null) { + continue; + } + registerElement(typedef != null? typedef : def, tagName, uri, className, loader); + } + } + } + } + + } }