diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/AntFile.java b/plugins/ant/src/com/intellij/lang/ant/psi/AntFile.java index 3b9664ccbf2d..1d26c13719f9 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/AntFile.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/AntFile.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.xml.XmlFile; +import com.intellij.util.Processor; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,6 +74,8 @@ public interface AntFile extends PsiFile, AntElement, PsiNamedElement, Modificat @Nullable AntProperty getProperty(@NonNls final String name); + void processAllProperties(@NonNls final String name, Processor processor); + void setProperty(final String name, final AntProperty element); @NotNull diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntFileImpl.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntFileImpl.java index 7dbc8a0a48cd..c707a2af5d6b 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntFileImpl.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntFileImpl.java @@ -50,6 +50,7 @@ import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.Alarm; import com.intellij.util.LocalTimeCounter; +import com.intellij.util.Processor; import com.intellij.util.StringBuilderSpinAllocator; import org.apache.tools.ant.Project; import org.apache.tools.ant.TaskContainer; @@ -101,7 +102,7 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { private ClassLoader myClassLoader; private Hashtable myProjectProperties; private boolean myNeedPropertiesRebuild = false; - private Map myProperties; + private Map> myProperties; private volatile AntProperty[] myPropertiesArray; private volatile PropertiesWatcher myDependentFilesWatcher; private List myEnvPrefixes; @@ -219,7 +220,16 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { copy.myProjectElements = myProjectElements == null ? null : new HashMap(myProjectElements); copy.myClassLoader = myClassLoader; copy.myExternalProperties = myExternalProperties != null? new HashMap(myExternalProperties) : null; - copy.myProperties = myProperties != null? new HashMap(myProperties) : null; + if (myProperties != null) { + final HashMap> map = new HashMap>(); + for (Map.Entry> entry : myProperties.entrySet()) { + map.put(entry.getKey(), new ArrayList(entry.getValue())); + } + copy.myProperties = map; + } + else { + copy.myProperties = null; + } return copy; } @@ -411,6 +421,10 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { return getPropertyRecursively(name, new HashSet()); } + public void processAllProperties(@NonNls String name, Processor processor) { + processAllPropertiesRecursively(name, new HashSet(), processor); + } + @Nullable private AntProperty getPropertyRecursively(final String name, final Set processed) { if (name == null || processed.contains(this)) { @@ -422,7 +436,13 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { AntProperty antProperty; synchronized (PsiLock.LOCK) { - antProperty = myProperties != null ? myProperties.get(name) : null; + if (myProperties != null) { + final List antProperties = myProperties.get(name); + antProperty = antProperties != null && !antProperties.isEmpty()? antProperties.get(0) : null; + } + else { + antProperty = null; + } } if (antProperty == null) { @@ -444,6 +464,43 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { } } + private void processAllPropertiesRecursively(final String name, final Set processed, Processor processor) { + if (name == null || processed.contains(this)) { + return; + } + processed.add(this); + + try { + synchronized (PsiLock.LOCK) { + if (myProperties != null) { + final List antProperties = myProperties.get(name); + if (antProperties != null) { + for (AntProperty property : antProperties) { + if (!processor.process(property)) { + return; + } + } + } + } + } + + final AntProject antProject = getAntProject(); + if (antProject != null) { + for (AntFile imported : antProject.getImportedFiles()) { + if (imported instanceof AntFileImpl) { + ((AntFileImpl)imported).processAllPropertiesRecursively(name, processed, processor); + } + else { + imported.processAllProperties(name, processor); + } + } + } + } + finally { + processed.remove(this); + } + } + public void buildPropertiesIfNeeded() { if (myNeedPropertiesRebuild || (myDependentFilesWatcher != null && myDependentFilesWatcher.needRebuildProperties())) { buildPropertiesMap(); @@ -453,7 +510,7 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { private void buildPropertiesMap() { myNeedPropertiesRebuild = false; myDependentFilesWatcher = null; - myProperties = new HashMap(); + myProperties = new HashMap>(); myPropertiesArray = null; loadPredefinedProperties(myProjectProperties, myExternalProperties); final List dependentFiles = PropertiesBuilder.defineProperties(this); @@ -577,7 +634,7 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { return super.getNavigationElement(); } }; - myProperties.put(property.getName(), property); + appendProperty(property.getName(), property); } } finally { @@ -591,17 +648,22 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { if (myNeedPropertiesRebuild) { buildPropertiesMap(); } - if (myProperties == null) { - myProperties = new HashMap(); - myPropertiesArray = null; - } - if (!myProperties.containsKey(name)) { - myProperties.put(name, element); - myPropertiesArray = null; - } + appendProperty(name, element); } } + private void appendProperty(String name, AntProperty element) { + if (myProperties == null) { + myProperties = new HashMap>(); + } + List props = myProperties.get(name); + if (props == null) { + myProperties.put(name, props = new ArrayList()); + } + props.add(element); + myPropertiesArray = null; + } + @NotNull public AntProperty[] getProperties() { synchronized (PsiLock.LOCK) { @@ -612,7 +674,13 @@ public class AntFileImpl extends LightPsiFileBase implements AntFile { return AntProperty.EMPTY_ARRAY; } if (myPropertiesArray == null) { - myPropertiesArray = myProperties.values().toArray(new AntProperty[myProperties.size()]); + final List props = new ArrayList(myProperties.size()); + for (List list : myProperties.values()) { + if (!list.isEmpty()) { + props.add(list.get(0)); + } + } + myPropertiesArray = props.toArray(new AntProperty[props.size()]); } return myPropertiesArray; } diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntPropertyImpl.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntPropertyImpl.java index 5617d379736f..21060dafd89c 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntPropertyImpl.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntPropertyImpl.java @@ -26,10 +26,10 @@ import com.intellij.psi.PsiLock; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.StringSetSpinAllocator; -import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,6 +42,13 @@ import java.util.*; public class AntPropertyImpl extends AntTaskImpl implements AntProperty { private PsiElement myPropertiesFile; + private static final Set ourPropertyDefiningTags = new HashSet(Arrays.asList( + AntFileImpl.PROPERTY, + "param", + "condition", + "input", + "available" + )); public AntPropertyImpl(final AntElement parent, final XmlTag sourceElement, @@ -111,7 +118,7 @@ public class AntPropertyImpl extends AntTaskImpl implements AntProperty { synchronized (PsiLock.LOCK) { final XmlTag se = getSourceElement(); final String tagName = se.getName(); - if (AntFileImpl.PROPERTY.equals(tagName) || "param".equals(tagName) || "condition".equals(tagName) || "input".equals(tagName)) { // todo: support conditions separately + if (ourPropertyDefiningTags.contains(tagName)) { // todo: support conditions separately String value = getPropertyValue(); if (value == null && propName != null) { final PropertiesFile propertiesFile = getPropertiesFile(); @@ -241,6 +248,20 @@ public class AntPropertyImpl extends AntTaskImpl implements AntProperty { @Nullable private String getPropertyValue() { final XmlTag sourceElement = getSourceElement(); + final String tagName = sourceElement.getName(); + if ("available".equals(tagName)) { + // check only 'file' tag because others could be examined only at runtime + final String filePath = sourceElement.getAttributeValue("file"); + if (filePath != null) { + final String _filePath = computeAttributeValue(filePath); + if (_filePath != null) { + if (!new File(_filePath).exists()) { + return null; + } + } + } + } + String value = sourceElement.getAttributeValue("value"); if (value == null) { value = sourceElement.getAttributeValue("location"); diff --git a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntStructuredElementImpl.java b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntStructuredElementImpl.java index 5b3e92a16637..5b35af82c062 100644 --- a/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntStructuredElementImpl.java +++ b/plugins/ant/src/com/intellij/lang/ant/psi/impl/AntStructuredElementImpl.java @@ -24,6 +24,7 @@ import com.intellij.lang.ant.psi.introspection.AntTypeDefinition; import com.intellij.lang.ant.psi.introspection.AntTypeId; import com.intellij.lang.ant.psi.introspection.impl.AntTypeDefinitionImpl; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -33,10 +34,7 @@ import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.SpinAllocator; -import com.intellij.util.StringBuilderSpinAllocator; -import com.intellij.util.StringSetSpinAllocator; +import com.intellij.util.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -527,28 +525,37 @@ public class AntStructuredElementImpl extends AntElementImpl implements AntStruc continue; } final String prop = value.substring(startProp + 2, endProp); - final AntProperty propElement = antFile.getProperty(prop); - String resolvedValue = null; - if (propElement != null) { - resolvedValue = propElement.getValue(prop); - if (resolvedValue != null) { - if (elementStack.contains(new Pair(propElement, resolvedValue))) { - return value; // prevent cycles + final Ref resolvedValueRef = new Ref(null); + final Ref shouldReturnOriginalValue = new Ref(Boolean.FALSE); + antFile.processAllProperties(prop, new Processor() { + public boolean process(AntProperty antProperty) { + final String resolvedValue = antProperty.getValue(prop); + if (resolvedValue == null) { + return true; } - resolvedValue = ((AntStructuredElementImpl)propElement).computeAttributeValue(resolvedValue, elementStack, antConfig); + if (elementStack.contains(new Pair(antProperty, resolvedValue))) { + shouldReturnOriginalValue.set(Boolean.TRUE); + } + else { + resolvedValueRef.set(((AntStructuredElementImpl)antProperty).computeAttributeValue(resolvedValue, elementStack, antConfig)); + } + return false; } + }); + if (shouldReturnOriginalValue.get()) { + return value; // prevent cycles } - if (resolvedValue == null) { + if (resolvedValueRef.get() == null) { startProp += 2; } else { - if (resolvedValue.equals(value) /*prevent tail recursion*/) { + if (resolvedValueRef.get().equals(value) /*prevent tail recursion*/) { return value; } final StringBuilder builder = StringBuilderSpinAllocator.alloc(); try { builder.append(value, 0, startProp); - builder.append(resolvedValue); + builder.append(resolvedValueRef.get()); if (endProp < value.length() - 1) { builder.append(value, endProp + 1, value.length()); }