// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.slim; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import static fitnesse.slim.SlimException.*; import static java.lang.String.format; /** * This is the API for executing a SLIM statement. This class should not know about the syntax of a SLIM statement. */ public class StatementExecutor implements StatementExecutorInterface { private static final String SLIM_HELPER_LIBRARY_INSTANCE_NAME = "SlimHelperLibrary"; public static final String SLIM_AGENT_FIXTURE_HANDLES_SYMBOLS = "SLIM_AGENT_FIXTURE_HANDLES_SYMBOLS"; private boolean stopRequested = false; private SlimExecutionContext context; private List<MethodExecutor> executorChain = new ArrayList<>(); private Pattern patternOfFixturesHandlingSymbols = null; public StatementExecutor() { this(new SlimExecutionContext(JavaSlimFactory.createInteraction(null))); } public StatementExecutor(SlimExecutionContext context) { this.context = context; executorChain.add(new FixtureMethodExecutor(this.context)); executorChain.add(new SystemUnderTestMethodExecutor(this.context)); executorChain.add(new LibraryMethodExecutor(this.context)); addSlimHelperLibraryToLibraries(); } private void addSlimHelperLibraryToLibraries() { SlimHelperLibrary slimHelperLibrary = new SlimHelperLibrary(); slimHelperLibrary.setStatementExecutor(this); context.addLibrary(new Library(SLIM_HELPER_LIBRARY_INSTANCE_NAME, slimHelperLibrary)); } @Override public Object getInstance(String instanceName) { return context.getInstance(instanceName); } @Override public void setInstance(final String actorInstanceName, final Object actor) { context.setInstance(actorInstanceName, actor); } @Override public void addPath(String path) throws SlimException { context.addPath(path); } @Override public void assign(String name, Object value) { context.setVariable(name, value); checkForPatternOfFixturesHandlingSymbols(name); } @Override public Object getSymbol(String symbolName) { MethodExecutionResult result = context.getVariable(symbolName); if (result == null) { return null; } return result.returnValue(); } @Override public void create(String instanceName, String className, Object... args) throws SlimException { try { context.create(instanceName, className, args); // TODO Hack for supporting SlimHelperLibrary, please remove. Object newInstance = context.getInstance(instanceName); if (newInstance instanceof StatementExecutorConsumer) { ((StatementExecutorConsumer) newInstance).setStatementExecutor(this); } } catch (SlimError e) { throw new SlimException(format("%s[%d]", className, args.length), e, SlimServer.COULD_NOT_INVOKE_CONSTRUCTOR, true); } catch (IllegalArgumentException e) { throw new SlimException(format("%s[%d]", className, args.length), e, SlimServer.COULD_NOT_INVOKE_CONSTRUCTOR, true); } catch (InvocationTargetException e) { checkExceptionForStop(e.getTargetException()); throw new SlimException(e.getTargetException(), true); } catch (Throwable e) { // NOSONAR checkExceptionForStop(e); throw new SlimException(e); } } @Override public Object call(String instanceName, String methodName, Object... args) throws SlimException { try { return getMethodExecutionResult(instanceName, methodName, args).returnValue(); } catch (Throwable e) { // NOSONAR checkExceptionForStop(e); throw new SlimException(e); } } @Override public Object callAndAssign(String variable, String instanceName, String methodName, Object... args) throws SlimException { try { MethodExecutionResult result = getMethodExecutionResult(instanceName, methodName, args); context.setVariable(variable, result); checkForPatternOfFixturesHandlingSymbols(variable); return result.returnValue(); } catch (Throwable e) { // NOSONAR checkExceptionForStop(e); throw new SlimException(e); } } private MethodExecutionResult getMethodExecutionResult(String instanceName, String methodName, Object... args) throws Throwable { MethodExecutionResults results = new MethodExecutionResults(); Boolean ignoreSymbols = ignoreSymbols( instanceName, methodName); if (!ignoreSymbols){ args = context.replaceSymbols(args); } for (MethodExecutor anExecutorChain : executorChain) { MethodExecutionResult result = anExecutorChain.execute(instanceName, methodName, args); if (result.hasResult()) { return result; } results.add(result); } return results.getFirstResult(); } /** * * @return true is the fixture will handles symbols assignments and lookups itself. * This should be a rare exception and is not recommended * * Would be nice to use the classname but we don't know it at this point. * */ private Boolean ignoreSymbols(String instanceName, String methodName){ try{ if (this.patternOfFixturesHandlingSymbols == null) return false; return patternOfFixturesHandlingSymbols.matcher(instanceName + "." + methodName).matches(); }catch (Exception e){ return false; } } private void checkForPatternOfFixturesHandlingSymbols(String symbolName){ if(!SLIM_AGENT_FIXTURE_HANDLES_SYMBOLS.equals(symbolName)) return; // Special Symbol Name need to update try{ MethodExecutionResult mer = context.getVariable(SLIM_AGENT_FIXTURE_HANDLES_SYMBOLS); if (mer == null) return; try{ if(mer.returnValue() == null){ patternOfFixturesHandlingSymbols = null; }else{ patternOfFixturesHandlingSymbols = Pattern.compile(mer.returnValue().toString()); } }catch (Exception e){ patternOfFixturesHandlingSymbols = null; } }catch (Exception e){ return; } } private void checkExceptionForStop(Throwable exception) { if (isStopTestException(exception) || isStopSuiteException(exception)) { stopRequested = true; } } @Override public boolean stopHasBeenRequested() { return stopRequested; } @Override public void reset() { stopRequested = false; } }