/*
* 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.smodel.action;
import jetbrains.mps.openapi.actions.descriptor.ActionAspectDescriptor;
import jetbrains.mps.openapi.actions.descriptor.NodeFactory;
import jetbrains.mps.openapi.editor.EditorContext;
import jetbrains.mps.smodel.CopyUtil;
import jetbrains.mps.smodel.SModelUtil_new;
import jetbrains.mps.smodel.adapter.MetaAdapterByDeclaration;
import jetbrains.mps.smodel.behaviour.BHReflection;
import jetbrains.mps.smodel.language.LanguageRegistry;
import jetbrains.mps.smodel.language.LanguageRuntime;
import jetbrains.mps.smodel.legacy.ConceptMetaInfoConverter;
import jetbrains.mps.util.annotation.ToRemove;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
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.SInterfaceConcept;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.util.DepthFirstConceptIterator;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class NodeFactoryManager {
private static Logger LOG = LogManager.getLogger(NodeFactoryManager.class);
public static SNode createNode(SNode enclosingNode, EditorContext editorContext, String linkRole) {
SAbstractConcept linkTarget = null;
// this mimics deprecated SAbstractConcept.getLink(String), first aggregation, then association, albeit without NPE for non-existent ConceptDescriptor
for (SContainmentLink link : enclosingNode.getConcept().getContainmentLinks()) {
if (link.getName().equals(linkRole)) {
linkTarget = link.getTargetConcept();
break;
}
}
if (linkTarget == null) {
linkTarget = ((ConceptMetaInfoConverter) enclosingNode.getConcept()).convertAssociation(linkRole).getTargetConcept();
}
SModel model = enclosingNode.getModel();
return createNode(linkTarget, null, enclosingNode, model);
}
/**
* @deprecated use {@link #createNode(org.jetbrains.mps.openapi.language.SAbstractConcept, org.jetbrains.mps.openapi.model.SNode, org.jetbrains.mps.openapi.model.SNode, org.jetbrains.mps.openapi.model.SModel)}
*/
@Deprecated
@ToRemove(version = 3.2)
public static SNode createNode(@NotNull SNode nodeConcept, SNode sampleNode, SNode enclosingNode, @Nullable SModel model) {
return createNode(asSConcept(nodeConcept), sampleNode, enclosingNode, model);
}
public static SNode createNode(@NotNull SAbstractConcept nodeConcept, SNode sampleNode, SNode enclosingNode, @Nullable SModel model) {
return createNode(nodeConcept, sampleNode, enclosingNode, model, new HashSet<SAbstractConcept>());
}
private static SNode createNode(@NotNull SAbstractConcept nodeConcept, SNode sampleNode, SNode enclosingNode, @Nullable SModel model,
Set<SAbstractConcept> visitedNonOptionalChildConcepts) {
SNode newNode = SModelUtil_new.instantiateConceptDeclaration(nodeConcept, model, null, false);
if (newNode == null) return null;
if (nodeConcept instanceof SInterfaceConcept) {
return newNode;
}
BHReflection.initNode(newNode);
if (sampleNode != null) {
sampleNode = CopyUtil.copy(sampleNode);
}
nodeConcept = newNode.getConcept(); // XXX is it possible to get another concept on creation?
setupNode(nodeConcept, newNode, sampleNode, enclosingNode, model);
createNodeStructure(nodeConcept, newNode, sampleNode, enclosingNode, model, visitedNonOptionalChildConcepts);
return newNode;
}
private static void createNodeStructure(SAbstractConcept nodeConcept, SNode newNode, SNode sampleNode, SNode enclosingNode, SModel model,
Set<SAbstractConcept> visitedNonOptionalChildConcepts) {
for (SContainmentLink linkDeclaration : nodeConcept.getContainmentLinks()) {
if (linkDeclaration.isOptional()) {
continue;
}
SAbstractConcept targetConcept = linkDeclaration.getTargetConcept();
if (!newNode.getChildren(linkDeclaration).iterator().hasNext()) {
if (visitedNonOptionalChildConcepts.add(targetConcept)) {
try {
SNode childNode = createNode(targetConcept, sampleNode, enclosingNode, model, visitedNonOptionalChildConcepts);
newNode.addChild(linkDeclaration, childNode);
} finally {
visitedNonOptionalChildConcepts.remove(targetConcept);
}
} else {
LOG.error("Cyclic containment found while initializing node: " + newNode.getNodeId() + ", containment link: " + linkDeclaration.getName());
}
}
}
}
/**
* @deprecated use {@link #setupNode(org.jetbrains.mps.openapi.language.SAbstractConcept, org.jetbrains.mps.openapi.model.SNode, org.jetbrains.mps.openapi.model.SNode, org.jetbrains.mps.openapi.model.SNode, org.jetbrains.mps.openapi.model.SModel)}
*/
@Deprecated
@ToRemove(version = 3.2)
public static void setupNode(SNode nodeConcept, SNode node, SNode sampleNode, SNode enclosingNode, SModel model) {
setupNode(asSConcept(nodeConcept), node, sampleNode, enclosingNode, model);
}
public static void setupNode(SAbstractConcept nodeConcept, SNode node, SNode sampleNode, SNode enclosingNode, SModel model) {
for (SAbstractConcept ancestor : new DepthFirstConceptIterator(nodeConcept)) {
ActionAspectDescriptor actionAspectDescriptor = null;
LanguageRuntime languageRuntime = LanguageRegistry.getInstance().getLanguage(ancestor.getLanguage());
if (languageRuntime != null) {
actionAspectDescriptor = languageRuntime.getAspect(ActionAspectDescriptor.class);
}
if (actionAspectDescriptor == null) {
continue;
}
Collection<NodeFactory> factories = actionAspectDescriptor.getFactories(ancestor);
for (NodeFactory factory : factories) {
factory.setup(node, sampleNode, enclosingNode, model);
}
}
}
private static SConcept asSConcept(SNode nodeConcept) {
return MetaAdapterByDeclaration.getInstanceConcept(nodeConcept);
}
}