/*
* 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.lang.editor.cellProviders;
import jetbrains.mps.editor.runtime.impl.cellActions.CommentUtil;
import jetbrains.mps.kernel.model.SModelUtil;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import jetbrains.mps.nodeEditor.cellProviders.AbstractCellListHandler;
import jetbrains.mps.nodeEditor.cells.EditorCell_Constant;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.smodel.NodeReadAccessCasterInEditor;
import jetbrains.mps.smodel.SNodeLegacy;
import jetbrains.mps.smodel.SNodeUtil;
import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration;
import jetbrains.mps.smodel.legacy.ConceptMetaInfoConverter;
import jetbrains.mps.util.IterableUtil;
import jetbrains.mps.util.annotation.ToRemove;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public abstract class RefNodeListHandler extends AbstractCellListHandler {
private SNode myChildConcept; //todo make it use S-entities internally
private SNode myLinkDeclaration; //todo make it use S-entities internally
private boolean myIsReverseOrder = false;
public RefNodeListHandler(final SNode ownerNode, final String childRole, EditorContext editorContext) {
// TODO: remove ownerNode parameter after MPS 3.5
super(ownerNode, childRole, editorContext);
NodeReadAccessCasterInEditor.runReadTransparentAction(new Runnable() {
@Override
public void run() {
myLinkDeclaration = new SNodeLegacy(ownerNode).getLinkDeclaration(childRole);
assert
myLinkDeclaration != null :
"link declaration was not found for role: \"" + childRole + "\" in concept: " + ownerNode.getConcept().getQualifiedName();
SNode genuineLink = SModelUtil.getGenuineLinkDeclaration(myLinkDeclaration);
myChildConcept = SModelUtil.getLinkDeclarationTarget(myLinkDeclaration);
if (SNodeUtil.getLinkDeclaration_IsReference(genuineLink)) {
throw new RuntimeException("Only Aggregation links can be used in list");
}
myElementRole = SModelUtil.getLinkDeclarationRole(genuineLink);
}
});
}
public RefNodeListHandler(SNode ownerNode, String childRole, EditorContext editorContext, boolean isReverseOrder) {
this(ownerNode, childRole, editorContext);
myIsReverseOrder = isReverseOrder;
}
@Deprecated
@ToRemove(version = 3.5)
public SNode getLinkDeclaration() {
return myLinkDeclaration;
}
/**
* @return original link (not specialized)
*/
public SContainmentLink getSLink() {
return MetaAdapterByDeclaration.getContainmentLink(SModelUtil.getGenuineLinkDeclaration(myLinkDeclaration));
}
@Deprecated
@ToRemove(version = 3.5)
public SNode getChildConcept() {
return myChildConcept;
}
public SAbstractConcept getChildSConcept() {
return MetaAdapterByDeclaration.getConcept(myChildConcept);
}
@Override
public EditorCell createNodeCell(EditorContext editorContext, SNode node) {
// TODO: after MPS 3.5 remove editorContext parameter & delete overridden deprecated method
return editorContext.getEditorComponent().getUpdater().getCurrentUpdateSession().updateChildNodeCell(node);
}
@Override
protected EditorCell createEmptyCell(EditorContext editorContext) {
// TODO: after MPS 3.5 remove editorContext parameter & delete overridden deprecated method
EditorCell_Constant emptyCell = new EditorCell_Constant(getEditorContext(), getNode(), null);
emptyCell.setDefaultText("<< ... >>");
emptyCell.setEditable(true);
emptyCell.setRole(getElementRole());
emptyCell.setCellId("empty_" + getElementRole());
return emptyCell;
}
@Override
protected SNode getAnchorNode(EditorCell anchorCell) {
SNode anchorNode = (anchorCell != null ? anchorCell.getSNode() : null);
if (anchorNode != null) {
Collection<? extends SNode> listElements = IterableUtil.asCollection(
AttributeOperations.getChildNodesAndAttributes(getNode(), ((ConceptMetaInfoConverter) getNode().getConcept()).convertAggregation(myElementRole)));
// anchor should be directly referenced from "list owner"
while (anchorNode != null && !listElements.contains(anchorNode)) {
anchorNode = anchorNode.getParent();
}
}
return anchorNode;
}
@Override
protected void doInsertNode(SNode nodeToInsert, SNode anchorNode, boolean insertBefore) {
insertBefore = insertBefore != myIsReverseOrder;
getNode().insertChildBefore(getElementRole(), nodeToInsert,
insertBefore ? anchorNode : anchorNode == null ? getNode().getFirstChild() : anchorNode.getNextSibling());
}
@Override
protected List<SNode> getNodesForList() {
List<SNode> resultList = new ArrayList<SNode>();
SContainmentLink containmentLink = ((ConceptMetaInfoConverter) getNode().getConcept()).convertAggregation(myElementRole);
Iterable<SNode> nodesAndComments =
AttributeOperations.getChildNodesAndAttributes(getNode(), containmentLink);
if (!myIsReverseOrder) {
resultList.addAll(IterableUtil.asCollection(nodesAndComments));
} else {
List<? extends SNode> children = IterableUtil.copyToList(nodesAndComments);
Collections.reverse(children);
resultList.addAll(children);
}
Iterator<SNode> it = resultList.iterator();
while (it.hasNext()) {
SNode next = it.next();
SNode nodeToFilter = next;
if (CommentUtil.isComment(next)) {
nodeToFilter = CommentUtil.getCommentedNode(next);
}
if (!filter(nodeToFilter)) {
it.remove();
}
}
return resultList;
}
protected boolean filter(SNode childNode) {
return true;
}
protected void setInnerCellsContext() {
setInnerCellsContext(myListEditorCell_Collection);
}
/**
* TODO: remove after MPS 3.5 was introduced for backward compatibility
*/
@NotNull
@Override
public SNode getNode() {
return myOwnerNode;
}
}