All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.epsilon.eol.launch.EolRunConfiguration Maven / Gradle / Ivy

/*********************************************************************
 * Copyright (c) 2018 The University of York.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.epsilon.eol.launch;

import java.util.*;
import org.eclipse.epsilon.common.concurrent.ConcurrencyUtils;
import org.eclipse.epsilon.common.launch.ProfilableRunConfiguration;
import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.common.util.CollectionUtil;
import org.eclipse.epsilon.common.util.StringProperties;
import static org.eclipse.epsilon.common.util.profiling.BenchmarkUtils.profileExecutionStage;
import org.eclipse.epsilon.eol.exceptions.EolParseException;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.eol.execute.context.concurrent.EolContextParallel;
import org.eclipse.epsilon.eol.execute.context.concurrent.IEolContextParallel;
import org.eclipse.epsilon.eol.execute.control.ExecutionController;
import org.eclipse.epsilon.eol.execute.control.ExecutionProfiler;
import org.eclipse.epsilon.eol.models.CachedModel;
import org.eclipse.epsilon.eol.models.IModel;
import org.eclipse.epsilon.eol.models.ModelRepository;
import org.eclipse.epsilon.eol.EolModule;
import org.eclipse.epsilon.eol.IEolModule;
import org.eclipse.epsilon.eol.concurrent.EolModuleParallel;

/**
 * Convenience class for running EOL programs over models.
 * 
 * @author Sina Madani
 * @since 1.6
 */
public class EolRunConfiguration extends ProfilableRunConfiguration {
	
	public final Map modelsAndProperties;
	public final Map parameters;
	protected boolean loadModels;
	private final IEolModule module;
	
	public static Builder Builder() {
		return new Builder<>(EolRunConfiguration.class);
	}
	
	public EolRunConfiguration(Builder builder) {
		super(builder);
		this.parameters = builder.parameters;
		this.modelsAndProperties = builder.modelsAndProperties;
		this.loadModels = builder.loadModels;
		this.module = Objects.requireNonNull(builder.module, "Module cannot be null!");
		
		IEolContext context = module.getContext();
		if (builder.isParallel() && builder.parallelism > 0 && context instanceof IEolContextParallel) {
			IEolContextParallel pContext = (IEolContextParallel) context;
			pContext.setParallelism(builder.parallelism);
		}
		
		this.id = Optional.ofNullable(builder.id).orElseGet(() ->
			Objects.hash(
				super.id,
				Objects.toString(this.modelsAndProperties),
				Objects.toString(this.module.getSourceUri())
			)
		);
	}
	
	public EolRunConfiguration(EolRunConfiguration other) {
		super(other);
		this.modelsAndProperties = other.modelsAndProperties;
		this.module = other.module;
		this.parameters = other.parameters;
		this.loadModels = other.loadModels;
	}
	
	/**
	 * 
	 * @return The concrete instance of IEolModule.
	 */
	public IEolModule getModule() {
		return module;
	}
	
	@Override
	protected void preExecute() throws Exception {
		super.preExecute();
		
		if (isFirstRepeat()) {
			prepareModule();
		}
		else if (targetRepeats > 1) {
			module.getContext().getFrameStack().dispose();
			prepareFrameStack();
		}
		
		if (modelsAndProperties != null && !modelsAndProperties.isEmpty()) {
			addModelsToRepo();
			if (loadModels && (isFirstRepeat() || targetRepeats == 1)) {
				loadModels();
			}
		}
	}
	
	protected void addModelsToRepo() throws Exception {
		ModelRepository modelRepo = module.getContext().getModelRepository();
		for (Map.Entry modelAndProp : modelsAndProperties.entrySet()) {
			IModel model = modelAndProp.getKey();
			if (!modelRepo.getModels().contains(model)) {
				modelRepo.addModel(model);
			}
		}
	}
	
	protected void prepareModule() throws Exception {
		if (profileExecution) {
			profileExecutionStage(profiledStages, "Parsing script", () -> module.parse(script));
		}
		else {
			module.parse(script);
		}
		Collection parseProblems = module.getParseProblems();
		if (!parseProblems.isEmpty()) {
			writeOut(parseProblems);
			throw new EolParseException(parseProblems);
		}
		
		prepareFrameStack();
		
		module.getContext().setProfilingEnabled(profileExecution);
	}
	
	protected void prepareFrameStack() {
		if (!parameters.isEmpty()) {
			module.getContext().getFrameStack().put(parameters, false);
		}
	}
	
	protected final void loadModels() throws EolModelLoadingException {
		if (profileExecution) {
			profileExecutionStage(profiledStages, "Loading model(s)", this::loadModelsImpl);
		}
		else {
			this.loadModelsImpl();
		}
	}
	
