blob: b99f2aa450570fae4c646f76eb4aefc7968d30c0 [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.match;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.DefaultArrangementSettingsSerializer;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor;
import com.intellij.psi.codeStyle.arrangement.std.ArrangementSettingsToken;
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokenType;
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 7/19/12 1:00 PM
*/
public class DefaultArrangementEntryMatcherSerializer {
private static final Comparator<ArrangementMatchCondition> CONDITION_COMPARATOR = new Comparator<ArrangementMatchCondition>() {
@Override
public int compare(ArrangementMatchCondition c1, ArrangementMatchCondition c2) {
boolean isAtom1 = c1 instanceof ArrangementAtomMatchCondition;
boolean isAtom2 = c2 instanceof ArrangementAtomMatchCondition;
if (isAtom1 ^ isAtom2) {
return isAtom1 ? 1 : -1; // Composite conditions before atom conditions.
}
else if (!isAtom1) {
return 0;
}
ArrangementAtomMatchCondition atom1 = (ArrangementAtomMatchCondition)c1;
ArrangementAtomMatchCondition atom2 = (ArrangementAtomMatchCondition)c2;
int cmp = atom1.getType().compareTo(atom2.getType());
if (cmp == 0) {
cmp = atom1.getValue().toString().compareTo(atom2.getValue().toString());
}
return cmp;
}
};
@NotNull private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementEntryMatcherSerializer.class.getName());
@NotNull private static final String COMPOSITE_CONDITION_NAME = "AND";
private final DefaultArrangementSettingsSerializer.Mixin myMixin;
public DefaultArrangementEntryMatcherSerializer(DefaultArrangementSettingsSerializer.Mixin mixin) {
myMixin = mixin;
}
@SuppressWarnings("MethodMayBeStatic")
@Nullable
public <T extends ArrangementEntryMatcher> Element serialize(@NotNull T matcher) {
if (matcher instanceof StdArrangementEntryMatcher) {
return serialize(((StdArrangementEntryMatcher)matcher).getCondition());
}
LOG.warn(String.format(
"Can't serialize arrangement entry matcher of class '%s'. Reason: expected to find '%s' instance instead",
matcher.getClass(), StdArrangementEntryMatcher.class
));
return null;
}
@NotNull
private static Element serialize(@NotNull ArrangementMatchCondition condition) {
MySerializationVisitor visitor = new MySerializationVisitor();
condition.invite(visitor);
return visitor.result;
}
@SuppressWarnings("MethodMayBeStatic")
@Nullable
public StdArrangementEntryMatcher deserialize(@NotNull Element matcherElement) {
ArrangementMatchCondition condition = deserializeCondition(matcherElement);
return condition == null ? null : new StdArrangementEntryMatcher(condition);
}
@Nullable
private ArrangementMatchCondition deserializeCondition(@NotNull Element matcherElement) {
String name = matcherElement.getName();
if (COMPOSITE_CONDITION_NAME.equals(name)) {
ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition();
for (Object child : matcherElement.getChildren()) {
ArrangementMatchCondition deserialised = deserializeCondition((Element)child);
if (deserialised != null) {
composite.addOperand(deserialised);
}
}
return composite;
}
else {
return deserializeAtomCondition(matcherElement);
}
}
@Nullable
private ArrangementMatchCondition deserializeAtomCondition(@NotNull Element matcherElement) {
String id = matcherElement.getName();
ArrangementSettingsToken token = StdArrangementTokens.byId(id);
boolean processInnerText = true;
if (token != null
&& (StdArrangementTokens.General.TYPE.equals(token) || StdArrangementTokens.General.MODIFIER.equals(token)))
{
// Backward compatibility with old arrangement settings format.
id = matcherElement.getText();
if (StringUtil.isEmpty(id)) {
LOG.warn("Can't deserialize match condition at legacy format");
return null;
}
token = StdArrangementTokens.byId(id);
processInnerText = false;
}
if (token == null) {
token = myMixin.deserializeToken(id);
}
if (token == null) {
LOG.warn(String.format("Can't deserialize match condition with id '%s'", id));
return null;
}
Object value = token;
String text = matcherElement.getText();
if (text != null && processInnerText) {
text = StringUtil.unescapeStringCharacters(matcherElement.getText());
if (!StringUtil.isEmpty(text)) {
value = text;
}
}
return new ArrangementAtomMatchCondition(token, value);
}
private static class MySerializationVisitor implements ArrangementMatchConditionVisitor {
Element result;
Element parent;
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
ArrangementSettingsToken type = condition.getType();
final Element element = new Element(type.getId());
if (StdArrangementTokenType.REG_EXP.is(type)) {
element.setText(StringUtil.escapeStringCharacters(condition.getValue().toString()));
}
register(element);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
Element composite = new Element(COMPOSITE_CONDITION_NAME);
register(composite);
parent = composite;
List<ArrangementMatchCondition> operands = ContainerUtilRt.newArrayList(condition.getOperands());
ContainerUtil.sort(operands, CONDITION_COMPARATOR);
for (ArrangementMatchCondition c : operands) {
c.invite(this);
}
}
private void register(@NotNull Element element) {
if (result == null) {
result = element;
}
if (parent != null) {
parent.addContent(element);
}
}
}
}