package jetbrains.mps.ide.java.platform.highlighters;
/*Generated by MPS */
import jetbrains.mps.nodeEditor.checking.BaseEventProcessingEditorChecker;
import jetbrains.mps.project.MPSProject;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.nodeEditor.checking.UpdateResult;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.util.Cancellable;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.baseLanguage.search.ClassifierSuccessors;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.internal.collections.runtime.Sequence;
import java.util.Collections;
import jetbrains.mps.nodeEditor.EditorMessage;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import com.intellij.openapi.project.IndexNotReadyException;
import jetbrains.mps.baseLanguage.util.OverridingMethodsFinder;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import java.util.Iterator;
import java.util.List;
import jetbrains.mps.project.GlobalScope;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import jetbrains.mps.smodel.behaviour.BHReflection;
import jetbrains.mps.core.aspects.behaviour.SMethodTrimmedId;
import jetbrains.mps.smodel.event.SModelEvent;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.util.Computable;
import jetbrains.mps.smodel.event.SModelRootEvent;
import jetbrains.mps.smodel.event.SModelFileChangedEvent;
import jetbrains.mps.smodel.event.SModelChildEvent;
import jetbrains.mps.smodel.event.SModelReferenceEvent;
import org.jetbrains.mps.openapi.model.SReference;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.smodel.event.SModelPropertyEvent;
public class OverrideMethodsChecker extends BaseEventProcessingEditorChecker {
private static final int MAX_MESSAGE_NUMBER = 5;
private static final String LF = "\n";
private static final String TOOLTIP_INDENT = LF + " ";
private final MPSProject myProject;
private boolean myIndexWasNotReady;
public OverrideMethodsChecker(MPSProject project) {
myProject = project;
}
@NotNull
@Override
public UpdateResult update(EditorComponent component, boolean b, boolean b1, Cancellable cancellable) {
SNode rootNode = component.getEditedNode();
this.myIndexWasNotReady = !(ClassifierSuccessors.getInstance().isIndexReady(myProject));
if (this.myIndexWasNotReady) {
return UpdateResult.CANCELLED;
}
Iterable<SNode> classifiers = ListSequence.fromList(SNodeOperations.getNodeDescendants(rootNode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"), true, new SAbstractConcept[]{})).where(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return SNodeOperations.isInstanceOf(it, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, "jetbrains.mps.baseLanguage.structure.ClassConcept")) || SNodeOperations.isInstanceOf(it, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, "jetbrains.mps.baseLanguage.structure.Interface"));
}
});
if (Sequence.fromIterable(classifiers).isEmpty()) {
return new UpdateResult.Completed(true, Collections.<EditorMessage>emptySet());
}
Set<EditorMessage> result = SetSequence.fromSet(new HashSet<EditorMessage>());
for (SNode containedClassifier : Sequence.fromIterable(classifiers)) {
// each classifier here is instance of ClassConcept or Interface
try {
collectOverridenMethods(containedClassifier, result);
} catch (IndexNotReadyException indexNotReady) {
// Catching IndexNotReadyException for now. In general suggestion of IDEA developers was to start using
// DaemonCodeAnalyzer for background highlighting processes execution
myIndexWasNotReady = true;
}
collectOverridingMethods(containedClassifier, result);
}
return new UpdateResult.Completed(true, result);
}
private void collectOverridingMethods(SNode container, Set<EditorMessage> messages) {
OverridingMethodsFinder finder = new OverridingMethodsFinder(container);
for (SNode overridingMethod : SetSequence.fromSet(finder.getOverridingMethods())) {
StringBuffer tooltip = new StringBuffer();
int messageCounter = 0;
Set<Tuples._2<SNode, SNode>> overridenMethods = finder.getOverridenMethods(overridingMethod);
boolean overrides = SPropertyOperations.getBoolean(overridingMethod, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b21dL, 0x1126a8d157dL, "isAbstract")) || SetSequence.fromSet(overridenMethods).where(new IWhereFilter<Tuples._2<SNode, SNode>>() {
public boolean accept(Tuples._2<SNode, SNode> it) {
return !(SPropertyOperations.getBoolean(it._0(), MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b21dL, 0x1126a8d157dL, "isAbstract")));
}
}).isNotEmpty();
for (Iterator<Tuples._2<SNode, SNode>> it = SetSequence.fromSet(overridenMethods).iterator(); it.hasNext();) {
SNode overridenClassifier = it.next()._1();
tooltip.append((overrides ? "Overrides" : "Implements"));
tooltip.append(" method in '");
tooltip.append(getClassifierPresentation(overridenClassifier));
tooltip.append("'");
if (it.hasNext()) {
tooltip.append(LF);
if (++messageCounter == MAX_MESSAGE_NUMBER) {
tooltip.append("...");
break;
}
}
}
SetSequence.fromSet(messages).addElement(new OverridingMethodEditorMessage(overridingMethod, this, tooltip.toString(), overrides));
}
}
private void collectOverridenMethods(SNode container, Set<EditorMessage> messages) {
List<SNode> derivedClassifiers = ClassifierSuccessors.getInstance().getDerivedClassifiers(container, GlobalScope.getInstance());
if (ListSequence.fromList(derivedClassifiers).isEmpty()) {
return;
}
boolean isInterface = SNodeOperations.isInstanceOf(container, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, "jetbrains.mps.baseLanguage.structure.Interface"));
StringBuffer superClassifierTooltip = new StringBuffer();
if (ListSequence.fromList(derivedClassifiers).count() > MAX_MESSAGE_NUMBER) {
superClassifierTooltip.append((isInterface ? "Has implementations" : "Has subclasses"));
} else {
superClassifierTooltip.append((isInterface ? "Is implemented by" : "Is subclassed by"));
for (SNode subClassifier : ListSequence.fromList(derivedClassifiers)) {
superClassifierTooltip.append(TOOLTIP_INDENT);
superClassifierTooltip.append(getClassifierPresentation(subClassifier));
if (SNodeOperations.isInstanceOf(subClassifier, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xfc367070a5L, "jetbrains.mps.baseLanguage.structure.EnumClass"))) {
for (SNode enumConstant : ListSequence.fromList(SLinkOperations.getChildren(SNodeOperations.cast(subClassifier, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xfc367070a5L, "jetbrains.mps.baseLanguage.structure.EnumClass")), MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xfc367070a5L, 0xfc367503acL, "enumConstant")))) {
superClassifierTooltip.append(TOOLTIP_INDENT);
superClassifierTooltip.append(getEnumConstantPresentation(enumConstant));
}
}
}
}
SetSequence.fromSet(messages).addElement(new SubclassedClassifierEditorMessage(container, this, superClassifierTooltip.toString(), isInterface));
Map<String, Set<SNode>> nameToMethodsMap = MapSequence.fromMap(new HashMap<String, Set<SNode>>());
for (SNode method : Sequence.fromIterable(((Iterable<SNode>) BHReflection.invoke(container, SMethodTrimmedId.create("methods", MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"), "4_LVZ3pBKCn")))).where(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return OverridingMethodsFinder.canBeOverriden(it);
}
})) {
SetSequence.fromSet(OverridingMethodsFinder.safeGet(nameToMethodsMap, SPropertyOperations.getString(method, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name")))).addElement(method);
}
if (MapSequence.fromMap(nameToMethodsMap).isEmpty()) {
return;
}
Map<SNode, Set<SNode>> overridenToOverridingMethodsMap = createOverridenToOverridingMethodsMap(nameToMethodsMap, derivedClassifiers);
for (SNode overridenMethod : SetSequence.fromSet(MapSequence.fromMap(overridenToOverridingMethodsMap).keySet())) {
if (SPropertyOperations.getBoolean(overridenMethod, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, 0x113294bffd2L, "isFinal"))) {
continue;
}
boolean overriden = !(SPropertyOperations.getBoolean(overridenMethod, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b21dL, 0x1126a8d157dL, "isAbstract")));
StringBuffer tooltip = new StringBuffer("Is ");
tooltip.append((overriden ? "overriden" : "implemented"));
tooltip.append(" in");
int messageCounter = 0;
for (Iterator<SNode> it = SetSequence.fromSet(MapSequence.fromMap(overridenToOverridingMethodsMap).get(overridenMethod)).iterator(); it.hasNext();) {
SNode overridingMethod = it.next();
tooltip.append(TOOLTIP_INDENT);
tooltip.append(getPresentation(SNodeOperations.getParent(overridingMethod)));
if (++messageCounter == MAX_MESSAGE_NUMBER && it.hasNext()) {
tooltip.append(TOOLTIP_INDENT);
tooltip.append("...");
break;
}
}
SetSequence.fromSet(messages).addElement(new OverridenMethodEditorMessage(overridenMethod, this, tooltip.toString(), overriden));
}
}
private Map<SNode, Set<SNode>> createOverridenToOverridingMethodsMap(Map<String, Set<SNode>> nameToMethodsMap, Iterable<SNode> derivedClassifiers) {
Map<SNode, Set<SNode>> result = MapSequence.fromMap(new HashMap<SNode, Set<SNode>>());
for (SNode derivedClassifier : Sequence.fromIterable(derivedClassifiers)) {
for (final SNode derivedClassifierMethod : Sequence.fromIterable(OverridingMethodsFinder.getInstanceMethods(derivedClassifier)).where(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return OverridingMethodsFinder.canOverride(it);
}
})) {
Set<SNode> similarMethods = MapSequence.fromMap(nameToMethodsMap).get(SPropertyOperations.getString(derivedClassifierMethod, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name")));
if (similarMethods == null) {
continue;
}
SNode overridenMethod = SetSequence.fromSet(similarMethods).findFirst(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return ((boolean) (Boolean) BHReflection.invoke(it, SMethodTrimmedId.create("hasSameSignature", MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, "jetbrains.mps.baseLanguage.structure.BaseMethodDeclaration"), "hEwIB0z"), derivedClassifierMethod));
}
});
if (overridenMethod != null) {
Set<SNode> overridingMethods = OverridingMethodsFinder.safeGet(result, overridenMethod);
SetSequence.fromSet(overridingMethods).addElement(derivedClassifierMethod);
if (SetSequence.fromSet(overridingMethods).count() > MAX_MESSAGE_NUMBER) {
SetSequence.fromSet(similarMethods).removeElement(overridenMethod);
if (SetSequence.fromSet(similarMethods).isEmpty()) {
MapSequence.fromMap(nameToMethodsMap).removeKey(SPropertyOperations.getString(derivedClassifierMethod, MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name")));
if (MapSequence.fromMap(nameToMethodsMap).isEmpty()) {
return result;
}
}
}
}
}
}
return result;
}
@Override
public boolean needsUpdateAfterEvents(final List<SModelEvent> events) {
if (this.myIndexWasNotReady) {
return true;
}
// TODO rewrite without read action, see doc of EditorChecker#processEvents
return new ModelAccessHelper(myProject.getRepository()).runReadAction(new Computable<Boolean>() {
public Boolean compute() {
for (SModelEvent event : ListSequence.fromList(events)) {
if (event instanceof SModelRootEvent || event instanceof SModelFileChangedEvent) {
return true;
}
if (event instanceof SModelChildEvent) {
SModelChildEvent childEvent = (SModelChildEvent) event;
SNode child = childEvent.getChild();
SNode parent = childEvent.getParent();
String childRole = childEvent.getChildRole();
// Class or Interface was added/removed
if (SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, "jetbrains.mps.baseLanguage.structure.Interface")) || SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, "jetbrains.mps.baseLanguage.structure.ClassConcept")) || SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1107e0cb103L, "jetbrains.mps.baseLanguage.structure.AnonymousClass")) || SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1133e3b449aL, "jetbrains.mps.baseLanguage.structure.AnonymousClassCreator"))) {
return true;
}
// method was added/removed from containing Classifier
if (SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b21dL, "jetbrains.mps.baseLanguage.structure.InstanceMethodDeclaration")) && SNodeOperations.isInstanceOf(parent, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"))) {
return true;
}
// one of extendedInterface/superclass/implementedInterface child elements was added/removed
if (SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, "jetbrains.mps.baseLanguage.structure.ClassifierType")) && (MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101edd46144L, 0x101eddadad7L, "extendedInterface").getName().equals(childRole) || MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, 0x10f6353296dL, "superclass").getName().equals(childRole) || MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, 0xff2ac0b419L, "implementedInterface").getName().equals(childRole))) {
return true;
}
// parameter was added/removed
if (SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c77f1e94L, "jetbrains.mps.baseLanguage.structure.ParameterDeclaration")) && MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, 0xf8cc56b1feL, "parameter").getName().equals(childRole)) {
return true;
}
if (SNodeOperations.isInstanceOf(child, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c37f506dL, "jetbrains.mps.baseLanguage.structure.Type")) && isParameterType(child)) {
return true;
}
}
if (event instanceof SModelReferenceEvent) {
SModelReferenceEvent referenceEvent = (SModelReferenceEvent) event;
SReference reference = referenceEvent.getReference();
SNode sourceNode = reference.getSourceNode();
SReferenceLink referenceRole = reference.getLink();
if (MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, 0x101de490babL, "classifier").equals(referenceRole) && SNodeOperations.isInstanceOf(sourceNode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, "jetbrains.mps.baseLanguage.structure.ClassifierType")) && (SNodeOperations.isInstanceOf(SNodeOperations.getParent(sourceNode), MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier")))) {
return true;
}
if (MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1107e0cb103L, 0x1107e0fd2a0L, "classifier").equals(referenceRole) && SNodeOperations.isInstanceOf(sourceNode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1107e0cb103L, "jetbrains.mps.baseLanguage.structure.AnonymousClass"))) {
return true;
}
if (SNodeOperations.isInstanceOf(sourceNode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c37f506dL, "jetbrains.mps.baseLanguage.structure.Type")) && isParameterType(sourceNode)) {
return true;
}
}
if (event instanceof SModelPropertyEvent) {
SModelPropertyEvent propertyEvent = (SModelPropertyEvent) event;
SNode node = propertyEvent.getNode();
if (SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, "jetbrains.mps.baseLanguage.structure.BaseMethodDeclaration"))) {
return true;
}
}
}
return false;
}
});
}
private String getPresentation(SNode node) {
if (SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"))) {
return getClassifierPresentation(SNodeOperations.cast(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier")));
}
if (SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xfc367388b3L, "jetbrains.mps.baseLanguage.structure.EnumConstantDeclaration"))) {
return getEnumConstantPresentation(SNodeOperations.cast(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xfc367388b3L, "jetbrains.mps.baseLanguage.structure.EnumConstantDeclaration")));
}
return ((String) BHReflection.invoke(node, SMethodTrimmedId.create("getPresentation", null, "hEwIMiw")));
}
private String getClassifierPresentation(SNode classifier) {
return ((String) (String) BHReflection.invoke(classifier, SMethodTrimmedId.create("getFqName", null, "hEwIO9y")));
}
private String getEnumConstantPresentation(SNode enumConstantDeclaration) {
return ((String) (String) BHReflection.invoke(enumConstantDeclaration, SMethodTrimmedId.create("getFqName", null, "hEwIO9y")));
}
private static boolean isParameterType(SNode type) {
SNode parent = SNodeOperations.getParent(type);
if (SNodeOperations.isInstanceOf(parent, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c77f1e94L, "jetbrains.mps.baseLanguage.structure.ParameterDeclaration"))) {
return true;
}
if (SNodeOperations.isInstanceOf(parent, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c37f506dL, "jetbrains.mps.baseLanguage.structure.Type"))) {
return isParameterType(parent);
}
return false;
}
}