blob: 0d193ed1aaa694d7b2f5e3d69d04a2ebe1832ae5 [file] [log] [blame]
/*
* Copyright 2000-2012 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.psi.codeStyle.arrangement;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule;
import com.intellij.psi.codeStyle.arrangement.match.*;
import com.intellij.psi.codeStyle.arrangement.std.*;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* {@link ArrangementSettingsSerializer} which knows how to handle {@link StdArrangementSettings built-in arrangement tokens}
* and {@link Mixin can be used as a base for custom serializer implementation}.
*
* @author Denis Zhdanov
* @since 7/18/12 10:37 AM
*/
public class DefaultArrangementSettingsSerializer implements ArrangementSettingsSerializer {
private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementSettingsSerializer.class.getName());
@NotNull @NonNls private static final String GROUPS_ELEMENT_NAME = "groups";
@NotNull @NonNls private static final String GROUP_ELEMENT_NAME = "group";
@NotNull @NonNls private static final String RULES_ELEMENT_NAME = "rules";
@NotNull @NonNls private static final String SECTION_ELEMENT_NAME = "section";
@NotNull @NonNls private static final String SECTION_START_ATTRIBUTE = "start_comment";
@NotNull @NonNls private static final String SECTION_END_ATTRIBUTE = "end_comment";
@NotNull @NonNls private static final String RULE_ELEMENT_NAME = "rule";
@NotNull @NonNls private static final String TYPE_ELEMENT_NAME = "type";
@NotNull @NonNls private static final String MATCHER_ELEMENT_NAME = "match";
@NotNull @NonNls private static final String ORDER_TYPE_ELEMENT_NAME = "order";
@NotNull private final DefaultArrangementEntryMatcherSerializer myMatcherSerializer;
@NotNull private final Mixin myMixin;
@NotNull private final ArrangementSettings myDefaultSettings;
public DefaultArrangementSettingsSerializer(@NotNull StdArrangementSettings defaultSettings) {
this(Mixin.NULL, defaultSettings);
}
public DefaultArrangementSettingsSerializer(@NotNull Mixin mixin, @NotNull StdArrangementSettings defaultSettings) {
myMixin = mixin;
myMatcherSerializer = new DefaultArrangementEntryMatcherSerializer(mixin);
myDefaultSettings = defaultSettings;
}
@Override
public void serialize(@NotNull ArrangementSettings s, @NotNull Element holder) {
if (!(s instanceof StdArrangementSettings)) {
return;
}
StdArrangementSettings settings = (StdArrangementSettings)s;
List<ArrangementGroupingRule> groupings = settings.getGroupings();
final boolean isDefaultGroupings = groupings.equals(myDefaultSettings.getGroupings());
if (!isDefaultGroupings) {
Element groupingsElement = new Element(GROUPS_ELEMENT_NAME);
holder.addContent(groupingsElement);
for (ArrangementGroupingRule group : groupings) {
Element groupElement = new Element(GROUP_ELEMENT_NAME);
groupingsElement.addContent(groupElement);
groupElement.addContent(new Element(TYPE_ELEMENT_NAME).setText(group.getGroupingType().getId()));
groupElement.addContent(new Element(ORDER_TYPE_ELEMENT_NAME).setText(group.getOrderType().getId()));
}
}
final List<ArrangementSectionRule> sections = settings.getSections();
final boolean isDefaultRules = sections.equals((myDefaultSettings).getSections());
if (!isDefaultRules) {
Element rulesElement = new Element(RULES_ELEMENT_NAME);
holder.addContent(rulesElement);
for (ArrangementSectionRule section : sections) {
rulesElement.addContent(serialize(section));
}
}
}
@Nullable
@Override
public ArrangementSettings deserialize(@NotNull Element element) {
final List<ArrangementGroupingRule> groupingRules = deserializeGropings(element, myDefaultSettings);
final Element rulesElement = element.getChild(RULES_ELEMENT_NAME);
final List<ArrangementSectionRule> sectionRules = ContainerUtil.newArrayList();
if(rulesElement == null) {
sectionRules.addAll(myDefaultSettings.getSections());
}
else {
sectionRules.addAll(deserializeSectionRules(rulesElement));
if (sectionRules.isEmpty()) {
// for backward compatibility
final List<StdArrangementMatchRule> rules = deserializeRules(rulesElement);
return StdArrangementSettings.createByMatchRules(groupingRules, rules);
}
}
return new StdArrangementSettings(groupingRules, sectionRules);
}
@NotNull
private List<ArrangementGroupingRule> deserializeGropings(@NotNull Element element, @Nullable ArrangementSettings defaultSettings) {
Element groups = element.getChild(GROUPS_ELEMENT_NAME);
if (groups == null) {
return defaultSettings == null ? ContainerUtil.<ArrangementGroupingRule>newSmartList() : defaultSettings.getGroupings();
}
final List<ArrangementGroupingRule> groupings = new ArrayList<ArrangementGroupingRule>();
for (Object group : groups.getChildren(GROUP_ELEMENT_NAME)) {
Element groupElement = (Element)group;
// Grouping type.
String groupingTypeId = groupElement.getChildText(TYPE_ELEMENT_NAME);
ArrangementSettingsToken groupingType = StdArrangementTokens.byId(groupingTypeId);
if (groupingType == null) {
groupingType = myMixin.deserializeToken(groupingTypeId);
}
if (groupingType == null) {
LOG.warn(String.format("Can't deserialize grouping type token by id '%s'", groupingTypeId));
continue;
}
// Order type.
String orderTypeId = groupElement.getChildText(ORDER_TYPE_ELEMENT_NAME);
ArrangementSettingsToken orderType = StdArrangementTokens.byId(orderTypeId);
if (orderType == null) {
orderType = myMixin.deserializeToken(orderTypeId);
}
if (orderType == null) {
LOG.warn(String.format("Can't deserialize grouping order type token by id '%s'", orderTypeId));
continue;
}
groupings.add(new ArrangementGroupingRule(groupingType, orderType));
}
return groupings;
}
@NotNull
private List<ArrangementSectionRule> deserializeSectionRules(@NotNull Element rulesElement) {
final List<ArrangementSectionRule> sectionRules = new ArrayList<ArrangementSectionRule>();
for (Object o : rulesElement.getChildren(SECTION_ELEMENT_NAME)) {
final Element sectionElement = (Element)o;
final List<StdArrangementMatchRule> rules = deserializeRules(sectionElement);
final Attribute start = sectionElement.getAttribute(SECTION_START_ATTRIBUTE);
final String startComment = start != null ? start.getValue().trim() : null;
final Attribute end = sectionElement.getAttribute(SECTION_END_ATTRIBUTE);
final String endComment = end != null ? end.getValue().trim() : null;
sectionRules.add(ArrangementSectionRule.create(startComment, endComment, rules));
}
return sectionRules;
}
@NotNull
private List<StdArrangementMatchRule> deserializeRules(@NotNull Element element) {
final List<StdArrangementMatchRule> rules = new ArrayList<StdArrangementMatchRule>();
for (Object o : element.getChildren(RULE_ELEMENT_NAME)) {
Element ruleElement = (Element)o;
Element matcherElement = ruleElement.getChild(MATCHER_ELEMENT_NAME);
if (matcherElement == null) {
continue;
}
StdArrangementEntryMatcher matcher = null;
for (Object c : matcherElement.getChildren()) {
matcher = myMatcherSerializer.deserialize((Element)c);
if (matcher != null) {
break;
}
}
if (matcher == null) {
return ContainerUtil.newSmartList();
}
Element orderTypeElement = ruleElement.getChild(ORDER_TYPE_ELEMENT_NAME);
ArrangementSettingsToken orderType = null;
if (orderTypeElement != null) {
String orderTypeId = orderTypeElement.getText();
orderType = StdArrangementTokens.byId(orderTypeId);
if (orderType == null) {
orderType = myMixin.deserializeToken(orderTypeId);
}
if (orderType == null) {
LOG.warn(String.format("Can't deserialize matching rule order type for id '%s'. Falling back to default (%s)",
orderTypeId, ArrangementMatchRule.DEFAULT_ORDER_TYPE.getId()));
}
}
if (orderType == null) {
orderType = ArrangementMatchRule.DEFAULT_ORDER_TYPE;
}
rules.add(new StdArrangementMatchRule(matcher, orderType));
}
return rules;
}
@Nullable
public Element serialize(@NotNull ArrangementMatchRule rule) {
Element matcherElement = myMatcherSerializer.serialize(rule.getMatcher());
if (matcherElement == null) {
return null;
}
Element result = new Element(RULE_ELEMENT_NAME);
result.addContent(new Element(MATCHER_ELEMENT_NAME).addContent(matcherElement));
if (rule.getOrderType() != ArrangementMatchRule.DEFAULT_ORDER_TYPE) {
result.addContent(new Element(ORDER_TYPE_ELEMENT_NAME).setText(rule.getOrderType().getId()));
}
return result;
}
@Nullable
public Element serialize(@NotNull ArrangementSectionRule section) {
final Element sectionElement = new Element(SECTION_ELEMENT_NAME);
if (StringUtil.isNotEmpty(section.getStartComment())) {
// or only != null ?
sectionElement.setAttribute(SECTION_START_ATTRIBUTE, section.getStartComment());
}
if (StringUtil.isNotEmpty(section.getEndComment())) {
sectionElement.setAttribute(SECTION_END_ATTRIBUTE, section.getEndComment());
}
//TODO: serialize start & end comment as rule?
final List<StdArrangementMatchRule> rules = section.getMatchRules();
for (int i = 0; i < rules.size(); i++) {
StdArrangementMatchRule rule = rules.get(i);
if ((i != 0 || StringUtil.isEmpty(section.getStartComment())) &&
(i != rules.size() - 1 || StringUtil.isEmpty(section.getEndComment()))) {
sectionElement.addContent(serialize(rule));
}
}
return sectionElement;
}
public interface Mixin {
Mixin NULL = new Mixin() {
@Nullable
@Override
public ArrangementSettingsToken deserializeToken(@NotNull String id) { return null; }
};
@Nullable
ArrangementSettingsToken deserializeToken(@NotNull String id);
}
}