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 c3f992f572da..ad61552763dd 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java @@ -53,7 +53,7 @@ public class AntDomExtender extends DomExtender{ TAG_MAPPING.put("dirset", AntDomDirSet.class); TAG_MAPPING.put("filelist", AntDomFileList.class); TAG_MAPPING.put("path", AntDomPath.class); - TAG_MAPPING.put("classpath", AntDomPath.class); + TAG_MAPPING.put("classpath", AntDomClasspath.class); TAG_MAPPING.put("typedef", AntDomTypeDef.class); TAG_MAPPING.put("taskdef", AntDomTaskdef.class); TAG_MAPPING.put("presetdef", AntDomPresetDef.class); @@ -62,7 +62,6 @@ public class AntDomExtender extends DomExtender{ TAG_MAPPING.put("antlib", AntDomAntlib.class); TAG_MAPPING.put("ant", AntDomAnt.class); TAG_MAPPING.put("antcall", AntDomAntCall.class); - TAG_MAPPING.put("classpath", AntDomClasspath.class); TAG_MAPPING.put("available", AntDomPropertyDefiningTaskWithDefaultValue.class); TAG_MAPPING.put("condition", AntDomPropertyDefiningTaskWithDefaultValue.class); TAG_MAPPING.put("uptodate", AntDomPropertyDefiningTaskWithDefaultValue.class); @@ -100,6 +99,7 @@ public class AntDomExtender extends DomExtender{ AntIntrospector classBasedIntrospector = null; final Hashtable coreTaskDefs = reflected.getTaskDefinitions(); final Hashtable coreTypeDefs = reflected.getDataTypeDefinitions(); + final boolean isCustom = antDomElement instanceof AntDomCustomElement; if ("project".equals(tagName)) { classBasedIntrospector = getIntrospector(reflected.getProject().getClass()); } @@ -107,7 +107,7 @@ public class AntDomExtender extends DomExtender{ classBasedIntrospector = getIntrospector(reflected.getTargetClass()); } else { - if (antDomElement instanceof AntDomCustomElement) { + if (isCustom) { final AntDomCustomElement custom = (AntDomCustomElement)antDomElement; final Class definitionClass = custom.getDefinitionClass(); if (definitionClass != null) { @@ -140,13 +140,13 @@ public class AntDomExtender extends DomExtender{ parentIntrospector = new ClassIntrospectorAdapter(classBasedIntrospector); } else { - if (antDomElement instanceof AntDomCustomElement) { + if (isCustom) { final AntDomNamedElement declaringElement = ((AntDomCustomElement)antDomElement).getDeclaringElement(); if (declaringElement instanceof AntDomMacroDef) { parentIntrospector = new MacrodefIntrospectorAdapter((AntDomMacroDef)declaringElement); } else if (declaringElement instanceof AntDomMacrodefElement){ - parentIntrospector = ContainerElementIntrospector.INSTANCE; + parentIntrospector = new MacrodefElementOccurrenceIntrospectorAdapter((AntDomMacrodefElement)declaringElement)/*ContainerElementIntrospector.INSTANCE*/; } else if (declaringElement instanceof AntDomScriptDef) { parentIntrospector = new ScriptdefIntrospectorAdapter((AntDomScriptDef)declaringElement); @@ -192,7 +192,7 @@ public class AntDomExtender extends DomExtender{ final DomExtension extension = registerChild(registrar, genericInfo, nestedElementName); if (extension != null) { Class type = parentIntrospector.getNestedElementType(nestedElementName); - if ("java.lang.Object".equals(type.getName())) { + if (type != null && "java.lang.Object".equals(type.getName())) { type = null; // hack to support badly written tasks } if (type == null) { @@ -203,13 +203,10 @@ public class AntDomExtender extends DomExtender{ if (type != null) { extension.putUserData(ELEMENT_IMPL_CLASS_KEY, type); } - AntDomElement.Role role = null; + AntDomElement.Role role = AntDomElement.Role.DATA_TYPE; if (coreTaskDefs != null && coreTaskDefs.containsKey(nestedElementName)) { role = AntDomElement.Role.TASK; } - else if (coreTypeDefs != null && coreTypeDefs.containsKey(nestedElementName)) { - role = AntDomElement.Role.DATA_TYPE; - } else if (type != null && isAssignableFrom(Task.class.getName(), type)) { role = AntDomElement.Role.TASK; } @@ -502,6 +499,94 @@ public class AntDomExtender extends DomExtender{ } } + private static class MacrodefElementOccurrenceIntrospectorAdapter extends AbstractIntrospector { + private final AntDomMacrodefElement myElement; + private volatile List myContexts; + private volatile Map myChildrenMap; + + private MacrodefElementOccurrenceIntrospectorAdapter(AntDomMacrodefElement element) { + myElement = element; + } + + public boolean isContainer() { + final List contexts = getContexts(); + for (AbstractIntrospector context : contexts) { + if (!context.isContainer()) { + return false; + } + } + return true; + } + + @NotNull + public Iterator getNestedElementsIterator() { + return getNestedElementsMap().keySet().iterator(); + } + + public Class getNestedElementType(String elementName) { + return getNestedElementsMap().get(elementName); + } + + private Map getNestedElementsMap() { + if (myChildrenMap != null) { + return myChildrenMap; + } + final List contexts = getContexts(); + Map names = null; + for (AbstractIntrospector context : contexts) { + if (context.isContainer()) { + continue; + } + final Set set = new HashSet(); + for (Iterator it = context.getNestedElementsIterator();it.hasNext();) { + final String name = it.next(); + set.add(name); + } + if (names == null) { + names = new HashMap(); + for (String s : set) { + names.put(s, context.getNestedElementType(s)); + } + } + else { + names.keySet().retainAll(set); + } + } + final Map result = names == null ? Collections.emptyMap() : names; + return myChildrenMap = result; + } + + private List getContexts() { + if (myContexts != null) { + return myContexts; + } + final List parents = new ArrayList(); + final AntDomMacroDef macroDef = myElement.getParentOfType(AntDomMacroDef.class, true); + if (macroDef != null) { + final AntDomSequentialTask body = macroDef.getMacroBody(); + if (body != null) { + body.accept(new AntDomRecursiveVisitor() { + public void visitAntDomCustomElement(AntDomCustomElement custom) { + if (myElement.equals(custom.getDeclaringElement())) { + final AntDomElement parent = custom.getParentOfType(AntDomElement.class, true); + if (parent != null) { + final Class type = parent.getChildDescription().getUserData(ELEMENT_IMPL_CLASS_KEY); + if (type != null) { + final AntIntrospector antIntrospector = AntIntrospector.getInstance(type); + if (antIntrospector != null) { + parents.add(new ClassIntrospectorAdapter(antIntrospector)); + } + } + } + } + } + }); + } + } + return myContexts = parents; + } + } + private static class ScriptdefIntrospectorAdapter extends AbstractIntrospector { private final AntDomScriptDef myScriptDef; diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomMacroDef.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomMacroDef.java index 81f65c663ada..329ebf18f683 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomMacroDef.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomMacroDef.java @@ -15,10 +15,7 @@ */ package com.intellij.lang.ant.dom; -import com.intellij.util.xml.Attribute; -import com.intellij.util.xml.GenericAttributeValue; -import com.intellij.util.xml.SubTag; -import com.intellij.util.xml.SubTagList; +import com.intellij.util.xml.*; import java.util.List; @@ -38,4 +35,8 @@ public abstract class AntDomMacroDef extends AntDomNamedElement{ @SubTag("text") public abstract AntDomMacrodefText getTextElement(); + + @SubTag("sequential") + @Required + public abstract AntDomSequentialTask getMacroBody(); } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomSequentialTask.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomSequentialTask.java new file mode 100644 index 000000000000..329fb4b0f64a --- /dev/null +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomSequentialTask.java @@ -0,0 +1,23 @@ +/* + * 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; + +/** + * @author Eugene Zhuravlev + * Date: Sep 2, 2010 + */ +public abstract class AntDomSequentialTask extends AntDomElement{ +} diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTypeDef.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTypeDef.java index 4a1ed27da9ad..56f7eee9aedd 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTypeDef.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTypeDef.java @@ -51,7 +51,7 @@ public abstract class AntDomTypeDef extends AntDomCustomClasspathComponent{ return CustomAntElementsRegistry.getInstance(getAntProject()).hasTypeLoadingErrors(this); } - private boolean isTask(final Class clazz) { + public boolean isTask(final Class clazz) { if ("taskdef".equals(getXmlTag().getName())) { // in taskdef, the adapter is always set to Task return true; } 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 e607f8ede571..a1759abe83be 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/CustomAntElementsRegistry.java @@ -85,13 +85,18 @@ public class CustomAntElementsRegistry { @NotNull public Set getCompletionVariants(AntDomElement parentElement) { + if (parentElement instanceof AntDomCustomElement) { + // this case is already handled in AntDomExtender when defining children + return Collections.emptySet(); + } final Set result = new HashSet(); final Pair contextMacroOrScriptDef = getContextMacroOrScriptDef(parentElement); final AntDomMacroDef restrictToMacroDef = contextMacroOrScriptDef != null? contextMacroOrScriptDef.getFirst() : null; final AntDomScriptDef restrictToScriptDef = contextMacroOrScriptDef != null? contextMacroOrScriptDef.getSecond() : null; - - for (XmlName xmlName : myCustomElements.keySet()) { + final boolean parentIsDataType = parentElement.isDataType(); + + for (final XmlName xmlName : myCustomElements.keySet()) { final AntDomNamedElement declaringElement = myDeclarations.get(xmlName); if (declaringElement instanceof AntDomMacrodefElement) { if (restrictToMacroDef == null || !restrictToMacroDef.equals(declaringElement.getParentOfType(AntDomMacroDef.class, true))) { @@ -103,6 +108,25 @@ public class CustomAntElementsRegistry { continue; } } + + if (declaringElement != null) { + if (declaringElement.equals(restrictToMacroDef) || declaringElement.equals(restrictToScriptDef)) { + continue; + } + } + + if (parentIsDataType) { + if (declaringElement instanceof AntDomMacroDef || declaringElement instanceof AntDomScriptDef || declaringElement instanceof AntDomTaskdef) { + continue; + } + if (declaringElement instanceof AntDomTypeDef) { + final AntDomTypeDef typedef = (AntDomTypeDef)declaringElement; + final Class clazz = myCustomElements.get(xmlName); + if (clazz != null && typedef.isTask(clazz)) { + continue; + } + } + } result.add(xmlName); }