/* * Copyright 2003-2014 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 jetbrains.mps.smodel.action; import jetbrains.mps.nodeEditor.cells.CellFinderUtil; import jetbrains.mps.openapi.editor.EditorContext; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.openapi.editor.cells.SubstituteAction; import jetbrains.mps.smodel.presentation.NodePresentationUtil; import jetbrains.mps.util.PatternUtil; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.model.SNode; public abstract class AbstractNodeSubstituteAction implements SubstituteAction { private static final Logger LOG = LogManager.getLogger(AbstractNodeSubstituteAction.class); private SNode mySourceNode; private Object myParameterObject; private SNode myOutputConcept; // todo: this class is still too abstract to have 'output concept' protected AbstractNodeSubstituteAction(SNode outputConcept, Object parameterObject, SNode sourceNode) { myOutputConcept = outputConcept; myParameterObject = parameterObject; mySourceNode = sourceNode; } protected AbstractNodeSubstituteAction() { } protected SNode doSubstitute(@Nullable final EditorContext editorContext, String pattern) { throw new UnsupportedOperationException(); } @Override public SNode getSourceNode() { return mySourceNode; } @Override public SNode getOutputConcept() { return myOutputConcept; } @Override public final Object getParameterObject() { return myParameterObject; } @Override public String getMatchingText(String pattern) { return getMatchingText(pattern, false, false); } @Override public String getVisibleMatchingText(String pattern) { return getMatchingText(pattern, false, true); } @Override public String getDescriptionText(String pattern) { if (myParameterObject instanceof SNode) { return NodePresentationUtil.descriptionText((SNode) myParameterObject); } if (myParameterObject instanceof SConcept) { return NodePresentationUtil.descriptionText((SConcept) myParameterObject); } return ""; } @Override public SNode getIconNode(String pattern) { return myParameterObject instanceof SNode ? (SNode) myParameterObject : null; } @Override public boolean isReferentPresentation() { return false; } @Override public SNode getActionType(String pattern) { return null; } @Override public SNode getActionType(String pattern, EditorCell contextCell) { return getActionType(pattern); } protected String getMatchingText(String pattern, boolean referent_presentation, boolean visible) { if (myParameterObject instanceof SNode) { return NodePresentationUtil.matchingText((SNode) myParameterObject, mySourceNode, visible); } if (myParameterObject instanceof SAbstractConcept) { return NodePresentationUtil.matchingText((SAbstractConcept) myParameterObject); } return "" + myParameterObject; } @Override public boolean canSubstituteStrictly(String pattern) { if (pattern == null || getMatchingText(pattern) == null) { return false; } return getMatchingText(pattern).equals(pattern); } /** * @param pattern . NULL if pattern is not available yet */ @Override public boolean canSubstitute(String pattern) { if (pattern == null || pattern.length() == 0) { return true; } String matchingText = null; try { matchingText = getMatchingText(pattern); } catch (Exception e) { LOG.error(null, e); } if (matchingText == null || matchingText.length() == 0) { return false; } if (matchingText.charAt(0) != pattern.charAt(0)) { return false; } return matches(pattern, matchingText); } private boolean matches(String pattern, String matchingText) { return matchingText.startsWith(pattern) || matchingText.matches(PatternUtil.getExactItemPatternBuilder(pattern, false, false).toString() + ".*"); } @Override public final SNode substitute(@Nullable final EditorContext context, final String pattern) { if (context != null) { // completion can be invoked by typing invalid stuff into existing cells, revert it back to the model state jetbrains.mps.nodeEditor.cells.EditorCell selectedCell = (jetbrains.mps.nodeEditor.cells.EditorCell) context.getSelectedCell(); if (selectedCell != null) { // Trying to invoke synchronizeViewWithModel() for the cell which was modified by "typing invalid stuff into" only. // // This is necessary to not reset all states of all "error" cells with modified text within them. // Important for auto-re-resolving functionality (see http://youtrack.jetbrains.com/issue/MPS-19751). // // In case this will break something we can thing of more careful "synchronizeViewWithModel()" execution. // For example: run synchronizeViewWithModel() only for constant cells or only for cells representing this // node only (not it's children). selectedCell.synchronizeViewWithModel(); } } SNode nodeToSelect = null; try { nodeToSelect = doSubstitute(context, pattern); } catch (RuntimeException rte) { LOG.error("Exception on calling doSubstitute() method for " + AbstractNodeSubstituteAction.this.getClass(), rte); } // similar to: IntellijentInputUtil.applyRigthTransform() logic if (context != null && nodeToSelect != null) { jetbrains.mps.nodeEditor.EditorComponent editorComponent = ((jetbrains.mps.nodeEditor.EditorComponent) context.getEditorComponent()); if (editorComponent != null) { editorComponent.getUpdater().flushModelEvents(); EditorCell cell = editorComponent.findNodeCell(nodeToSelect); if (cell != null) { EditorCell errorCell = CellFinderUtil.findFirstError(cell, true); if (errorCell != null) { editorComponent.changeSelectionWRTFocusPolicy(errorCell); } else { editorComponent.changeSelectionWRTFocusPolicy(cell); } } } } return nodeToSelect; } public String toString() { return getMatchingText(""); } }