/* * Copyright 2015 ArcBees Inc. * * 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 com.gwtplatform.processors.tools.outputter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Set; import javax.annotation.processing.Filer; import javax.annotation.processing.Processor; import javax.tools.FileObject; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.Ordering; import com.gwtplatform.processors.tools.domain.Type; import com.gwtplatform.processors.tools.exceptions.UnableToProcessException; import com.gwtplatform.processors.tools.logger.Logger; import com.gwtplatform.processors.tools.utils.Primitives; import static com.google.common.base.Predicates.containsPattern; import static com.google.common.base.Predicates.not; import static com.google.common.base.Predicates.or; public class Outputter { private static final String PROPERTIES = "/com/gwtplatform/processors/tools/velocity.properties"; private static final String DEFAULT_MACRO_FILE = "/com/gwtplatform/processors/tools/macros.vm"; private static final String ENCODING = "UTF-8"; private static final SimpleDateFormat PROCESSING_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); private final Logger logger; private final Type processor; private final Filer filer; private final Set<String> macroFiles; private VelocityEngine velocityEngine; public Outputter( Logger logger, Processor processor, Filer filer, String... macroFiles) { this.logger = logger; this.processor = new Type(processor.getClass()); this.filer = filer; this.macroFiles = FluentIterable.of(macroFiles).append(DEFAULT_MACRO_FILE).toSet(); } public Outputter( Logger logger, Processor processor, Filer filer, Collection<String> macroFiles) { this.logger = logger; this.processor = new Type(processor.getClass()); this.filer = filer; this.macroFiles = FluentIterable.from(macroFiles).append(DEFAULT_MACRO_FILE).toSet(); } public OutputBuilder configure(String templateFile) { return new OutputBuilder(this, processor, templateFile); } CodeSnippet parse(OutputBuilder builder) { try (Writer writer = new StringWriter()) { merge(builder, writer); String code = writer.toString(); Collection<String> imports = builder.getImports(); return new CodeSnippet(code, imports); } catch (IOException e) { logger.error().throwable(e).log("Can not parse `%s`.", builder.getErrorLogParameter().get()); throw new UnableToProcessException(); } } void writeSource(OutputBuilder builder) { try (Writer writer = prepareSourceFile(builder).openWriter()) { merge(builder, writer); } catch (IOException e) { logger.error().throwable(e).log("Can not write `%s`.", builder.getErrorLogParameter().get()); throw new UnableToProcessException(); } } private FileObject prepareSourceFile(OutputBuilder builder) throws IOException { Optional<FileObject> sourceFile = builder.getSourceFile(); if (sourceFile.isPresent()) { return sourceFile.get(); } else { return prepareSourceFile(builder.getType().get(), builder.getOutputType().get()); } } public FileObject prepareSourceFile(Type type) { return prepareSourceFile(type, OutputType.GWT); } public FileObject prepareSourceFile(Type type, OutputType outputType) { try { return outputType.createFileObject(logger, filer, type); } catch (IOException e) { logger.error().throwable(e).log("Can not create source file `%s`.", type.getQualifiedName()); throw new UnableToProcessException(); } } private void merge(OutputBuilder builder, Writer writer) throws IOException { VelocityContext context = builder.getContext(); Optional<Type> type = builder.getType(); Collection<String> imports = cleanupImports(builder.getImports(), type); if (type.isPresent()) { context.put("impl", type.get()); } context.put("processor", builder.getProcessor()); context.put("processingDate", PROCESSING_DATE_FORMAT.format(new Date())); context.put("imports", imports); getEngine().mergeTemplate(builder.getTemplateFile(), ENCODING, context, writer); } private Collection<String> cleanupImports(Collection<String> imports, Optional<Type> type) { List<Predicate<CharSequence>> predicates = new ArrayList<>(); predicates.add(Predicates.<CharSequence>isNull()); predicates.add(Primitives.IS_PRIMITIVE_PREDICATE); predicates.add(containsPattern("^java\\.lang\\.[^.]+$")); if (type.isPresent()) { String packageName = type.get().getPackageName(); predicates.add(containsPattern("^" + packageName.replace(".", "\\.") + "\\.[^.]+$")); } return FluentIterable.from(imports) .filter(not(or(predicates))) .toSortedSet(Ordering.natural()); } private VelocityEngine getEngine() throws IOException { if (velocityEngine == null) { Properties properties = new Properties(); properties.load(getClass().getResourceAsStream(PROPERTIES)); properties.put("velocimacro.library", Joiner.on(",").join(macroFiles)); velocityEngine = new VelocityEngine(properties); } return velocityEngine; } }