Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git
M build.gradle
1717
dependencies {
1818
  compile 'org.controlsfx:controlsfx:8.40.12'
19
  //compile 'org.fxmisc.richtext:richtextfx:0.7-M4'
20
  compile group: 'org.fxmisc.richtext', name: 'richtextfx', version: '1.0.0-SNAPSHOT'
19
  compile group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.8.1'
20
  //compile group: 'org.fxmisc.richtext', name: 'richtextfx', version: '1.0.0-SNAPSHOT'
2121
  compile 'com.miglayout:miglayout-javafx:5.0'
2222
  compile 'de.jensd:fontawesomefx-fontawesome:4.5.0'
...
3535
  compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
3636
  compile 'org.apache.commons:commons-configuration2:2.1'
37
  compile files('libs/renjin-script-engine-0.8.2354-jar-with-dependencies.jar')
37
  compile files('libs/renjin-script-engine-0.8.2514-jar-with-dependencies.jar')
3838
}
3939
40
version = '1.2.9'
40
version = '1.3.0'
4141
applicationName = 'scrivenvar'
4242
mainClassName = 'com.scrivenvar.Main'
D libs/renjin-script-engine-0.8.2354-jar-with-dependencies.jar
Binary file
A libs/renjin-script-engine-0.8.2514-jar-with-dependencies.jar
Binary file
M src/main/java/com/scrivenvar/FileEditorTab.java
3030
import com.scrivenvar.service.events.Notification;
3131
import com.scrivenvar.service.events.Notifier;
32
import java.io.IOException;
3233
import java.nio.charset.Charset;
3334
import static java.nio.charset.StandardCharsets.UTF_8;
...
8182
  private Path path;
