better completion logic for macrodef elements

This commit is contained in:
Eugene Zhuravlev
2010-09-02 19:34:20 +04:00
parent 4c551c12c7
commit 90e91801be
5 changed files with 150 additions and 17 deletions
@@ -53,7 +53,7 @@ public class AntDomExtender extends DomExtender<AntDomElement>{
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<AntDomElement>{
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<AntDomElement>{
AntIntrospector classBasedIntrospector = null;
final Hashtable<String,Class> coreTaskDefs = reflected.getTaskDefinitions();
final Hashtable<String, Class> 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<AntDomElement>{
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<AntDomElement>{
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<AntDomElement>{
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<AntDomElement>{
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<AntDomElement>{
}
}
private static class MacrodefElementOccurrenceIntrospectorAdapter extends AbstractIntrospector {
private final AntDomMacrodefElement myElement;
private volatile List<AbstractIntrospector> myContexts;
private volatile Map<String, Class> myChildrenMap;
private MacrodefElementOccurrenceIntrospectorAdapter(AntDomMacrodefElement element) {
myElement = element;
}
public boolean isContainer() {
final List<AbstractIntrospector> contexts = getContexts();
for (AbstractIntrospector context : contexts) {
if (!context.isContainer()) {
return false;
}
}
return true;
}
@NotNull
public Iterator<String> getNestedElementsIterator() {
return getNestedElementsMap().keySet().iterator();
}
public Class getNestedElementType(String elementName) {
return getNestedElementsMap().get(elementName);
}
private Map<String, Class> getNestedElementsMap() {
if (myChildrenMap != null) {
return myChildrenMap;
}
final List<AbstractIntrospector> contexts = getContexts();
Map<String, Class> names = null;
for (AbstractIntrospector context : contexts) {
if (context.isContainer()) {
continue;
}
final Set<String> set = new HashSet<String>();
for (Iterator<String> it = context.getNestedElementsIterator();it.hasNext();) {
final String name = it.next();
set.add(name);
}
if (names == null) {
names = new HashMap<String, Class>();
for (String s : set) {
names.put(s, context.getNestedElementType(s));
}
}
else {
names.keySet().retainAll(set);
}
}
final Map<String, Class> result = names == null ? Collections.<String, Class>emptyMap() : names;
return myChildrenMap = result;
}
private List<AbstractIntrospector> getContexts() {
if (myContexts != null) {
return myContexts;
}
final List<AbstractIntrospector> parents = new ArrayList<AbstractIntrospector>();
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;
@@ -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();
}
@@ -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{
}
@@ -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;
}
@@ -85,13 +85,18 @@ public class CustomAntElementsRegistry {
@NotNull
public Set<XmlName> getCompletionVariants(AntDomElement parentElement) {
if (parentElement instanceof AntDomCustomElement) {
// this case is already handled in AntDomExtender when defining children
return Collections.emptySet();
}
final Set<XmlName> result = new HashSet<XmlName>();
final Pair<AntDomMacroDef, AntDomScriptDef> 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);
}