/*
* Copyright 2003-2015 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.nodeEditor.cellMenu;
import jetbrains.mps.nodeEditor.SubstituteActionUtil;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.cells.SubstituteAction;
import jetbrains.mps.openapi.editor.cells.SubstituteInfo;
import jetbrains.mps.smodel.IOperationContext;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.smodel.SModelInternal;
import jetbrains.mps.smodel.SModelOperations;
import jetbrains.mps.smodel.tempmodel.TempModuleOptions;
import jetbrains.mps.smodel.tempmodel.TemporaryModels;
import jetbrains.mps.typesystem.inference.InequalitySystem;
import jetbrains.mps.util.Computable;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Author: Sergey Dmitriev.
* Time: Oct 29, 2003 2:17:38 PM
*/
public abstract class AbstractNodeSubstituteInfo implements SubstituteInfo {
private static final Logger LOG = LogManager.getLogger(AbstractNodeSubstituteInfo.class);
private static SModel ourModelForTypechecking = null;
public static SModel getModelForTypechecking() {
return ourModelForTypechecking;
}
private List<SubstituteAction> myCachedActionList;
private Map<String, List<SubstituteAction>> myPatternsToActionListsCache = new HashMap<String, List<SubstituteAction>>();
private Map<String, List<SubstituteAction>> myStrictPatternsToActionListsCache = new HashMap<String, List<SubstituteAction>>();
private String myOriginalText;
private EditorContext myEditorContext;
public AbstractNodeSubstituteInfo(EditorContext editorContext) {
myEditorContext = editorContext;
}
public EditorContext getEditorContext() {
return myEditorContext;
}
public IOperationContext getOperationContext() {
return myEditorContext.getOperationContext();
}
@Override
public void setOriginalText(String text) {
myOriginalText = text;
}
@Override
public String getOriginalText() {
return myOriginalText;
}
protected abstract List<SubstituteAction> createActions();
@Override
public void invalidateActions() {
myCachedActionList = null;
myPatternsToActionListsCache.clear();
myStrictPatternsToActionListsCache.clear();
}
@Override
public boolean hasExactlyNActions(final String pattern, final boolean strictMatching, final int n) {
return new ModelAccessHelper(myEditorContext.getRepository()).runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
int count = 0;
for (SubstituteAction action : getActionsFromCache(pattern, strictMatching)) {
if (strictMatching ? action.canSubstituteStrictly(pattern) : action.canSubstitute(pattern)) {
count++;
}
if (count > n) return false;
}
return n == count;
}
});
}
protected InequalitySystem getInequalitiesSystem(EditorCell contextCell) {
return null;
}
@Override
public List<SubstituteAction> getSmartMatchingActions(final String pattern, final boolean strictMatching, EditorCell contextCell) {
// TODO make this thread local maybe?
ourModelForTypechecking = TemporaryModels.getInstance().create(false, false, TempModuleOptions.forDefaultModule());
for (SLanguage l : SModelOperations.getAllLanguageImports(getEditorContext().getModel())) {
((SModelInternal) ourModelForTypechecking).addLanguage(l);
}
try {
final InequalitySystem inequalitiesSystem = getInequalitiesSystem(contextCell);
List<SubstituteAction> substituteActionList = getMatchingActions(pattern, strictMatching);
if (inequalitiesSystem == null) return substituteActionList;
List<SubstituteAction> result = new ArrayList<SubstituteAction>();
for (SubstituteAction nodeSubstituteAction : substituteActionList) {
try {
SNode type = nodeSubstituteAction.getActionType(pattern);
if (type != null && inequalitiesSystem.satisfies(type)) {
result.add(nodeSubstituteAction);
}
} catch (Throwable th) {
LOG.error("Exception on checking smart matching conditions for action: " + (nodeSubstituteAction == null ? "null" : nodeSubstituteAction.getClass()),
th);
}
}
return result;
} catch (RuntimeException rte) {
LOG.error("Exception while building SmartMatchingActions list", rte);
return new ArrayList<>();
} finally {
TemporaryModels.getInstance().dispose(ourModelForTypechecking);
ourModelForTypechecking = null;
}
}
@Override
public List<SubstituteAction> getMatchingActions(final String pattern, final boolean strictMatching) {
return new ModelAccessHelper(myEditorContext.getRepository()).runReadAction(new Computable<List<SubstituteAction>>() {
@Override
public List<SubstituteAction> compute() {
List<SubstituteAction> actionsFromCache = getActionsFromCache(pattern, strictMatching);
ArrayList<SubstituteAction> result = new ArrayList<SubstituteAction>(actionsFromCache.size());
for (SubstituteAction item : actionsFromCache) {
try {
if (strictMatching ? item.canSubstituteStrictly(pattern) : SubstituteActionUtil.canSubstitute(item, pattern)) {
result.add(item);
}
} catch (Throwable th) {
LOG.error("Exception on calling canSubstitute on a substitute action " + (item == null ? "null" : item.getClass()), th);
}
}
putActionsToCache(pattern, strictMatching, result);
result.trimToSize();
return result;
}
});
}
private List<SubstituteAction> getActions() {
if (myCachedActionList == null) {
try {
myCachedActionList = createActions();
} catch (Throwable th) {
LOG.error("Exception while creating substitute actions in " + this.getClass(), th);
return new LinkedList<SubstituteAction>();
}
}
return myCachedActionList;
}
private void putActionsToCache(String pattern, boolean strictMatching, List<SubstituteAction> actions) {
List<SubstituteAction> actionsCopy = new ArrayList<SubstituteAction>(actions);
if (strictMatching) {
myStrictPatternsToActionListsCache.put(pattern, actionsCopy);
} else {
myPatternsToActionListsCache.put(pattern, actionsCopy);
}
}
private List<SubstituteAction> getActionsFromCache(String pattern, boolean strictMatching) {
if (pattern == null) {
return Collections.unmodifiableList(getActions());
}
if (!strictMatching) {
if (pattern.isEmpty()) {
if (myPatternsToActionListsCache.containsKey(pattern)) {
return Collections.unmodifiableList(myPatternsToActionListsCache.get(pattern));
}
} else {
for (; pattern.length() > 0; pattern = pattern.substring(0, pattern.length() - 1)) {
if (myPatternsToActionListsCache.containsKey(pattern)) {
return Collections.unmodifiableList(myPatternsToActionListsCache.get(pattern));
}
}
}
} else {
if (myStrictPatternsToActionListsCache.containsKey(pattern)) {
return Collections.unmodifiableList(myStrictPatternsToActionListsCache.get(pattern));
} else if (myPatternsToActionListsCache.containsKey(pattern)) {
return Collections.unmodifiableList(myPatternsToActionListsCache.get(pattern));
}
}
return Collections.unmodifiableList(getActions());
}
}