blob: cc26b8c828108ceb0e9a70270d8b7064169cbcec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 Xored Software Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Xored Software Inc - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.rcptt.ecl.runtime;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.rcptt.ecl.core.Binding;
import org.eclipse.rcptt.ecl.core.Block;
import org.eclipse.rcptt.ecl.core.Command;
import org.eclipse.rcptt.ecl.core.CoreFactory;
import org.eclipse.rcptt.ecl.core.CorePackage;
import org.eclipse.rcptt.ecl.core.Exec;
import org.eclipse.rcptt.ecl.core.ExecutableParameter;
import org.eclipse.rcptt.ecl.core.LiteralParameter;
import org.eclipse.rcptt.ecl.core.Parallel;
import org.eclipse.rcptt.ecl.core.Parameter;
import org.eclipse.rcptt.ecl.core.Pipeline;
import org.eclipse.rcptt.ecl.core.Sequence;
import org.eclipse.rcptt.ecl.core.util.EclCommandNameConvention;
import org.eclipse.rcptt.ecl.internal.core.CorePlugin;
import org.eclipse.rcptt.ecl.internal.core.ParamConverterManager;
public class EclCompiler {
public static Command compile(Command command) throws CoreException {
return compile(command, false);
}
public static Command compile(Command command, boolean hasInput)
throws CoreException {
if (command instanceof Exec) {
Exec exec = (Exec) command;
return compile(
new FQName(null,
EclCommandNameConvention.toScriptletName(exec
.getName())), exec.getParameters(),
hasInput, false);
}
if (command instanceof Block) {
Block block = (Block) command;
for (int i = 0; i < block.getCommands().size(); i++) {
boolean innerHasInput = false;
if (block instanceof Sequence)
innerHasInput = hasInput;
else if (block instanceof Pipeline)
if (i == 0)
innerHasInput = hasInput;
else
innerHasInput = true;
else if (block instanceof Parallel)
innerHasInput = hasInput;
block.getCommands().set(i,
compile(block.getCommands().get(i), innerHasInput));
}
}
return command;
}
public static Command rawCompile(Exec exec, boolean hasInput)
throws CoreException {
return compile(
new FQName(null, EclCommandNameConvention.toScriptletName(exec
.getName())), exec.getParameters(), hasInput, true);
}
public static Command compile(FQName fqn, List<Parameter> params,
boolean hasInput) throws CoreException {
return compile(fqn, params, hasInput, false);
}
public static Command compile(FQName fqn, List<Parameter> params,
boolean hasInput, boolean inDepth) throws CoreException {
Command target = CoreUtils.createCommand(fqn.ns, fqn.name);
EClass targetClass = target.eClass();
List<EStructuralFeature> features = CoreUtils.getFeatures(targetClass);
Map<String, EStructuralFeature> map = new HashMap<String, EStructuralFeature>();
for (EStructuralFeature feature : features) {
map.put(feature.getName(), feature);
// FIXME handle name conflicts!!!!
}
int i = 0;
boolean processUnnamed = canProcessUnnamed(targetClass);
for (Parameter param : params) {
if (param.eIsSet(CorePackage.eINSTANCE.getParameter_Name())) {
processUnnamed = false;
} else {
if (!processUnnamed) {
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID,
"Unnamed parameters disallowed after named ones");
throw new CoreException(status);
}
}
EStructuralFeature feature = processUnnamed ? features.get(i++)
: map.get(param.getName());
if (feature == null) {
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID, MessageFormat.format(
"Invalid parameter name: {0}",
new Object[] { param.getName() }));
throw new CoreException(status);
}
if (processUnnamed
&& feature.getEAnnotation(CoreUtils.INPUT_ANN) != null
&& hasInput)
feature = features.get(i++);
if (feature.getEAnnotation(CoreUtils.INTERNAL_ANN) != null)
feature = features.get(i++);
evalFeatureValue(target, param, feature, hasInput, inDepth);
// TODO support any upper bound
if (feature.getUpperBound() == -1)
i--;
}
return target;
}
private static boolean canProcessUnnamed(EClass targetClass)
throws CoreException {
boolean wasOptional = false;
for (EStructuralFeature feature : targetClass.getEStructuralFeatures()) {
int lower = feature.getLowerBound();
int upper = feature.getUpperBound();
checkBounds(lower, upper);
if (lower == 0) {
wasOptional = true;
} else if (wasOptional) {
return false;
}
}
return true;
}
private static void checkBounds(int lower, int upper) throws CoreException {
if (lower < 0) {
IStatus status = new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID,
"Lower bounds is negative");
throw new CoreException(status);
}
if (upper > 1) {
// TODO correct message
IStatus status = new Status(IStatus.ERROR, CorePlugin.PLUGIN_ID,
"Upper bounds more than one is not supported yet");
throw new CoreException(status);
}
}
private static void evalFeatureValue(Command target, Parameter param,
EStructuralFeature feature, boolean hasInput, boolean inDepth)
throws CoreException {
Object value = null;
if (param instanceof LiteralParameter) {
LiteralParameter literal = (LiteralParameter) param;
Class<?> instanceClass = feature.getEType().getInstanceClass();
List<String> allowedTypes = CoreUtils.getMetaTypeList(feature);
try {
if (feature.getEType() instanceof EEnum) {
EEnum en = (EEnum) feature.getEType();
EEnumLiteral eEnumLiteral = en.getEEnumLiteral(literal
.getLiteral());
if (eEnumLiteral == null) {
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID, "Invalid constant: "
+ literal.getLiteral());
throw new CoreException(status);
}
value = eEnumLiteral.getInstance();
}
// Type to converter thought IParamConverter
if (value == null) {
IParamConverter<?> converter = ParamConverterManager
.getInstance().getConverter(instanceClass);
if (converter != null) {
value = converter.convert(literal, allowedTypes);
if (value instanceof Command && inDepth) {
value = compile((Command) value, true);
}
}
}
// Type to converter thought EcoreUtil.createFromString
if (value == null && feature.getEType() instanceof EDataType) {
value = EcoreUtil.createFromString(
(EDataType) feature.getEType(),
literal.getLiteral());
}
} catch (Exception e) {
// Exception while converting
if (e instanceof CoreException) {
throw (CoreException) e;
}
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID, "Parameter conversion failed: "
+ e.getMessage(), e);
throw new CoreException(status);
}
// If failed to convert emit error
if (value == null) {
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID, "Can't convert value "
+ literal.getLiteral() + " to type "
+ instanceClass.getSimpleName());
throw new CoreException(status);
}
try {
if (feature.getUpperBound() == 1) {
target.eSet(feature, value);
} else {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) target.eGet(feature);
list.add(value);
}
} catch (ClassCastException cce) {
IStatus status = new Status(IStatus.ERROR,
CorePlugin.PLUGIN_ID, "Can't assign value " + value
+ " to attribute " + feature.getName(), cce);
throw new CoreException(status);
}
} else if (param instanceof ExecutableParameter) {
Binding binding = CoreFactory.eINSTANCE.createBinding();
binding.setFeature(feature);
ExecutableParameter execParam = (ExecutableParameter) param;
binding.setCommand(compile(execParam.getCommand(), hasInput));
target.getBindings().add(binding);
} else {
throw new RuntimeException("Invalid parameter");
}
}
}