| /* |
| * Copyright 2000-2012 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 org.jetbrains.idea.maven.dom; |
| |
| import com.intellij.lang.properties.IProperty; |
| import com.intellij.lang.properties.psi.PropertiesFile; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.ProjectFileIndex; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.VirtualFileManager; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.xml.XmlElement; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.psi.xml.XmlTag; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.xml.*; |
| import com.intellij.util.xml.reflect.DomCollectionChildDescription; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.maven.dom.model.*; |
| import org.jetbrains.idea.maven.model.MavenConstants; |
| import org.jetbrains.idea.maven.model.MavenId; |
| import org.jetbrains.idea.maven.model.MavenResource; |
| import org.jetbrains.idea.maven.project.MavenProject; |
| import org.jetbrains.idea.maven.project.MavenProjectsManager; |
| import org.jetbrains.idea.maven.utils.MavenLog; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class MavenDomUtil { |
| |
| private static final Key<Pair<Long, Set<VirtualFile>>> FILTERED_RESOURCES_ROOTS_KEY = Key.create("MavenDomUtil.FILTERED_RESOURCES_ROOTS"); |
| |
| // see http://maven.apache.org/settings.html |
| private static final Set<String> SUBTAGS_IN_SETTINGS_FILE = ContainerUtil.newHashSet("localRepository", "interactiveMode", |
| "usePluginRegistry", "offline", "pluginGroups", |
| "servers", "mirrors", "proxies", "profiles", |
| "activeProfiles"); |
| |
| public static boolean isMavenFile(PsiFile file) { |
| return isProjectFile(file) || isProfilesFile(file) || isSettingsFile(file); |
| } |
| |
| public static boolean isProjectFile(PsiFile file) { |
| if (!(file instanceof XmlFile)) return false; |
| |
| String name = file.getName(); |
| return name.equals(MavenConstants.POM_XML) || |
| name.endsWith(".pom") || |
| name.equals(MavenConstants.SUPER_POM_XML); |
| } |
| |
| public static boolean isProfilesFile(PsiFile file) { |
| if (!(file instanceof XmlFile)) return false; |
| |
| return MavenConstants.PROFILES_XML.equals(file.getName()); |
| } |
| |
| public static boolean isSettingsFile(PsiFile file) { |
| if (!(file instanceof XmlFile)) return false; |
| |
| String name = file.getName(); |
| if (!name.equals(MavenConstants.SETTINGS_XML)) return false; |
| |
| XmlTag rootTag = ((XmlFile)file).getRootTag(); |
| if (rootTag == null || !"settings".equals(rootTag.getName())) return false; |
| |
| String xmlns = rootTag.getAttributeValue("xmlns"); |
| if (xmlns != null) { |
| return xmlns.contains("maven"); |
| } |
| |
| boolean hasTag = false; |
| |
| for (PsiElement e = rootTag.getFirstChild(); e != null; e = e.getNextSibling()) { |
| if (e instanceof XmlTag) { |
| if (SUBTAGS_IN_SETTINGS_FILE.contains(((XmlTag)e).getName())) return true; |
| hasTag = true; |
| } |
| } |
| |
| return !hasTag; |
| } |
| |
| public static boolean isMavenFile(PsiElement element) { |
| return isMavenFile(element.getContainingFile()); |
| } |
| |
| @Nullable |
| public static Module findContainingMavenizedModule(@NotNull PsiFile psiFile) { |
| VirtualFile file = psiFile.getVirtualFile(); |
| if (file == null) return null; |
| |
| Project project = psiFile.getProject(); |
| |
| MavenProjectsManager manager = MavenProjectsManager.getInstance(project); |
| if (!manager.isMavenizedProject()) return null; |
| |
| ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); |
| |
| Module module = index.getModuleForFile(file); |
| if (module == null || !manager.isMavenizedModule(module)) return null; |
| return module; |
| } |
| |
| public static boolean isMavenProperty(PsiElement target) { |
| XmlTag tag = PsiTreeUtil.getParentOfType(target, XmlTag.class, false); |
| if (tag == null) return false; |
| return DomUtil.findDomElement(tag, MavenDomProperties.class) != null; |
| } |
| |
| public static String calcRelativePath(VirtualFile parent, VirtualFile child) { |
| String result = FileUtil.getRelativePath(parent.getPath(), child.getPath(), '/'); |
| if (result == null) { |
| MavenLog.LOG.warn("cannot calculate relative path for\nparent: " + parent + "\nchild: " + child); |
| result = child.getPath(); |
| } |
| return FileUtil.toSystemIndependentName(result); |
| } |
| |
| public static MavenDomParent updateMavenParent(MavenDomProjectModel mavenModel, MavenProject parentProject) { |
| MavenDomParent result = mavenModel.getMavenParent(); |
| |
| VirtualFile pomFile = DomUtil.getFile(mavenModel).getVirtualFile(); |
| Project project = mavenModel.getXmlElement().getProject(); |
| |
| MavenId parentId = parentProject.getMavenId(); |
| result.getGroupId().setStringValue(parentId.getGroupId()); |
| result.getArtifactId().setStringValue(parentId.getArtifactId()); |
| result.getVersion().setStringValue(parentId.getVersion()); |
| |
| if (!Comparing.equal(pomFile.getParent().getParent(), parentProject.getDirectoryFile())) { |
| result.getRelativePath().setValue(PsiManager.getInstance(project).findFile(parentProject.getFile())); |
| } |
| |
| return result; |
| } |
| |
| public static <T> T getImmediateParent(ConvertContext context, Class<T> clazz) { |
| DomElement parentElement = context.getInvocationElement().getParent(); |
| return clazz.isInstance(parentElement) ? (T)parentElement : null; |
| } |
| |
| @Nullable |
| public static VirtualFile getVirtualFile(@NotNull DomElement element) { |
| PsiFile psiFile = DomUtil.getFile(element); |
| return getVirtualFile(psiFile); |
| } |
| |
| @Nullable |
| public static VirtualFile getVirtualFile(@NotNull PsiElement element) { |
| PsiFile psiFile = element.getContainingFile(); |
| return getVirtualFile(psiFile); |
| } |
| |
| @Nullable |
| private static VirtualFile getVirtualFile(PsiFile psiFile) { |
| if (psiFile == null) return null; |
| psiFile = psiFile.getOriginalFile(); |
| return psiFile.getVirtualFile(); |
| } |
| |
| @Nullable |
| public static MavenProject findProject(@NotNull MavenDomProjectModel projectDom) { |
| XmlElement element = projectDom.getXmlElement(); |
| if (element == null) return null; |
| |
| VirtualFile file = getVirtualFile(element); |
| if (file == null) return null; |
| MavenProjectsManager manager = MavenProjectsManager.getInstance(element.getProject()); |
| return manager.findProject(file); |
| } |
| |
| @Nullable |
| public static MavenProject findContainingProject(@NotNull DomElement element) { |
| PsiElement psi = element.getXmlElement(); |
| return psi == null ? null : findContainingProject(psi); |
| } |
| |
| @Nullable |
| public static MavenProject findContainingProject(@NotNull PsiElement element) { |
| VirtualFile file = getVirtualFile(element); |
| if (file == null) return null; |
| MavenProjectsManager manager = MavenProjectsManager.getInstance(element.getProject()); |
| return manager.findContainingProject(file); |
| } |
| |
| @Nullable |
| public static MavenDomProjectModel getMavenDomProjectModel(@NotNull Project project, @NotNull VirtualFile file) { |
| return getMavenDomModel(project, file, MavenDomProjectModel.class); |
| } |
| |
| @Nullable |
| public static MavenDomProfiles getMavenDomProfilesModel(@NotNull Project project, @NotNull VirtualFile file) { |
| MavenDomProfilesModel model = getMavenDomModel(project, file, MavenDomProfilesModel.class); |
| if (model != null) return model.getProfiles(); |
| return getMavenDomModel(project, file, MavenDomProfiles.class); // try old-style model |
| } |
| |
| @Nullable |
| public static <T extends MavenDomElement> T getMavenDomModel(@NotNull Project project, |
| @NotNull VirtualFile file, |
| @NotNull Class<T> clazz) { |
| if (!file.isValid()) return null; |
| PsiFile psiFile = PsiManager.getInstance(project).findFile(file); |
| if (psiFile == null) return null; |
| return getMavenDomModel(psiFile, clazz); |
| } |
| |
| @Nullable |
| public static <T extends MavenDomElement> T getMavenDomModel(@NotNull PsiFile file, @NotNull Class<T> clazz) { |
| DomFileElement<T> fileElement = getMavenDomFile(file, clazz); |
| return fileElement == null ? null : fileElement.getRootElement(); |
| } |
| |
| @Nullable |
| private static <T extends MavenDomElement> DomFileElement<T> getMavenDomFile(@NotNull PsiFile file, @NotNull Class<T> clazz) { |
| if (!(file instanceof XmlFile)) return null; |
| return DomManager.getDomManager(file.getProject()).getFileElement((XmlFile)file, clazz); |
| } |
| |
| @Nullable |
| public static XmlTag findTag(@NotNull DomElement domElement, @NotNull String path) { |
| List<String> elements = StringUtil.split(path, "."); |
| if (elements.isEmpty()) return null; |
| |
| Pair<String, Integer> nameAndIndex = translateTagName(elements.get(0)); |
| String name = nameAndIndex.first; |
| Integer index = nameAndIndex.second; |
| |
| XmlTag result = domElement.getXmlTag(); |
| if (result == null || !name.equals(result.getName())) return null; |
| result = getIndexedTag(result, index); |
| |
| for (String each : elements.subList(1, elements.size())) { |
| nameAndIndex = translateTagName(each); |
| name = nameAndIndex.first; |
| index = nameAndIndex.second; |
| |
| result = result.findFirstSubTag(name); |
| if (result == null) return null; |
| result = getIndexedTag(result, index); |
| } |
| return result; |
| } |
| |
| private static final Pattern XML_TAG_NAME_PATTERN = Pattern.compile("(\\S*)\\[(\\d*)\\]\\z"); |
| |
| private static Pair<String, Integer> translateTagName(String text) { |
| String tagName = text.trim(); |
| Integer index = null; |
| |
| Matcher matcher = XML_TAG_NAME_PATTERN.matcher(tagName); |
| if (matcher.find()) { |
| tagName = matcher.group(1); |
| try { |
| index = Integer.parseInt(matcher.group(2)); |
| } |
| catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| return Pair.create(tagName, index); |
| } |
| |
| private static XmlTag getIndexedTag(XmlTag parent, Integer index) { |
| if (index == null) return parent; |
| |
| XmlTag[] children = parent.getSubTags(); |
| if (index < 0 || index >= children.length) return null; |
| return children[index]; |
| } |
| |
| @Nullable |
| public static PropertiesFile getPropertiesFile(@NotNull Project project, @NotNull VirtualFile file) { |
| PsiFile psiFile = PsiManager.getInstance(project).findFile(file); |
| if (!(psiFile instanceof PropertiesFile)) return null; |
| return (PropertiesFile)psiFile; |
| } |
| |
| @Nullable |
| public static IProperty findProperty(@NotNull Project project, @NotNull VirtualFile file, @NotNull String propName) { |
| PropertiesFile propertiesFile = getPropertiesFile(project, file); |
| return propertiesFile == null ? null : propertiesFile.findPropertyByKey(propName); |
| } |
| |
| @Nullable |
| public static PsiElement findPropertyValue(@NotNull Project project, @NotNull VirtualFile file, @NotNull String propName) { |
| IProperty prop = findProperty(project, file, propName); |
| return prop == null ? null : prop.getPsiElement().getFirstChild().getNextSibling().getNextSibling(); |
| } |
| |
| private static Set<VirtualFile> getFilteredResourcesRoots(@NotNull MavenProject mavenProject) { |
| Pair<Long, Set<VirtualFile>> cachedValue = mavenProject.getCachedValue(FILTERED_RESOURCES_ROOTS_KEY); |
| |
| if (cachedValue == null || cachedValue.first != VirtualFileManager.getInstance().getModificationCount()) { |
| Set<VirtualFile> set = null; |
| |
| for (MavenResource resource : ContainerUtil.concat(mavenProject.getResources(), mavenProject.getTestResources())) { |
| if (!resource.isFiltered()) continue; |
| |
| VirtualFile resourceDir = LocalFileSystem.getInstance().findFileByPath(resource.getDirectory()); |
| if (resourceDir == null) continue; |
| |
| if (set == null) { |
| set = new HashSet<VirtualFile>(); |
| } |
| |
| set.add(resourceDir); |
| } |
| |
| if (set == null) { |
| set = Collections.emptySet(); |
| } |
| |
| cachedValue = Pair.create(VirtualFileManager.getInstance().getModificationCount(), set); |
| mavenProject.putCachedValue(FILTERED_RESOURCES_ROOTS_KEY, cachedValue); |
| } |
| |
| return cachedValue.second; |
| } |
| |
| public static boolean isFilteredResourceFile(PsiElement element) { |
| VirtualFile file = getVirtualFile(element); |
| if (file == null) return false; |
| |
| MavenProjectsManager manager = MavenProjectsManager.getInstance(element.getProject()); |
| MavenProject mavenProject = manager.findContainingProject(file); |
| if (mavenProject == null) return false; |
| |
| Set<VirtualFile> filteredRoots = getFilteredResourcesRoots(mavenProject); |
| |
| if (!filteredRoots.isEmpty()) { |
| for (VirtualFile f = file.getParent(); f != null; f = f.getParent()) { |
| if (filteredRoots.contains(f)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public static List<DomFileElement<MavenDomProjectModel>> collectProjectModels(Project p) { |
| return DomService.getInstance().getFileElements(MavenDomProjectModel.class, p, GlobalSearchScope.projectScope(p)); |
| } |
| |
| public static MavenId describe(PsiFile psiFile) { |
| MavenDomProjectModel model = getMavenDomModel(psiFile, MavenDomProjectModel.class); |
| |
| String groupId = model.getGroupId().getStringValue(); |
| String artifactId = model.getArtifactId().getStringValue(); |
| String version = model.getVersion().getStringValue(); |
| |
| if (groupId == null) { |
| groupId = model.getMavenParent().getGroupId().getStringValue(); |
| } |
| |
| if (version == null) { |
| version = model.getMavenParent().getVersion().getStringValue(); |
| } |
| |
| return new MavenId(groupId, artifactId, version); |
| } |
| |
| @NotNull |
| public static MavenDomDependency createDomDependency(MavenDomProjectModel model, |
| @Nullable Editor editor, |
| @NotNull final MavenId id) { |
| return createDomDependency(model.getDependencies(), editor, id); |
| } |
| |
| @NotNull |
| public static MavenDomDependency createDomDependency(MavenDomDependencies dependencies, |
| @Nullable Editor editor, |
| @NotNull final MavenId id) { |
| MavenDomDependency dep = createDomDependency(dependencies, editor); |
| |
| dep.getGroupId().setStringValue(id.getGroupId()); |
| dep.getArtifactId().setStringValue(id.getArtifactId()); |
| dep.getVersion().setStringValue(id.getVersion()); |
| |
| return dep; |
| } |
| |
| @NotNull |
| public static MavenDomDependency createDomDependency(@NotNull MavenDomProjectModel model, @Nullable Editor editor) { |
| return createDomDependency(model.getDependencies(), editor); |
| } |
| |
| @NotNull |
| public static MavenDomDependency createDomDependency(@NotNull MavenDomDependencies dependencies, @Nullable Editor editor) { |
| int index = getCollectionIndex(dependencies, editor); |
| if (index >= 0) { |
| DomCollectionChildDescription childDescription = dependencies.getGenericInfo().getCollectionChildDescription("dependency"); |
| if (childDescription != null) { |
| DomElement element = childDescription.addValue(dependencies, index); |
| if (element instanceof MavenDomDependency) { |
| return (MavenDomDependency)element; |
| } |
| } |
| } |
| return dependencies.addDependency(); |
| } |
| |
| |
| public static int getCollectionIndex(@NotNull final MavenDomDependencies dependencies, @Nullable final Editor editor) { |
| if (editor != null) { |
| int offset = editor.getCaretModel().getOffset(); |
| |
| List<MavenDomDependency> dependencyList = dependencies.getDependencies(); |
| |
| for (int i = 0; i < dependencyList.size(); i++) { |
| MavenDomDependency dependency = dependencyList.get(i); |
| XmlElement xmlElement = dependency.getXmlElement(); |
| |
| if (xmlElement != null && xmlElement.getTextRange().getStartOffset() >= offset) { |
| return i; |
| } |
| } |
| } |
| return -1; |
| } |
| } |