View Javadoc
1   /**************************************************************************
2    *
3    * Copyright (c) 2016-2020 Yawg project contributors.
4    *
5    **************************************************************************/
6   
7   package com.varmateo.yawg.core;
8   
9   import java.io.IOException;
10  import java.nio.file.Files;
11  import java.nio.file.Path;
12  import java.util.function.Function;
13  
14  import io.vavr.collection.Seq;
15  
16  import com.varmateo.yawg.api.YawgException;
17  import com.varmateo.yawg.logging.Log;
18  import com.varmateo.yawg.logging.LogFactory;
19  import com.varmateo.yawg.spi.DirBakeListener;
20  import com.varmateo.yawg.spi.PageContext;
21  import com.varmateo.yawg.spi.TemplateService;
22  
23  
24  /**
25   * Bakes the files contained in a directory, and recursively bake all
26   * sub-directories.
27   */
28  /* default */ final class DirBaker {
29  
30  
31      private final Log _log;
32      private final FileBaker _fileBaker;
33      private final DirBakeOptionsDao _dirBakeOptionsDao;
34      private final DirBakeListener _listener;
35      private final TemplateService _templateService;
36  
37  
38      /**
39       * @param log Used for logging.
40       *
41       * @param fileBaker Baker to be used on regular files.
42       *
43       * @param templateService Will provide the templates used in the
44       * baking of individual files.
45       *
46       * @param dirBakeOptionsDao Used for reading the bake configuration
47       * for each directory.
48       *
49       * @param dirBakeListener Will be notified when the bake of a
50       * directory starts.
51       */
52      /* default */ DirBaker(
53              final FileBaker fileBaker,
54              final TemplateService templateService,
55              final DirBakeOptionsDao dirBakeOptionsDao,
56              final DirBakeListener dirBakeListener) {
57  
58          _log = LogFactory.createFor(DirBaker.class);
59          _fileBaker = fileBaker;
60          _dirBakeOptionsDao = dirBakeOptionsDao;
61          _listener = dirBakeListener;
62          _templateService = templateService;
63      }
64  
65  
66      /**
67       * 
68       */
69      public void bakeDirectory(
70              final Path sourceDir,
71              final Path targetDir,
72              final DirBakeOptions parentDirBakeOptions) {
73  
74          final DirBakerContext context = DirBakerContext.create(
75                  sourceDir, targetDir, _templateService, parentDirBakeOptions);
76  
77          doBakeDirectory(sourceDir, targetDir, context);
78      }
79  
80  
81      /**
82       * 
83       */
84      private void doBakeDirectory(
85              final Path sourceDir,
86              final Path targetDir,
87              final DirBakerContext parentContext) {
88  
89          final Path relSourceDir = parentContext.sourceRootDir().relativize(sourceDir);
90          _log.debug("Baking directory {0}", relSourceDir);
91  
92          createDirIfNeeded(targetDir);
93  
94          final DirBakeOptions specificDirBakeOptions = _dirBakeOptionsDao.loadFromDir(sourceDir);
95          final DirBakerContext context = parentContext.buildForChildDir(
96                  targetDir,
97                  specificDirBakeOptions,
98                  x -> _listener.onDirBake(x).pageVars());
99          final DirBakeOptions dirBakeOptions = context.dirBakeOptions();
100         final Seq<Path> dirEntries = getDirEntries(sourceDir, dirBakeOptions);
101 
102         bakeChildFiles(dirEntries, targetDir, dirBakeOptions, context.pageContext());
103         bakeChildDirectories(dirEntries, targetDir, context);
104         bakeExtraDirectories(sourceDir, targetDir, context);
105     }
106 
107 
108     /**
109      *
110      */
111     private void createDirIfNeeded(final Path targetDir) {
112 
113         if ( !Files.exists(targetDir) ) {
114             doIoAction(
115                     () -> Files.createDirectory(targetDir),
116                     DirBakerException.directoryCreationFailure(targetDir));
117         }
118     }
119 
120 
121     /**
122      *
123      */
124     private Seq<Path> getDirEntries(
125             final Path dir,
126             final DirBakeOptions dirBakeOptions) {
127 
128         final DirEntryScanner.html#DirEntryScanner">DirEntryScanner scanner = new DirEntryScanner(dirBakeOptions);
129 
130         return doIoAction(
131                 () -> scanner.getDirEntries(dir),
132                 DirBakerException.directoryListFailure(dir));
133     }
134 
135 
136     /**
137      *
138      */
139     private void bakeChildFiles(
140             final Seq<Path> dirEntries,
141             final Path targetDir,
142             final DirBakeOptions dirBakeOptions,
143             final PageContext context) {
144 
145         final Seq<Path> filePathList = dirEntries.filter(Files::isRegularFile);
146 
147         for ( final Path path : filePathList ) {
148             _fileBaker.bakeFile(path, context, targetDir, dirBakeOptions);
149         }
150     }
151 
152 
153     /**
154      *
155      */
156     private void bakeChildDirectories(
157             final Seq<Path> dirEntries,
158             final Path targetDir,
159             final DirBakerContext context) {
160 
161         final Seq<Path> dirPathList = dirEntries.filter(Files::isDirectory);
162 
163         for ( final Path childSourceDir : dirPathList ) {
164             final Path dirBasename = childSourceDir.getFileName();
165             final Path childTargetDir = targetDir.resolve(dirBasename);
166 
167             doBakeDirectory(childSourceDir, childTargetDir, context);
168         }
169     }
170 
171 
172     /**
173      *
174      */
175     private void bakeExtraDirectories(
176             final Path sourceDir,
177             final Path targetDir,
178             final DirBakerContext context) {
179 
180         final Seq<Path> extraDirPathList = context.dirBakeOptions()
181                 .extraDirsHere
182                 .map(path -> sourceDir.resolve(path));
183 
184         for ( final Path extraSourceDir : extraDirPathList ) {
185             doBakeDirectory(extraSourceDir, targetDir, context);
186         }
187     }
188 
189 
190     /**
191      * A simple wrapper to change a method signature from the checked
192      * IOException to the unchecked YawgException.
193      */
194     private <T> T doIoAction(
195             final IoSupplier<T> supplier,
196             final Function<IOException, DirBakerException> exceptionBuilder) {
197 
198         final T result;
199 
200         try {
201             result = supplier.get();
202         } catch ( IOException e ) {
203             throw exceptionBuilder.apply(e);
204         }
205 
206         return result;
207     }
208 
209 
210     /**
211      * Utility interface to simplify creating lambdas whose body may
212      * throw exceptions.
213      *
214      * @param <T> Type of the values returned by the function.
215      */
216     @FunctionalInterface
217     private interface IoSupplier<T> {
218 
219 
220         /**
221          *
222          */
223         T get() throws IOException;
224     }
225 
226 
227 }