	protected void loadModelsImpl() throws EolModelLoadingException {
		for (Map.Entry modelAndProp : modelsAndProperties.entrySet()) {
			IModel model = modelAndProp.getKey();
			if (!(model instanceof CachedModel) || !((CachedModel) model).isLoaded()) {
				StringProperties modelProperties = modelAndProp.getValue();
				if (modelProperties != null) {
					model.load(modelProperties); 
				}
				else {
					model.load();
				}
			}
		}
	}
	
	@Override
	public void reset() throws Exception {
		super.reset();
		module.getContext().dispose();
	}
	
	public void dispose() throws Exception {
		reset();
		module.getContext().getFrameStack().dispose();
		module.getContext().getModelRepository().dispose();
	}
	
	@Override
	protected Object execute() throws EolRuntimeException {
		Object execResult;
		
		if (profileExecution) {
			if (module instanceof ProfilableIEolModule) {
				ProfilableIEolModule profMod = (ProfilableIEolModule) module;
				execResult = profMod.profileExecution();
				profiledStages.addAll(profMod.getProfiledStages());
			}
			else {
				execResult = profileExecutionStage(profiledStages, "execute()", module::execute);
			}
		}
		else {
			execResult = module.execute();
		}
		
		return execResult;
	}
	
	@Override
	protected List getProfilingOutput() {
		ExecutionController executionController = getModule().getContext().getExecutorFactory().getExecutionController();
		List prof = new ArrayList<>(super.getProfilingOutput());
		if (executionController instanceof ExecutionProfiler) {
			prof.addAll(Arrays.asList(printMarker, executionController));
		}
		return prof;
	}
	
	@Override
	public String toString() {
		return super.toString()+
		", moduleClass="+module.getClass().getName()+
		", module="+module+
		"', models='"+Objects.toString(modelsAndProperties)+"'";
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(super.hashCode(), module, modelsAndProperties);
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (!super.equals(obj)) return false;
		
		EolRunConfiguration other = (EolRunConfiguration) obj;
		return
			Objects.equals(this.module, other.module) &&
			CollectionUtil.equalsIgnoreOrder(this.modelsAndProperties.keySet(), other.modelsAndProperties.keySet());
	}
	
	
	@SuppressWarnings("unchecked")
	public static class Builder> extends ProfilableRunConfiguration.Builder {
		protected Builder() {
			super();
		}
		protected Builder(Class runConfigClass) {
			super(runConfigClass);
		}
		
		@Override
		public C build() {
			if (module == null) {
				module = createModule();
			}
			
			module.getContext().setProfilingEnabled(profileExecution);
			
			return buildReflective(() -> {
				class InstantiableEOC extends EolRunConfiguration {
					public InstantiableEOC(Builder builder) {
						super(builder);
					}
				};
				
				return (C) new InstantiableEOC(this);
			});
		}
		
		protected IEolModule createModule() {
			return isParallel() ? new EolModuleParallel(new EolContextParallel(parallelism)) : new EolModule();
		}
		
		public IEolModule module;
		public Map modelsAndProperties = new LinkedHashMap<>(4);
		public Map parameters = new LinkedHashMap<>(4);
		public boolean loadModels = true;
		public int parallelism = Integer.MIN_VALUE;
		protected boolean sequential = false;

		public B skipModelLoading() {
			return loadModels(false);
		}
		public B withModelLoading(boolean load) {
			return loadModels(load);
		}
		public B loadModels(boolean load) {
			this.loadModels = load;
			return (B) this;
		}
		public B withModule(IEolModule module) {
			this.module = module;
			return (B) this;
		}
		public B withModel(IModel model) {
			return withModel(model, null);
		}
		public B withModel(IModel model, StringProperties properties) {
			this.modelsAndProperties.put(model, properties);
			return (B) this;
		}
		public B withModels(Map modelsAndProps) {
			this.modelsAndProperties.putAll(modelsAndProps);
			return (B) this;
		}
		public B withModels(IModel... models) {
			for (IModel model : models) {
				withModel(model);
			}
			return (B) this;
		}
		public B withProperties(Map properties) {
			modelsAndProperties.values().forEach(prop -> prop.putAll(properties));
			return (B) this;
		}
		public B withProperty(String name, Object value) {
			modelsAndProperties.values().forEach(prop -> prop.put(name, value));
			return (B) this;
		}
		public B withParameter(String name, Object value) {
			this.parameters.put(name, value);
			return (B) this;
		}
		public B withParameters(Map params) {
			this.parameters.putAll(params);
			return (B) this;
		}
		public B withParallelism(int parallelism) {
			sequential = (this.parallelism = parallelism) < 0;
			return (B) this;
		}
		public B withParallelism() {
			return parallel(true);
		}
		public B sequential() {
			return parallel(false);
		}
		public B parallel(boolean parallel) {
			return withParallelism(parallel ? ConcurrencyUtils.DEFAULT_PARALLELISM : Integer.MIN_VALUE);
		}
		public B parallel() {
			return parallel(true);
		}
		public boolean isSequential() {
			return sequential;
		}
		public boolean isParallel() {
			return parallelism > -1;
		}
	}
}