package com.intellij.lang.javascript.flex.actions.newfile;
import com.intellij.javascript.flex.mxml.schema.ClassBackedElementDescriptor;
import com.intellij.javascript.flex.resolve.ActionScriptClassResolver;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.lang.javascript.psi.ecmal4.JSAttribute;
import com.intellij.lang.javascript.psi.ecmal4.JSAttributeList;
import com.intellij.lang.javascript.psi.ecmal4.JSAttributeNameValuePair;
import com.intellij.lang.javascript.psi.ecmal4.JSClass;
import com.intellij.lang.javascript.validation.fixes.CreateClassOrInterfaceFix;
import com.intellij.lang.javascript.validation.fixes.CreateMxmlFileIntentionBase;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.XmlElementDescriptor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
public class CreateFlexSkinIntention extends CreateMxmlFileIntentionBase {
public CreateFlexSkinIntention(final String skinFqn, final @NotNull PsiElement element) {
super(skinFqn, element);
}
@NotNull
public String getText() {
return FlexBundle.message("create.skin", myClassName);
}
protected Pair<String, PsiDirectory> getFileTextAndDir(final @NotNull Module module) {
final String hostComponent;
final PsiDirectory targetDirectory;
final JSClass hostComponentClass = getHostComponentClass();
final String defaultHostComponent = hostComponentClass == null ? "" : hostComponentClass.getQualifiedName();
if (ApplicationManager.getApplication().isUnitTestMode()) {
hostComponent = defaultHostComponent;
targetDirectory = WriteAction
.compute(() -> CreateClassOrInterfaceFix.findOrCreateDirectory(myPackageName, myElement));
}
else {
final CreateFlexSkinDialog dialog = new CreateFlexSkinDialog(module, myClassName, myPackageName,
defaultHostComponent,
myElement.getContainingFile());
if (!dialog.showAndGet()) {
return Pair.empty();
}
hostComponent = dialog.getHostComponent();
targetDirectory = dialog.getTargetDirectory();
}
return Pair.create(getSkinContent(hostComponent, myElement), targetDirectory);
}
private static String getSkinContent(final String hostComponent, final PsiElement context) {
final StringBuilder builder = new StringBuilder();
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
// TODO insert MXML file header
builder.append("<s:Skin xmlns:fx=\"http://ns.adobe.com/mxml/2009\" xmlns:s=\"library://ns.adobe.com/flex/spark\">\n");
builder.append("\n");
if (!StringUtil.isEmpty(hostComponent)) {
builder.append("<fx:Metadata>\n");
builder.append("[HostComponent(\"").append(hostComponent).append("\")]\n");
builder.append("</fx:Metadata>\n");
builder.append("\n");
}
final PsiElement element = ActionScriptClassResolver.findClassByQNameStatic(hostComponent, context);
if (element instanceof JSClass) {
final JSClass jsClass = (JSClass)element;
final Collection<String> skinStates = getSkinStates(jsClass);
if (!skinStates.isEmpty()) {
builder.append("<s:states>\n");
for (final String skinState : skinStates) {
builder.append("<s:State name=\"").append(skinState).append("\"/>\n");
}
builder.append("</s:states>\n");
builder.append("\n");
}
}
builder.append("</s:Skin>\n");
return builder.toString();
}
private static Collection<String> getSkinStates(final JSClass jsClass) {
final Collection<String> skinStates = new ArrayList<>();
appendSkinStates(skinStates, jsClass, new THashSet<>());
return skinStates;
}
private static void appendSkinStates(final Collection<String> skinStates, final JSClass jsClass, final Set<JSClass> visited) {
visited.add(jsClass);
final JSAttributeList attributeList = jsClass.getAttributeList();
if (attributeList != null) {
final JSAttribute[] attributes = attributeList.getAttributesByName("SkinState");
for (final JSAttribute attribute : attributes) {
final JSAttributeNameValuePair pair = attribute.getValueByName(null);
if (pair != null) {
final String state = pair.getSimpleValue();
if (!skinStates.contains(state)) {
skinStates.add(state);
}
}
}
}
for (final JSClass superClass : jsClass.getSuperClasses()) {
if (!visited.contains(superClass)) {
appendSkinStates(skinStates, superClass, visited);
}
}
}
@Nullable
private JSClass getHostComponentClass() {
final XmlTag tag = myElement instanceof XmlTag
? ((XmlTag)myElement).getParentTag()
: myElement instanceof XmlAttributeValue ? (XmlTag)myElement.getParent().getParent() : null;
final XmlElementDescriptor descriptor = tag == null ? null : tag.getDescriptor();
if (descriptor instanceof ClassBackedElementDescriptor) {
final PsiElement declaration = descriptor.getDeclaration();
if (declaration instanceof JSClass) {
return (JSClass)declaration;
}
}
return null;
}
}