| /* |
| * 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.application.options.codeStyle.arrangement.match; |
| |
| import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants; |
| import com.intellij.psi.codeStyle.arrangement.std.ArrangementStandardSettingsManager; |
| import com.intellij.application.options.codeStyle.arrangement.animation.ArrangementAnimationManager; |
| import com.intellij.application.options.codeStyle.arrangement.animation.ArrangementAnimationPanel; |
| import com.intellij.application.options.codeStyle.arrangement.color.ArrangementColorsProvider; |
| import com.intellij.application.options.codeStyle.arrangement.component.ArrangementAndMatchConditionComponent; |
| import com.intellij.application.options.codeStyle.arrangement.component.ArrangementAtomMatchConditionComponent; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; |
| import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; |
| 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.ArrangementUiComponent; |
| import com.intellij.util.Consumer; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.Set; |
| |
| /** |
| * @author Denis Zhdanov |
| * @since 8/10/12 2:53 PM |
| */ |
| public class ArrangementMatchNodeComponentFactory { |
| |
| private static final Logger LOG = Logger.getInstance("#" + ArrangementMatchNodeComponentFactory.class.getName()); |
| |
| @NotNull private final ArrangementStandardSettingsManager mySettingsManager; |
| @NotNull private final ArrangementColorsProvider myColorsProvider; |
| @NotNull private final ArrangementMatchingRulesControl myList; |
| |
| public ArrangementMatchNodeComponentFactory(@NotNull ArrangementStandardSettingsManager manager, |
| @NotNull ArrangementColorsProvider provider, |
| @NotNull ArrangementMatchingRulesControl list) |
| { |
| mySettingsManager = manager; |
| myColorsProvider = provider; |
| myList = list; |
| } |
| |
| /** |
| * Allows to build UI component for the given model. |
| * |
| * @param rendererTarget target model element for which UI component should be built |
| * @param rule rule which contains given 'renderer target' condition and serves as |
| * a data entry for the target list model |
| * @param allowModification flag which indicates whether given model can be changed at future |
| * @return renderer for the given model |
| */ |
| @NotNull |
| public ArrangementUiComponent getComponent(@NotNull final ArrangementMatchCondition rendererTarget, |
| @NotNull final StdArrangementMatchRule rule, |
| final boolean allowModification) |
| { |
| final Ref<ArrangementUiComponent> ref = new Ref<ArrangementUiComponent>(); |
| rendererTarget.invite(new ArrangementMatchConditionVisitor() { |
| @Override |
| public void visit(@NotNull ArrangementAtomMatchCondition condition) { |
| RemoveAtomConditionCallback callback = allowModification ? new RemoveAtomConditionCallback(rule) : null; |
| ArrangementUiComponent component = new ArrangementAtomMatchConditionComponent( |
| mySettingsManager, myColorsProvider, condition, callback |
| ); |
| ref.set(component); |
| } |
| |
| @Override |
| public void visit(@NotNull ArrangementCompositeMatchCondition condition) { |
| ref.set(new ArrangementAndMatchConditionComponent(rule, condition, ArrangementMatchNodeComponentFactory.this, mySettingsManager, allowModification)); |
| } |
| }); |
| return ref.get(); |
| } |
| |
| private class RemoveAtomConditionCallback implements Consumer<ArrangementAtomMatchConditionComponent>, |
| ArrangementAnimationManager.Callback |
| { |
| |
| @NotNull private final StdArrangementMatchRule myRule; |
| |
| @NotNull private Object myModelValue; |
| private int myRow; |
| |
| RemoveAtomConditionCallback(@NotNull StdArrangementMatchRule rule) { |
| myRule = rule; |
| myModelValue = myRule; |
| } |
| |
| @Override |
| public void consume(@NotNull ArrangementAtomMatchConditionComponent component) { |
| ArrangementAtomMatchCondition condition = component.getMatchCondition(); |
| ArrangementMatchingRulesModel model = myList.getModel(); |
| int i = getModelIndex(); |
| if (i < 0) { |
| return; |
| } |
| myRow = i; |
| |
| ArrangementMatchCondition existingCondition = myRule.getMatcher().getCondition(); |
| if (existingCondition.equals(condition)) { |
| // We can't just remove an element at this time because that breaks last row rendering. |
| model.set(i, myModelValue = new DummyElement()); |
| } |
| else { |
| assert existingCondition instanceof ArrangementCompositeMatchCondition; |
| Set<ArrangementMatchCondition> operands = ((ArrangementCompositeMatchCondition)existingCondition).getOperands(); |
| operands.remove(condition); |
| if (operands.isEmpty()) { |
| // We can't just remove an element at this time because that breaks last row rendering. |
| model.set(i, myModelValue = new DummyElement()); |
| } |
| else if (operands.size() == 1) { |
| myModelValue = new StdArrangementMatchRule(new StdArrangementEntryMatcher(operands.iterator().next()), myRule.getOrderType()); |
| model.set(i, myModelValue); |
| } |
| else if (ArrangementConstants.LOG_RULE_MODIFICATION) { |
| LOG.info(String.format("Removed '%s' condition. Current rule state: %s", condition, myRule)); |
| myModelValue = myRule; |
| } |
| } |
| |
| ArrangementAnimationPanel panel = component.getAnimationPanel(); |
| new ArrangementAnimationManager(panel, this).startAnimation(); |
| } |
| |
| @Override |
| public void onAnimationIteration(boolean finished) { |
| refreshRow(); |
| if (myRow < 0) { |
| return; |
| } |
| myList.repaintRows(myRow, myRow, finished); |
| if (!finished) { |
| return; |
| } |
| if (myModelValue instanceof DummyElement) { |
| myList.removeRow(myRow); |
| } |
| } |
| |
| private void refreshRow() { |
| ArrangementMatchingRulesModel model = myList.getModel(); |
| if (myRow < 0 || myRow >= model.getSize()) { |
| myRow = getModelIndex(); |
| } |
| else { |
| Object o = model.getElementAt(myRow); |
| if (o != myModelValue) { |
| myRow = getModelIndex(); |
| } |
| } |
| } |
| |
| private int getModelIndex() { |
| // We can't just use model.indexOf(myRule) because there is a possible case that the model contain equal |
| // rules (rule1.equals(rule2) == true). That's why we have a helper method for search by reference identity. |
| ArrangementMatchingRulesModel model = myList.getModel(); |
| for (int i = 0, max = model.getSize(); i < max; i++) { |
| if (model.getElementAt(i) == myModelValue) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| } |
| |
| private static class DummyElement { |
| @Override |
| public String toString() { |
| return "dummy-" + System.identityHashCode(this); |
| } |
| } |
| } |