View Javadoc
1   /**************************************************************************
2    *
3    * Copyright (c) 2016-2020 Yawg project contributors.
4    *
5    **************************************************************************/
6   
7   package com.varmateo.yawg.html;
8   
9   import java.nio.file.Path;
10  import java.util.Optional;
11  import java.util.function.Function;
12  import java.util.regex.Pattern;
13  
14  import io.vavr.control.Try;
15  
16  import com.varmateo.yawg.api.Result;
17  import com.varmateo.yawg.spi.PageBakeResult;
18  import com.varmateo.yawg.spi.PageBaker;
19  import com.varmateo.yawg.spi.PageContext;
20  import com.varmateo.yawg.spi.Template;
21  import com.varmateo.yawg.spi.TemplateContext;
22  import com.varmateo.yawg.util.FileUtils;
23  import com.varmateo.yawg.util.PageBakeResults;
24  import com.varmateo.yawg.util.Results;
25  
26  
27  /**
28   * A <code>Baker</code> that transforms HTML files into other HTML
29   * files.
30   */
31  public final class HtmlPageBaker
32          implements PageBaker {
33  
34  
35      private static final String NAME = "html";
36  
37      private static final Pattern RE_HTML = Pattern.compile(".*\\.html$");
38  
39      private static final String TARGET_EXTENSION = ".html";
40  
41  
42      private HtmlPageBaker() {
43          // Nothing to do.
44      }
45  
46  
47      /**
48       *
49       */
50      public static PageBaker create() {
51  
52          return new HtmlPageBaker();
53      }
54  
55  
56      /**
57       * {@inheritDoc}
58       */
59      @Override
60      public String shortName() {
61  
62          return NAME;
63      }
64  
65  
66      /**
67       * Checks if the given file name has one of the known extensions.
68       *
69       * <p>The following extensions will be allowed:</p>
70       *
71       * <ul>
72       *   <li>.adoc</li>
73       * </ul>
74       *
75       * @return True if the given file name has one of the allowed
76       * extensions.
77       */
78      @Override
79      public boolean isBakeable(final Path path) {
80  
81          return FileUtils.isNameMatch(path, RE_HTML);
82      }
83  
84  
85      /**
86       * Converts the given text file in HTML format into another HTML
87       * file processed through the template engine.
88       *
89       * <p>The target directory must already exist. Otherwise an
90       * exception will be thrown.</p>
91       *
92       * @param sourcePath The file to be baked.
93       *
94       * @param context Provides the template for generating the target
95       * document. If no template is provided, then the source document
96       * is just copied to the target directory.
97       *
98       * @param targetDir The directory where the baked file will be
99       * copied to.
100      *
101      * @return A result signaling success of failure.
102      */
103     @Override
104     public PageBakeResult bake(
105             final Path sourcePath,
106             final PageContext context,
107             final Path targetDir) {
108 
109         final Try<Void> result = doBake(sourcePath, context, targetDir);
110 
111         return PageBakeResults.fromTry(result);
112     }
113 
114 
115     /**
116      *
117      */
118     private Try<Void> doBake(
119             final Path sourcePath,
120             final PageContext context,
121             final Path targetDir) {
122 
123         final Path targetPath = getTargetPath(sourcePath, targetDir);
124         final Optional<Template> template = context.templateFor(sourcePath);
125         final Try<Void> result;
126 
127         if ( template.isPresent() ) {
128             result = doBakeWithTemplate(sourcePath, context, targetPath, template.get());
129         } else {
130             result = doBakeWithoutTemplate(sourcePath, targetPath);
131         }
132 
133         return result;
134     }
135 
136 
137     /**
138      *
139      */
140     private Path getTargetPath(
141             final Path sourcePath,
142             final Path targetDir) {
143 
144         final String sourceBasename = FileUtils.basename(sourcePath);
145         final String targetName = sourceBasename + TARGET_EXTENSION;
146 
147         return targetDir.resolve(targetName);
148     }
149 
150 
151     /**
152      * Baking without a template just copies the source HTML file to
153      * the target location.
154      */
155     private Try<Void> doBakeWithoutTemplate(
156             final Path sourcePath,
157             final Path targetPath) {
158 
159         return FileUtils.safeCopy(sourcePath, targetPath)
160                 .recoverWith(HtmlPageBakerException.copyFailureTry(sourcePath));
161     }
162 
163 
164     /**
165      *
166      */
167     private Try<Void> doBakeWithTemplate(
168             final Path sourcePath,
169             final PageContext context,
170             final Path targetPath,
171             final Template template) {
172 
173         final Try<TemplateContext> templateContext = HtmlTemplateContext.create(
174                 sourcePath, targetPath, context);
175 
176         return templateContext.flatMap(processTemplate(sourcePath, targetPath, template));
177     }
178 
179 
180     private Function<TemplateContext, Try<Void>> processTemplate(
181             final Path sourcePath,
182             final Path targetPath,
183             final Template template) {
184 
185         return (TemplateContext templateContext) -> {
186             final Try<Result<Void>> result = FileUtils.safeWriteWith(
187                     targetPath,
188                     writer -> template.process(templateContext, writer));
189 
190             return result
191                     .recoverWith(HtmlPageBakerException.templateFailureTry(sourcePath))
192                     .flatMap(Results::toTry);
193         };
194     }
195 
196 
197 }