Repository: incubator-freemarker Updated Branches: refs/heads/3 eb56a9b6c -> 17279ba64
Continued working on FM2 to FM3 converter... Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/17279ba6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/17279ba6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/17279ba6 Branch: refs/heads/3 Commit: 17279ba64045239fe3b8cc39d187fbf27ddd7992 Parents: eb56a9b Author: ddekany <[email protected]> Authored: Tue Jul 11 00:31:27 2017 +0200 Committer: ddekany <[email protected]> Committed: Tue Jul 11 00:31:27 2017 +0200 ---------------------------------------------------------------------- .../apache/freemarker/converter/Converter.java | 6 + .../freemarker/converter/FM2ToFM3Converter.java | 2 +- .../converter/FM2ToFM3ConverterCLI.java | 195 +++++++++++++++++++ .../converter/FM2ToFM3ConverterCLITest.java | 97 +++++++++ 4 files changed, 299 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/17279ba6/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java index 2c4fa0b..3d4d4f6 100644 --- a/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java +++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/Converter.java @@ -65,6 +65,7 @@ public abstract class Converter { private boolean executed; private Set<File> directoriesKnownToExist = new HashSet<>(); private Writer conversionMarkersWriter; + private int convertedFileCount; public Converter() { include = getDefaultInclude(); @@ -86,6 +87,10 @@ public abstract class Converter { this.source = source != null ? source.getAbsoluteFile() : null; } + public int getConvertedFileCount() { + return convertedFileCount; + } + /** * Getter pair of {@link #setDestinationDirectory(File)}. */ @@ -250,6 +255,7 @@ public abstract class Converter { FILE_CONVERSION_CONTEXT_TLS.set(ctx); convertFile(ctx); storeConversionMarkers(ctx.getConversionMarkers(), ctx); + convertedFileCount++; } catch (IOException e) { throw new ConverterException("I/O exception while converting " + _StringUtil.jQuote(src) + ".", e); } finally { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/17279ba6/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java index 0e2e114..041258b 100644 --- a/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java +++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3Converter.java @@ -36,7 +36,7 @@ import freemarker.template._TemplateAPI; /** * Converts FreeMarker 2 templates to FreeMarker 3 templates, as far as it's possible automatically. While the output - * will contain syntactically correct FreeMarker 3 templates, the templates will have to be reviewed by humans, as + * will contain syntactically correct FreeMarker 3 templates, the templates will have to be reviewed by humans, * due to the semantic differences (such as a different treatment of {@code null}). * <p> * This is work in progress... new conversion are mostly only added when the syntactical change was http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/17279ba6/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3ConverterCLI.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3ConverterCLI.java b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3ConverterCLI.java new file mode 100644 index 0000000..d17c113 --- /dev/null +++ b/freemarker-converter/src/main/java/org/apache/freemarker/converter/FM2ToFM3ConverterCLI.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.freemarker.converter; + +import java.io.File; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import freemarker.core.FM2ASTToFM3SourceConverter; + +public class FM2ToFM3ConverterCLI { + + public static int SUCCESS_EXIT_STATUS = 0; + public static int COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS = -1; + public static int EXECUTION_ERROR_EXIT_STATUS = 1; + + private static final String DESTINATION_OPTION = "destination"; + private static final String CREATE_DESTINATION_OPTION = "create-destination"; + private static final String INCLUDE_OPTION = "include"; + private static final String EXCLUDE_OPTION = "exclude"; + private static final String FREEMARKER_2_SETTING_OPTION_SHORT = "S"; + private static final String HELP_OPTION = "help"; + private static final String HELP_OPTION_SHORT = "h"; + + private static final Options OPTIONS = new Options() + .addOption(Option.builder("d").longOpt(DESTINATION_OPTION) + .required().hasArg().argName("dir") + .desc("The directory where the converted files will be written. Required!") + .build()) + .addOption(Option.builder("p").longOpt(CREATE_DESTINATION_OPTION) + .desc("Whether the top level destination directory will be created if it doesn't yet exists.") + .build()) + .addOption(Option.builder(null).longOpt(INCLUDE_OPTION) + .hasArg().argName("regexp") + .desc("Only files that match this Java regular expression will be processed. Default matches " + + "files with the commonly used FreeMarker 2 file extensions (ftl, ftlh, ftlx, fm, " + + "case insensitive). For the file to be included, the pattern must fully match the " + + "path of the file relative to the source directory, that has all backslashes " + + "replaced with slashes (relevant on Windows).") + .build()) + .addOption(Option.builder(null).longOpt(EXCLUDE_OPTION) + .hasArg().argName("regexp") + .desc("Files that match the this Java regular expression will not be processed. This filters " + + "the files already matched by the \"include\" option. The default matches nothing " + + "(nothing is excluded). See the \"include\" option about the matched path.") + .build()) + .addOption(Option.builder(FREEMARKER_2_SETTING_OPTION_SHORT) + .hasArg().argName("name=value") + .desc("FreeMarker 2 configuration settings, to influence the parsing of the source. You can have " + + "multiple instances of this option, to set multiple settings. For the possible names " + + "and values see the FreeMarker 2 documentation, especially " + + "http://freemarker.org/docs/api/freemarker/template/Configuration.html#setSetting-java" + + ".lang.String-java.lang.String-" + + ".") + .build()) + .addOption(Option.builder(HELP_OPTION_SHORT).longOpt(HELP_OPTION) + .desc("Prints command-line help.") + .build()); + + private static final String SYNOPSIS_START = "java " + FM2ToFM3Converter.class.getName() + " <srcFileOrDir>"; + + private final PrintWriter out; + + private FM2ToFM3ConverterCLI(PrintWriter out) { + this.out = out; + } + + public static void main(String[] args) { + System.exit(execute(new PrintWriter(System.out), args)); + } + + public static int execute(PrintWriter out, String... args) { + return new FM2ToFM3ConverterCLI(out).executeInternal(args); + } + + private int executeInternal(String... args) { + if (args.length == 0) { + printHelp(true); + return COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS; + } + + if (Arrays.asList(args).contains("--" + HELP_OPTION) || Arrays.asList(args).contains("-" + HELP_OPTION_SHORT)) { + printHelp(false); + return SUCCESS_EXIT_STATUS; + } + + try { + CommandLine cl = new DefaultParser().parse(OPTIONS, args); + + if (cl.hasOption(HELP_OPTION)) { + printHelp(false); + } else { + List<String> unparsedArgs = cl.getArgList(); + if (unparsedArgs.size() != 1) { + throw new ParseException("You must specify exactly one source file or directory."); + } + + FM2ToFM3Converter converter = new FM2ToFM3Converter(); + converter.setSource(new File(unparsedArgs.get(0))); + converter.setDestinationDirectory(new File(cl.getOptionValue(DESTINATION_OPTION))); + if (cl.hasOption(CREATE_DESTINATION_OPTION)) { + converter.setCreateDestinationDirectory(true); + } + converter.setFreeMarker2Settings(cl.getOptionProperties(FREEMARKER_2_SETTING_OPTION_SHORT)); + try { + converter.execute(); + + if (converter.getConvertedFileCount() == 0) { + printWrapped("No file to convert was found."); + } else { + printWrapped("Conversion was finished successfully. " + + "Converted " + converter.getConvertedFileCount() + " file(s)."); + } + } catch (Throwable e) { + printWrapped("Conversion was terminated with error. See details in the Java stack " + + "trace:\n"); + printConciseStackTrace(e); + return EXECUTION_ERROR_EXIT_STATUS; + } + } + } catch (ParseException e) { + printWrapped("Wrong command line input: " + e.getMessage() + "\n"); + printHelp(true); + + return COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS; + } + return SUCCESS_EXIT_STATUS; + } + + private void printConciseStackTrace(Throwable e) { + boolean first = true; + while (e != null) { + printWrapped((first ? "" : "Caused by ") + e.getClass().getName() + ": " + e.getMessage()); + e = e.getCause(); + first = false; + } + } + + private void printHelp(boolean usageOnly) { + HelpFormatter hf = new HelpFormatter(); + if (usageOnly) { + hf.printUsage(out, hf.getWidth(), SYNOPSIS_START, OPTIONS); + printWrapped("Use option -" + HELP_OPTION_SHORT + " for more help."); + } else { + hf.printHelp( + out, hf.getWidth(), + SYNOPSIS_START, + "\n" + + "Converts FreeMarker 2 templates to FreeMarker 3 templates, as far as it's possible automatically. " + + + "While the output will consist of syntactically correct FreeMarker 3 templates, the " + + "templates will have to be reviewed by humans, due to the semantic differences (such as" + + " a " + + "different treatment of null).\n\nOptions:", + OPTIONS, + hf.getLeftPadding(), hf.getDescPadding(), + "\nFor more information check the Javadoc of " + FM2ASTToFM3SourceConverter.class.getName() + + ", for now...", + true); + } + out.flush(); + } + + private void printWrapped(String s) { + HelpFormatter hf = new HelpFormatter(); + hf.printWrapped(out, hf.getWidth(), s); + out.flush(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/17279ba6/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterCLITest.java ---------------------------------------------------------------------- diff --git a/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterCLITest.java b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterCLITest.java new file mode 100644 index 0000000..982cccf --- /dev/null +++ b/freemarker-converter/src/test/java/org/freemarker/converter/FM2ToFM3ConverterCLITest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.freemarker.converter; + +import static java.nio.charset.StandardCharsets.*; +import static org.apache.commons.io.FileUtils.*; +import static org.apache.freemarker.converter.FM2ToFM3ConverterCLI.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.apache.freemarker.converter.FM2ToFM3ConverterCLI; +import org.freemarker.converter.test.ConverterTest; +import org.junit.Test; + +public class FM2ToFM3ConverterCLITest extends ConverterTest { + + @Override + protected void createSourceFiles() throws IOException { + write(new File(srcDir, "1.ftl"), "", UTF_8); + write(new File(srcDir, "2.ftl"), "", UTF_8); + write(new File(srcDir, "3.txt"), "", UTF_8); + } + + @Test + public void testHelp() { + assertCLIResult(SUCCESS_EXIT_STATUS, "Options:", null, "-h"); + assertCLIResult(SUCCESS_EXIT_STATUS, "Options:", null, "--help"); + assertCLIResult(COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS, "usage", "Options:"); + } + + @Test + public void testMissingOptions() { + assertCLIResult(COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS, "source", null, "-d", dstDir.toString()); + assertCLIResult(COMMAND_LINE_FORMAT_ERROR_EXIT_STATUS, "required", "source", srcDir.toString()); + } + + @Test + public void testBasic() { + assertCLIResult(SUCCESS_EXIT_STATUS, "2 file", null, + srcDir.toString(), "-d", dstDir.toString()); + assertTrue(new File(dstDir, "1.fm3").exists()); + assertTrue(new File(dstDir, "2.fm3").exists()); + assertFalse(new File(dstDir, "3.txt").exists()); + } + + @Test + public void testExecutionError() { + assertCLIResult(EXECUTION_ERROR_EXIT_STATUS, "Exception", null, + new File(srcDir, "doesNotExists").toString(), "-d", dstDir.toString()); + } + + @Test + public void testIncludeAndExclude() { + assertCLIResult(SUCCESS_EXIT_STATUS, "2 file", null, + srcDir.toString(), "-d", dstDir.toString(), + "--include", ".*", "--exclude", ".*2\\.ftl"); + assertTrue(new File(dstDir, "1.fm3").exists()); + assertFalse(new File(dstDir, "3.txt").exists()); + } + + public void assertCLIResult(int expectedExitStatus, String stdoutContains, String stdoutNotContains, String... + args) { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + int actualExitStatus = FM2ToFM3ConverterCLI.execute(out, args); + assertEquals(actualExitStatus, expectedExitStatus); + if (stdoutContains != null) { + assertThat(sw.toString(), containsString(stdoutContains)); + } + if (stdoutNotContains != null) { + assertThat(sw.toString(), not(containsString(stdoutNotContains))); + } + } + +}
