/*
* 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.smodel.behaviour;
import jetbrains.mps.core.aspects.behaviour.BaseBHDescriptor;
import jetbrains.mps.core.aspects.behaviour.IllegalBHDescriptor;
import jetbrains.mps.core.aspects.behaviour.api.BHDescriptor;
import jetbrains.mps.core.aspects.behaviour.api.SConstructor;
import jetbrains.mps.core.aspects.behaviour.api.SMethod;
import jetbrains.mps.core.aspects.behaviour.api.SMethodId;
import jetbrains.mps.smodel.language.ConceptRegistry;
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.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
/**
* Behavior Reflection Facade.
* API for the generated behavior code.
* The API is null-safe
*/
@SuppressWarnings("ALL")
public final class BHReflection {
@NotNull
public static SNode newNode(@Nullable SModel model, @NotNull SConstructor constructor, Object... parameters) {
return constructor.newNode(model, parameters);
}
public static void initNode(@NotNull SNode node) {
BHDescriptor bhDescriptor = getBHDescriptor(node.getConcept());
if (bhDescriptor instanceof BaseBHDescriptor) {
((BaseBHDescriptor) bhDescriptor).initNode(node);
} else {
throw new IllegalArgumentException("Impossible to init node " + node);
}
}
public static Object invoke(@Nullable SNode operand, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
return invoke0(operand, operand.getConcept(), methodId, parameters);
}
public static Object invoke(@Nullable SAbstractConcept operand, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
return invoke0(operand, operand, methodId, parameters);
}
public static Object invoke0(@Nullable SNode operand, @NotNull SAbstractConcept concept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invoke(operand, method, parameters);
}
public static Object invoke0(@Nullable SAbstractConcept operand, @NotNull SAbstractConcept concept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invoke(operand, method, parameters);
}
/**
* invokes a method specifically in the concreteConcept behavior.
*/
public static Object invokeSpecial(@Nullable SNode operand, @NotNull SAbstractConcept concreteConcept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concreteConcept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invokeSpecial(operand, method, parameters);
}
public static Object invokeSpecial(@Nullable SAbstractConcept operand, @NotNull SAbstractConcept concreteConcept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concreteConcept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invokeSpecial(operand, method, parameters);
}
/**
* method has to be virtual
* invokes method implementation which is strictly after the given concrete concept in the ancestor linearization of the node's concept
*/
public static Object invokeSuper(@Nullable SNode operand, @NotNull SAbstractConcept concreteConcept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concreteConcept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invokeSuper(operand, method, parameters);
}
public static Object invokeSuper(@Nullable SAbstractConcept operand, @NotNull SAbstractConcept concreteConcept, @NotNull SMethodId methodId, Object... parameters) {
if (operand == null) {
return null;
}
BHDescriptor bhDescriptor = getBHDescriptor(concreteConcept);
SMethod<?> method = bhDescriptor.getMethod(methodId);
if (method == null) {
throw new BHNoSuchMethodException(methodId, bhDescriptor);
}
return bhDescriptor.invokeSuper(operand, method, parameters);
}
@NotNull
private static BHDescriptor getBHDescriptor(@NotNull SAbstractConcept concept) {
return ConceptRegistry.getInstance().getBehaviorRegistry().getBHDescriptor(concept);
}
/**
* We have it extending the RuntimeException opposed to java {@link java.lang.NoSuchMethodException}
*/
private static class BHNoSuchMethodException extends RuntimeException {
public BHNoSuchMethodException(@NotNull SMethodId methodId, BHDescriptor descriptor) {
super("SMethod with id '" + methodId + "' could not be found within the " + descriptor + " behavior descriptor");
}
}
}