org.agiso.tempel.core.DefaultTemplateExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.agiso.tempel.core.DefaultTemplateExecutor.java

Source

/* org.agiso.tempel.core.DefaultTemplateExecutor (02-10-2012)
 * 
 * DefaultTemplateExecutor.java
 * 
 * Copyright 2012 agiso.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.agiso.tempel.core;

import static org.agiso.core.lang.util.AnsiUtils.*;
import static org.agiso.core.lang.util.AnsiUtils.AnsiElement.*;
import static org.agiso.tempel.ITempel.*;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.agiso.core.i18n.annotation.I18n;
import org.agiso.core.i18n.util.I18nUtils.I18nId;
import org.agiso.core.lang.type.MapStack;
import org.agiso.core.lang.type.SimpleMapStack;
import org.agiso.core.lang.util.StringUtils;
import org.agiso.core.logging.I18nLogger;
import org.agiso.core.logging.util.LogUtils;
import org.agiso.tempel.api.ITempelEngine;
import org.agiso.tempel.api.ITemplateParamConverter;
import org.agiso.tempel.api.ITemplateParamValidator;
import org.agiso.tempel.api.internal.IExpressionEvaluator;
import org.agiso.tempel.api.internal.IParamReader;
import org.agiso.tempel.api.internal.ITemplateExecutor;
import org.agiso.tempel.api.internal.ITemplateProvider;
import org.agiso.tempel.api.internal.ITemplateVerifier;
import org.agiso.tempel.api.model.Template;
import org.agiso.tempel.api.model.TemplateParam;
import org.agiso.tempel.api.model.TemplateParamConverter;
import org.agiso.tempel.api.model.TemplateParamFetcher;
import org.agiso.tempel.api.model.TemplateParamValidator;
import org.agiso.tempel.api.model.TemplateReference;
import org.agiso.tempel.api.model.TemplateResource;
import org.agiso.tempel.core.converter.DateParamConverter;
import org.agiso.tempel.core.converter.IntegerParamConverter;
import org.agiso.tempel.core.converter.LongParamConverter;
import org.agiso.tempel.core.model.exceptions.AbstractTemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 
 * 
 * @author Karol Kopacz
 * @since 1.0
 */
@Component
public class DefaultTemplateExecutor implements ITemplateExecutor {
    private static final I18nLogger<Logs> coreLogger = LogUtils.getLogger(LOGGER_CORE);

    private static enum Logs implements I18nId {
        @I18n(def = "Executing template {0}")
        LOG_01,

        @I18n(def = "Executing template {0} with classpath {1}")
        LOG_02,

        @I18n(def = "Preparing template {0}")
        LOG_03,

        @I18n(def = "Reference property {0}: {1} <-- {2}")
        LOG_04,

        @I18n(def = "Running template {0}")
        LOG_05,
    }

    private ITemplateProvider templateProvider;
    private ITemplateVerifier templateVerifier;

    private IParamReader paramReader;
    private IExpressionEvaluator expressionEvaluator;

    //   --------------------------------------------------------------------------
    @Autowired
    public void setTemplateProvider(ITemplateProvider templateProvider) {
        this.templateProvider = templateProvider;
    }

    @Autowired
    public void setTemplateVerifier(ITemplateVerifier templateVerifier) {
        this.templateVerifier = templateVerifier;
    }

    @Override
    @Autowired
    public void setParamReader(IParamReader paramReader) {
        this.paramReader = paramReader;
    }

    @Autowired
    public void setExpressionEvaluator(IExpressionEvaluator expressionEvaluator) {
        this.expressionEvaluator = expressionEvaluator;
    }

    //   --------------------------------------------------------------------------
    /**
     * Uruchamia proces generacji treci w oparciu o szablon.
     * 
     * @param templateName Szablon do uruchomienia.
     * @param properties Mapa parametrw uruchomienia szablonu.
     * @param workDir cieka do katalogu roboczego.
     */
    @Override
    public void executeTemplate(String templateName, Map<String, Object> properties, String workDir) {
        // Pobieranie definicji szablonu do uycia:
        Template<?> template = templateProvider.get(templateName, null, null, null);
        if (template == null) {
            throw new RuntimeException("Nie znaleziono szablonu " + templateName);
        }
        if (template.isAbstract()) {
            throw new AbstractTemplateException(templateName);
        }

        // Weryfikowanie definicji szablonu, szablonu nadrzdnego i wszystkich
        // szablonw uywanych. Sprawdzanie dostpno klas silnikw generatorw.
        templateVerifier.verifyTemplate(template, templateProvider);

        MapStack<String, Object> propertiesStack = new SimpleMapStack<String, Object>();
        propertiesStack.push(new HashMap<String, Object>(properties));
        doExecuteTemplate(template, propertiesStack, workDir);
        propertiesStack.pop();
    }

