Java tutorial
/* 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); } } }