blob: af78b1d4da16c4343b51b3b85ffdbd14cb3a7ab1 [file] [log] [blame]
package org.jetbrains.android.formatter;
import com.intellij.formatting.FormattingDocumentModel;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
import com.intellij.psi.formatter.xml.XmlPolicy;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.android.dom.resources.Style;
import static com.android.SdkConstants.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidXmlCodeStyleSettings extends CustomCodeStyleSettings {
public boolean USE_CUSTOM_SETTINGS = false;
public LayoutSettings LAYOUT_SETTINGS = new LayoutSettings();
public ManifestSettings MANIFEST_SETTINGS = new ManifestSettings();
public ValueResourceFileSettings VALUE_RESOURCE_FILE_SETTINGS = new ValueResourceFileSettings();
public OtherSettings OTHER_SETTINGS = new OtherSettings();
public AndroidXmlCodeStyleSettings(CodeStyleSettings container) {
super("AndroidXmlCodeStyleSettings", container);
}
public static AndroidXmlCodeStyleSettings getInstance(CodeStyleSettings settings) {
return settings.getCustomSettings(AndroidXmlCodeStyleSettings.class);
}
@Override
public Object clone() {
try {
final AndroidXmlCodeStyleSettings cloned = (AndroidXmlCodeStyleSettings)super.clone();
cloned.LAYOUT_SETTINGS = (LayoutSettings)LAYOUT_SETTINGS.clone();
cloned.MANIFEST_SETTINGS = (ManifestSettings)MANIFEST_SETTINGS.clone();
cloned.VALUE_RESOURCE_FILE_SETTINGS = (ValueResourceFileSettings)VALUE_RESOURCE_FILE_SETTINGS.clone();
cloned.OTHER_SETTINGS = (OtherSettings)OTHER_SETTINGS.clone();
return cloned;
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public static class MySettings implements JDOMExternalizable, Cloneable {
public int WRAP_ATTRIBUTES;
public boolean INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE;
public boolean INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE;
@Override
public void readExternal(Element element) throws InvalidDataException {
XmlSerializer.deserializeInto(this, element);
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
XmlSerializer.serializeInto(this, element, new SkipDefaultValuesSerializationFilters());
}
public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) {
return new AndroidXmlPolicy(settings, this, documentModel);
}
@Override
protected MySettings clone() throws CloneNotSupportedException {
try {
return (MySettings)super.clone();
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MySettings s = (MySettings)o;
return INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE == s.INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE &&
INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE == s.INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE &&
WRAP_ATTRIBUTES == s.WRAP_ATTRIBUTES;
}
@Override
public int hashCode() {
int result = WRAP_ATTRIBUTES;
result = 31 * result + (INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE ? 1 : 0);
result = 31 * result + (INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE ? 1 : 0);
return result;
}
}
public static class LayoutSettings extends MySettings {
public boolean INSERT_BLANK_LINE_BEFORE_TAG = true;
{
WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS;
INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true;
}
@Override
public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) {
return new AndroidXmlPolicy(settings, this, documentModel) {
@Override
public boolean insertLineBreakBeforeTag(XmlTag xmlTag) {
return INSERT_BLANK_LINE_BEFORE_TAG;
}
@Override
public boolean insertLineBreakAfterTagBegin(XmlTag tag) {
return INSERT_BLANK_LINE_BEFORE_TAG;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
final LayoutSettings settings = (LayoutSettings)o;
return INSERT_BLANK_LINE_BEFORE_TAG == settings.INSERT_BLANK_LINE_BEFORE_TAG;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (INSERT_BLANK_LINE_BEFORE_TAG ? 1 : 0);
return result;
}
}
public static class ManifestSettings extends MySettings {
public boolean GROUP_TAGS_WITH_SAME_NAME = true;
{
WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS;
INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true;
}
@Override
public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) {
return new AndroidXmlPolicy(settings, this, documentModel) {
@Override
public boolean insertLineBreakBeforeTag(XmlTag xmlTag) {
if (GROUP_TAGS_WITH_SAME_NAME) {
PsiElement element = getPrevSiblingElement(xmlTag);
if (element instanceof XmlTag) {
final String name1 = ((XmlTag)element).getName();
final String name2 = xmlTag.getName();
if (!name1.equals(name2)) {
element = getPrevSiblingElement(element);
if (element instanceof XmlTag && ((XmlTag)element).getName().equals(name1)) {
return true;
}
element = getNextSiblingElement(xmlTag);
return element instanceof XmlTag && ((XmlTag)element).getName().equals(name2);
}
}
}
return false;
}
@Override
public boolean insertLineBreakAfterTagBegin(XmlTag tag) {
return GROUP_TAGS_WITH_SAME_NAME && tag.getParentTag() == null;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
final ManifestSettings settings = (ManifestSettings)o;
return GROUP_TAGS_WITH_SAME_NAME == settings.GROUP_TAGS_WITH_SAME_NAME;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (GROUP_TAGS_WITH_SAME_NAME ? 1 : 0);
return result;
}
}
public static class ValueResourceFileSettings extends MySettings {
public boolean INSERT_LINE_BREAKS_AROUND_STYLE = true;
{
WRAP_ATTRIBUTES = CommonCodeStyleSettings.DO_NOT_WRAP;
INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = false;
}
@Override
public XmlPolicy createXmlPolicy(CodeStyleSettings settings, FormattingDocumentModel documentModel) {
return new AndroidXmlPolicy(settings, this, documentModel) {
@Override
public boolean insertLineBreakAfterTagBegin(XmlTag tag) {
if (!INSERT_LINE_BREAKS_AROUND_STYLE) {
return false;
}
final XmlTag[] subTags = tag.getSubTags();
return subTags.length != 0 && isStyleTag(subTags[0]);
}
@Override
public boolean keepWhiteSpacesInsideTag(XmlTag tag) {
if (super.keepWhiteSpacesInsideTag(tag)) {
return true;
}
boolean inItem = false;
while (tag != null) {
String tagName = tag.getName();
if (TAG_ITEM.equals(tagName)) {
inItem = true;
} else if (TAG_STRING.equals(tagName)) {
return true;
} else if (TAG_STRING_ARRAY.equals(tagName) || TAG_PLURALS.equals(tagName)) {
return inItem;
}
tag = tag.getParentTag();
}
return false;
}
@Override
public boolean insertLineBreakBeforeTag(XmlTag xmlTag) {
if (!INSERT_LINE_BREAKS_AROUND_STYLE) {
return false;
}
if (isStyleTag(xmlTag)) {
return true;
}
final PsiElement sibling = getPrevSiblingElement(xmlTag);
return sibling instanceof XmlTag && isStyleTag((XmlTag)sibling);
}
private boolean isStyleTag(XmlTag tag) {
return DomManager.getDomManager(tag.getProject()).
getDomElement(tag) instanceof Style;
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
final ValueResourceFileSettings settings = (ValueResourceFileSettings)o;
return INSERT_LINE_BREAKS_AROUND_STYLE == settings.INSERT_LINE_BREAKS_AROUND_STYLE;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (INSERT_LINE_BREAKS_AROUND_STYLE ? 1 : 0);
return result;
}
}
public static class OtherSettings extends MySettings {
{
WRAP_ATTRIBUTES = CommonCodeStyleSettings.WRAP_ALWAYS;
INSERT_LINE_BREAK_BEFORE_FIRST_ATTRIBUTE = true;
}
}
}