8283
83
  FileEditorTab( final Path path ) {
84
  public FileEditorTab( final Path path ) {
8485
    setPath( path );
8586
...
267268
      try {
268269
        getEditorPane().setText( asString( Files.readAllBytes( filePath ) ) );
269
      } catch( final Exception ex ) {
270
        getEditorPane().scrollToTop();
271
      } catch( final IOException ex ) {
270272
        getNotifyService().notify( ex );
271273
      }
...
280282
  public boolean save() {
281283
    try {
282
      Files.write( getPath(), asBytes( getEditorPane().getText() ) );
283
      getEditorPane().getUndoManager().mark();
284
      final EditorPane editor = getEditorPane();
285
      Files.write( getPath(), asBytes( editor.getText() ) );
286
      editor.getUndoManager().mark();
284287
      return true;
285
    } catch( final Exception ex ) {
288
    } catch( final IOException ex ) {
286289
      return alert(
287290
        "FileEditor.saveFailed.title", "FileEditor.saveFailed.message", ex
M src/main/java/com/scrivenvar/definition/yaml/YamlParser.java
3333
import com.fasterxml.jackson.databind.JsonNode;
3434
import com.fasterxml.jackson.databind.ObjectMapper;
35
import com.fasterxml.jackson.databind.node.ObjectNode;
36
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
37
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
38
import com.scrivenvar.decorators.VariableDecorator;
39
import com.scrivenvar.decorators.YamlVariableDecorator;
40
import java.io.IOException;
41
import java.io.InputStream;
42
import java.io.Writer;
43
import java.text.MessageFormat;
44
import java.util.HashMap;
45
import java.util.Map;
46
import java.util.Map.Entry;
47
import java.util.regex.Matcher;
48
import java.util.regex.Pattern;
49
import org.yaml.snakeyaml.DumperOptions;
50
51
/**
52
 * <p>
53
 * This program loads a YAML document into memory, scans for variable
54
 * declarations, then substitutes any self-referential values back into the
55
 * document. Its output is the given YAML document without any variables.
56
 * Variables in the YAML document are denoted using a bracketed dollar symbol
57
 * syntax. For example: $field.name$. Some nomenclature to keep from going
58
 * squirrely, consider:
59
 * </p>
60
 *
61
 * <pre>
62
 *   root:
63
 *     node:
64
 *       name: $field.name$
65
 *   field:
66
 *     name: Alan Turing
67
 * </pre>
68
 *
69
 * The various components of the given YAML are called:
70
 *
71
 * <ul>
72
 * <li><code>$field.name$</code> - delimited reference</li>
73
 * <li><code>field.name</code> - reference</li>
74
 * <li><code>name</code> - YAML field</li>
75
 * <li><code>Alan Turing</code> - (dereferenced) field value</li>
76
 * </ul>
77
 *
78
 * @author White Magic Software, Ltd.
79
 */
80
public class YamlParser {
81
82
  /**
83
   * Separates YAML variable nodes (e.g., the dots in
84
   * <code>$root.node.var$</code>).
85
   */
86
  public static final String SEPARATOR = ".";
87
  public static final char SEPARATOR_CHAR = SEPARATOR.charAt( 0 );
88
89
  private final static int GROUP_DELIMITED = 1;
90
  private final static int GROUP_REFERENCE = 2;
91
92
  private final static VariableDecorator VARIABLE_DECORATOR
93
    = new YamlVariableDecorator();
94
95
  private String error;
96
97
  /**
98
   * Compiled version of DEFAULT_REGEX.
99
   */
100
  private final static Pattern REGEX_PATTERN
101
    = Pattern.compile( YamlVariableDecorator.REGEX );
102
103
  /**
104
   * Should be JsonPointer.SEPARATOR, but Jackson YAML uses magic values.
105
   */
106
  private final static char SEPARATOR_YAML = '/';
107
108
  /**
109
   * Start of the Universe (the YAML document node that contains all others).
110
   */
111
  private JsonNode documentRoot;
112
113
  /**
114
   * Map of references to dereferenced field values.
115
   */
116
  private Map<String, String> references;
117
118
  public YamlParser( final InputStream in ) throws IOException {
119
    process( in );
120
  }
121
122
  /**
123
   * Returns the given string with all the delimited references swapped with
124
   * their recursively resolved values.
125
   *
126
   * @param text The text to parse with zero or more delimited references to
127
   * replace.
128
   *
129
   * @return The substituted value.
130
   */
131
  public String substitute( String text ) {
132
    final Matcher matcher = patternMatch( text );
133
    final Map<String, String> map = getReferences();
134
135
    while( matcher.find() ) {
136
      final String key = matcher.group( GROUP_DELIMITED );
137
      final String value = map.get( key );
138
139
      if( value == null ) {
140
        missing( text );
141
      }
142
      else {
143
        text = text.replace( key, value );
144
      }
145
    }
146
147
    return text;
148
  }
149
150
  /**
151
   * Returns all the strings with their values resolved in a flat hierarchy.
152
   * This copies all the keys and resolved values into a new map.
153
   *
154
   * @return The new map created with all values having been resolved,
155
   * recursively.
156
   */
157
  public Map<String, String> createResolvedMap() {
158
    final Map<String, String> map = new HashMap<>( 1024 );
159
160
    resolve( getDocumentRoot(), "", map );
161
162
    return map;
163
  }
164
165
  /**
166
   * Iterate over a given root node (at any level of the tree) and adapt each
167
   * leaf node.
168
   *
169
   * @param rootNode A JSON node (YAML node) to adapt.
170
   * @param map Container that associates definitions with values.
171
   */
172
  private void resolve(
173
    final JsonNode rootNode,
174
    final String path,
175
    final Map<String, String> map ) {
176
177
    if( rootNode != null ) {
178
      rootNode.fields().forEachRemaining(
179
        (Entry<String, JsonNode> leaf) -> resolve( leaf, path, map )
180
      );
181
    }
182
  }
183
184
  /**
185
   * Recursively adapt each rootNode to a corresponding rootItem.
186
   *
187
   * @param rootNode The node to adapt.
188
   */
189
  private void resolve(
190
    final Entry<String, JsonNode> rootNode,
191
    final String path,
192
    final Map<String, String> map ) {
193
194
    final JsonNode leafNode = rootNode.getValue();
195
    final String key = rootNode.getKey();
196
197
    if( leafNode.isValueNode() ) {
198
      final String value = rootNode.getValue().asText();
199
200
      map.put( VARIABLE_DECORATOR.decorate( path + key ), substitute( value ) );
201
    }
202
203
    if( leafNode.isObject() ) {
204
      resolve( leafNode, path + key + SEPARATOR, map );
205
    }
206
  }
207
208
  /**
209
   * Reads the first document from the given stream of YAML data and returns a
210
   * corresponding object that represents the YAML hierarchy. The calling class
211
   * is responsible for closing the stream. Calling classes should use
212
   * <code>JsonNode.fields()</code> to walk through the YAML tree of fields.
213
   *
214
   * @param in The input stream containing YAML content.
215
   *
216
   * @return An object hierarchy to represent the content.
217
   *
218
   * @throws IOException Could not read the stream.
219
   */
220
  private JsonNode process( final InputStream in ) throws IOException {
221
    final ObjectNode root = (ObjectNode)getObjectMapper().readTree( in );
222
    setDocumentRoot( root );
223
    process( root );
224
    return getDocumentRoot();
225
  }
226
227
  /**
228
   * Iterate over a given root node (at any level of the tree) and process each
229
   * leaf node.
230
   *
231
   * @param root A node to process.
232
   */
233
  private void process( final JsonNode root ) {
234
    root.fields().forEachRemaining( this::process );
235
  }
236
237
  /**
238
   * Process the given field, which is a named node. This is where the
239
   * application does the up-front work of mapping references to their fully
240
   * recursively dereferenced values.
241
   *
242
   * @param field The named node.
243
   */
244
  private void process( final Entry<String, JsonNode> field ) {
245
    final JsonNode node = field.getValue();
246
247
    if( node.isObject() ) {
248
      process( node );
249
    }
250
    else {
251
      final JsonNode fieldValue = field.getValue();
252
253
      // Only basic data types can be parsed into variable values. For
254
      // node structures, YAML has a built-in mechanism.
255
      if( fieldValue.isValueNode() ) {
256
        try {
257
          resolve( fieldValue.asText() );
258
        } catch( StackOverflowError e ) {
259
          setError( "Unresolvable: " + node.textValue() + " = " + fieldValue );
260
        }
261
      }
262
    }
263
  }
264
265
  /**
266
   * Inserts the delimited references and field values into the cache. This will
267
   * overwrite existing references.
268
   *
269
   * @param fieldValue YAML field containing zero or more delimited references.
270
   * If it contains a delimited reference, the parameter is modified with the
271
   * dereferenced value before it is returned.
272
   *
273
   * @return fieldValue without delimited references.
274
   */
275
  private String resolve( String fieldValue ) {
276
    final Matcher matcher = patternMatch( fieldValue );
277
278
    while( matcher.find() ) {
279
      final String delimited = matcher.group( GROUP_DELIMITED );
280
      final String reference = matcher.group( GROUP_REFERENCE );
281
      final String dereference = resolve( lookup( reference ) );
282
283
      fieldValue = fieldValue.replace( delimited, dereference );
284
285
      // This will perform some superfluous calls by overwriting existing
286
      // items in the delimited reference map.
287
      put( delimited, dereference );
288
    }
289
290
    return fieldValue;
291
  }
292
293
  /**
294
   * Inserts a key/value pair into the references map. The map retains
295
   * references and dereferenced values found in the YAML. If the reference
296
   * already exists, this will overwrite with a new value.
297
   *
298
   * @param delimited The variable name.
299
   * @param dereferenced The resolved value.
300
   */
301
  private void put( String delimited, String dereferenced ) {
302
    if( dereferenced.isEmpty() ) {
303
      missing( delimited );
304
    }
305
    else {
306
      getReferences().put( delimited, dereferenced );
307
    }
308
  }
309
310
  /**
311
   * Writes the modified YAML document to standard output.
312
   */
313
  private void writeDocument() throws IOException {
314
    getObjectMapper().writeValue( System.out, getDocumentRoot() );
315
  }
316
317
  /**
318
   * Called when a delimited reference is dereferenced to an empty string. This
319
   * should produce a warning for the user.
320
   *
321
   * @param delimited Delimited reference with no derived value.
322
   */
323
  private void missing( final String delimited ) {
324
    setError( MessageFormat.format( "Missing value for '{0}'.", delimited ) );
325
  }
326
327
  /**
328
   * Returns a REGEX_PATTERN matcher for the given text.
329
   *
330
   * @param text The text that contains zero or more instances of a
331
   * REGEX_PATTERN that can be found using the regular expression.
332
   */
333
  private Matcher patternMatch( String text ) {
334
    return getPattern().matcher( text );
335
  }
336
337
  /**
338
   * Finds the YAML value for a reference.
339
   *
340
   * @param reference References a value in the YAML document.
341
   *
342
   * @return The dereferenced value.
343
   */
344
  private String lookup( final String reference ) {
345
    return getDocumentRoot().at( asPath( reference ) ).asText();
346
  }
347
348
  /**
349
   * Converts a reference (not delimited) to a path that can be used to find a
350
   * value that should exist inside the YAML document.
351
   *
352
   * @param reference The reference to convert to a YAML document path.
353
   *
354
   * @return The reference with a leading slash and its separator characters
355
   * converted to slashes.
356
   */
357
  private String asPath( final String reference ) {
358
    return SEPARATOR_YAML + reference.replace( getDelimitedSeparator(), SEPARATOR_YAML );
359
  }
360
361
  /**
362
   * Sets the parent node for the entire YAML document tree.
363
   *
364
   * @param documentRoot The parent node.
365
   */
366
  private void setDocumentRoot( ObjectNode documentRoot ) {
35
import com.fasterxml.jackson.databind.node.NullNode;
36
import com.fasterxml.jackson.databind.node.ObjectNode;
37
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
38
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
39
import com.scrivenvar.decorators.VariableDecorator;
40
import com.scrivenvar.decorators.YamlVariableDecorator;
41
import java.io.IOException;
42
import java.io.InputStream;
43
import java.io.Writer;
44
import java.text.MessageFormat;
45
import java.util.HashMap;
46
import java.util.Map;
47
import java.util.Map.Entry;
48
import java.util.regex.Matcher;
49
import java.util.regex.Pattern;
50
import org.yaml.snakeyaml.DumperOptions;
51
52
/**
53
 * <p>
54
 * This program loads a YAML document into memory, scans for variable
55
 * declarations, then substitutes any self-referential values back into the
56
 * document. Its output is the given YAML document without any variables.
57
 * Variables in the YAML document are denoted using a bracketed dollar symbol
58
 * syntax. For example: $field.name$. Some nomenclature to keep from going
59
 * squirrely, consider:
60
 * </p>
61
 *
62
 * <pre>
63
 *   root:
64
 *     node:
65
 *       name: $field.name$
66
 *   field:
67
 *     name: Alan Turing
68
 * </pre>
69
 *
70
 * The various components of the given YAML are called:
71
 *
72
 * <ul>
73
 * <li><code>$field.name$</code> - delimited reference</li>
74
 * <li><code>field.name</code> - reference</li>
75
 * <li><code>name</code> - YAML field</li>
76
 * <li><code>Alan Turing</code> - (dereferenced) field value</li>
77
 * </ul>
78
 *
79
 * @author White Magic Software, Ltd.
80
 */
81
public class YamlParser {
82
83
  /**
84
   * Separates YAML variable nodes (e.g., the dots in
85
   * <code>$root.node.var$</code>).
86
   */
87
  public static final String SEPARATOR = ".";
88
  public static final char SEPARATOR_CHAR = SEPARATOR.charAt( 0 );
89
90
  private final static int GROUP_DELIMITED = 1;
91
  private final static int GROUP_REFERENCE = 2;
92
93
  private final static VariableDecorator VARIABLE_DECORATOR
94
    = new YamlVariableDecorator();
95
96
  private String error;
97
98
  /**
99
   * Compiled version of DEFAULT_REGEX.
100
   */
101
  private final static Pattern REGEX_PATTERN
102
    = Pattern.compile( YamlVariableDecorator.REGEX );
103
104
  /**
105
   * Should be JsonPointer.SEPARATOR, but Jackson YAML uses magic values.
106
   */
107
  private final static char SEPARATOR_YAML = '/';
108
109
  /**
110
   * Start of the Universe (the YAML document node that contains all others).
111
   */
112
  private JsonNode documentRoot;
113
114
  /**
115
   * Map of references to dereferenced field values.
116
   */
117
  private Map<String, String> references;
118
119
  public YamlParser( final InputStream in ) throws IOException {
120
    process( in );
121
  }
122
123
  /**
124
   * Returns the given string with all the delimited references swapped with
125
   * their recursively resolved values.
126
   *
127
   * @param text The text to parse with zero or more delimited references to
128
   * replace.
129
   *
130
   * @return The substituted value.
131
   */
132
  public String substitute( String text ) {
133
    final Matcher matcher = patternMatch( text );
134
    final Map<String, String> map = getReferences();
135
136
    while( matcher.find() ) {
137
      final String key = matcher.group( GROUP_DELIMITED );
138
      final String value = map.get( key );
139
140
      if( value == null ) {
141
        missing( text );
142
      }
143
      else {
144
        text = text.replace( key, value );
145
      }
146
    }
147
148
    return text;
149
  }
150
151
  /**
152
   * Returns all the strings with their values resolved in a flat hierarchy.
153
   * This copies all the keys and resolved values into a new map.
154
   *
155
   * @return The new map created with all values having been resolved,
156
   * recursively.
157
   */
158
  public Map<String, String> createResolvedMap() {
159
    final Map<String, String> map = new HashMap<>( 1024 );
160
161
    resolve( getDocumentRoot(), "", map );
162
163
    return map;
164
  }
165
166
  /**
167
   * Iterate over a given root node (at any level of the tree) and adapt each
168
   * leaf node.
169
   *
170
   * @param rootNode A JSON node (YAML node) to adapt.
171
   * @param map Container that associates definitions with values.
172
   */
173
  private void resolve(
174
    final JsonNode rootNode,
175
    final String path,
176
    final Map<String, String> map ) {
177
178
    if( rootNode != null ) {
179
      rootNode.fields().forEachRemaining(
180
        (Entry<String, JsonNode> leaf) -> resolve( leaf, path, map )
181
      );
182
    }
183
  }
184
185
  /**
186
   * Recursively adapt each rootNode to a corresponding rootItem.
187
   *
188
   * @param rootNode The node to adapt.
189
   */
190
  private void resolve(
191
    final Entry<String, JsonNode> rootNode,
192
    final String path,
193
    final Map<String, String> map ) {
194
195
    final JsonNode leafNode = rootNode.getValue();
196
    final String key = rootNode.getKey();
197
198
    
199
    if( leafNode.isValueNode() ) {
200
      final String value;
201
      
202
      if( leafNode instanceof NullNode ) {
203
        value = "";
204
      }
205
      else {
206
        value = rootNode.getValue().asText();
207
      }
208
      
209
      map.put( VARIABLE_DECORATOR.decorate( path + key ), substitute( value ) );
210
    }
211
212
    if( leafNode.isObject() ) {
213
      resolve( leafNode, path + key + SEPARATOR, map );
214
    }
215
  }
216
217
  /**
218
   * Reads the first document from the given stream of YAML data and returns a
219
   * corresponding object that represents the YAML hierarchy. The calling class
220
   * is responsible for closing the stream. Calling classes should use
221
   * <code>JsonNode.fields()</code> to walk through the YAML tree of fields.
222
   *
223
   * @param in The input stream containing YAML content.
224
   *
225
   * @return An object hierarchy to represent the content.
226
   *
227
   * @throws IOException Could not read the stream.
228
   */
229
  private JsonNode process( final InputStream in ) throws IOException {
230
    final ObjectNode root = (ObjectNode)getObjectMapper().readTree( in );
231
    setDocumentRoot( root );
232
    process( root );
233
    return getDocumentRoot();
234
  }
235
236
  /**
237
   * Iterate over a given root node (at any level of the tree) and process each
238
   * leaf node.
239
   *
240
   * @param root A node to process.
241
   */
242
  private void process( final JsonNode root ) {
243
    root.fields().forEachRemaining( this::process );
244
  }
245
246
  /**
247
   * Process the given field, which is a named node. This is where the
248
   * application does the up-front work of mapping references to their fully
249
   * recursively dereferenced values.
250
   *
251
   * @param field The named node.
252
   */
253
  private void process( final Entry<String, JsonNode> field ) {
254
    final JsonNode node = field.getValue();
255
256
    if( node.isObject() ) {
257
      process( node );
258
    }
259
    else {
260
      final JsonNode fieldValue = field.getValue();
261
262
      // Only basic data types can be parsed into variable values. For
263
      // node structures, YAML has a built-in mechanism.
264
      if( fieldValue.isValueNode() ) {
265
        try {
266
          resolve( fieldValue.asText() );
267
        } catch( StackOverflowError e ) {
268
          setError( "Unresolvable: " + node.textValue() + " = " + fieldValue );
269
        }
270
      }
271
    }
272
  }
273
274
  /**
275
   * Inserts the delimited references and field values into the cache. This will
276
   * overwrite existing references.
277
   *
278
   * @param fieldValue YAML field containing zero or more delimited references.
279
   * If it contains a delimited reference, the parameter is modified with the
280
   * dereferenced value before it is returned.
281
   *
282
   * @return fieldValue without delimited references.
283
   */
284
  private String resolve( String fieldValue ) {
285
    final Matcher matcher = patternMatch( fieldValue );
286
287
    while( matcher.find() ) {
288
      final String delimited = matcher.group( GROUP_DELIMITED );
289
      final String reference = matcher.group( GROUP_REFERENCE );
290
      final String dereference = resolve( lookup( reference ) );
291
292
      fieldValue = fieldValue.replace( delimited, dereference );
293
294
      // This will perform some superfluous calls by overwriting existing
295
      // items in the delimited reference map.
296
      put( delimited, dereference );
297
    }
298
299
    return fieldValue;
300
  }
301
302
  /**
303
   * Inserts a key/value pair into the references map. The map retains
304
   * references and dereferenced values found in the YAML. If the reference
305
   * already exists, this will overwrite with a new value.
306
   *
307
   * @param delimited The variable name.
308
   * @param dereferenced The resolved value.
309
   */
310
  private void put( String delimited, String dereferenced ) {
311
    if( dereferenced.isEmpty() ) {
312
      missing( delimited );
313
    }
314
    else {
315
      getReferences().put( delimited, dereferenced );
316
    }
317
  }
318
319
  /**
320
   * Writes the modified YAML document to standard output.
321
   */
322
  private void writeDocument() throws IOException {
323
    getObjectMapper().writeValue( System.out, getDocumentRoot() );
324
  }
325
326
  /**
327
   * Called when a delimited reference is dereferenced to an empty string. This
328
   * should produce a warning for the user.
329
   *
330
   * @param delimited Delimited reference with no derived value.
331
   */
332
  private void missing( final String delimited ) {
333
    setError( MessageFormat.format( "Missing value for '{0}'.", delimited ) );
334
  }
335
336
  /**
337
   * Returns a REGEX_PATTERN matcher for the given text.
338
   *
339
   * @param text The text that contains zero or more instances of a
340
   * REGEX_PATTERN that can be found using the regular expression.
341
   */
342
  private Matcher patternMatch( String text ) {
343
    return getPattern().matcher( text );
344
  }
345
346
  /**
347
   * Finds the YAML value for a reference.
348
   *
349
   * @param reference References a value in the YAML document.
350
   *
351
   * @return The dereferenced value.
352
   */
353
  private String lookup( final String reference ) {
354
    return getDocumentRoot().at( asPath( reference ) ).asText();
355
  }
356
357
  /**
358
   * Converts a reference (not delimited) to a path that can be used to find a
359
   * value that should exist inside the YAML document.
360
   *
361
   * @param reference The reference to convert to a YAML document path.
362
   *
363
   * @return The reference with a leading slash and its separator characters
364
   * converted to slashes.
365
   */
366
  private String asPath( final String reference ) {
367
    return SEPARATOR_YAML + reference.replace( getDelimitedSeparator(), SEPARATOR_YAML );
368
  }
369
370
  /**
371
   * Sets the parent node for the entire YAML document tree.
372
   *
373
   * @param documentRoot The parent node.
374
   */
375
  private void setDocumentRoot( final ObjectNode documentRoot ) {
367376
    this.documentRoot = documentRoot;
368377
  }
A src/main/java/com/scrivenvar/definition/yaml/resolvers/ResolverYAMLFactory.java
1
/*
2
 * The MIT License
3
 *
4
 * Copyright 2017 White Magic Software, Ltd..
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
package com.scrivenvar.definition.yaml.resolvers;
25
26
import com.fasterxml.jackson.core.io.IOContext;
27
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
28
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
29
import com.scrivenvar.definition.yaml.YamlParser;
30
import java.io.IOException;
31
import java.io.Writer;
32
33
34
/**
35
 *
36
 * @author White Magic Software, Ltd.
37
 */
38
public final class ResolverYAMLFactory extends YAMLFactory {
39
40
  private static final long serialVersionUID = 1L;
41
42
  private YamlParser yamlParser;
43
44
  public ResolverYAMLFactory( final YamlParser yamlParser ) {
45
    setYamlParser( yamlParser );
46
  }
47
48
  @Override
49
  protected YAMLGenerator _createGenerator(
50
    final Writer out, final IOContext ctxt ) throws IOException {
51
52
    return new ResolverYAMLGenerator(
53
      getYamlParser(),
54
      ctxt, _generatorFeatures, _yamlGeneratorFeatures, _objectCodec,
55
      out, _version );
56
  }
57
58
  /**
59
   * Returns the YAML parser used when constructing this instance.
60
   * 
61
   * @return A non-null instance.
62
   */
63
  private YamlParser getYamlParser() {
64
    return this.yamlParser;
65
  }
66
67
  /**
68
   * Sets the YAML parser used when constructing this instance.
69
   * 
70
   * @param yamlParser A non-null instance.
71
   */
72
  private void setYamlParser( final YamlParser yamlParser ) {
73
    this.yamlParser = yamlParser;
74
  }
75
}
176
A src/main/java/com/scrivenvar/definition/yaml/resolvers/ResolverYAMLGenerator.java
1
/*
2
 * The MIT License
3
 *
4
 * Copyright 2017 White Magic Software, Ltd..
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
package com.scrivenvar.definition.yaml.resolvers;
25
26
import com.fasterxml.jackson.core.JsonGenerationException;
27
import com.fasterxml.jackson.core.ObjectCodec;
28
import com.fasterxml.jackson.core.io.IOContext;
29
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
30
import com.scrivenvar.definition.yaml.YamlParser;
31
import java.io.IOException;
32
import java.io.Writer;
33
import org.yaml.snakeyaml.DumperOptions;
34
35
/**
36
 *
37
 * @author White Magic Software, Ltd.
38
 */
39
/**
40
 * Intercepts the string writing functionality to resolve the definition
41
 * value.
42
 */
43
public class ResolverYAMLGenerator extends YAMLGenerator {
44
45
  private YamlParser yamlParser;
46
47
  public ResolverYAMLGenerator(
48
    final YamlParser yamlParser,
49
    final IOContext ctxt,
50
    final int jsonFeatures,
51
    final int yamlFeatures,
52
    final ObjectCodec codec,
53
    final Writer out,
54
    final DumperOptions.Version version ) throws IOException {
55
    super( ctxt, jsonFeatures, yamlFeatures, codec, out, version );
56
    setYamlParser( yamlParser );
57
  }
58
59
  @Override
60
  public void writeString( final String text )
61
    throws IOException, JsonGenerationException {
62
    final YamlParser parser = getYamlParser();
63
    super.writeString( parser.substitute( text ) );
64
  }
65
66
  private YamlParser getYamlParser() {
67
    return yamlParser;
68
  }
69
70
  private void setYamlParser( final YamlParser yamlParser ) {
71
    this.yamlParser = yamlParser;
72
  }
73
}
174
M src/main/java/com/scrivenvar/editors/EditorPane.java
5252
 */
5353
public class EditorPane extends AbstractPane {
54
54
  
5555
  private StyleClassedTextArea editor;
5656
  private VirtualizedScrollPane<StyleClassedTextArea> scrollPane;
5757
  private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
5858
5959
  /**
6060
   * Set when entering variable edit mode; retrieved upon exiting.
6161
   */
6262
  private InputMap<InputEvent> nodeMap;
63
63
  
6464
  @Override
6565
  public void requestFocus() {
6666
    Platform.runLater( () -> getEditor().requestFocus() );
6767
  }
68
68
  
6969
  public void undo() {
7070
    getUndoManager().undo();
7171
  }
72
72
  
7373
  public void redo() {
7474
    getUndoManager().redo();
...
8686
  public void findPrevious() {
8787
  }
88
88
  
8989
  public UndoManager getUndoManager() {
9090
    return getEditor().getUndoManager();
9191
  }
92
92
  
9393
  public String getText() {
9494
    return getEditor().getText();
9595
  }
96
96
  
9797
  public void setText( final String text ) {
9898
    getEditor().deselect();
...
106106
   * @param listener Receives editor text change events.
107107
   */
108
  public void addTextChangeListener( final ChangeListener<? super String> listener ) {
108
  public void addTextChangeListener(
109
    final ChangeListener<? super String> listener ) {
109110
    getEditor().textProperty().addListener( listener );
110111
  }
...
175176
    return "org.fxmisc.wellbehaved.event.inputmap";
176177
  }
177
178
  
179
  /**
180
   * Repositions the cursor and scroll bar to the top of the file.
181
   */
178182
  public void scrollToTop() {
179183
    getEditor().moveTo( 0 );
184
    getScrollPane().scrollYToPixel( 0 );
180185
  }
181
182
  private void setEditor( StyleClassedTextArea textArea ) {
186
  
187
  private void setEditor( final StyleClassedTextArea textArea ) {
183188
    this.editor = textArea;
184189
  }
185
190
  
186191
  public synchronized StyleClassedTextArea getEditor() {
187192
    if( this.editor == null ) {
188193
      setEditor( createTextArea() );
189194
    }
190
195
    
191196
    return this.editor;
192197
  }
...
201206
      this.scrollPane = createScrollPane();
202207
    }
203
208
    
204209
    return this.scrollPane;
205210
  }
206
211
  
207212
  protected VirtualizedScrollPane<StyleClassedTextArea> createScrollPane() {
208213
    final VirtualizedScrollPane<StyleClassedTextArea> pane
209214
      = new VirtualizedScrollPane<>( getEditor() );
210215
    pane.setVbarPolicy( ScrollPane.ScrollBarPolicy.ALWAYS );
211
216
    
212217
    return pane;
213218
  }
214
219
  
215220
  protected StyleClassedTextArea createTextArea() {
216221
    return new StyleClassedTextArea( false );
217222
  }
218
223
  
219224
  public Path getPath() {
220225
    return this.path.get();
221226
  }
222
227
  
223228
  public void setPath( final Path path ) {
224229
    this.path.set( path );
225230
  }
226
231
  
227232
  public ObjectProperty<Path> pathProperty() {
228233
    return this.path;