/*
* Copyright 2000-2007 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.javaee.util;
import com.intellij.javaee.model.annotations.JamElement;
import com.intellij.javaee.model.annotations.JamMemberElement;
import com.intellij.javaee.model.annotations.JamModuleManager;
import com.intellij.javaee.model.annotations.JamRootElement;
import com.intellij.javaee.model.common.CommonModelElement;
import com.intellij.javaee.model.xml.CommonDomModelElement;
import com.intellij.javaee.model.xml.impl.RootBaseImpl;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.*;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Processor;
import com.intellij.util.ReflectionCache;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.*;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Gregory.Shrago
*/
public class JamCommonUtil {
@NonNls public static final String VALUE_PARAMETER = "value";
@Nullable
public static Object computeMemberValue(final PsiElement value) {
if (value instanceof PsiExpression) {
try {
return value.getManager().getConstantEvaluationHelper().computeConstantExpression((PsiExpression)value, false);
}
catch (UnsupportedOperationException e) {
// nothing
}
}
else if (value instanceof XmlAttributeValue) {
final GenericAttributeValue genericValue =
DomManager.getDomManager(value.getProject()).getDomElement((XmlAttribute)value.getParent());
return genericValue != null ? genericValue.getValue() : ((XmlAttributeValue)value).getValue();
}
return null;
}
public static boolean isSuperClass(@Nullable final PsiClass firstClass, final String superClassQName) {
return !processSuperClassList(firstClass, new SmartList<PsiClass>(), new Processor<PsiClass>() {
public boolean process(final PsiClass superClass) {
return !Comparing.equal(superClass.getQualifiedName(), superClassQName);
}
});
}
@NotNull
public static List<PsiClass> getSuperClassList(@Nullable final PsiClass firstClass) {
final SmartList<PsiClass> list = new SmartList<PsiClass>();
processSuperClassList(firstClass, list, new Processor<PsiClass>() {
public boolean process(final PsiClass psiClass) {
return true;
}
});
return list;
}
public static boolean processSuperClassList(@Nullable final PsiClass firstClass, @NotNull final Collection<PsiClass> supers, final Processor<PsiClass> processor) {
for (PsiClass curClass = firstClass;
curClass != null
&& !CommonClassNames.JAVA_LANG_OBJECT.equals(curClass.getQualifiedName())
&& !supers.contains(curClass);
curClass = curClass.getSuperClass()) {
ProgressManager.getInstance().checkCanceled();
if (!processor.process(curClass)) return false;
supers.add(curClass);
}
return true;
}
@Nullable
public static <T> T getRootElement(final PsiFile file, final Class<T> domClass, final Module module) {
if (!(file instanceof XmlFile)) return null;
final DomManager domManager = DomManager.getDomManager(file.getProject());
final DomFileElement<DomElement> element = domManager.getFileElement((XmlFile)file, DomElement.class);
if (element == null) return null;
final DomElement root = element.getRootElement();
if (!ReflectionCache.isAssignable(domClass, root.getClass())) return null;
if (module != null && root instanceof RootBaseImpl) {
((RootBaseImpl)root).registerDomModule(module);
}
return (T)root;
}
@NotNull
public static <T extends JamRootElement> List<T> getRootElements(final PsiElement psiElement, final Class<T> rootClass) {
final Module module = ModuleUtil.findModuleForPsiElement(psiElement);
if (module == null) {
return Collections.emptyList();
}
final SmartList<T> result = new SmartList<T>();
for (Module depModule : getAllDependentModules(module)) {
ContainerUtil.addIfNotNull(JamModuleManager.getInstance(depModule).getRootElement(rootClass), result);
}
return result;
}
@Nullable
public static String getDisplayName(final Object element) {
if (element instanceof CommonModelElement) {
final CommonModelElement javaeeModelElement = (CommonModelElement)element;
String name = getElementName(javaeeModelElement);
if (name == null) name = "";
return getClassName(javaeeModelElement) + " '" + name + "'";
}
return null;
}
@Nullable
public static String getElementName(final CommonModelElement element) {
return ElementPresentationManager.getElementName(element);
}
public static String getClassName(final CommonModelElement element) {
return ElementPresentationManager.getTypeNameForObject(element);
}
@Nullable
public static Module findModuleForPsiElement(final PsiElement element) {
PsiFile psiFile = element.getContainingFile();
if (psiFile == null) {
final VirtualFile virtualFile = PsiUtil.getVirtualFile(element);
return virtualFile == null ? null : ProjectRootManager.getInstance(element.getProject()).getFileIndex().getModuleForFile(virtualFile);
}
final PsiFile originalFile = psiFile.getOriginalFile();
if (originalFile != null) {
psiFile = originalFile;
}
if (psiFile instanceof XmlFile) {
final DomFileElement<CommonDomModelElement> domFileElement =
DomManager.getDomManager(element.getProject()).getFileElement((XmlFile)psiFile, CommonDomModelElement.class);
if (domFileElement != null) {
Module module = domFileElement.getRootElement().getModule();
if (module != null) {
return module;
}
}
}
return ModuleUtil.findModuleForPsiElement(psiFile);
}
@NotNull
public static PsiElement[] getTargetPsiElements(final CommonModelElement element) {
ArrayList<PsiElement> list = new ArrayList<PsiElement>();
for (CommonModelElement modelElement : ModelMergerUtil.getFilteredImplementations(element)) {
if (modelElement instanceof DomElement) {
final DomElement domModelElement = (DomElement)modelElement;
if (domModelElement.getXmlTag() != null) {
list.add(domModelElement.getXmlTag());
}
}
else if (modelElement instanceof JamElement) {
final JamElement annotationModelElement = (JamElement)modelElement;
if (annotationModelElement instanceof JamMemberElement) {
final JamMemberElement annotatedElement = (JamMemberElement)annotationModelElement;
list.add(annotatedElement.getPsiMember());
}
else {
ContainerUtil.addIfNotNull(annotationModelElement.getIdentifyingAnnotation(), list);
}
}
else {
ContainerUtil.addIfNotNull(modelElement.getIdentifyingPsiElement(), list);
}
}
final PsiElement[] result = list.toArray(PsiElement.EMPTY_ARRAY);
Arrays.sort(result, new Comparator<PsiElement>() {
public int compare(final PsiElement o1, final PsiElement o2) {
return getWeight(o1) - getWeight(o2);
}
private int getWeight(PsiElement o) {
if (o instanceof XmlTag) return 0;
if (o instanceof PsiMember) return 1;
if (o instanceof PsiAnnotation) return 2;
return 3;
}
});
return result;
}
public static boolean isInLibrary(final CommonModelElement modelElement) {
if (modelElement == null) return false;
final PsiElement psiElement = modelElement.getIdentifyingPsiElement();
if (psiElement == null) return false;
final PsiFile psiFile = psiElement.getContainingFile();
if (psiFile == null) return false;
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) return false;
return ProjectRootManager.getInstance(modelElement.getPsiManager().getProject()).getFileIndex().isInLibraryClasses(virtualFile);
}
public static boolean isPlainJavaFile(final PsiElement element) {
return element instanceof PsiJavaFile && element.getLanguage() == StdLanguages.JAVA;
}
public static boolean isPlainXmlFile(final PsiElement element) {
return element instanceof XmlFile && element.getLanguage() == StdLanguages.XML;
}
private static Key<CachedValue<Module[]>> MODULE_DEPENDENCIES = Key.create("MODULE_DEPENDENCIES");
private static Key<CachedValue<Module[]>> MODULE_DEPENDENTS = Key.create("MODULE_DEPENDENTS");
@NotNull
public static Module[] getAllModuleDependencies(final Module module) {
CachedValue<Module[]> value = module.getUserData(MODULE_DEPENDENCIES);
if (value == null) {
module.putUserData(MODULE_DEPENDENCIES, value = PsiManager.getInstance(module.getProject()).getCachedValuesManager().createCachedValue(new CachedValueProvider<Module[]>() {
public Result<Module[]> compute() {
final Set<Module> result = addModuleDependencies(module, new THashSet<Module>(), false);
return new Result<Module[]>(result.toArray(new Module[result.size()]), ProjectRootManager.getInstance(module.getProject()));
}
}, false));
}
return value.getValue();
}
@NotNull
public static Module[] getAllDependentModules(final Module module) {
CachedValue<Module[]> value = module.getUserData(MODULE_DEPENDENTS);
if (value == null) {
module.putUserData(MODULE_DEPENDENTS, value = PsiManager.getInstance(module.getProject()).getCachedValuesManager().createCachedValue(new CachedValueProvider<Module[]>() {
public Result<Module[]> compute() {
final Module[] modules = ModuleManager.getInstance(module.getProject()).getModules();
final Set<Module> result = addModuleDependents(module, new THashSet<Module>(), modules);
return new Result<Module[]>(result.toArray(new Module[result.size()]), ProjectRootManager.getInstance(module.getProject()));
}
}, false));
}
return value.getValue();
}
public static Set<Module> addModuleDependencies(final Module module, final Set<Module> result, final boolean exported) {
final OrderEntry[] orderEntries = ModuleRootManager.getInstance(module).getOrderEntries();
for (OrderEntry orderEntry : orderEntries) {
if (orderEntry.isValid() && orderEntry instanceof ModuleOrderEntry && (!exported || ((ModuleOrderEntry)orderEntry).isExported())) {
final Module exportedModule = ((ModuleOrderEntry)orderEntry).getModule();
if (result.add(exportedModule)) {
addModuleDependencies(exportedModule, result, true);
}
}
}
return result;
}
public static Set<Module> addModuleDependents(final Module module, final Set<Module> result, final Module[] modules) {
if (!result.add(module)) return result;
for (Module allModule : modules) {
final OrderEntry[] orderEntries = ModuleRootManager.getInstance(allModule).getOrderEntries();
for (OrderEntry orderEntry : orderEntries) {
if (orderEntry.isValid() && orderEntry instanceof ModuleOrderEntry) {
final Module exportedModule = ((ModuleOrderEntry)orderEntry).getModule();
if (exportedModule == module && result.add(allModule)) {
if (((ModuleOrderEntry)orderEntry).isExported()) {
addModuleDependents(allModule, result, modules);
}
}
}
}
}
return result;
}
}