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

org.eclipse.epsilon.egl.dom.GenerationRule Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2012 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/
 * 
 * Contributors:
 *     Dimitrios Kolovos - initial API and implementation
 ******************************************************************************/
package org.eclipse.epsilon.egl.dom;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.epsilon.common.module.IModule;
import org.eclipse.epsilon.common.parse.AST;
import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.common.util.AstUtil;
import org.eclipse.epsilon.egl.EglFileGeneratingTemplate;
import org.eclipse.epsilon.egl.EglPersistentTemplate;
import org.eclipse.epsilon.egl.EglTemplate;
import org.eclipse.epsilon.egl.EglTemplateFactory;
import org.eclipse.epsilon.egl.execute.context.IEgxContext;
import org.eclipse.epsilon.egl.formatter.Formatter;
import org.eclipse.epsilon.egl.parse.EgxParser;
import org.eclipse.epsilon.eol.dom.ExecutableBlock;
import org.eclipse.epsilon.eol.dom.IExecutableModuleElement;
import org.eclipse.epsilon.eol.dom.IExecutableModuleElementParameter;
import org.eclipse.epsilon.eol.dom.Parameter;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.execute.ExecutorFactory;
import org.eclipse.epsilon.eol.execute.context.FrameStack;
import org.eclipse.epsilon.eol.execute.context.FrameType;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.models.IModel;
import org.eclipse.epsilon.eol.parse.EolParser;
import org.eclipse.epsilon.eol.types.EolMap;
import org.eclipse.epsilon.eol.types.EolModelElementType;
import org.eclipse.epsilon.eol.types.EolType;
import org.eclipse.epsilon.erl.dom.ExtensibleNamedRule;

public class GenerationRule extends ExtensibleNamedRule implements IExecutableModuleElementParameter, IExecutableModuleElement {
	
	protected Parameter sourceParameter;
	protected ExecutableBlock> domainBlock;
	protected ExecutableBlock targetBlock, templateBlock;
	protected ExecutableBlock guardBlock, overwriteBlock, mergeBlock, appendBlock, patchBlock;
	protected ExecutableBlock preBlock, postBlock;
	protected ExecutableBlock> parametersBlock;
	protected ExecutableBlock formatterBlock;

