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; } }