/*******************************************************************************
* Copyright (c) 2011 Sebastian Benz 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sebastian Benz - initial API and implementation
*******************************************************************************/
package org.xrepl;
import static org.eclipse.emf.ecore.util.EcoreUtil.resolveAll;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.StringInputStream;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.interpreter.IEvaluationContext;
import org.eclipse.xtext.xbase.interpreter.IEvaluationResult;
import org.eclipse.xtext.xbase.interpreter.IExpressionInterpreter;
import org.eclipse.xtext.xbase.interpreter.impl.DefaultEvaluationContext;
import org.eclipse.xtext.xbase.interpreter.impl.EvaluationException;
import org.eclipse.xtext.xbase.interpreter.impl.InterpreterCanceledException;
import com.google.inject.Inject;
import com.google.inject.name.Named;
@SuppressWarnings("restriction")
public class DefaultEvaluator implements Evaluator {
private static final String PREFIX = "__REPL_RESOURCE_";
public static final String LINE_BREAK = "\n";
private ResourceSet resourceSet;
private final IExpressionInterpreter interpreter;
private History history;
private Resource currentResource;
private int steps = 0;
private IEvaluationContext context;
private String fileExtension;
private String lastEvaluatedString = "";
@Inject
public DefaultEvaluator(IExpressionInterpreter interpreter, XreplResourceSetProvider resourceSetProvider, @Named(Constants.FILE_EXTENSIONS) String fileExtension) {
super();
this.interpreter = interpreter;
this.resourceSet = resourceSetProvider.get();
this.fileExtension = fileExtension;
}
protected void appendToHistory(String toEvaluate) {
history().append(toEvaluate + LINE_BREAK).toString();
lastEvaluatedString = toEvaluate;
}
private History history() {
if (history == null) {
history = new History();
}
return history;
}
public boolean canEvaluate(String input) {
try {
parseScript(input);
if(currentResource == null){
return false;
}
resolveAll(currentResource);
return currentResource.getErrors().isEmpty();
} catch (IOException e) {
return false;
}
}
public Object evaluate(String input) throws Throwable{
return evaluate(input, CancelIndicator.NullImpl);
}
public Object evaluate(String input, CancelIndicator cancelIndicator) throws Throwable {
try {
return execute(input, cancelIndicator);
} catch (Exception e) {
history().undo();
throw e;
}
}
protected Object execute(String toEvaluate, CancelIndicator cancelIndicator) throws Throwable {
parseScript(toEvaluate);
try {
IEvaluationResult evaluation = interpreter.evaluate(
currentExpression(), getContext(), cancelIndicator);
if(evaluation == null){
throw new InterpreterCanceledException();
}
Throwable exception = evaluation.getException();
if (exception != null) {
handleEvaluationException(exception);
}
steps++;
return evaluation.getResult();
} catch (Exception e) {
handleEvaluationException(e);
return null; // not reachable
}
}
protected IEvaluationContext getContext() {
if (context == null) {
context = new DefaultEvaluationContext();
}
return context;
}
protected void handleEvaluationException(Throwable exception)
throws Throwable {
if (exception instanceof EvaluationException) {
exception = exception.getCause();
}
while (exception instanceof WrappedException) {
exception = exception.getCause();
}
throw exception;
}
protected void clearReplResources() {
Iterator<Resource> allResources = resourceSet.getResources().iterator();
while (allResources.hasNext()) {
Resource resource = (Resource) allResources.next();
if(resource.getURI().lastSegment().startsWith(PREFIX)){
allResources.remove();
}
}
}
protected void parseScript(String toEvaluate) throws IOException {
if (isAlreadyParsed(toEvaluate)) {
return;
}
load(toEvaluate);
appendToHistory(toEvaluate);
}
protected XExpression currentExpression() {
if(currentResource.getContents().isEmpty()){
return null;
}
return (XExpression) currentResource.getContents()
.get(0);
}
protected boolean isAlreadyParsed(String toEvaluate) {
return lastEvaluatedString.equals(toEvaluate);
}
protected void load(String input) throws IOException {
URI uri = newResourceUri();
currentResource = resourceSet.getResource(
uri, false);
if (currentResource == null) {
currentResource = resourceSet.createResource(uri);
} else {
currentResource.unload();
}
currentResource.load(new StringInputStream(input), null);
resolveAll(currentResource);
}
protected URI newResourceUri() {
return URI.createURI(newResourceName());
}
protected String newResourceName() {
return PREFIX + steps + "." + fileExtension;
}
public void reset() {
history = null;
currentResource = null;
lastEvaluatedString = "";
context = null;
steps = 0;
clearReplResources();
}
protected String getFileExtension() {
return fileExtension;
}
protected ResourceSet getResourceSet() {
return resourceSet;
}
public IExpressionInterpreter getInterpreter() {
return interpreter;
}
}