/*
* Copyright 2003-2016 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.lang.editor.menus.substitute;
import jetbrains.mps.kernel.model.SModelUtil;
import jetbrains.mps.openapi.editor.menus.substitute.SubstituteMenuContext;
import jetbrains.mps.openapi.editor.menus.substitute.SubstituteMenuItem;
import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration;
import jetbrains.mps.smodel.constraints.ModelConstraints;
import jetbrains.mps.smodel.constraints.ReferenceDescriptor;
import jetbrains.mps.smodel.presentation.ReferenceConceptUtil;
import jetbrains.mps.util.IterableUtil;
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.language.SReferenceLink;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author simon
*/
public class SimpleConceptSubstituteMenuPart implements SubstituteMenuPart {
@NotNull
private SConcept myConcept;
public SimpleConceptSubstituteMenuPart(@NotNull SAbstractConcept concept) {
if (concept instanceof SConcept) {
myConcept = ((SConcept) concept);
} else {
myConcept = MetaAdapterByDeclaration.asInstanceConcept(concept);
}
}
@NotNull
@Override
public List<SubstituteMenuItem> createItems(SubstituteMenuContext context) {
List<SubstituteMenuItem> smartItems = createSmartItemsItems(context);
if (smartItems != null) {
return smartItems;
}
return Collections.singletonList(
new DefaultSubstituteMenuItem(myConcept, context.getParentNode(), context.getCurrentTargetNode(), context.getEditorContext()));
}
@Nullable
private List<SubstituteMenuItem> createSmartItemsItems(SubstituteMenuContext context) {
SReferenceLink smartReference = ReferenceConceptUtil.getCharacteristicReference(myConcept);
SNode smartReferenceNode = getSmartReferenceNode(context);
final SAbstractConcept smartReferenceNodeTargetConcept = getSpecialSmartReferenceTargetConcept(smartReferenceNode);
if (smartReference != null) {
SAbstractConcept targetConcept = smartReference.getTargetConcept();
if (smartReferenceNodeTargetConcept != null && checkSmartReferenceNodeSpecializesSmartReference(smartReferenceNode, smartReference)) {
targetConcept = smartReferenceNodeTargetConcept;
}
return createSmartSubstituteMenuItems(context, smartReference, targetConcept, createSmartReferenceDescriptor(context));
} else if (smartReferenceNode != null && smartReferenceNodeTargetConcept != null) {
final SReferenceLink smartReferenceNodeReference = getReferenceLink(smartReferenceNode);
if (smartReferenceNodeReference != null) {
return createSmartSubstituteMenuItems(context, smartReferenceNodeReference,
smartReferenceNodeTargetConcept, createSmartReferenceDescriptorByNode(context, getMyConceptSourceNode(context)));
}
}
return null;
}
private SNode getSmartReferenceNode(SubstituteMenuContext context) {
final SNode conceptSourceNode = getMyConceptSourceNode(context);
if (conceptSourceNode == null) {
return null;
}
return ReferenceConceptUtil.getCharacteristicReference(
conceptSourceNode);
}
@Nullable
private SNode getMyConceptSourceNode(SubstituteMenuContext context) {
final SNodeReference conceptSourceNodeRef = myConcept.getSourceNode();
if (conceptSourceNodeRef == null) {
return null;
}
final SNode conceptSourceNode = conceptSourceNodeRef.resolve(context.getEditorContext().getRepository());
if (conceptSourceNode == null) {
return null;
}
return conceptSourceNode;
}
//todo remove this method when we will get rid of specialized concepts
@Nullable
private SAbstractConcept getSpecialSmartReferenceTargetConcept(SNode specialSmartReference) {
final SNode specializedTargetConceptNode = SModelUtil.getLinkDeclarationTarget(specialSmartReference);
if (specializedTargetConceptNode == null) {
return null;
}
return MetaAdapterByDeclaration.getConcept(specializedTargetConceptNode);
}
//todo remove this method when we will get rid of specialized concepts
private boolean checkSmartReferenceNodeSpecializesSmartReference(SNode specialSmartReference, @NotNull SReferenceLink smartReference) {
if (specialSmartReference == null) {
return false;
}
final SNode genuineLinkDeclaration = SModelUtil.getGenuineLinkDeclaration(specialSmartReference);
if (genuineLinkDeclaration == null || genuineLinkDeclaration == specialSmartReference) {
return false;
}
return smartReference.equals(MetaAdapterByDeclaration.getReferenceLink(genuineLinkDeclaration));
}
//todo remove this method when we will get rid of specialized concepts
private SReferenceLink getReferenceLink(SNode referenceNode) {
final SNode genuineLinkDeclaration = SModelUtil.getGenuineLinkDeclaration(referenceNode);
if (genuineLinkDeclaration == null) {
return null;
}
return MetaAdapterByDeclaration.getReferenceLink(genuineLinkDeclaration);
}
@NotNull
private List<SubstituteMenuItem> createSmartSubstituteMenuItems(SubstituteMenuContext context, SReferenceLink smartReference,
SAbstractConcept targetConcept, ReferenceDescriptor refDescriptor) {
SNode currentChild = context.getCurrentTargetNode();
SNode parentNode = context.getParentNode();
List<SubstituteMenuItem> result = new ArrayList<>();
Iterable<SNode> referentNodes = refDescriptor.getScope().getAvailableElements(null);
for (SNode referentNode : referentNodes) {
if (referentNode == null ||
!checkForSubconcept(targetConcept, referentNode)) {
continue;
}
result.add(new SmartReferenceSubstituteMenuItem(referentNode, parentNode,
currentChild, myConcept, smartReference, refDescriptor, context.getEditorContext()));
}
return result;
}
@NotNull
private ReferenceDescriptor createSmartReferenceDescriptor(SubstituteMenuContext context) {
SNode currentChild = context.getCurrentTargetNode();
final SNode parentNode = context.getParentNode();
int index = getIndex(currentChild, parentNode);
return ModelConstraints.getSmartReferenceDescriptor(parentNode,
context.getLink(), index, myConcept, context.getEditorContext().getRepository());
}
@NotNull
private ReferenceDescriptor createSmartReferenceDescriptorByNode(SubstituteMenuContext context, SNode conceptNode) {
SNode currentChild = context.getCurrentTargetNode();
final SNode parentNode = context.getParentNode();
int index = getIndex(currentChild, parentNode);
return ModelConstraints.getSmartReferenceDescriptor(parentNode,
context.getLink() != null ? context.getLink().getName() : null, index, conceptNode);
}
private int getIndex(@Nullable SNode currentChild, @NotNull SNode parentNode) {
int index = 0;
if (currentChild != null) {
final SContainmentLink containmentLink = currentChild.getContainmentLink();
index = IterableUtil.indexOf(parentNode.getChildren(containmentLink), currentChild);
}
return index;
}
private boolean checkForSubconcept(SAbstractConcept concept, SNode referentNode) {
return referentNode.getConcept().isSubConceptOf(concept);
}
}