    private void doExecuteTemplate(Template<?> template, MapStack<String, Object> properties, String workDir) {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

        try {
            StringBuilder deps = null;
            if (coreLogger.isTraceEnabled()) {
                deps = new StringBuilder();
            } else if (coreLogger.isDebugEnabled())
                coreLogger.debug(Logs.LOG_01,
                        ansiString(GREEN,
                                ansiString(GREEN, template.getKey()) + ": "
                                        + ansiString(GREEN, template.getGroupId() + ":" + template.getTemplateId()
                                                + ":" + template.getVersion())));

            // FIXME: Zastosowa zbir Set<URL>
            Set<String> classPath = template.getTemplateClassPath();
            if (classPath != null && !classPath.isEmpty()) {
                List<URL> urls = new ArrayList<URL>(classPath.size());
                for (String classPathEntry : classPath) {
                    urls.add(new File(classPathEntry).toURI().toURL());
                    if (coreLogger.isTraceEnabled()) {
                        if (deps.length() > 0) {
                            deps.append(", ");
                        }
                        deps.append(classPathEntry);
                    }
                }
                ClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]),
                        contextClassLoader);
                Thread.currentThread().setContextClassLoader(classLoader);
            }

            if (coreLogger.isTraceEnabled())
                coreLogger.trace(Logs.LOG_02, ansiString(GREEN,
                        ansiString(GREEN, template.getKey()) + ": " + ansiString(GREEN, template.getGroupId() + ":"
                                + template.getTemplateId() + ":" + template.getVersion())),
                        ansiString(GREEN, deps.toString()));

            doExecuteTemplateInternal(template, properties, workDir);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void doExecuteTemplateInternal(Template<?> template, MapStack<String, Object> properties,
            String workDir) {
        if (!StringUtils.isEmpty(template.getWorkDir())) {
            workDir = workDir + "/" + template.getWorkDir();
        }

        // Instancjonowanie klasy silnika generatora:
        ITempelEngine engine = template.getEngine().getInstance();

        // Wczytywanie parametrw wejciowych ze standardowego wejcia:
        Map<String, Object> params = properties.peek();

        if (template.getParams() != null) {
            for (TemplateParam<?, ?, ?> param : template.getParams()) {
                // Wypenianie parametrw wewntrznych i konwersja parametru:

                String count = param.getCount();
                int countParam = 1;
                Object object = null;
                // jeli jest okrelony atrybut count - to wartoci parametrw bd zbierane do listy
                if (count != null) {
                    List<Object> list = new ArrayList<Object>();
                    // jeli warto count jest liczbowa to zapytanie o warto parametru wykonywane jest 
                    // dokadnie tyle razy ile okrela atrybut count
                    if (StringUtils.isNumeric(count)) {
                        countParam = Integer.parseInt(count);
                        for (int i = 0; i < countParam; i++) {
                            list.add(processParam(param, properties.peek(), template));
                        }
                    } else {
                        // jeli count okrelone jest symbolem '*' to po kadym pobraniu wartoci 
                        // pytanie jest czy zakoczy podawanie parametru
                        if (count.equals("*")) {
                            boolean finish = false;
                            do {
                                list.add(processParam(param, properties.peek(), template));

                                finish = paramReader.getParamValue("finish",
                                        "Do you want to add another? (0 - NO, 1 - YES)", "0").equals("0");
                            } while (!finish);
                        }
                    }
                    object = list;
                } else {
                    object = processParam(param, properties.peek(), template);
                }
                params.put(param.getKey(), object);
            }
        }

        // Wykonywanie podszablonw w oparciu o zdefiniowane referencje:
        if (template.getReferences() != null) {
            for (TemplateReference refTemplate : template.getReferences()) {
                // Map<String, Object> params2 = new HashMap<String, Object>(params);
                // stack.push(params2);

                // Sprawdzanie, czy istnieje szablon opisywany przez podszablon:
                String key = StringUtils.nullIfBlank(refTemplate.getKey());
                String gId = StringUtils.emptyIfBlank(refTemplate.getGroupId());
                String tId = StringUtils.emptyIfBlank(refTemplate.getTemplateId());
                String ver = StringUtils.nullIfBlank(refTemplate.getVersion()); // dla nieokrelonej wersji null

                if (key == null) {
                    key = gId + ":" + tId + ":" + ver;
                }

                Template<?> subTemplate = templateProvider.get(key, gId, tId, ver);
                if (subTemplate == null) {
                    throw new IllegalStateException(
                            "Nieznany podszablon '" + refTemplate.getKey() + ": " + refTemplate.getGroupId() + ":"
                                    + refTemplate.getTemplateId() + ":" + refTemplate.getVersion() + "' szablonu '"
                                    + template.getKey() + ": " + template.getGroupId() + ":"
                                    + template.getTemplateId() + ":" + template.getVersion() + "'");
                }

                // Szablon istnieje. Kopiowanie jego standardowej definicji i aktualizowanie
                // w oparciu o informacje zdefiniowane w podszablonie:
                coreLogger.debug(Logs.LOG_03,
                        ansiString(GREEN, subTemplate.getKey() + ": " + subTemplate.getGroupId() + ":"
                                + subTemplate.getTemplateId() + ":" + subTemplate.getVersion()));

                subTemplate = subTemplate.clone(); // kopia podszablonu z repozytorium (do modyfikacji)
                // subTemplate.setScope(template.getScope());               // podszablon ma to samo repozytorium co szablon

                Map<String, Object> subParams = new HashMap<String, Object>(); // parametry dodane podszablonu
                subParams.put("top", params);
                properties.push(subParams);

                // Aktualizacja parametrw przecionych i dodawanie nowych:
                if (refTemplate.getParams() != null) {
                    for (TemplateParam<?, ?, ?> refParam : refTemplate.getParams()) {
                        // Wyszukujemy parametr pord parametrw kopii standardowej definicji
                        // szablonu i aktualizujemy jego definicj na podstawie podszablonu:
                        String id = refParam.getKey();
                        TemplateParam param = null;
                        if (subTemplate.getParams() != null) {
                            for (TemplateParam<?, ?, ?> subParam : subTemplate.getParams()) {
                                if (subParam.getKey().equals(id)) {
                                    param = subParam;
                                    break;
                                }
                            }
                        }
                        if (param == null) {
                            // throw new IllegalStateException("Nie znaleziono parametru '" + id + "' podszablonu '" + reference.getKey() + "'");

                            boolean byReference = true;
                            if (byReference) {
                                subTemplate.getParams().add(refParam);
                            } else {
                                Object value = fetchParamValue(refParam, properties.peek(), refParam.getFetcher());
                                value = convertParamValue(value, refParam.getType(), refParam.getConverter());
                                validateParamValue(value, refParam.getValidator());
                                subParams.put(refParam.getKey(), value);
                            }
                        } else {
                            if (refParam.getValue() != null) {
                                String refParamValue = expressionEvaluator.evaluate(refParam.getValue(),
                                        properties.peek());
                                coreLogger.debug(Logs.LOG_04, refParam.getKey(), param.getValue(), refParamValue);

                                param.setValue(refParamValue);
                            }
                            if (refParam.getFixed() != null) {
                                param.setFixed(refParam.getFixed());
                            }
                            if (refParam.getConverter() != null) {
                                param.setConverter(refParam.getConverter());
                            }
                            if (refParam.getValidator() != null) {
                                param.setValidator(refParam.getValidator());
                            }
                        }
                    }
                }

                // Aktualizacja listy zasobw tworzonych przez szablon:
                if (refTemplate.getResources() != null) {
                    for (TemplateResource refResource : refTemplate.getResources()) {
                        refResource.setParentTemplateReference(template);
                        subTemplate.getResources().add(refResource);
                    }
                }

                // Ustalanie katalogu roboczego dla podszablonu:
                if (!StringUtils.isEmpty(refTemplate.getWorkDir())) {
                    subTemplate
                            .setWorkDir(expressionEvaluator.evaluate(refTemplate.getWorkDir(), properties.peek()));
                }

                // Wykonywanie szablonu w zaktualizowanej wersji:
                String subWorkDir = workDir;
                //            if(!StringUtils.isEmpty(template.getWorkDir())) {
                //               subWorkDir = workDir + "/" + template.getWorkDir();
                //            }
                doExecuteTemplate(subTemplate, properties, subWorkDir);

                properties.pop();
            }
        }

        // Generacja zasobw:
        if (engine != null) {
            coreLogger.debug(Logs.LOG_05, ansiString(GREEN, template.getKey() + ": " + template.getGroupId() + ":"
                    + template.getTemplateId() + ":" + template.getVersion()));

            // Uruchomienie silnika do generacji zasobw tworzonych przez szablon:
            if (template.getResources() != null && !template.getResources().isEmpty()) {
                // Katalog roboczy dla wszystkich zasobw geneorowanych przez aktualnie
                // wykonywany szablon jest okrelany na poziomie tego szablonu i jest
                // wsplny dla wszystkich zdefiniowanych w nim zasobw:
                for (TemplateResource resource : template.getResources()) {
                    template = resource.getParentTemplateReference();

                    doEngineRun(engine, template, resource.getSource(), workDir, resource.getTarget(), properties);
                }
            } else {
                doEngineRun(engine, template, null, workDir, null, properties);
            }
        }
    }

    /**
     * Przetwarzanie waroci parametru (pobieranie, konwersja i walidacja)
     * @param param
     * @param params
     * @param template
     * @return
     */
    private Object processParam(TemplateParam<?, ?, ?> param, Map<String, Object> params, Template<?> template) {
        Object value = fetchParamValue(param, params, param.getFetcher());
        value = convertParamValue(value, param.getType(), param.getConverter());
        validateParamValue(value, param.getValidator());
        return value;
    }

    /**
     * @param engine 
     * @param srcDir
     * @param workDir
     * @param resource
     * @param params
     */
    private void doEngineRun(ITempelEngine engine, Template<?> template, String source, String workDir,
            String target, MapStack<String, Object> stack) {
        if (StringUtils.isEmpty(target)) {
            target = workDir + "/";
        } else {
            target = workDir + "/" + expressionEvaluator.evaluate(target, stack.peek());
        }

        engine.run(template.getTemplateSource(source), stack, target);
    }

    //   --------------------------------------------------------------------------
    private static final Map<String, Class<?>> paramTypes = new HashMap<String, Class<?>>();
    private static final List<ITemplateParamConverter<?, ?>> paramConverters = new ArrayList<ITemplateParamConverter<?, ?>>();
    static {
        paramConverters.add(new IntegerParamConverter());
        paramConverters.add(new LongParamConverter());
        paramConverters.add(new DateParamConverter());
    }

    /**
     * @param param
     * @param params 
     * @return
     */
    private Object fetchParamValue(TemplateParam<?, ?, ?> param, Map<String, Object> params,
            TemplateParamFetcher fetcher) {
        boolean fixed = param.getFixed() != null && param.getFixed() == true;
        String value = expressionEvaluator.evaluate(param.getValue(), params);

        // Parametr oznaczony musi mie zdefiniowan warto:
        if (fixed && StringUtils.isEmpty(value)) {
            throw new IllegalStateException("Brak wartoci dla parametru oznaczonego '" + param.getKey() + "'");
        }

        if (params.containsKey(param.getKey())) {
            // Parametr moe by zdefiniowany jako parametr wywoania (przez -Dkey=value):
            value = expressionEvaluator.evaluate(params.get(param.getKey()).toString(), params);
        } else if (!fixed) {
            param.setValue(value);
            return fetcher.getInstance().fetch(paramReader, param);
        }

        param.setValue(value);
        return value;
    }

    /**
     * @param value
     * @param type
     * @param converter
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private Object convertParamValue(Object value, String type, TemplateParamConverter converter) {
        Class<?> valueClass = (value == null ? null : value.getClass());
        ITemplateParamConverter typeConverter = converter.getInstance();
        if (typeConverter == null) {
            if (type == null || valueClass == null || type.equals(valueClass.getCanonicalName())) {
                return value;
            } else {
                Class<?> typeClass;
                if (paramTypes.containsKey(type)) {
                    typeClass = paramTypes.get(type);
                } else {
                    try {
                        typeClass = Class.forName(type);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
                for (ITemplateParamConverter<?, ?> paramConverter : paramConverters) {
                    if (paramConverter.canConvert(valueClass, typeClass)) {
                        typeConverter = paramConverter;
                        break;
                    }
                }
                if (typeConverter == null) {
                    throw new RuntimeException("Brak konwertera dla parametru typu: " + type);
                }
            }
        }
        return typeConverter.convert(value);
    }

    /**
     * @param value
     * @param validator
     */
    @SuppressWarnings("unchecked")
    private void validateParamValue(Object value, TemplateParamValidator validator) {
        ITemplateParamValidator<?> valueValidator = validator.getInstance();
        if (valueValidator != null) {
            ((ITemplateParamValidator<Object>) valueValidator).validate(value);
        }
    }
}