/*
* Copyright 2003-2011 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.presentation;
import jetbrains.mps.smodel.Generator;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.smodel.SModelStereotype;
import jetbrains.mps.smodel.SNodeUtil;
import jetbrains.mps.smodel.SmartReferentUtil;
import jetbrains.mps.util.NameUtil;
import jetbrains.mps.util.StringUtil;
import jetbrains.mps.util.annotation.ToRemove;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SModule;
import java.awt.Font;
/**
* This class provides utility methods for default presentation logic for referenced nodes.
*
* Here is some implementation notes how presentation text is calculated:
*
* 1) default presentation:
* - custom text by overriding BC#getPresentation() or ISmartReferent#getPresentation(context)
* - name from INamedConcept
* - special presentation for smart. refs. TODO should be removed
* - concept alias
* 2) matching text:
* - custom text by overriding ISmartReferent#getMatchingText(context)
* - resolveInfo from IResolveInfo
* TODO IResolveInfo now uses mostly to separate matchingText and visibleMatchingText
* - default presentation
* 3) visible matching text:
* - custom text by overriding ISmartReferent#getVisibleMatchingText(context)
* - default presentation
* 4) description text:
* - custom text by overriding ISmartReferent#getDescriptionText(context)
* - {conceptName} ( {where} )
*
* These facilities should be used only in editor-runtime for specifying the textual presentation of referenced node in completion, etc.
* Other subsystems should not rely on it and use {@link SNode#getPresentation()} or {@link SNode#getName()} instead.
* TODO Should be moved to editor-runtime?
*/
public class NodePresentationUtil {
public static boolean isLocalTo(SNode referenceNode, SNode referentNode) {
SModel toModel = referenceNode.getModel();
if (toModel == null) return false;
SModel fromModel = referentNode.getModel();
if (fromModel == null) return false;
SModule referenceModule = toLanguage(toModel.getModule());
if (referenceModule instanceof Language) {
SModule referentModule = toLanguage(fromModel.getModule());
return referentModule == referenceModule;
}
return toModel == fromModel;
}
public static int getFontStyle(SNode referenceNode, SNode referentNode) {
SModel model = referentNode.getModel();
if (model == null) return Font.PLAIN;
if (!SModelStereotype.isUserModel(model)) return Font.PLAIN;
return isLocalTo(referenceNode, referentNode) ? Font.BOLD : Font.PLAIN;
}
public static int getSortPriority(SNode referenceNode, SNode referentNode) {
if (isLocalTo(referenceNode, referentNode)) return -2;
SModel model = referentNode.getModel();
if (model == null) return 0;
if (SModelStereotype.isUserModel(model)) return -1;
return 0;
}
private static SModule toLanguage(SModule m) {
if (m instanceof Generator) {
return ((Generator) m).getSourceLanguage();
}
return m;
}
/**
* Provides a text that should be shown in editor to present a referenced node.
*/
public static String presentation(@NotNull SNode node, @Nullable SNode context) {
return getPresentation(node, SmartReferentUtil.getPresentation(context, node));
}
public static String matchingText(SAbstractConcept concept) {
if (!concept.getConceptAlias().isEmpty()) {
return concept.getConceptAlias();
}
return concept.getName();
}
/**
*
* @deprecated use {@link #matchingText(SAbstractConcept)} instead.
*/
@Deprecated
@ToRemove(version = 3.5)
public static String matchingText(SAbstractConcept concept, boolean referentPresentation) {
return matchingText(concept);
}
/**
*
* @deprecated This method provides a visible matching text instead of real matching text, that might be confusing.
* Should be replaced with {@link #visibleMatchingText(SNode, SNode)}.
*/
@Deprecated
@ToRemove(version = 3.5)
public static String matchingText(SNode node) {
return visibleMatchingText(node, null);
}
/**
*
* @deprecated This method provides a visible matching text instead of real matching text, that might be confusing.
* Should be replaced with {@link #visibleMatchingText(SNode, SNode)}.
*/
@Deprecated
@ToRemove(version = 3.5)
public static String matchingText(SNode node, boolean referent_presentation) {
return visibleMatchingText(node, null);
}
/**
*
* @deprecated use {@link #matchingText(SNode, SNode)}, {@link #matchingText(SNode, SNode, boolean)} or {@link #visibleMatchingText(SNode, SNode)}
*/
@Deprecated
@ToRemove(version = 3.5)
public static String matchingText(SNode node, boolean referent_presentation, boolean visible) {
return matchingText(node, null, visible);
}
/**
* Provides a text that will be matched with user-typed pattern in completion menu.
*/
public static String matchingText(@NotNull SNode node, @Nullable SNode context) {
return getPresentation(node, SmartReferentUtil.getMatchingText(context, node));
}
/**
* It's preferred to use {@link #matchingText(SNode, SNode)} or {@link #visibleMatchingText(SNode, SNode)} instead.
*/
public static String matchingText(@NotNull SNode node, @Nullable SNode context, boolean visible) {
return getPresentation(node, SmartReferentUtil.getMatchingText(context, node, visible));
}
/**
* Provides a text that presents given node in completion menu.
* This text also can be used for matching with user-typed pattern,
* if there is no necessity to facilitate the separation between real matching text and visible matching text.
*/
public static String visibleMatchingText(@NotNull SNode node, @Nullable SNode context) {
return getPresentation(node, SmartReferentUtil.getVisibleMatchingText(context, node));
}
private static String getPresentation(SNode node, String custom) {
if (!StringUtil.isEmpty(custom)) {
return custom;
}
return node.getPresentation();
}
public static String descriptionText(SAbstractConcept concept) {
if (!concept.getShortDescription().isEmpty()) {
return concept.getShortDescription();
}
// Maybe its better to simply return a language fqName?
if (concept instanceof SConcept) {
SConcept superConcept = ((SConcept) concept).getSuperConcept();
if (superConcept != null) {
return "(" + superConcept.getName() + " in " + superConcept.getLanguage().getQualifiedName() + ")";
}
}
return "";
}
/**
* @deprecated use {@link #descriptionText(SAbstractConcept)} instead
*/
@Deprecated
@ToRemove(version = 3.5)
public static String descriptionText(SAbstractConcept concept, boolean referentPresentation) {
return descriptionText(concept);
}
public static String descriptionText(SNode node) {
return descriptionText(node, null);
}
/**
* @deprecated use {@link #descriptionText(SNode)} instead.
*/
@Deprecated
@ToRemove(version = 3.5)
public static String descriptionText(SNode node, boolean referent_presentation) {
return descriptionText(node, null);
}
public static String descriptionText(SNode node, SNode context) {
String description = SmartReferentUtil.getDescriptionText(context, node);
if (!StringUtil.isEmpty(description)) {
return description;
}
return descriptionText_internal(node);
}
private static String descriptionText_internal(SNode node) {
if (node == null) {
return "";
}
if (node.getParent() == null) {
if (node.getModel() != null) {
return node.getConcept().getName() + " (" + NameUtil.compactModelName(node.getModel().getReference()) + ")";
} else {
return node.getConcept().getName();
}
}
SContainmentLink containmentLink = node.getContainmentLink();
assert containmentLink != null;
return containmentLink.getName() + " (" + NameUtil.compactNodeFQName(node.getContainingRoot()) + ")";
}
public static String getAliasOrConceptName(SNode node) {
String alias = SNodeUtil.getConceptAlias(jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations.getConceptDeclaration(node));
if (alias != null) {
return alias;
}
return node.getConcept().getName();
}
public static String getRoleInParentOrConceptName(SNode node) {
String role = node.getRoleInParent();
if (role != null) {
return role;
}
if (SNodeUtil.isInstanceOfConceptDeclaration(node) && node.getName() != null) {
return node.getName();
}
return NameUtil.shortNameFromLongName(node.getClass().getName());
}
public static String getPathToRoot(SNode node) {
if (node == null) return "null";
if (node.getModel() != null && node.getParent() == null) return node.getName();
return getPathToRoot(node.getParent()) + " > " + node.getName();
}
}