blob: 3225405b94cb4e4a2109aed30bda722c9f7a4c71 [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.util.MultiValuesMap;
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
import com.intellij.psi.codeStyle.arrangement.model.*;
import com.intellij.psi.codeStyle.arrangement.std.ArrangementSettingsToken;
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementSettingsToken;
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokenType;
import com.intellij.psi.codeStyle.arrangement.std.StdArrangementTokens;
import com.intellij.util.containers.ContainerUtilRt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* {@link ArrangementEntryMatcher} which is based on standard match conditions in form of {@link ArrangementSettingsToken}.
* <p/>
* Implementations of this interface are expected to be thread-safe.
*
* @author Denis Zhdanov
* @since 8/26/12 11:07 PM
*/
public class StdArrangementEntryMatcher implements ArrangementEntryMatcher {
@NotNull private final ArrangementMatchCondition myCondition;
@NotNull private final ArrangementEntryMatcher myDelegate;
public StdArrangementEntryMatcher(@NotNull ArrangementMatchCondition condition) {
this(condition, new StdMatcherBuilderImpl());
}
public StdArrangementEntryMatcher(@NotNull ArrangementMatchCondition condition, @NotNull StdMatcherBuilder builder) {
myCondition = condition;
myDelegate = doBuildMatcher(condition, builder);
}
@NotNull
public ArrangementMatchCondition getCondition() {
return myCondition;
}
@Override
public boolean isMatched(@NotNull ArrangementEntry entry) {
return myDelegate.isMatched(entry);
}
@Override
public int hashCode() {
return myCondition.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StdArrangementEntryMatcher matcher = (StdArrangementEntryMatcher)o;
return myCondition.equals(matcher.myCondition);
}
@Override
public String toString() {
return myCondition.toString();
}
@NotNull
private static ArrangementEntryMatcher doBuildMatcher(@NotNull ArrangementMatchCondition condition, @NotNull StdMatcherBuilder builder) {
MyVisitor visitor = new MyVisitor(builder);
condition.invite(visitor);
return visitor.getMatcher();
}
/**
* Used by inner visitor to build matchers from atom conditions.
*/
public interface StdMatcherBuilder {
/**
* Parses given condition storing all data required to later produce a matcher based on the condition. It is called each time an
* {@link ArrangementAtomMatchCondition} is encountered when traversing {@link ArrangementMatchCondition} on the
* {@link StdArrangementEntryMatcher} creation.
* @param condition condition to parse
*/
void onCondition(@NotNull ArrangementAtomMatchCondition condition);
/**
* Returns a collection of matchers obtained through {@link #addMatcher(ArrangementEntryMatcher) addMatcher} calls or
* built from info gained by {@link #onCondition(ArrangementAtomMatchCondition) onCondition} calls.
* @return a collection of matchers
*/
@Nullable Collection<ArrangementEntryMatcher> buildMatchers();
/**
* Adds given matcher to collection provided by {@link #buildMatchers() buildMatchers} calls.
* @param matcher matcher to be added
*/
void addMatcher(@NotNull ArrangementEntryMatcher matcher);
}
/**
* Standard implementation of {@link StdMatcherBuilder}. Constructs entry matchers of types {@link ByTypeArrangementEntryMatcher},
* {@link ByModifierArrangementEntryMatcher}, {@link ByNameArrangementEntryMatcher}, {@link ByNamespaceArrangementEntryMatcher}.
*/
public static class StdMatcherBuilderImpl implements StdMatcherBuilder {
@NotNull private final List<ArrangementEntryMatcher> myMatchers = ContainerUtilRt.newArrayList();
/**
* Maps token type to all arrangement tokens that were encountered so far by parsing conditions with
* {@link #onCondition(ArrangementAtomMatchCondition) onCondition} calls.
*/
@NotNull protected final MultiValuesMap<StdArrangementTokenType, ArrangementSettingsToken > context =
new MultiValuesMap<StdArrangementTokenType, ArrangementSettingsToken>();
@Nullable private String myNamePattern;
@Nullable private String myNamespacePattern;
@Nullable private String myText;
/**
* Adds given entry to context by given entry type.
* @param token token added to context
*/
protected void addToContext(@NotNull StdArrangementSettingsToken token) {
StdArrangementTokenType tokenType = token.getTokenType();
context.put(tokenType, token);
}
@Override
public void onCondition(@NotNull ArrangementAtomMatchCondition condition) {
if (StdArrangementTokens.Regexp.NAME.equals(condition.getType())) {
myNamePattern = condition.getValue().toString();
return;
}
else if (StdArrangementTokens.Regexp.XML_NAMESPACE.equals(condition.getType())) {
myNamespacePattern = condition.getValue().toString();
}
else if (StdArrangementTokens.Regexp.TEXT.equals(condition.getType())) {
myText = condition.getValue().toString();
}
Object v = condition.getValue();
//Process any StdArrangementSettingsToken. No need to change it when new types of tokens will be processed.
if (v instanceof StdArrangementSettingsToken) {
StdArrangementSettingsToken token = (StdArrangementSettingsToken)v;
addToContext(token);
}
}
@Nullable
@Override
public Collection<ArrangementEntryMatcher> buildMatchers() {
List<ArrangementEntryMatcher> result = ContainerUtilRt.newArrayList(myMatchers);
Collection<ArrangementSettingsToken> entryTokens = context.get(StdArrangementTokenType.ENTRY_TYPE);
if (entryTokens!= null) {
result.add(new ByTypeArrangementEntryMatcher(entryTokens));
}
Collection<ArrangementSettingsToken> modifierTokens = context.get(StdArrangementTokenType.MODIFIER);
if (modifierTokens != null) {
result.add(new ByModifierArrangementEntryMatcher(modifierTokens));
}
if (myNamePattern != null) {
result.add(new ByNameArrangementEntryMatcher(myNamePattern));
}
if (myNamespacePattern != null) {
result.add(new ByNamespaceArrangementEntryMatcher(myNamespacePattern));
}
if (myText != null) {
result.add(new ByTextArrangementEntryMatcher(myText));
}
return result;
}
@Override
public void addMatcher(@NotNull ArrangementEntryMatcher matcher) {
myMatchers.add(matcher);
}
}
private static class MyVisitor implements ArrangementMatchConditionVisitor {
@NotNull private final StdMatcherBuilder myMatcherBuilder;
private boolean nestedComposite;
private MyVisitor(@NotNull StdMatcherBuilder matcherBuilder) {
myMatcherBuilder = matcherBuilder;
}
@Override
public void visit(@NotNull ArrangementAtomMatchCondition condition) {
myMatcherBuilder.onCondition(condition);
}
@Override
public void visit(@NotNull ArrangementCompositeMatchCondition condition) {
if (!nestedComposite) {
nestedComposite = true;
for (ArrangementMatchCondition c : condition.getOperands()) {
c.invite(this);
}
}
else {
myMatcherBuilder.addMatcher(doBuildMatcher(condition, myMatcherBuilder));
}
}
@SuppressWarnings("ConstantConditions")
@NotNull
public ArrangementEntryMatcher getMatcher() {
Collection<ArrangementEntryMatcher> matchers = myMatcherBuilder.buildMatchers();
if (matchers.size() == 1) {
return matchers.iterator().next();
} else {
CompositeArrangementEntryMatcher result = new CompositeArrangementEntryMatcher();
for (ArrangementEntryMatcher matcher: matchers) {
result.addMatcher(matcher);
}
return result;
}
}
}
}