package org.jetbrains.android.formatter; import com.intellij.formatting.*; import com.intellij.lang.ASTNode; import com.intellij.lang.xml.XmlFormattingModelBuilder; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.formatter.xml.XmlBlock; import com.intellij.psi.formatter.xml.XmlPolicy; import com.intellij.psi.formatter.xml.XmlTagBlock; import com.intellij.psi.xml.XmlFile; import com.intellij.util.xml.DomFileDescription; import com.intellij.util.xml.DomManager; import org.jetbrains.android.dom.AndroidResourceDomFileDescription; import org.jetbrains.android.dom.color.ColorDomFileDescription; import org.jetbrains.android.dom.drawable.DrawableStateListDomFileDescription; import org.jetbrains.android.dom.layout.LayoutDomFileDescription; import org.jetbrains.android.dom.manifest.ManifestDomFileDescription; import org.jetbrains.android.dom.resources.ResourcesDomFileDescription; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * @author Eugene.Kudelevsky */ public class AndroidXmlFormattingModelBuilder implements CustomFormattingModelBuilder { private final XmlFormattingModelBuilder myXmlFormattingModelBuilder = new XmlFormattingModelBuilder(); @Override public boolean isEngagedToFormat(PsiElement context) { return getContextSpecificSettings(context) != null; } @NotNull @Override public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { final FormattingModel baseModel = myXmlFormattingModelBuilder.createModel(element, settings); final AndroidXmlCodeStyleSettings baseSettings = AndroidXmlCodeStyleSettings.getInstance(settings); if (!baseSettings.USE_CUSTOM_SETTINGS) { return baseModel; } final ContextSpecificSettingsProviders.Provider provider = getContextSpecificSettings(element); final AndroidXmlCodeStyleSettings.MySettings s = provider != null ? provider.getSettings(baseSettings) : null; return s != null ? new DelegatingFormattingModel(baseModel, createDelegatingBlock(baseModel, s, settings)) : baseModel; } private static Block createDelegatingBlock(FormattingModel model, AndroidXmlCodeStyleSettings.MySettings customSettings, CodeStyleSettings settings) { final Block block = model.getRootBlock(); if (block instanceof XmlBlock) { final XmlBlock b = (XmlBlock)block; final XmlPolicy policy = customSettings.createXmlPolicy(settings, model.getDocumentModel()); return new XmlBlock(b.getNode(), b.getWrap(), b.getAlignment(), policy, b.getIndent(), b.getTextRange()) { @Override protected XmlTagBlock createTagBlock(ASTNode child, Indent indent, Wrap wrap, Alignment alignment) { return new XmlTagBlock(child, wrap, alignment, myXmlFormattingPolicy, indent != null ? indent : Indent.getNoneIndent(), isPreserveSpace()); } }; } else { return block; } } @Nullable @Override public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { return myXmlFormattingModelBuilder.getRangeAffectingIndent(file, offset, elementAtOffset); } @Nullable private static ContextSpecificSettingsProviders.Provider getContextSpecificSettings(PsiElement context) { final PsiFile file = context.getContainingFile(); if (!(file instanceof XmlFile) || AndroidFacet.getInstance(file) == null) { return null; } final DomFileDescription<?> description = DomManager.getDomManager( context.getProject()).getDomFileDescription((XmlFile)file); if (description instanceof LayoutDomFileDescription) { return ContextSpecificSettingsProviders.LAYOUT; } else if (description instanceof ManifestDomFileDescription) { return ContextSpecificSettingsProviders.MANIFEST; } else if (description instanceof ResourcesDomFileDescription || description instanceof DrawableStateListDomFileDescription || description instanceof ColorDomFileDescription) { return ContextSpecificSettingsProviders.VALUE_RESOURCE_FILE; } else if (description instanceof AndroidResourceDomFileDescription) { return ContextSpecificSettingsProviders.OTHER; } return null; } }