package ch.vorburger.el.engine; import static com.google.common.collect.Iterables.filter; import java.io.IOException; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtext.diagnostics.Severity; import org.eclipse.xtext.resource.IResourceServiceProvider; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.StringInputStream; import org.eclipse.xtext.validation.CheckMode; import org.eclipse.xtext.validation.IResourceValidator; import org.eclipse.xtext.validation.Issue; import org.eclipse.xtext.xbase.XExpression; import com.google.common.base.Predicate; import com.google.inject.Injector; /** * Expression Factory which can create new Expression objects from some textual representation. * * Initialization of this thing is probably expensive, so create only one and keep it around (Singleton) ! * * @see Expression * @author Michael Vorburger */ @SuppressWarnings("restriction") public class ExpressionFactory { private static final URI exprURI = URI.createURI("ExpressionFactory.expr"); protected Injector guiceInjector; public ExpressionFactory() { super(); IResourceServiceProvider rsp = IResourceServiceProvider.Registry.INSTANCE.getResourceServiceProvider(exprURI); this.guiceInjector = rsp.get(Injector.class); // NOT ELStandaloneSetup.getInjector(); // NOT new ELStandaloneSetup().createInjectorAndDoEMFRegistration(); } public Injector getInjector() { return guiceInjector; } public Expression newExpressionFromString(final String expressionAsString) throws ExpressionParsingException { return newExpressionFromString(expressionAsString, true); } public Expression newExpressionFromString(final String expressionAsString, ExpressionContext context) throws ExpressionParsingException { return newExpressionFromString(expressionAsString, context, true); } /** * Parse text expression and return a parsed expression object. * * @see ExpressionEngine * * @param expressionAsString Expression expression * @return expression object, which can be evaluated * @throws ExpressionParsingException * @throws ExpressionCompilationException */ public Expression newExpressionFromString(final String expressionAsString, boolean validate) throws ExpressionParsingException { return newExpressionFromString(expressionAsString, null, validate); } public Expression newExpressionFromString(final String expressionAsString, ExpressionContext context, boolean validate) throws ExpressionParsingException { ExpressionImpl expression = (ExpressionImpl) guiceInjector.getInstance(Expression.class); expression.setXExpression(parseExpressionIntoXTextEObject(expressionAsString, context, validate)); return expression; } /** * @param validate * @param varTypes * @see http://wiki.eclipse.org/Xtext/FAQ#How_do_I_load_my_model_in_a_standalone_Java_application.C2.A0.3F * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=287413 */ protected XExpression parseExpressionIntoXTextEObject(final String expressionAsString, ExpressionContext context, boolean validate) throws ExpressionParsingException { final ResourceSet resourceSet = context.getResourceSet(); Resource resource = resourceSet.createResource(computeUnusedUri(resourceSet)); // IS-A XtextResource if(context!=null) { resource.eAdapters().add(context); } try { resource.load(new StringInputStream(expressionAsString), resourceSet.getLoadOptions()); } catch (IOException e) { throw new ExpressionParsingException("Unexpected IOException; from close() of a String-based ByteArrayInputStream, no real I/O; how is that possible???", expressionAsString, e); } List<Diagnostic> errors = resource.getErrors(); if (errors.size() != 0) { throw new ExpressionParsingException("Failed to parse expression (due to managed SyntaxError/s)", expressionAsString).addDiagnosticErrors(errors); } EList<EObject> contents = resource.getContents(); if (!contents.isEmpty()) { if (validate) { Iterable<Issue> validationErrors = getValidationErrors(contents.get(0)); if(validationErrors.iterator().hasNext()) { throw new ExpressionParsingException("Failed to parse expression (due to managed ValidationError/s)", expressionAsString).addValidationIssues(validationErrors); } } return (XExpression) contents.get(0); } else { return null; } } /** * This code is copy/pasted from org.eclipse.xtext.junit4.util.ParseHelper.computeUnusedUri(ResourceSet). * We have only change the String concatenation to use StringBuilder. */ protected URI computeUnusedUri(ResourceSet resourceSet) { StringBuilder name = new StringBuilder("__synthetic"); for(int i=0; i < Integer.MAX_VALUE; i++) { // NOTE: The "filename extension" ("expr") must match the file.extensions in the *.mwe2 URI syntheticUri = URI.createURI(name.append(i).append('.').append(getFileExtension()).toString()); if (resourceSet.getResource(syntheticUri, false)==null) return syntheticUri; } throw new IllegalStateException(); } protected List<Issue> validate(EObject model) { IResourceValidator validator = ((XtextResource) model.eResource()).getResourceServiceProvider().getResourceValidator(); return validator.validate(model.eResource(), CheckMode.ALL, CancelIndicator.NullImpl); } protected Iterable<Issue> getValidationErrors(final EObject model) { final List<Issue> validate = validate(model); Iterable<Issue> issues = filter(validate, new Predicate<Issue>() { @Override public boolean apply(Issue input) { return Severity.ERROR == input.getSeverity(); } }); return issues; } protected String getFileExtension() { return "expr"; } }