Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git
M src/main/java/com/keenwrite/MainPane.java
10911091
      .with( Mutator::setLocale, w::getLocale )
10921092
      .with( Mutator::setMetadata, w::getMetadata )
1093
      .with( Mutator::setThemesDir, w::getThemesPath )
1094
      .with( Mutator::setCachesDir,
1095
             () -> w.getFile( KEY_CACHES_DIR ) )
1096
      .with( Mutator::setImagesDir,
1097
             () -> w.getFile( KEY_IMAGES_DIR ) )
1093
      .with( Mutator::setThemeDir, w::getThemesPath )
1094
      .with( Mutator::setCacheDir,
1095
             () -> w.getFile( KEY_CACHE_DIR ) )
1096
      .with( Mutator::setImageDir,
1097
             () -> w.getFile( KEY_IMAGE_DIR ) )
10981098
      .with( Mutator::setImageOrder,
1099
             () -> w.getString( KEY_IMAGES_ORDER ) )
1099
             () -> w.getString( KEY_IMAGE_ORDER ) )
11001100
      .with( Mutator::setImageServer,
1101
             () -> w.getString( KEY_IMAGES_SERVER ) )
1102
      .with( Mutator::setFontsDir,
1101
             () -> w.getString( KEY_IMAGE_SERVER ) )
1102
      .with( Mutator::setFontDir,
11031103
             () -> w.getFile( KEY_TYPESET_CONTEXT_FONTS_DIR ) )
11041104
      .with( Mutator::setCaret,
M src/main/java/com/keenwrite/cmdline/Arguments.java
241241
      .with( Mutator::setSourcePath, mSourcePath )
242242
      .with( Mutator::setTargetPath, mTargetPath )
243
      .with( Mutator::setThemesDir, () -> mThemesDir )
244
      .with( Mutator::setCachesDir, () -> mCachesDir )
245
      .with( Mutator::setImagesDir, () -> mImagesDir )
243
      .with( Mutator::setThemeDir, () -> mThemesDir )
244
      .with( Mutator::setCacheDir, () -> mCachesDir )
245
      .with( Mutator::setImageDir, () -> mImagesDir )
246246
      .with( Mutator::setImageServer, () -> mImageServer )
247247
      .with( Mutator::setImageOrder, () -> mImageOrder )
248
      .with( Mutator::setFontsDir, () -> mFontDir )
248
      .with( Mutator::setFontDir, () -> mFontDir )
249249
      .with( Mutator::setExportFormat, format )
250250
      .with( Mutator::setDefinitions, () -> definitions )
M src/main/java/com/keenwrite/constants/Constants.java
102102
103103
  /**
104
   * Default working directory to use for R startup script.
104
   * Default working directory.
105105
   */
106106
  public static final File USER_DIRECTORY =
107107
    new File( System.getProperty( "user.dir" ) );
108
109
  /**
110
   * Location to write temporary files.
111
   */
112
  public static final String TEMPORARY_DIRECTORY =
113
    System.getProperty( "java.io.tmpdir" );
108114
109115
  public static final String NEWLINE = System.lineSeparator();
M src/main/java/com/keenwrite/preferences/AppKeys.java
3030
3131
32
  public static final Key KEY_IMAGES = key( KEY_ROOT, "images" );
33
  public static final Key KEY_CACHES_DIR = key( KEY_IMAGES, "cache" );
34
  public static final Key KEY_IMAGES_DIR = key( KEY_IMAGES, "dir" );
35
  public static final Key KEY_IMAGES_ORDER = key( KEY_IMAGES, "order" );
36
  public static final Key KEY_IMAGES_RESIZE = key( KEY_IMAGES, "resize" );
37
  public static final Key KEY_IMAGES_SERVER = key( KEY_IMAGES, "server" );
32
  public static final Key KEY_IMAGE = key( KEY_ROOT, "images" );
33
  public static final Key KEY_CACHE_DIR = key( KEY_IMAGE, "cache" );
34
  public static final Key KEY_IMAGE_DIR = key( KEY_IMAGE, "dir" );
35
  public static final Key KEY_IMAGE_ORDER = key( KEY_IMAGE, "order" );
36
  public static final Key KEY_IMAGE_RESIZE = key( KEY_IMAGE, "resize" );
37
  public static final Key KEY_IMAGE_SERVER = key( KEY_IMAGE, "server" );
3838
3939
  public static final Key KEY_DEF = key( KEY_ROOT, "definition" );
M src/main/java/com/keenwrite/preferences/PreferencesController.java
186186
      ),
