package com.intellij.javascript.flex.mxml; import com.intellij.lang.javascript.JavaScriptSupportLoader; import com.intellij.lang.javascript.flex.XmlBackedJSClassImpl; import com.intellij.lang.javascript.psi.ecmal4.JSClass; import com.intellij.lang.javascript.psi.ecmal4.XmlBackedJSClass; import com.intellij.lang.javascript.psi.ecmal4.XmlBackedJSClassFactory; import com.intellij.lang.javascript.psi.ecmal4.XmlBackedJSClassProvider; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataCache; import com.intellij.psi.PsiElement; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; /** * @author yole */ public class MxmlJSClassProvider extends XmlBackedJSClassProvider { @NonNls public static final String SCRIPT_TAG_NAME = "Script"; private static final Key<CachedValue<XmlTag[]>> CHILD_INLINE_COMPONENTS_TAGS_KEY = Key.create("child.inline.components.tags"); private static final UserDataCache<CachedValue<XmlTag[]>, XmlTag, Object> ourChildComponentsTagsCache = new UserDataCache<CachedValue<XmlTag[]>, XmlTag, Object>() { protected CachedValue<XmlTag[]> compute(final XmlTag tag, final Object p) { return CachedValuesManager.getManager(tag.getProject()).createCachedValue(new CachedValueProvider<XmlTag[]>() { public Result<XmlTag[]> compute() { final Collection<XmlTag> result = new ArrayList<>(); tag.processElements(new PsiElementProcessor() { public boolean execute(@NotNull PsiElement element) { if (element instanceof XmlTag) { XmlTag tag = (XmlTag)element; if (XmlBackedJSClassImpl.isComponentTag(tag)) { final XmlTag[] subtags = tag.getSubTags(); if (subtags.length > 0) { result.add(subtags[0]); } } else { tag.processElements(this, null); } } return true; } }, null); return new Result<>(result.toArray(new XmlTag[result.size()]), tag); } }, false); } }; public static MxmlJSClassProvider getInstance() { for (XmlBackedJSClassProvider provider : Extensions.getExtensions(EP_NAME)) { if (provider instanceof MxmlJSClassProvider) { return (MxmlJSClassProvider)provider; } } assert false; return null; } @Override public boolean hasJSClass(XmlFile file) { return JavaScriptSupportLoader.isMxmlOrFxgFile(file); } @Override public boolean isScriptTag(XmlTag tag) { return SCRIPT_TAG_NAME.equals(tag.getLocalName()); } @Override public boolean canCreateClassFromTag(XmlTag tag) { return isComponentSubTag(tag); } @Override public XmlTag getClassOwnerTag(XmlTag tag) { if (isComponentSubTag(tag)) { return tag.getParentTag().getSubTags()[0]; } return tag; } @Override public XmlBackedJSClass createClassFromTag(XmlTag tag) { XmlFile file = (XmlFile)tag.getContainingFile(); if (file.getRootTag() == tag && JavaScriptSupportLoader.isMxmlOrFxgFile(file)) { return new MxmlJSClass(tag); } if (isComponentSubTag(tag)) { return new MxmlJSClass(tag.getParentTag().getSubTags()[0]); } return null; } private static boolean isComponentSubTag(XmlTag tag) { XmlTag parentTag = tag.getParentTag(); return parentTag != null && XmlBackedJSClassImpl.isComponentTag(parentTag); } @Override public Collection<? extends JSClass> getChildClasses(XmlFile file) { return getChildInlineComponents(file.getRootTag(), true); } private static void collectComponentsTagRecursively(XmlTag[] parents, Collection<XmlTag> result) { ContainerUtil.addAll(result, parents); for (XmlTag parent : parents) { collectComponentsTagRecursively(ourChildComponentsTagsCache.get(CHILD_INLINE_COMPONENTS_TAGS_KEY, parent, null).getValue(), result); } } public static Collection<XmlBackedJSClass> getChildInlineComponents(XmlTag rootTag, final boolean recursive) { final XmlTag[] directChildren = ourChildComponentsTagsCache.get(CHILD_INLINE_COMPONENTS_TAGS_KEY, rootTag, null).getValue(); Collection<XmlTag> allChildren; if (recursive) { allChildren = new ArrayList<>(); collectComponentsTagRecursively(directChildren, allChildren); } else { allChildren = Arrays.asList(directChildren); } Collection<XmlBackedJSClass> result = new ArrayList<>(allChildren.size()); for (XmlTag tag : allChildren) { result.add(XmlBackedJSClassFactory.getInstance().getXmlBackedClass(tag)); } return result; } }