support loading taskdefs and typedefs from antlibs (both explicit and auto-discover...
authorEugene Zhuravlev <jeka@intellij.com>
Fri, 16 Jul 2010 13:59:20 +0000 (17:59 +0400)
committerEugene Zhuravlev <jeka@intellij.com>
Fri, 16 Jul 2010 13:59:56 +0000 (17:59 +0400)
plugins/ant/src/com/intellij/lang/ant/dom/AntDomAntlib.java
plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java
plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java
plugins/ant/src/com/intellij/lang/ant/dom/AntFileDescription.java [new file with mode: 0644]
plugins/ant/src/com/intellij/lang/ant/dom/AntlibDomFileDescription.java [new file with mode: 0644]
plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java

index 60d59c70ab649843a36c7170aae00d88beb3cf42..23ed607df9ec959e74f60cbb3b0a8b4a6a9af4b7 100644 (file)
@@ -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<AntDomTypeDef> getTypedefs();
 
@@ -39,4 +43,12 @@ public abstract class AntDomAntlib extends AntDomElement{
 
   @SubTagList("scriptdef")
   public abstract List<AntDomTypeDef> 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;
+  }
 }
index 26888f508b999dbe741779d86a6f0eed1981a5b4..80f4ac830c6d7dee2bcaff4c5f532c238ac16f17 100644 (file)
@@ -59,6 +59,9 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
       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<AntDomElement>{
         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<EvaluatedXmlName> result = new HashSet<EvaluatedXmlName>();
       for (XmlName variant : registry.getCompletionVariants(element)) {
         final String ns = variant.getNamespaceKey();
@@ -245,7 +252,11 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
         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;
index 8c16c85b56eb2bc0fdd83ade9cdc857106128c6a..fc6e097ca891d7380a860c0a1d80a21b56ce64a3 100644 (file)
@@ -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<AntDomProject> {
-  private static final String PROJECT_TAG_NAME = "project";
+public class AntDomFileDescription extends AntFileDescription<AntDomProject> {
+  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<AntDomProject> {
     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 (file)
index 0000000..34ebba9
--- /dev/null
@@ -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<T extends AntDomElement> extends DomFileDescription<T> {
+
+  public AntFileDescription(final Class<T> 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 (file)
index 0000000..9a8be78
--- /dev/null
@@ -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<AntDomAntlib> {
+  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;
+  }
+
+}
index dcdfe8a7e1e6020b4373d06bbabbb5d812de27fe..c2709fee190395aefb39f5dc681b549dc1a96069 100644 (file)
@@ -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<String, ClassLoader> myNamedLoaders = new HashMap<String, ClassLoader>();
 
   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);
-      }
+    try {
+      return loadContentAsFile(originalFile.getProject(), vFile.getInputStream(), fileType);
     }
+    catch (IOException e) {
+      LOG.info(e);
+    }
+    return null;
   }
 
-  /*
-  static void loadAntlibStream(@NotNull final InputStream antlibStream, final String nsPrefix, Project project) {
-    final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+  private static PsiFile loadContentAsFile(Project project, InputStream stream, LanguageFileType fileType) throws IOException {
+    final StringBuilder builder = new StringBuilder();
     try {
       int nextByte;
-      while ((nextByte = antlibStream.read()) >= 0) {
+      while ((nextByte = stream.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);
-              }
-            }
-          }
-        }
-      }
-    }
-    catch (IOException e) {
-      LOG.info(e);
     }
     finally {
-      StringBuilderSpinAllocator.dispose(builder);
+      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<String> processedAntlibs = new HashSet<String>();
+    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<AntDomTypeDef> defs = new ArrayList<AntDomTypeDef>();
+        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);
+          }
+        }
+      }
+    }
+
+  }
 }