187187
      Category.of(
188
        get( KEY_IMAGES ),
188
        get( KEY_IMAGE ),
189189
        Group.of(
190
          get( KEY_IMAGES_DIR ),
191
          Setting.of( label( KEY_IMAGES_DIR ) ),
192
          Setting.of( title( KEY_IMAGES_DIR ),
193
                      directoryProperty( KEY_IMAGES_DIR ),
190
          get( KEY_IMAGE_DIR ),
191
          Setting.of( label( KEY_IMAGE_DIR ) ),
192
          Setting.of( title( KEY_IMAGE_DIR ),
193
                      directoryProperty( KEY_IMAGE_DIR ),
194194
                      true ),
195
          Setting.of( label( KEY_CACHES_DIR ) ),
196
          Setting.of( title( KEY_CACHES_DIR ),
197
                      directoryProperty( KEY_CACHES_DIR ),
195
          Setting.of( label( KEY_CACHE_DIR ) ),
196
          Setting.of( title( KEY_CACHE_DIR ),
197
                      directoryProperty( KEY_CACHE_DIR ),
198198
                      true )
199199
        ),
200200
        Group.of(
201
          get( KEY_IMAGES_ORDER ),
202
          Setting.of( label( KEY_IMAGES_ORDER ) ),
203
          Setting.of( title( KEY_IMAGES_ORDER ),
204
                      stringProperty( KEY_IMAGES_ORDER ) )
201
          get( KEY_IMAGE_ORDER ),
202
          Setting.of( label( KEY_IMAGE_ORDER ) ),
203
          Setting.of( title( KEY_IMAGE_ORDER ),
204
                      stringProperty( KEY_IMAGE_ORDER ) )
205205
        ),
206206
        Group.of(
207
          get( KEY_IMAGES_RESIZE ),
208
          Setting.of( label( KEY_IMAGES_RESIZE ) ),
209
          Setting.of( title( KEY_IMAGES_RESIZE ),
210
                      booleanProperty( KEY_IMAGES_RESIZE ) )
207
          get( KEY_IMAGE_RESIZE ),
208
          Setting.of( label( KEY_IMAGE_RESIZE ) ),
209
          Setting.of( title( KEY_IMAGE_RESIZE ),
210
                      booleanProperty( KEY_IMAGE_RESIZE ) )
211211
        ),
212212
        Group.of(
213
          get( KEY_IMAGES_SERVER ),
214
          Setting.of( label( KEY_IMAGES_SERVER ) ),
215
          Setting.of( title( KEY_IMAGES_SERVER ),
216
                      stringProperty( KEY_IMAGES_SERVER ) )
213
          get( KEY_IMAGE_SERVER ),
214
          Setting.of( label( KEY_IMAGE_SERVER ) ),
215
          Setting.of( title( KEY_IMAGE_SERVER ),
216
                      stringProperty( KEY_IMAGE_SERVER ) )
217217
        )
218218
      ),
M src/main/java/com/keenwrite/preferences/Workspace.java
7171
    entry( KEY_R_DELIM_ENDED, asStringProperty( R_DELIM_ENDED_DEFAULT ) ),
7272
73
    entry( KEY_CACHES_DIR, asFileProperty( USER_CACHE_DIR ) ),
74
    entry( KEY_IMAGES_DIR, asFileProperty( USER_DIRECTORY ) ),
75
    entry( KEY_IMAGES_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ),
76
    entry( KEY_IMAGES_RESIZE, asBooleanProperty( true ) ),
77
    entry( KEY_IMAGES_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ),
73
    entry( KEY_CACHE_DIR, asFileProperty( USER_CACHE_DIR ) ),
74
    entry( KEY_IMAGE_DIR, asFileProperty( USER_DIRECTORY ) ),
75
    entry( KEY_IMAGE_ORDER, asStringProperty( PERSIST_IMAGES_DEFAULT ) ),
76
    entry( KEY_IMAGE_RESIZE, asBooleanProperty( true ) ),
77
    entry( KEY_IMAGE_SERVER, asStringProperty( DIAGRAM_SERVER_NAME ) ),
7878
7979
    entry( KEY_DEF_PATH, asFileProperty( DEFINITION_DEFAULT ) ),
M src/main/java/com/keenwrite/preview/HtmlPreview.java
347347
  @Override
348348
  public void componentResized( final ComponentEvent e ) {
349
    if( mWorkspace.getBoolean( KEY_IMAGES_RESIZE ) ) {
349
    if( mWorkspace.getBoolean( KEY_IMAGE_RESIZE ) ) {
350350
      mPreview.clearCache();
351351
    }
M src/main/java/com/keenwrite/processors/PdfProcessor.java
3434
    try {
3535
      clue( "Main.status.typeset.create" );
36
3637
      final var context = mProcessorContext;
37
      final var parent = context.getTargetPath().getParent();
38
      final var document =
39
        TEXT_XML.createTempFile( APP_TITLE_ABBR, parent );
38
      final var targetPath = context.getTargetPath();
39
      clue( "Main.status.typeset.setting", "target", targetPath );
40
41
      final var parent = targetPath.toAbsolutePath().getParent();
42
43
      final var document = TEXT_XML.createTempFile( APP_TITLE_ABBR, parent );
44
      final var sourcePath = writeString( document, xhtml );
45
      clue( "Main.status.typeset.setting", "source", sourcePath );
46
47
      final var themeDir = context.getThemeDir();
48
      clue( "Main.status.typeset.setting", "themes", themeDir );
49
50
      final var imageDir = context.getImageDir();
51
      clue( "Main.status.typeset.setting", "images", imageDir );
52
53
      final var cacheDir = context.getCacheDir();
54
      clue( "Main.status.typeset.setting", "caches", cacheDir );
55
56
      final var fontDir = context.getFontDir();
57
      clue( "Main.status.typeset.setting", "fonts", fontDir );
58
59
      final var autoRemove = context.getAutoRemove();
60
      clue( "Main.status.typeset.setting", "purge", autoRemove );
61
62
      final var rWorkDir = context.getRWorkingDir();
63
      clue( "Main.status.typeset.setting", "r-work", rWorkDir );
64
65
      final var imageOrder = context.getImageOrder();
66
      clue( "Main.status.typeset.setting", "order", imageOrder );
67
4068
      final var typesetter = Typesetter
4169
        .builder()
42
        .with( Mutator::setAutoRemove, context.getAutoRemove() )
43
        .with( Mutator::setSourcePath, writeString( document, xhtml ) )
44
        .with( Mutator::setTargetPath, context.getTargetPath() )
45
        .with( Mutator::setThemesPath, context.getThemesDir() )
46
        .with( Mutator::setImagesPath, context.getImagesDir() )
47
        .with( Mutator::setCachesPath, context.getCachesPath() )
48
        .with( Mutator::setFontsPath, context.getFontsDir() )
70
        .with( Mutator::setSourcePath, sourcePath )
71
        .with( Mutator::setTargetPath, targetPath )
72
        .with( Mutator::setThemeDir, themeDir )
73
        .with( Mutator::setImageDir, imageDir )
74
        .with( Mutator::setCacheDir, cacheDir )
75
        .with( Mutator::setFontDir, fontDir )
76
        .with( Mutator::setAutoRemove, autoRemove )
4977
        .build();
5078
M src/main/java/com/keenwrite/processors/ProcessorContext.java
3030
import static com.keenwrite.io.FileType.UNKNOWN;
3131
import static com.keenwrite.io.MediaType.TEXT_PROPERTIES;
32
import static com.keenwrite.io.MediaType.valueFrom;
33
import static com.keenwrite.predicates.PredicateFactory.createFileTypePredicate;
34
35
/**
36
 * Provides a context for configuring a chain of {@link Processor} instances.
37
 */
38
public final class ProcessorContext {
39
40
  private final Mutator mMutator;
41
42
  /**
43
   * Determines the file type from the path extension. This should only be
44
   * called when it is known that the file type won't be a definition file
45
   * (e.g., YAML or other definition source), but rather an editable file
46
   * (e.g., Markdown, R Markdown, etc.).
47
   *
48
   * @param path The path with a file name extension.
49
   * @return The FileType for the given path.
50
   */
51
  private static FileType lookup( final Path path ) {
52
    assert path != null;
53
54
    final var prefix = GLOB_PREFIX_FILE;
55
    final var keys = sSettings.getKeys( prefix );
56
57
    var found = false;
58
    var fileType = UNKNOWN;
59
60
    while( keys.hasNext() && !found ) {
61
      final var key = keys.next();
62
      final var patterns = sSettings.getStringSettingList( key );
63
      final var predicate = createFileTypePredicate( patterns );
64
65
      if( predicate.test( path.toFile() ) ) {
66
        // Remove the EXTENSIONS_PREFIX to get the file name extension mapped
67
        // to a standard name (as defined in the settings.properties file).
68
        final String suffix = key.replace( prefix + '.', "" );
69
        fileType = FileType.from( suffix );
70
        found = true;
71
      }
72
    }
73
74
    return fileType;
75
  }
76
77
  public boolean isExportFormat( final ExportFormat exportFormat ) {
78
    return mMutator.mExportFormat == exportFormat;
79
  }
80
81
  /**
82
   * Responsible for populating the instance variables required by the
83
   * context.
84
   */
85
  public static class Mutator {
86
    private Path mSourcePath;
87
    private Path mTargetPath;
88
    private ExportFormat mExportFormat;
89
    private Supplier<Boolean> mConcatenate = () -> true;
90
    private Supplier<String> mChapters = () -> "";
91
92
    private Supplier<Path> mThemesDir = USER_DIRECTORY::toPath;
93
    private Supplier<Locale> mLocale = () -> Locale.ENGLISH;
94
95
    private Supplier<Map<String, String>> mDefinitions = HashMap::new;
96
    private Supplier<Map<String, String>> mMetadata = HashMap::new;
97
    private Supplier<Caret> mCaret = () -> Caret.builder().build();
98
99
    private Supplier<Path> mFontsDir = () -> getFontDirectory().toPath();
100
101
    private Supplier<Path> mImagesDir = USER_DIRECTORY::toPath;
102
    private Supplier<String> mImageServer = () -> DIAGRAM_SERVER_NAME;
103
    private Supplier<String> mImageOrder = () -> PERSIST_IMAGES_DEFAULT;
104
105
    private Supplier<Path> mCachesPath = USER_CACHE_DIR::toPath;
106
107
    private Supplier<String> mSigilBegan = () -> DEF_DELIM_BEGAN_DEFAULT;
108
    private Supplier<String> mSigilEnded = () -> DEF_DELIM_ENDED_DEFAULT;
109
110
    private Supplier<Path> mRWorkingDir = USER_DIRECTORY::toPath;
111
    private Supplier<String> mRScript = () -> "";
112
113
    private Supplier<Boolean> mCurlQuotes = () -> true;
114
    private Supplier<Boolean> mAutoRemove = () -> true;
115
116
    public void setSourcePath( final Path sourcePath ) {
117
      assert sourcePath != null;
118
      mSourcePath = sourcePath;
119
    }
120
121
    public void setTargetPath( final Path outputPath ) {
122
      assert outputPath != null;
123
      mTargetPath = outputPath;
124
    }
125
126
    public void setTargetPath( final File targetPath ) {
127
      assert targetPath != null;
128
      setTargetPath( targetPath.toPath() );
129
    }
130
131
    public void setThemesDir( final Supplier<Path> themesDir ) {
132
      assert themesDir != null;
133
      mThemesDir = themesDir;
134
    }
135
136
    public void setCachesDir( final Supplier<File> cachesDir ) {
137
      assert cachesDir != null;
138
139
      mCachesPath = () -> {
140
        final var dir = cachesDir.get();
141
142
        return (dir == null ? USER_DATA_DIR.toFile() : dir).toPath();
143
      };
144
    }
145
146
    public void setImagesDir( final Supplier<File> imagesDir ) {
147
      assert imagesDir != null;
148
149
      mImagesDir = () -> {
150
        final var dir = imagesDir.get();
151
152
        return (dir == null ? USER_DIRECTORY : dir).toPath();
153
      };
154
    }
155
156
    public void setImageOrder( final Supplier<String> imageOrder ) {
157
      assert imageOrder != null;
158
      mImageOrder = imageOrder;
159
    }
160
161
    public void setImageServer( final Supplier<String> imageServer ) {
162
      assert imageServer != null;
163
      mImageServer = imageServer;
164
    }
165
166
    public void setFontsDir( final Supplier<File> fontsDir ) {
167
      assert fontsDir != null;
168
      mFontsDir = () -> {
169
        final var dir = fontsDir.get();
170
171
        return (dir == null ? USER_DIRECTORY : dir).toPath();
172
      };
173
    }
174
175
    public void setExportFormat( final ExportFormat exportFormat ) {
176
      assert exportFormat != null;
177
      mExportFormat = exportFormat;
178
    }
179
180
    public void setConcatenate( final Supplier<Boolean> concatenate ) {
181
      mConcatenate = concatenate;
182
    }
183
184
    public void setChapters( final Supplier<String> chapters ) {
185
      mChapters = chapters;
186
    }
187
188
    public void setLocale( final Supplier<Locale> locale ) {
189
      assert locale != null;
190
      mLocale = locale;
191
    }
192
193
    /**
194
     * Sets the list of fully interpolated key-value pairs to use when
195
     * substituting variable names back into the document as variable values.
196
     * This uses a {@link Callable} reference so that GUI and command-line
197
     * usage can insert their respective behaviours. That is, this method
198
     * prevents coupling the GUI to the CLI.
199
     *
200
     * @param supplier Defines how to retrieve the definitions.
201
     */
202
    public void setDefinitions( final Supplier<Map<String, String>> supplier ) {
203
      assert supplier != null;
204
      mDefinitions = supplier;
205
    }
206
207
    public void setMetadata( final Supplier<Map<String, String>> metadata ) {
208
      assert metadata != null;
209
      mMetadata = metadata.get() == null ? HashMap::new : metadata;
210
    }
211
212
    /**
213
     * Sets the source for deriving the {@link Caret}. Typically, this is
214
     * the text editor that has focus.
215
     *
216
     * @param caret The source for the currently active caret.
217
     */
218
    public void setCaret( final Supplier<Caret> caret ) {
219
      assert caret != null;
220
      mCaret = caret;
221
    }
222
223
    public void setSigilBegan( final Supplier<String> sigilBegan ) {
224
      assert sigilBegan != null;
225
      mSigilBegan = sigilBegan;
226
    }
227
228
    public void setSigilEnded( final Supplier<String> sigilEnded ) {
229
      assert sigilEnded != null;
230
      mSigilEnded = sigilEnded;
231
    }
232
233
    public void setRWorkingDir( final Supplier<Path> rWorkingDir ) {
234
      assert rWorkingDir != null;
235
236
      mRWorkingDir = rWorkingDir;
237
    }
238
239
    public void setRScript( final Supplier<String> rScript ) {
240
      assert rScript != null;
241
      mRScript = rScript;
242
    }
243
244
    public void setCurlQuotes( final Supplier<Boolean> curlQuotes ) {
245
      assert curlQuotes != null;
246
      mCurlQuotes = curlQuotes;
247
    }
248
249
    public void setAutoRemove( final Supplier<Boolean> autoRemove ) {
250
      assert autoRemove != null;
251
      mAutoRemove = autoRemove;
252
    }
253
254
    private boolean isExportFormat( final ExportFormat format ) {
255
      return mExportFormat == format;
256
    }
257
  }
258
259
  public static GenericBuilder<Mutator, ProcessorContext> builder() {
260
    return GenericBuilder.of( Mutator::new, ProcessorContext::new );
261
  }
262
263
  /**
264
   * Creates a new context for use by the {@link ProcessorFactory} when
265
   * instantiating new {@link Processor} instances. Although all the
266
   * parameters are required, not all {@link Processor} instances will use
267
   * all parameters.
268
   */
269
  private ProcessorContext( final Mutator mutator ) {
270
    assert mutator != null;
271
272
    mMutator = mutator;
273
  }
274
275
  public Path getSourcePath() {
276
    return mMutator.mSourcePath;
277
  }
278
279
  /**
280
   * Answers what type of input document is to be processed.
281
   *
282
   * @return The input document's {@link MediaType}.
283
   */
284
  public MediaType getSourceType() {
285
    return MediaTypeExtension.fromPath( mMutator.mSourcePath );
286
  }
287
288
  /**
289
   * Fully qualified file name to use when exporting (e.g., document.pdf).
290
   *
291
   * @return Full path to a file name.
292
   */
293
  public Path getTargetPath() {
294
    return mMutator.mTargetPath;
295
  }
296
297
  public ExportFormat getExportFormat() {
298
    return mMutator.mExportFormat;
299
  }
300
301
  public Locale getLocale() {
302
    return mMutator.mLocale.get();
303
  }
304
305
  /**
306
   * Returns the variable map of definitions, without interpolation.
307
   *
308
   * @return A map to help dereference variables.
309
   */
310
  public Map<String, String> getDefinitions() {
311
    return mMutator.mDefinitions.get();
312
  }
313
314
  /**
315
   * Returns the variable map of definitions, with interpolation.
316
   *
317
   * @return A map to help dereference variables.
318
   */
319
  public InterpolatingMap getInterpolatedDefinitions() {
320
    return new InterpolatingMap(
321
      createDefinitionKeyOperator(), getDefinitions()
322
    ).interpolate();
323
  }
324
325
  public Map<String, String> getMetadata() {
326
    return mMutator.mMetadata.get();
327
  }
328
329
  /**
330
   * Returns the current caret position in the document being edited and is
331
   * always up-to-date.
332
   *
333
   * @return Caret position in the document.
334
   */
335
  public Supplier<Caret> getCaret() {
336
    return mMutator.mCaret;
337
  }
338
339
  /**
340
   * Returns the directory that contains the file being edited. When
341
   * {@link Constants#DOCUMENT_DEFAULT} is created, the parent path is
342
   * {@code null}. This will get absolute path to the file before trying to
343
   * get te parent path, which should always be a valid path. In the unlikely
344
   * event that the base path cannot be determined by the path alone, the
345
   * default user directory is returned. This is necessary for the creation
346
   * of new files.
347
   *
348
   * @return Path to the directory containing a file being edited, or the
349
   * default user directory if the base path cannot be determined.
350
   */
351
  public Path getBaseDir() {
352
    final var path = getSourcePath().toAbsolutePath().getParent();
353
    return path == null ? DEFAULT_DIRECTORY : path;
354
  }
355
356
  FileType getSourceFileType() {
357
    return lookup( getSourcePath() );
358
  }
359
360
  public Path getThemesDir() {
361
    return mMutator.mThemesDir.get();
362
  }
363
364
  public Path getImagesDir() {
365
    return mMutator.mImagesDir.get();
366
  }
367
368
  public Path getCachesPath() {
369
    return mMutator.mCachesPath.get();
370
  }
371
372
  public Iterable<String> getImageOrder() {
373
    assert mMutator.mImageOrder != null;
374
375
    final var order = mMutator.mImageOrder.get();
376
    final var token = order.contains( "," ) ? ',' : ' ';
377
378
    return Splitter.on( token ).split( token + order );
379
  }
380
381
  public String getImageServer() {
382
    return mMutator.mImageServer.get();
383
  }
384
385
  public Path getFontsDir() {
386
    return mMutator.mFontsDir.get();
32
import static com.keenwrite.predicates.PredicateFactory.createFileTypePredicate;
33
34
/**
35
 * Provides a context for configuring a chain of {@link Processor} instances.
36
 */
37
public final class ProcessorContext {
38
39
  private final Mutator mMutator;
40
41
  /**
42
   * Determines the file type from the path extension. This should only be
43
   * called when it is known that the file type won't be a definition file
44
   * (e.g., YAML or other definition source), but rather an editable file
45
   * (e.g., Markdown, R Markdown, etc.).
46
   *
47
   * @param path The path with a file name extension.
48
   * @return The FileType for the given path.
49
   */
50
  private static FileType lookup( final Path path ) {
51
    assert path != null;
52
53
    final var prefix = GLOB_PREFIX_FILE;
54
    final var keys = sSettings.getKeys( prefix );
55
56
    var found = false;
57
    var fileType = UNKNOWN;
58
59
    while( keys.hasNext() && !found ) {
60
      final var key = keys.next();
61
      final var patterns = sSettings.getStringSettingList( key );
62
      final var predicate = createFileTypePredicate( patterns );
63
64
      if( predicate.test( path.toFile() ) ) {
65
        // Remove the EXTENSIONS_PREFIX to get the file name extension mapped
66
        // to a standard name (as defined in the settings.properties file).
67
        final String suffix = key.replace( prefix + '.', "" );
68
        fileType = FileType.from( suffix );
69
        found = true;
70
      }
71
    }
72
73
    return fileType;
74
  }
75
76
  public boolean isExportFormat( final ExportFormat exportFormat ) {
77
    return mMutator.mExportFormat == exportFormat;
78
  }
79
80
  /**
81
   * Responsible for populating the instance variables required by the
82
   * context.
83
   */
84
  public static class Mutator {
85
    private Path mSourcePath;
86
    private Path mTargetPath;
87
    private ExportFormat mExportFormat;
88
    private Supplier<Boolean> mConcatenate = () -> true;
89
    private Supplier<String> mChapters = () -> "";
90
91
    private Supplier<Path> mThemeDir = USER_DIRECTORY::toPath;
92
    private Supplier<Locale> mLocale = () -> Locale.ENGLISH;
93
94
    private Supplier<Map<String, String>> mDefinitions = HashMap::new;
95
    private Supplier<Map<String, String>> mMetadata = HashMap::new;
96
    private Supplier<Caret> mCaret = () -> Caret.builder().build();
97
98
    private Supplier<Path> mFontDir = () -> getFontDirectory().toPath();
99
100
    private Supplier<Path> mImageDir = USER_DIRECTORY::toPath;
101
    private Supplier<String> mImageServer = () -> DIAGRAM_SERVER_NAME;
102
    private Supplier<String> mImageOrder = () -> PERSIST_IMAGES_DEFAULT;
103
104
    private Supplier<Path> mCacheDir = USER_CACHE_DIR::toPath;
105
106
    private Supplier<String> mSigilBegan = () -> DEF_DELIM_BEGAN_DEFAULT;
107
    private Supplier<String> mSigilEnded = () -> DEF_DELIM_ENDED_DEFAULT;
108
109
    private Supplier<Path> mRWorkingDir = USER_DIRECTORY::toPath;
110
    private Supplier<String> mRScript = () -> "";
111
112
    private Supplier<Boolean> mCurlQuotes = () -> true;
113
    private Supplier<Boolean> mAutoRemove = () -> true;
114
115
    public void setSourcePath( final Path sourcePath ) {
116
      assert sourcePath != null;
117
      mSourcePath = sourcePath;
118
    }
119
120
    public void setTargetPath( final Path outputPath ) {
121
      assert outputPath != null;
122
      mTargetPath = outputPath;
123
    }
124
125
    public void setThemeDir( final Supplier<Path> themeDir ) {
126
      assert themeDir != null;
127
      mThemeDir = themeDir;
128
    }
129
130
    public void setCacheDir( final Supplier<File> cacheDir ) {
131
      assert cacheDir != null;
132
133
      mCacheDir = () -> {
134
        final var dir = cacheDir.get();
135
136
        return (dir == null ? USER_DATA_DIR.toFile() : dir).toPath();
137
      };
138
    }
139
140
    public void setImageDir( final Supplier<File> imageDir ) {
141
      assert imageDir != null;
142
143
      mImageDir = () -> {
144
        final var dir = imageDir.get();
145
146
        return (dir == null ? USER_DIRECTORY : dir).toPath();
147
      };
148
    }
149
150
    public void setImageOrder( final Supplier<String> imageOrder ) {
151
      assert imageOrder != null;
152
      mImageOrder = imageOrder;
153
    }
154
155
    public void setImageServer( final Supplier<String> imageServer ) {
156
      assert imageServer != null;
157
      mImageServer = imageServer;
158
    }
159
160
    public void setFontDir( final Supplier<File> fontDir ) {
161
      assert fontDir != null;
162
      mFontDir = () -> {
163
        final var dir = fontDir.get();
164
165
        return (dir == null ? USER_DIRECTORY : dir).toPath();
166
      };
167
    }
168
169
    public void setExportFormat( final ExportFormat exportFormat ) {
170
      assert exportFormat != null;
171
      mExportFormat = exportFormat;
172
    }
173
174
    public void setConcatenate( final Supplier<Boolean> concatenate ) {
175
      mConcatenate = concatenate;
176
    }
177
178
    public void setChapters( final Supplier<String> chapters ) {
179
      mChapters = chapters;
180
    }
181
182
    public void setLocale( final Supplier<Locale> locale ) {
183
      assert locale != null;
184
      mLocale = locale;
185
    }
186
187
    /**
188
     * Sets the list of fully interpolated key-value pairs to use when
189
     * substituting variable names back into the document as variable values.
190
     * This uses a {@link Callable} reference so that GUI and command-line
191
     * usage can insert their respective behaviours. That is, this method
192
     * prevents coupling the GUI to the CLI.
193
     *
194
     * @param supplier Defines how to retrieve the definitions.
195
     */
196
    public void setDefinitions( final Supplier<Map<String, String>> supplier ) {
197
      assert supplier != null;
198
      mDefinitions = supplier;
199
    }
200
201
    public void setMetadata( final Supplier<Map<String, String>> metadata ) {
202
      assert metadata != null;
203
      mMetadata = metadata.get() == null ? HashMap::new : metadata;
204
    }
205
206
    /**
207
     * Sets the source for deriving the {@link Caret}. Typically, this is
208
     * the text editor that has focus.
209
     *
210
     * @param caret The source for the currently active caret.
211
     */
212
    public void setCaret( final Supplier<Caret> caret ) {
213
      assert caret != null;
214
      mCaret = caret;
215
    }
216
217
    public void setSigilBegan( final Supplier<String> sigilBegan ) {
218
      assert sigilBegan != null;
219
      mSigilBegan = sigilBegan;
220
    }
221
222
    public void setSigilEnded( final Supplier<String> sigilEnded ) {
223
      assert sigilEnded != null;
224
      mSigilEnded = sigilEnded;
225
    }
226
227
    public void setRWorkingDir( final Supplier<Path> rWorkingDir ) {
228
      assert rWorkingDir != null;
229
230
      mRWorkingDir = rWorkingDir;
231
    }
232
233
    public void setRScript( final Supplier<String> rScript ) {
234
      assert rScript != null;
235
      mRScript = rScript;
236
    }
237
238
    public void setCurlQuotes( final Supplier<Boolean> curlQuotes ) {
239
      assert curlQuotes != null;
240
      mCurlQuotes = curlQuotes;
241
    }
242
243
    public void setAutoRemove( final Supplier<Boolean> autoRemove ) {
244
      assert autoRemove != null;
245
      mAutoRemove = autoRemove;
246
    }
247
248
    private boolean isExportFormat( final ExportFormat format ) {
249
      return mExportFormat == format;
250
    }
251
  }
252
253
  public static GenericBuilder<Mutator, ProcessorContext> builder() {
254
    return GenericBuilder.of( Mutator::new, ProcessorContext::new );
255
  }
256
257
  /**
258
   * Creates a new context for use by the {@link ProcessorFactory} when
259
   * instantiating new {@link Processor} instances. Although all the
260
   * parameters are required, not all {@link Processor} instances will use
261
   * all parameters.
262
   */
263
  private ProcessorContext( final Mutator mutator ) {
264
    assert mutator != null;
265
266
    mMutator = mutator;
267
  }
268
269
  public Path getSourcePath() {
270
    return mMutator.mSourcePath;
271
  }
272
273
  /**
274
   * Answers what type of input document is to be processed.
275
   *
276
   * @return The input document's {@link MediaType}.
277
   */
278
  public MediaType getSourceType() {
279
    return MediaTypeExtension.fromPath( mMutator.mSourcePath );
280
  }
281
282
  /**
283
   * Fully qualified file name to use when exporting (e.g., document.pdf).
284
   *
285
   * @return Full path to a file name.
286
   */
287
  public Path getTargetPath() {
288
    return mMutator.mTargetPath;
289
  }
290
291
  public ExportFormat getExportFormat() {
292
    return mMutator.mExportFormat;
293
  }
294
295
  public Locale getLocale() {
296
    return mMutator.mLocale.get();
297
  }
298
299
  /**
300
   * Returns the variable map of definitions, without interpolation.
301
   *
302
   * @return A map to help dereference variables.
303
   */
304
  public Map<String, String> getDefinitions() {
305
    return mMutator.mDefinitions.get();
306
  }
307
308
  /**
309
   * Returns the variable map of definitions, with interpolation.
310
   *
311
   * @return A map to help dereference variables.
312
   */
313
  public InterpolatingMap getInterpolatedDefinitions() {
314
    return new InterpolatingMap(
315
      createDefinitionKeyOperator(), getDefinitions()
316
    ).interpolate();
317
  }
318
319
  public Map<String, String> getMetadata() {
320
    return mMutator.mMetadata.get();
321
  }
322
323
  /**
324
   * Returns the current caret position in the document being edited and is
325
   * always up-to-date.
326
   *
327
   * @return Caret position in the document.
328
   */
329
  public Supplier<Caret> getCaret() {
330
    return mMutator.mCaret;
331
  }
332
333
  /**
334
   * Returns the directory that contains the file being edited. When
335
   * {@link Constants#DOCUMENT_DEFAULT} is created, the parent path is
336
   * {@code null}. This will get absolute path to the file before trying to
337
   * get te parent path, which should always be a valid path. In the unlikely
338
   * event that the base path cannot be determined by the path alone, the
339
   * default user directory is returned. This is necessary for the creation
340
   * of new files.
341
   *
342
   * @return Path to the directory containing a file being edited, or the
343
   * default user directory if the base path cannot be determined.
344
   */
345
  public Path getBaseDir() {
346
    final var path = getSourcePath().toAbsolutePath().getParent();
347
    return path == null ? DEFAULT_DIRECTORY : path;
348
  }
349
350
  FileType getSourceFileType() {
351
    return lookup( getSourcePath() );
352
  }
353
354
  public Path getThemeDir() {
355
    return mMutator.mThemeDir.get();
356
  }
357
358
  public Path getImageDir() {
359
    return mMutator.mImageDir.get();
360
  }
361
362
  public Path getCacheDir() {
363
    return mMutator.mCacheDir.get();
364
  }
365
366
  public Iterable<String> getImageOrder() {
367
    assert mMutator.mImageOrder != null;
368
369
    final var order = mMutator.mImageOrder.get();
370
    final var token = order.contains( "," ) ? ',' : ' ';
371
372
    return Splitter.on( token ).split( token + order );
373
  }
374
375
  public String getImageServer() {
376
    return mMutator.mImageServer.get();
377
  }
378
379
  public Path getFontDir() {
380
    return mMutator.mFontDir.get();
387381
  }
388382
M src/main/java/com/keenwrite/processors/XhtmlProcessor.java
238238
239239
  private Path getImagesPath() {
240
    return mContext.getImagesDir();
240
    return mContext.getImageDir();
241241
  }
242242
243243
  private Path getCachesPath() {
244
    return mContext.getCachesPath();
244
    return mContext.getCacheDir();
245245
  }
246246
M src/main/java/com/keenwrite/processors/markdown/extensions/ImageLinkExtension.java
180180
181181
    private Path getImageDir() {
182
      return mContext.getImagesDir();
182
      return mContext.getImageDir();
183183
    }
184184
M src/main/java/com/keenwrite/typesetting/GuestTypesetter.java
4343
    final var sourcePath = getSourcePath();
4444
    final var targetPath = getTargetPath();
45
    final var themesPath = getThemesPath();
45
    final var themesPath = getThemeDir();
4646
4747
    final var sourceDir = normalize( sourcePath.getParent() );
4848
    final var targetDir = normalize( targetPath.getParent() );
4949
    final var themesDir = normalize( themesPath.getParent() );
50
    final var imagesDir = normalize( getImagesPath() );
51
    final var cachesDir = normalize( getCachesPath() );
52
    final var fontsDir = normalize( getFontsPath() );
50
    final var imagesDir = normalize( getImageDir() );
51
    final var cachesDir = normalize( getCacheDir() );
52
    final var fontsDir = normalize( getFontDir() );
5353
5454
    final var sourceFile = sourcePath.getFileName();
M src/main/java/com/keenwrite/typesetting/HostTypesetter.java
1515
1616
import static com.keenwrite.constants.Constants.DEFAULT_DIRECTORY;
17
import static com.keenwrite.constants.Constants.TEMPORARY_DIRECTORY;
1718
import static com.keenwrite.events.StatusEvent.clue;
1819
import static java.lang.ProcessBuilder.Redirect.DISCARD;
19
import static java.lang.System.getProperty;
2020
import static java.nio.file.Files.*;
2121
import static java.util.Arrays.asList;
...
202202
    @SuppressWarnings( "SpellCheckingInspection" )
203203
    private java.io.File getCacheDir() {
204
      final var temp = getProperty( "java.io.tmpdir" );
205
      final var cache = Path.of( temp, "luatex-cache" );
204
      final var cache = Path.of( TEMPORARY_DIRECTORY, "luatex-cache" );
206205
      return cache.toFile();
207206
    }
M src/main/java/com/keenwrite/typesetting/Typesetter.java
1111
import java.util.concurrent.Callable;
1212
13
import static com.keenwrite.Bootstrap.USER_CACHE_DIR;
14
import static com.keenwrite.constants.Constants.USER_DIRECTORY;
15
import static com.keenwrite.constants.Constants.getFontDirectory;
1316
import static com.keenwrite.events.StatusEvent.clue;
1417
import static com.keenwrite.util.Time.toElapsedTime;
...
3538
    private Path mSourcePath;
3639
    private Path mTargetPath;
37
    private Path mThemesPath;
38
    private Path mImagesPath;
39
    private Path mCachesPath;
40
    private Path mFontsPath;
40
    private Path mThemeDir = USER_DIRECTORY.toPath();
41
    private Path mImageDir = USER_DIRECTORY.toPath();
42
    private Path mCacheDir = USER_CACHE_DIR.toPath();
43
    private Path mFontDir = getFontDirectory().toPath();
4144
    private boolean mAutoRemove;
4245
...
5659
5760
    /**
58
     * @param themePath Fully qualified path to the theme directory, which
59
     *                  ends with the selected theme name.
61
     * @param themeDir Fully qualified path to the theme directory, which
62
     *                 ends with the selected theme name.
6063
     */
61
    public void setThemesPath( final Path themePath ) {
62
      mThemesPath = themePath;
64
    public void setThemeDir( final Path themeDir ) {
65
      mThemeDir = themeDir;
6366
    }
6467
6568
    /**
66
     * @param imagePath Fully qualified path to the "images" directory.
69
     * @param imageDir Fully qualified path to the "images" directory.
6770
     */
68
    public void setImagesPath( final Path imagePath ) {
69
      mImagesPath = imagePath;
71
    public void setImageDir( final Path imageDir ) {
72
      mImageDir = imageDir;
7073
    }
7174
7275
    /**
73
     * @param cachePath Fully qualified path to the "caches" directory.
76
     * @param cacheDir Fully qualified path to the "caches" directory.
7477
     */
75
    public void setCachesPath( final Path cachePath ) {
76
      mCachesPath = cachePath;
78
    public void setCacheDir( final Path cacheDir ) {
79
      mCacheDir = cacheDir;
7780
    }
7881
7982
    /**
80
     * @param fontsPath Fully qualified path to the "fonts" directory.
83
     * @param fontDir Fully qualified path to the "fonts" directory.
8184
     */
82
    public void setFontsPath( final Path fontsPath ) {
83
      mFontsPath = fontsPath;
85
    public void setFontDir( final Path fontDir ) {
86
      mFontDir = fontDir;
8487
    }
8588
...
100103
    }
101104
102
    public Path getThemesPath() {
103
      return mThemesPath;
105
    public Path getThemeDir() {
106
      return mThemeDir;
104107
    }
105108
106
    public Path getImagesPath() {
107
      return mImagesPath;
109
    public Path getImageDir() {
110
      return mImageDir;
108111
    }
109112
110
    public Path getCachesPath() {
111
      return mCachesPath;
113
    public Path getCacheDir() {
114
      return mCacheDir;
112115
    }
113116
114
    public Path getFontsPath() {
115
      return mFontsPath;
117
    public Path getFontDir() {
118
      return mFontDir;
116119
    }
117120
...
167170
    final var sourcePath = getSourcePath().toString();
168171
    final var targetPath = getTargetPath().getFileName();
169
    final var themesPath = getThemesPath();
170
    final var imagesPath = getImagesPath();
171
    final var cachesPath = getCachesPath();
172
    final var themesPath = getThemeDir();
173
    final var imagesPath = getImageDir();
174
    final var cachesPath = getCacheDir();
172175
173176
    args.add(
174177
      format( "--arguments=themesdir=%s,imagesdir=%s,cachesdir=%s",
175
              themesPath, imagesPath, cachesPath  )
178
              themesPath, imagesPath, cachesPath )
176179
    );
177180
    args.add( format( "--path='%s'", themesPath ) );
...
205208
  }
206209
207
  protected Path getThemesPath() {
208
    return mMutator.getThemesPath();
210
  protected Path getThemeDir() {
211
    return mMutator.getThemeDir();
209212
  }
210213
211
  protected Path getImagesPath() {
212
    return mMutator.getImagesPath();
214
  protected Path getImageDir() {
215
    return mMutator.getImageDir();
213216
  }
214217
215
  protected Path getCachesPath() {
216
    return mMutator.getCachesPath();
218
  protected Path getCacheDir() {
219
    return mMutator.getCacheDir();
217220
  }
218221
219
  protected Path getFontsPath() {
220
    return mMutator.getFontsPath();
222
  protected Path getFontDir() {
223
    return mMutator.getFontDir();
221224
  }
222225
M src/main/resources/com/keenwrite/messages.properties
202202
Main.status.typeset.ended.success=Finished typesetting ''{0}'' ({1} elapsed)
203203
Main.status.typeset.ended.failure=Failed to typeset ''{0}'' ({1} elapsed)
204
Main.status.typeset.setting=Set {0} to ''{1}''
204205
205206
Main.status.lexicon.loading=Loading lexicon: {0} words
M src/test/java/com/keenwrite/processors/html/XhtmlProcessorTest.java
4747
      .with( ProcessorContext.Mutator::setLocale, () -> ENGLISH )
4848
      .with( ProcessorContext.Mutator::setMetadata, HashMap::new )
49
      .with( ProcessorContext.Mutator::setThemesDir, () -> Path.of( "b" ) )
49
      .with( ProcessorContext.Mutator::setThemeDir, () -> Path.of( "b" ) )
5050
      .with( ProcessorContext.Mutator::setCaret, () -> caret )
51
      .with( ProcessorContext.Mutator::setImagesDir, () -> new File( "i" ) )
51
      .with( ProcessorContext.Mutator::setImageDir, () -> new File( "i" ) )
5252
      .with( ProcessorContext.Mutator::setImageOrder, () -> "" )
5353
      .with( ProcessorContext.Mutator::setImageServer, () -> "" )
M src/test/java/com/keenwrite/processors/markdown/ImageLinkExtensionTest.java
116116
      .with( ProcessorContext.Mutator::setExportFormat, XHTML_TEX )
117117
      .with( ProcessorContext.Mutator::setCaret, () -> Caret.builder().build() )
118
      .with( ProcessorContext.Mutator::setImagesDir, imagesDir::toFile )
118
      .with( ProcessorContext.Mutator::setImageDir, imagesDir::toFile )
119119
      .build();
120120
  }