From 6faa0055b18c306482bdb3dfdb0217675a972557 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 28 Jun 2010 21:44:21 +0400 Subject: [PATCH] 1. path-like structures support 2. documentation provider for ant's dom --- .../ant/doc/AntDomDocumentationProvider.java | 285 ++++++++++++++++++ .../intellij/lang/ant/dom/AntDomDirSet.java | 81 ++++- .../intellij/lang/ant/dom/AntDomElement.java | 5 + .../intellij/lang/ant/dom/AntDomExtender.java | 41 ++- .../intellij/lang/ant/dom/AntDomFileList.java | 50 ++- .../intellij/lang/ant/dom/AntDomFileSet.java | 83 ++++- .../lang/ant/dom/AntDomFilesProviderImpl.java | 80 ++++- .../com/intellij/lang/ant/dom/AntDomPath.java | 58 +++- .../intellij/lang/ant/dom/AntDomProject.java | 2 +- .../intellij/lang/ant/dom/AntDomTarget.java | 3 + 10 files changed, 664 insertions(+), 24 deletions(-) create mode 100644 plugins/ant/src/com/intellij/lang/ant/doc/AntDomDocumentationProvider.java diff --git a/plugins/ant/src/com/intellij/lang/ant/doc/AntDomDocumentationProvider.java b/plugins/ant/src/com/intellij/lang/ant/doc/AntDomDocumentationProvider.java new file mode 100644 index 000000000000..258a1560baba --- /dev/null +++ b/plugins/ant/src/com/intellij/lang/ant/doc/AntDomDocumentationProvider.java @@ -0,0 +1,285 @@ +/* + * 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.doc; + +import com.intellij.lang.ant.AntSupport; +import com.intellij.lang.ant.config.impl.AntInstallation; +import com.intellij.lang.ant.dom.AntDomElement; +import com.intellij.lang.ant.dom.AntDomProject; +import com.intellij.lang.ant.dom.AntDomTarget; +import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.lang.documentation.DocumentationProvider; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.*; +import com.intellij.pom.PomTarget; +import com.intellij.pom.PomTargetPsiElement; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomTarget; +import com.intellij.util.xml.reflect.DomChildrenDescription; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +public class AntDomDocumentationProvider implements DocumentationProvider { + + public String generateDoc(PsiElement element, PsiElement originalElement) { + final String mainDoc = getMainDocumentation(originalElement); + final String additionalDoc = getAdditionalDocumentation(originalElement); + if (mainDoc == null && additionalDoc == null) { + return null; + } + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + if (additionalDoc != null) { + builder.append(additionalDoc); + } + if (mainDoc != null) { + builder.append(mainDoc); + } + return builder.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + + @Nullable + private static String getMainDocumentation(PsiElement elem) { + final VirtualFile helpFile = getHelpFile(elem); + if (helpFile != null) { + try { + return VfsUtil.loadText(helpFile); + } + catch (IOException ignored) { + } + } + return null; + } + + @Nullable + private static String getAdditionalDocumentation(PsiElement elem) { + final XmlTag xmlTag = PsiTreeUtil.getParentOfType(elem, XmlTag.class); + if (xmlTag == null) { + return null; + } + final AntDomElement antElement = AntSupport.getAntDomElement(xmlTag); + if (antElement instanceof AntFilesProvider) { + final List list = ((AntFilesProvider)antElement).getFiles(new HashSet()); + if (list.size() > 0) { + final @NonNls StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + final XmlTag tag = antElement.getXmlTag(); + if (tag != null) { + builder.append(""); + builder.append(tag.getName()); + builder.append(":"); + } + for (File file : list) { + if (builder.length() > 0) { + builder.append("
"); + } + builder.append(file.getPath()); + } + return builder.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + } + return null; + } + + @Nullable + private static VirtualFile getHelpFile(final PsiElement element) { + final XmlTag xmlTag = PsiTreeUtil.getParentOfType(element, XmlTag.class); + if (xmlTag == null) { + return null; + } + final AntDomElement antElement = AntSupport.getAntDomElement(xmlTag); + if (antElement == null) { + return null; + } + final AntDomProject antProject = antElement.getAntProject(); + if (antProject == null) { + return null; + } + final AntInstallation installation = antProject.getAntInstallation(); + if (installation == null) { + return null; // not configured properly and bundled installation missing + } + final String antHomeDir = AntInstallation.HOME_DIR.get(installation.getProperties()); + + if (antHomeDir == null) { + return null; + } + + @NonNls String path = antHomeDir + "/docs/manual"; + String url; + if (new File(path).exists()) { + url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(path)); + } + else { + path = antHomeDir + "/docs.zip"; + if (new File(path).exists()) { + url = VirtualFileManager.constructUrl(JarFileSystem.PROTOCOL, FileUtil.toSystemIndependentName(path) + JarFileSystem.JAR_SEPARATOR + "docs/manual"); + } + else { + return null; + } + } + + final VirtualFile documentationRoot = VirtualFileManager.getInstance().findFileByUrl(url); + if (documentationRoot == null) { + return null; + } + + return getHelpFile(antElement, documentationRoot); + } + + @NonNls private static final String CORE_TASKS_FOLDER_NAME = "CoreTasks"; + @NonNls private static final String CORE_TYPES_FOLDER_NAME = "CoreTypes"; + @NonNls private static final String OPTIONAL_TYPES_FOLDER_NAME = "OptionalTypes"; + @NonNls private static final String OPTIONAL_TASKS_FOLDER_NAME = "OptionalTasks"; + + @Nullable + private static VirtualFile getHelpFile(AntDomElement antElement, final VirtualFile documentationRoot) { + final XmlTag xmlTag = antElement.getXmlTag(); + if (xmlTag == null) { + return null; + } + @NonNls final String helpFileShortName = "/" + xmlTag.getName() + ".html"; + + VirtualFile candidateHelpFile = documentationRoot.findFileByRelativePath(CORE_TASKS_FOLDER_NAME + helpFileShortName); + if (candidateHelpFile != null) { + return candidateHelpFile; + } + + candidateHelpFile = documentationRoot.findFileByRelativePath(OPTIONAL_TASKS_FOLDER_NAME + helpFileShortName); + if (candidateHelpFile != null) { + return candidateHelpFile; + } + + candidateHelpFile = documentationRoot.findFileByRelativePath(CORE_TYPES_FOLDER_NAME + helpFileShortName); + if (candidateHelpFile != null) { + return candidateHelpFile; + } + + candidateHelpFile = documentationRoot.findFileByRelativePath(OPTIONAL_TYPES_FOLDER_NAME + helpFileShortName); + if (candidateHelpFile != null) { + return candidateHelpFile; + } + + if(antElement instanceof AntDomTarget|| antElement instanceof AntDomProject) { + candidateHelpFile = documentationRoot.findFileByRelativePath("using.html"); + if (candidateHelpFile != null) { + return candidateHelpFile; + } + } + + return null; + } + + @Nullable + public String getQuickNavigateInfo(PsiElement element) { // todo! + if (element instanceof PomTargetPsiElement) { + final PomTarget pomTarget = ((PomTargetPsiElement)element).getTarget(); + if (pomTarget instanceof DomTarget) { + final DomElement domElement = ((DomTarget)pomTarget).getDomElement(); + if (domElement instanceof AntDomTarget) { + final AntDomTarget antTarget = (AntDomTarget)domElement; + final String description = antTarget.getDescription().getRawText(); + if (description != null && description.length() > 0) { + final String targetName = antTarget.getName().getRawText(); + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + builder.append("Target"); + if (targetName != null) { + builder.append(" \"").append(targetName).append("\""); + } + final XmlElement xmlElement = antTarget.getXmlElement(); + if (xmlElement != null) { + final PsiFile containingFile = xmlElement.getContainingFile(); + if (containingFile != null) { + final String fileName = containingFile.getName(); + builder.append(" [").append(fileName).append("]"); + } + } + return builder.append(" ").append(description).toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + } + } + else if (pomTarget instanceof DomChildrenDescription) { + final DomChildrenDescription description = (DomChildrenDescription)pomTarget; + final Type type = description.getType(); + if (type instanceof Class && AntDomElement.class.isAssignableFrom(((Class)type))) { + final String elemName = description.getName(); + if (elemName != null) { + final AntDomElement.Role role = description.getUserData(AntDomElement.ROLE); + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + if (role == AntDomElement.Role.TASK) { + builder.append("Task "); + } + else if (role == AntDomElement.Role.DATA_TYPE) { + builder.append("Data structure "); + } + builder.append(elemName); + return builder.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + } + } + } + return null; + } + + public List getUrlFor(PsiElement element, PsiElement originalElement) { + final VirtualFile helpFile = getHelpFile(originalElement); + if (helpFile == null || !(helpFile.getFileSystem() instanceof LocalFileSystem)) { + return null; + } + return Collections.singletonList(helpFile.getUrl()); + } + + public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { + return null; + } + + public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { + return null; + } +} diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomDirSet.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomDirSet.java index 95836354ce06..9e8fa85bed9c 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomDirSet.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomDirSet.java @@ -16,9 +16,16 @@ package com.intellij.lang.ant.dom; import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.Convert; +import com.intellij.util.xml.GenericAttributeValue; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,8 +35,78 @@ import java.util.Set; * Date: Jun 22, 2010 */ public abstract class AntDomDirSet extends AntDomFilesProviderImpl{ + + @Attribute("dir") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getDir(); + + @Attribute("file") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getFile(); + @NotNull - public List getFiles(Set processed) { - return Collections.emptyList(); // todo + protected List getFiles(@Nullable AntDomPattern pattern, Set processed) { + assert pattern != null; + final File singleFile = getCanonicalFile(getFile().getStringValue()); + if (singleFile == null || pattern.hasIncludePatterns()) { + // if singleFile is specified, there are no implicit includes + final File root = getCanonicalFile(getDir().getStringValue()); + if (root != null) { + final ArrayList files = new ArrayList(); + if (singleFile != null && singleFile.isDirectory()) { + files.add(singleFile); + } + new FilesCollector().collectFiles(files, root, "", pattern); + return files; + } + } + + if (singleFile != null && singleFile.isDirectory()) { + Collections.singletonList(singleFile); + } + + return Collections.emptyList(); } + + + private static class FilesCollector { + private static final int MAX_DIRS_TO_PROCESS = 100; + private int myDirsProcessed = 0; + + public void collectFiles(List container, File from, String relativePath, final AntDomPattern pattern) { + if (myDirsProcessed > MAX_DIRS_TO_PROCESS) { + return; + } + myDirsProcessed++; + final File[] children = from.listFiles(); + if (children != null) { + for (File child : children) { + if (child.isDirectory()) { + final String childPath = makePath(relativePath, child.getName()); + if (pattern.acceptPath(childPath)) { + container.add(child); + } + if (pattern.couldBeIncluded(childPath)) { + collectFiles(container, child, childPath, pattern); + } + } + } + } + } + + private static String makePath(final String parentPath, final String name) { + if (parentPath.length() == 0) { + return name; + } + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + return builder.append(parentPath).append("/").append(name).toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + + } + } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomElement.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomElement.java index fdea304f0027..4b3fcc3c0e17 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomElement.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomElement.java @@ -15,6 +15,7 @@ */ package com.intellij.lang.ant.dom; +import com.intellij.openapi.util.Key; import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.*; @@ -26,6 +27,10 @@ import java.util.List; */ @SuppressWarnings({"AbstractClassNeverImplemented"}) public abstract class AntDomElement implements DomElement { + public static enum Role { + TASK, DATA_TYPE + } + public static final Key ROLE = Key.create("element_role"); @Attribute("id") public abstract GenericAttributeValue getId(); 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 b33b077701ed..f25bd558c589 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomExtender.java @@ -28,13 +28,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Enumeration; -import java.util.Map; +import java.util.Hashtable; /** * @author Eugene Zhuravlev * Date: Apr 9, 2010 */ public class AntDomExtender extends DomExtender{ + public void registerExtensions(@NotNull AntDomElement antDomElement, @NotNull DomExtensionsRegistrar registrar) { final XmlElement xmlElement = antDomElement.getXmlElement(); if (xmlElement instanceof XmlTag) { @@ -45,6 +46,8 @@ public class AntDomExtender extends DomExtender{ final DomGenericInfo genericInfo = antDomElement.getGenericInfo(); AntIntrospector parentElementIntrospector = null; + final Hashtable taskDefs = reflected.getTaskDefinitions(); + final Hashtable dataTypeDefs = reflected.getDataTypeDefinitions(); if ("project".equals(tagName)) { parentElementIntrospector = getIntrospector(reflected.getProject().getClass()); } @@ -52,14 +55,12 @@ public class AntDomExtender extends DomExtender{ parentElementIntrospector = getIntrospector(reflected.getTargetClass()); } else { - final Map tasks = reflected.getTaskDefinitions(); - final Class taskClass = tasks.get(tagName); + final Class taskClass = taskDefs.get(tagName); if (taskClass != null) { parentElementIntrospector = getIntrospector(taskClass); } else { - final Map dataTypes = reflected.getDataTypeDefinitions(); - final Class dataClass = dataTypes.get(tagName); + final Class dataClass = dataTypeDefs.get(tagName); if (dataClass != null) { parentElementIntrospector = getIntrospector(dataClass); } @@ -67,7 +68,6 @@ public class AntDomExtender extends DomExtender{ } if (parentElementIntrospector != null) { - final Enumeration attributes = parentElementIntrospector.getAttributes(); while (attributes.hasMoreElements()) { registerAttribute(registrar, genericInfo, (String)attributes.nextElement()); @@ -75,17 +75,36 @@ public class AntDomExtender extends DomExtender{ // todo: handle custom tasks registered in typedefs if ("project".equals(tagName) || parentElementIntrospector.isContainer()) { // can contain any task or/and type definition - for (Object nestedName : reflected.getTaskDefinitions().keySet()) { - registerChild(registrar, genericInfo, (String)nestedName); + for (String nestedName : taskDefs.keySet()) { + final DomExtension extension = registerChild(registrar, genericInfo, nestedName); + if (extension != null) { + extension.putUserData(AntDomElement.ROLE, AntDomElement.Role.TASK); + } } - for (Object nestedTypeDef : reflected.getDataTypeDefinitions().keySet()) { - registerChild(registrar, genericInfo, (String)nestedTypeDef); + for (String nestedTypeDef : dataTypeDefs.keySet()) { + final DomExtension extension = registerChild(registrar, genericInfo, nestedTypeDef); + if (extension != null) { + extension.putUserData(AntDomElement.ROLE, AntDomElement.Role.DATA_TYPE); + } } } else { final Enumeration nested = parentElementIntrospector.getNestedElements(); while (nested.hasMoreElements()) { - registerChild(registrar, genericInfo, nested.nextElement()); + final String nestedElementName = nested.nextElement(); + final DomExtension extension = registerChild(registrar, genericInfo, nestedElementName); + if (extension != null) { + AntDomElement.Role role = null; + if (taskDefs.containsKey(nestedElementName)) { + role = AntDomElement.Role.TASK; + } + else if (dataTypeDefs.containsKey(nestedElementName)) { + role = AntDomElement.Role.DATA_TYPE; + } + if (role != null) { + extension.putUserData(AntDomElement.ROLE, role); + } + } } } } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileList.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileList.java index 8ad1a2120d04..d274c75cdd33 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileList.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileList.java @@ -16,9 +16,17 @@ package com.intellij.lang.ant.dom; import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.text.StringTokenizer; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.Convert; +import com.intellij.util.xml.GenericAttributeValue; +import com.intellij.util.xml.SubTagList; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,8 +36,46 @@ import java.util.Set; * Date: Jun 22, 2010 */ public abstract class AntDomFileList extends AntDomFilesProviderImpl{ + + @Attribute("dir") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getDir(); + + @Attribute("files") + public abstract GenericAttributeValue getFilesString(); + + @SubTagList("file") + public abstract List getFiles(); // todo: add filename completion relative to the filelist's basedir + + + @Nullable + protected AntDomPattern getAntPattern() { + return null; // not available for this data type + } + @NotNull - public List getFiles(Set processed) { - return Collections.emptyList(); // todo + protected List getFiles(@Nullable AntDomPattern pattern, Set processed) { + final File root = getCanonicalFile(getDir().getStringValue()); + if (root == null) { + return Collections.emptyList(); + } + + final ArrayList files = new ArrayList(); + + final String filenames = getFilesString().getStringValue(); + if (filenames != null) { + final StringTokenizer tokenizer = new StringTokenizer(filenames, ", \t\n\r\f", false); + while (tokenizer.hasMoreTokens()) { + files.add(new File(root, tokenizer.nextToken())); + } + } + + for (AntDomNamedElement child : getFiles()) { + final String fileName = child.getName().getStringValue(); + if (fileName != null) { + files.add(new File(root, fileName)); + } + } + return files; } } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileSet.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileSet.java index aee84f8b6ea7..207dbb6f57ac 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileSet.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileSet.java @@ -16,9 +16,16 @@ package com.intellij.lang.ant.dom; import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.Convert; +import com.intellij.util.xml.GenericAttributeValue; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,8 +35,78 @@ import java.util.Set; * Date: Jun 22, 2010 */ public abstract class AntDomFileSet extends AntDomFilesProviderImpl{ - @NotNull - public List getFiles(Set processed) { - return Collections.emptyList(); // todo + @Attribute("dir") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getDir(); + + @Attribute("file") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getFile(); + + @NotNull protected List getFiles(@Nullable AntDomPattern pattern, Set processed) { + assert pattern != null; + final File singleFile = getCanonicalFile(getFile().getStringValue()); + if (singleFile == null || pattern.hasIncludePatterns()) { + // if singleFile is specified, there are no implicit includes + final File root = getCanonicalFile(getDir().getStringValue()); + if (root != null) { + final ArrayList files = new ArrayList(); + if (singleFile != null) { + files.add(singleFile); + } + new FilesCollector().collectFiles(files, root, "", pattern); + return files; + } + } + if (singleFile != null) { + return Collections.singletonList(singleFile); + } + return Collections.emptyList(); } + + private static class FilesCollector { + private static final int MAX_DIRS_TO_PROCESS = 100; + private int myDirsProcessed = 0; + private boolean myDirCheckEnabled = false; + + public void collectFiles(List container, File from, String relativePath, final AntDomPattern pattern) { + if (myDirsProcessed > MAX_DIRS_TO_PROCESS) { + return; + } + final File[] children = from.listFiles(); + if (children != null && children.length > 0) { + if (myDirCheckEnabled) { + if (!pattern.couldBeIncluded(relativePath)) { + return; + } + } + else { + myDirCheckEnabled = true; + } + myDirsProcessed++; + for (File child : children) { + final String childPath = makePath(relativePath, child.getName()); + if (pattern.acceptPath(childPath)) { + container.add(child); + } + collectFiles(container, child, childPath, pattern); + } + } + } + + private static String makePath(final String parentPath, final String name) { + if (parentPath.length() == 0) { + return name; + } + final StringBuilder builder = StringBuilderSpinAllocator.alloc(); + try { + return builder.append(parentPath).append("/").append(name).toString(); + } + finally { + StringBuilderSpinAllocator.dispose(builder); + } + } + + } + } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFilesProviderImpl.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFilesProviderImpl.java index c05fe3726fcd..a7791df98cd2 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFilesProviderImpl.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFilesProviderImpl.java @@ -16,9 +16,14 @@ package com.intellij.lang.ant.dom; import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.GenericAttributeValue; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Set; @@ -28,8 +33,81 @@ import java.util.Set; * Date: Jun 22, 2010 */ public abstract class AntDomFilesProviderImpl extends AntDomElement implements AntFilesProvider{ + private volatile List myCachedFiles; + + @Attribute("defaultexcludes") + public abstract GenericAttributeValue getDefaultExcludes(); + @Attribute("casesensitive") + public abstract GenericAttributeValue getCaseSensitive(); + + @NotNull - public List getFiles(Set processed) { + public final List getFiles(Set processed) { + if (processed.contains(this)) { + return Collections.emptyList(); + } + List result = myCachedFiles; + if (result == null) { + myCachedFiles = result = getFilesImpl(processed); + } + return result; + } + + private List getFilesImpl(final Set processed) { + processed.add(this); + try { + final AntDomElement referenced = getRefId().getValue(); + if (referenced instanceof AntFilesProvider) { + return ((AntFilesProvider)referenced).getFiles(processed); + } + return getFiles(getAntPattern(), processed); + } + finally { + processed.remove(this); + } + } + + @Nullable + protected AntDomPattern getAntPattern() { + return AntDomPattern.create(this, shouldHonorDefaultExcludes(), matchPatternsCaseSensitive()); + } + + @NotNull + protected List getFiles(@Nullable AntDomPattern pattern, final Set processed) { return Collections.emptyList(); } + + + private boolean shouldHonorDefaultExcludes() { + @NonNls final String value = getDefaultExcludes().getRawText(); + return value == null || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true"); + } + + private boolean matchPatternsCaseSensitive() { + @NonNls final String value = getCaseSensitive().getRawText(); + return value == null || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true"); + } + + @Nullable + protected File getCanonicalFile(final String path) { + if (path == null) { + return null; + } + try { + final File file = new File(path); + if (file.isAbsolute()) { + return file.getCanonicalFile(); + } + // todo: context project for basedir resolution? + final String baseDir = getAntProject().getProjectBasedirPath(); + if (baseDir == null) { + return null; + } + return new File(baseDir, path).getCanonicalFile(); + } + catch (IOException e) { + return null; + } + } + } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomPath.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomPath.java index 3da0dfb2b4bb..9032ce806d6f 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomPath.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomPath.java @@ -16,10 +16,16 @@ package com.intellij.lang.ant.dom; import com.intellij.lang.ant.psi.AntFilesProvider; +import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.xml.Attribute; +import com.intellij.util.xml.Convert; +import com.intellij.util.xml.GenericAttributeValue; +import org.apache.tools.ant.PathTokenizer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -28,8 +34,52 @@ import java.util.Set; * Date: Jun 22, 2010 */ public abstract class AntDomPath extends AntDomFilesProviderImpl{ - @NotNull - public List getFiles(Set processed) { - return Collections.emptyList(); // todo + + @Attribute("location") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getLocation(); + + @Attribute("path") + @Convert(value = AntPathConverter.class) + public abstract GenericAttributeValue getPath(); + + @Nullable + protected AntDomPattern getAntPattern() { + return null; // not available + } + + @NotNull protected List getFiles(AntDomPattern pattern, Set processed) { + final List files = new ArrayList(); + final File baseDir = getCanonicalFile("."); + + addLocation(baseDir, files, getLocation().getStringValue()); + + final String pathString = getPath().getStringValue(); + if (pathString != null) { + final PathTokenizer tokenizer = new PathTokenizer(pathString); + while (tokenizer.hasMoreTokens()) { + addLocation(baseDir, files, tokenizer.nextToken()); + } + } + + for (AntDomElement child : getAntChildren()) { + if (child instanceof AntFilesProvider) { + files.addAll(((AntFilesProvider)child).getFiles(processed)); + } + } + return files; + } + + + private static void addLocation(final File baseDir, final List files, final String locationPath) { + if (locationPath != null) { + File file = new File(locationPath); + if (file.isAbsolute()) { + files.add(file); + } + else { + files.add(new File(baseDir, locationPath)); + } + } } } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomProject.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomProject.java index ef14871d4f4c..49860176b841 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomProject.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomProject.java @@ -137,7 +137,7 @@ public abstract class AntDomProject extends AntDomNamedElement implements Proper return myClassLoader; } - private AntInstallation getAntInstallation() { + public AntInstallation getAntInstallation() { final AntConfigurationBase configuration = AntConfigurationBase.getInstance(getXmlTag().getProject()); AntInstallation antInstallation = null; if (configuration != null) { diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTarget.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTarget.java index 897416b559d3..f1222d02998e 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTarget.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomTarget.java @@ -32,6 +32,9 @@ public abstract class AntDomTarget extends AntDomNamedElement{ @Attribute("unless") public abstract GenericAttributeValue getUnlessPropertyName(); + @Attribute("description") + public abstract GenericAttributeValue getDescription(); + @Attribute("depends") @Convert(value = AntDomTargetDependsListConverter.class) public abstract GenericAttributeValue getDependsList();