	@SuppressWarnings("unchecked")
	public void build(AST cst, IModule module) {
		super.build(cst, module);
		validateConstructs(cst, module.getParseProblems());
		
		AST sourceParameterAst = cst.getFirstChild().getNextSibling();
		if (sourceParameterAst != null && sourceParameterAst.getType() == EolParser.FORMAL) {
			sourceParameter = (Parameter) module.createAst(sourceParameterAst, this);
		}
		domainBlock = (ExecutableBlock>) module.createAst(AstUtil.getChild(cst, EgxParser.DOMAIN), this);
		guardBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.GUARD), this);
		preBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.PRE), this);
		overwriteBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.OVERWRITE), this);
		mergeBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.MERGE), this);
		appendBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.APPEND), this);
		patchBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.PATCH), this);
		templateBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.TEMPLATE), this);
		parametersBlock = (ExecutableBlock>) module.createAst(AstUtil.getChild(cst, EgxParser.PARAMETERS), this);
		targetBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.TARGET), this);
		postBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.POST), this);
		formatterBlock = (ExecutableBlock) module.createAst(AstUtil.getChild(cst, EgxParser.FORMATTER), this);
	}
	
	/**
	 * 
	 * @param parent
	 * @param problems
	 * @since 1.6
	 */
	private static void validateConstructs(AST parent, Collection problems) {
		int[] types = {
			EgxParser.DOMAIN,
			EgxParser.GUARD,
			EgxParser.PRE,
			EgxParser.OVERWRITE,
			EgxParser.MERGE,
			EgxParser.APPEND,
			EgxParser.PATCH,
			EgxParser.TEMPLATE,
			EgxParser.PARAMETERS,
			EgxParser.TARGET,
			EgxParser.POST
		};
		String[] names = {
			"domain",
			"guard",
			"pre",
			"overwrite",
			"merge",
			"append",
			"patch",
			"template",
			"parameters",
			"target",
			"post"
		};
		for (int i = 0; i < types.length; i++) {
			if (!AstUtil.hasAtMostNChildrenOfTypes(1, parent, types[i])) {
				problems.add(new ParseProblem(parent.getLine(), parent.getColumn(),
					"At most one '"+names[i]+"' block is permitted in each rule",
					ParseProblem.ERROR));
			}
		}
	}
	
	public Collection getAllElements(IEolContext context) throws EolRuntimeException {
		if (sourceParameter != null) {
			if (domainBlock == null) {
				return getAllInstances(sourceParameter, context, !isGreedy(context));
			}
			else {
				return domainBlock.execute(context, true);
			}
		}
		else {
			return Collections.singleton(null);
		}
	}
	
	@Override
	public Object execute(IEolContext context_, Object element) throws EolRuntimeException {
		IEgxContext context = (IEgxContext) context_;
		FrameStack frameStack = context.getFrameStack();
		
		if (sourceParameter != null) {
			frameStack.enterLocal(FrameType.PROTECTED, this, Variable.createReadOnlyVariable(sourceParameter.getName(), element));
		}
		else {
			frameStack.enterLocal(FrameType.PROTECTED, this);
		}
		
		if (guardBlock != null && !guardBlock.execute(context, false)) {
			frameStack.leaveLocal(this);
			return null;
		}
		
		if (preBlock != null) {
			preBlock.execute(context, false);
		}
		
		final boolean overwrite = (overwriteBlock == null) ? true : overwriteBlock.execute(context, false);
		final boolean merge = (mergeBlock == null) ? true : mergeBlock.execute(context, false);			
		final boolean append = (appendBlock == null) ? false : appendBlock.execute(context, false);			
		final boolean patch = (patchBlock == null) ? false : patchBlock.execute(context, false);			
		final String templateName = (templateBlock == null) ? "" : templateBlock.execute(context, false);
		final EglTemplateFactory templateFactory = context.getTemplateFactory();
		final Map templateCache = context.getTemplateCache();
		
		URI templateUri = templateFactory.resolveTemplate(templateName);
		EglTemplate eglTemplate;
		
		if (templateCache == null || (eglTemplate = templateCache.get(templateUri)) == null) {
			eglTemplate = templateFactory.load(templateUri);

			List problems = eglTemplate.getParseProblems();
			if (!problems.isEmpty()) {
				ParseProblem problem = problems.get(0);
				String reason = "Parse error(s) in " + templateUri + ": " + problem.toString();
				throw new EolRuntimeException(reason, templateBlock);
			}
				
			if (templateCache != null) {
				templateCache.put(templateUri, eglTemplate);
			}
		}

		if (sourceParameter != null) {
			eglTemplate.populate(sourceParameter.getName(), element);
		}
		
		if (parametersBlock != null) {
			for (Map.Entry entry : parametersBlock.execute(context, false).entrySet()) {
				eglTemplate.populate(entry.getKey(), entry.getValue());
			}
		}
		
		final String target = targetBlock != null ? targetBlock.execute(context, false) : null;

		if (formatterBlock != null) {
			final Formatter formatter = formatterBlock.execute(context, false);
			eglTemplate.setFormatter(formatter);
		}

		Object generated;
		if (eglTemplate instanceof EglPersistentTemplate && target != null) {
			if ((getBooleanAnnotationValue("patch", context) || patch) && eglTemplate instanceof EglFileGeneratingTemplate) {
				generated = ((EglFileGeneratingTemplate) eglTemplate).patch(target);
			}
			else if ((getBooleanAnnotationValue("append", context) || append) && eglTemplate instanceof EglFileGeneratingTemplate) {
				generated = ((EglFileGeneratingTemplate) eglTemplate).append(target);
			}
			else {
				generated = ((EglPersistentTemplate) eglTemplate).generate(target, overwrite, merge);
			}
		}
		else {
			generated = eglTemplate.process();
		}
		
		context.getInvokedTemplates().add(eglTemplate.getTemplate());
		
		
		if (postBlock != null) {
			frameStack.enterLocal(FrameType.UNPROTECTED, postBlock, Variable.createReadOnlyVariable("generated", generated));
			postBlock.execute(context, false);
			frameStack.leaveLocal(postBlock);
		}
		
		frameStack.leaveLocal(this);
		eglTemplate.reset();
		return generated;
	}
	
	@Override
	public Object execute(IEolContext context) throws EolRuntimeException {
		ExecutorFactory executorFactory = context.getExecutorFactory();
		for (Object element : getAllElements(context)) {
			executorFactory.execute(this, context, element);
		}
		return null;
	}

	/**
	 * Gets the model which the "transform" parameter type expression belongs to.
	 * @param context
	 * @return The model for the parameter.
	 * @throws EolRuntimeException
	 * @since 1.6
	 */
	public IModel getOwningModelForType(IEolContext context) throws EolRuntimeException {
		if (sourceParameter == null) return null;
		EolType parameterType = sourceParameter.getType(context);
		if (parameterType instanceof EolModelElementType) 
			return ((EolModelElementType)parameterType).getModel();
		else
			return null;
	}
	
	@Override
	public String toString() {
		String label = getName();
		if (sourceParameter != null) {
			label += " (" + sourceParameter.getTypeName() + ")";
		}
		return label;
	}

	@Override
	public AST getSuperRulesAst(AST cst) {
		return null;
	}
	
	public void accept(IEgxVisitor visitor) {
		visitor.visit(this);
	}
	
	/**
	 * Gets the parameter bound to the "transform" part, if {@link #hasTransformSource()} == true.
	 * 
	 * @return The part following "transform", or null if absent.
	 * @since 2.3
	 */
	public Parameter getTransformSource() {
		return sourceParameter;
	}
	
	/**
	 * Used to determine whether this rule operates over a collection of model elements.
	 * 
	 * @return true if the rule has a "transform" part.
	 * @since 2.3
	 */
	public boolean hasTransformSource() {
		return sourceParameter != null;
	}
	
	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock> getDomainBlock() {
		return domainBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getTargetBlock() {
		return targetBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getTemplateBlock() {
		return templateBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getGuardBlock() {
		return guardBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getOverwriteBlock() {
		return overwriteBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getMergeBlock() {
		return mergeBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getPreBlock() {
		return preBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock getPostBlock() {
		return postBlock;
	}

	/**
	 * 
	 * @return
	 * @since 2.3
	 */
	public ExecutableBlock> getParametersBlock() {
		return parametersBlock;
	}

	/**
	 *
	 * @return
	 * @since 2.5
	 */
	public ExecutableBlock getFormatterBlock() {
		return formatterBlock;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy