Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Migrate UI-specific code out of definition source into defintion pane

AuthorDaveJarvis <email>
Date2020-06-02 20:11:33 GMT-0700
Commit564bf6caecfff19ad2641c3b27eba8cfa6394388
Parentf5ae4f9
Delta517 lines added, 467 lines removed, 50-line increase
src/main/java/com/scrivenvar/definition/yaml/YamlFileDefinitionSource.java
import com.scrivenvar.definition.FileDefinitionSource;
-import javafx.scene.control.TreeView;
+import com.scrivenvar.definition.TreeAdapter;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
-
-import static com.scrivenvar.Messages.get;
/**
* Represents a definition data source for YAML files.
*
* @author White Magic Software, Ltd.
*/
public class YamlFileDefinitionSource extends FileDefinitionSource {
- private final YamlTreeAdapter mYamlTreeAdapter;
private final YamlParser mYamlParser;
+ private final YamlTreeAdapter mYamlTreeAdapter;
/**
* Constructs a new YAML definition source, populated from the given file.
*
* @param path Path to the YAML definition file.
*/
public YamlFileDefinitionSource( final Path path ) {
super( path );
-
mYamlParser = createYamlParser( path );
- mYamlTreeAdapter = createYamlTreeAdapter( mYamlParser );
+ mYamlTreeAdapter = createTreeAdapter( mYamlParser );
}
@Override
- public Map<String, String> getResolvedMap() {
- return getYamlParser().createResolvedMap();
+ public TreeAdapter getTreeAdapter() {
+ return mYamlTreeAdapter;
}
- private YamlTreeAdapter getYamlTreeAdapter() {
- return mYamlTreeAdapter;
+ @Override
+ public Map<String, String> getResolvedMap() {
+ return getYamlParser().createResolvedMap();
}
- private YamlParser getYamlParser() {
- return mYamlParser;
+ @Override
+ public String getError() {
+ return getYamlParser().getError();
}
private YamlParser createYamlParser( final Path path ) {
try( final InputStream in = Files.newInputStream( path ) ) {
return new YamlParser( in );
} catch( final Exception ex ) {
throw new RuntimeException( ex );
}
- }
-
- private YamlTreeAdapter createYamlTreeAdapter( final YamlParser yamlParser ) {
- return new YamlTreeAdapter( yamlParser );
}
- @Override
- protected TreeView<String> createTreeView() {
- return getYamlTreeAdapter().adapt(
- get( "Pane.definition.node.root.title" )
- );
+ private YamlParser getYamlParser() {
+ return mYamlParser;
}
- @Override
- public String getError() {
- return getYamlParser().getError();
+ private YamlTreeAdapter createTreeAdapter( final YamlParser parser ) {
+ return new YamlTreeAdapter( parser );
}
}
src/main/java/com/scrivenvar/definition/yaml/YamlParser.java
import com.scrivenvar.decorators.VariableDecorator;
import com.scrivenvar.decorators.YamlVariableDecorator;
-import org.yaml.snakeyaml.DumperOptions;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * <p>
- * This program loads a YAML document into memory, scans for variable
- * declarations, then substitutes any self-referential values back into the
- * document. Its output is the given YAML document without any variables.
- * Variables in the YAML document are denoted using a bracketed dollar symbol
- * syntax. For example: $field.name$. Some nomenclature to keep from going
- * squirrely, consider:
- * </p>
- *
- * <pre>
- * root:
- * node:
- * name: $field.name$
- * field:
- * name: Alan Turing
- * </pre>
- * <p>
- * The various components of the given YAML are called:
- *
- * <ul>
- * <li><code>$field.name$</code> - delimited reference</li>
- * <li><code>field.name</code> - reference</li>
- * <li><code>name</code> - YAML field</li>
- * <li><code>Alan Turing</code> - (dereferenced) field value</li>
- * </ul>
- *
- * @author White Magic Software, Ltd.
- */
-public class YamlParser {
-
- /**
- * Separates YAML variable nodes (e.g., the dots in
- * <code>$root.node.var$</code>).
- */
- public static final String SEPARATOR = ".";
-
- private final static int GROUP_DELIMITED = 1;
- private final static int GROUP_REFERENCE = 2;
-
- private final static VariableDecorator VARIABLE_DECORATOR
- = new YamlVariableDecorator();
-
- private String mError;
-
- /**
- * Compiled version of DEFAULT_REGEX.
- */
- private final static Pattern REGEX_PATTERN
- = Pattern.compile( YamlVariableDecorator.REGEX );
-
- /**
- * Should be JsonPointer.SEPARATOR, but Jackson YAML uses magic values.
- */
- private final static char SEPARATOR_YAML = '/';
-
- /**
- * Start of the Universe (the YAML document node that contains all others).
- */
- private JsonNode documentRoot;
-
- /**
- * Map of references to dereferenced field values.
- */
- private Map<String, String> references;
-
- public YamlParser( final InputStream in ) {
- process( in );
- }
-
- /**
- * Returns the given string with all the delimited references swapped with
- * their recursively resolved values.
- *
- * @param text The text to parse with zero or more delimited references to
- * replace.
- * @return The substituted value.
- */
- public String substitute( String text ) {
- final Matcher matcher = patternMatch( text );
- final Map<String, String> map = getReferences();
-
- while( matcher.find() ) {
- final String key = matcher.group( GROUP_DELIMITED );
- final String value = map.get( key );
-
- if( value == null ) {
- missing( text );
- }
- else {
- text = text.replace( key, value );
- }
- }
-
- return text;
- }
-
- /**
- * Returns all the strings with their values resolved in a flat hierarchy.
- * This copies all the keys and resolved values into a new map.
- *
- * @return The new map created with all values having been resolved,
- * recursively.
- */
- public Map<String, String> createResolvedMap() {
- final Map<String, String> map = new HashMap<>( 1024 );
-
- resolve( getDocumentRoot(), "", map );
-
- return map;
- }
-
- /**
- * Iterate over a given root node (at any level of the tree) and adapt each
- * leaf node.
- *
- * @param rootNode A JSON node (YAML node) to adapt.
- * @param map Container that associates definitions with values.
- */
- private void resolve(
- final JsonNode rootNode,
- final String path,
- final Map<String, String> map ) {
-
- if( rootNode != null ) {
- rootNode.fields().forEachRemaining(
- ( Entry<String, JsonNode> leaf ) -> resolve( leaf, path, map )
- );
- }
- }
-
- /**
- * Recursively adapt each rootNode to a corresponding rootItem.
- *
- * @param rootNode The node to adapt.
- */
- private void resolve(
- final Entry<String, JsonNode> rootNode,
- final String path,
- final Map<String, String> map ) {
-
- final JsonNode leafNode = rootNode.getValue();
- final String key = rootNode.getKey();
-
-
- if( leafNode.isValueNode() ) {
- final String value;
-
- if( leafNode instanceof NullNode ) {
- value = "";
- }
- else {
- value = rootNode.getValue().asText();
- }
-
- map.put( VARIABLE_DECORATOR.decorate( path + key ), substitute( value ) );
- }
-
- if( leafNode.isObject() ) {
- resolve( leafNode, path + key + SEPARATOR, map );
- }
- }
-
- /**
- * Reads the first document from the given stream of YAML data and returns a
- * corresponding object that represents the YAML hierarchy. The calling class
- * is responsible for closing the stream. Calling classes should use
- * <code>JsonNode.fields()</code> to walk through the YAML tree of fields.
- *
- * @param in The input stream containing YAML content.
- */
- private void process( final InputStream in ) {
- setError( Messages.get( "Main.statusbar.state.default" ) );
-
- try {
- final ObjectNode root = (ObjectNode) getObjectMapper().readTree( in );
- setDocumentRoot( root );
- process( root );
- } catch( final Exception e ) {
- setDocumentRoot( new ObjectMapper().createObjectNode() );
- setError( Messages.get( "yaml.error.open" ) );
- }
- }
-
- /**
- * Iterate over a given root node (at any level of the tree) and process each
- * leaf node.
- *
- * @param root A node to process.
- */
- private void process( final JsonNode root ) {
- root.fields().forEachRemaining( this::process );
- }
-
- /**
- * Process the given field, which is a named node. This is where the
- * application does the up-front work of mapping references to their fully
- * recursively dereferenced values.
- *
- * @param field The named node.
- */
- private void process( final Entry<String, JsonNode> field ) {
- final JsonNode node = field.getValue();
-
- if( node.isObject() ) {
- process( node );
- }
- else {
- final JsonNode fieldValue = field.getValue();
-
- // Only basic data types can be parsed into variable values. For
- // node structures, YAML has a built-in mechanism.
- if( fieldValue.isValueNode() ) {
- try {
- resolve( fieldValue.asText() );
- } catch( StackOverflowError e ) {
- final String msg = Messages.get(
- "yaml.error.unresolvable", node.textValue(), fieldValue );
- setError( msg );
- }
- }
- }
- }
-
- /**
- * Inserts the delimited references and field values into the cache. This will
- * overwrite existing references.
- *
- * @param fieldValue YAML field containing zero or more delimited references.
- * If it contains a delimited reference, the parameter is
- * modified with the
- * dereferenced value before it is returned.
- * @return fieldValue without delimited references.
- */
- private String resolve( String fieldValue ) {
- final Matcher matcher = patternMatch( fieldValue );
-
- while( matcher.find() ) {
- final String delimited = matcher.group( GROUP_DELIMITED );
- final String reference = matcher.group( GROUP_REFERENCE );
- final String dereference = resolve( lookup( reference ) );
-
- fieldValue = fieldValue.replace( delimited, dereference );
-
- // This will perform some superfluous calls by overwriting existing
- // items in the delimited reference map.
- put( delimited, dereference );
- }
-
- return fieldValue;
- }
-
- /**
- * Inserts a key/value pair into the references map. The map retains
- * references and dereferenced values found in the YAML. If the reference
- * already exists, this will overwrite with a new value.
- *
- * @param delimited The variable name.
- * @param dereferenced The resolved value.
- */
- private void put( String delimited, String dereferenced ) {
- if( dereferenced.isEmpty() ) {
- missing( delimited );
- }
- else {
- getReferences().put( delimited, dereferenced );
- }
- }
-
- /**
- * Writes the modified YAML document to standard output.
- */
- @SuppressWarnings("unused")
- private void writeDocument() throws IOException {
- getObjectMapper().writeValue( System.out, getDocumentRoot() );
- }
-
- /**
- * Called when a delimited reference is dereferenced to an empty string. This
- * should produce a warning for the user.
- *
- * @param delimited Delimited reference with no derived value.
- */
- private void missing( final String delimited ) {
- setError( Messages.get( "yaml.error.missing", delimited ) );
- }
-
- /**
- * Returns a REGEX_PATTERN matcher for the given text.
- *
- * @param text The text that contains zero or more instances of a
- * REGEX_PATTERN that can be found using the regular expression.
- */
- private Matcher patternMatch( String text ) {
- return getPattern().matcher( text );
- }
-
- /**
- * Finds the YAML value for a reference.
- *
- * @param reference References a value in the YAML document.
- * @return The dereferenced value.
- */
- private String lookup( final String reference ) {
- return getDocumentRoot().at( asPath( reference ) ).asText();
- }
-
- /**
- * Converts a reference (not delimited) to a path that can be used to find a
- * value that should exist inside the YAML document.
- *
- * @param reference The reference to convert to a YAML document path.
- * @return The reference with a leading slash and its separator characters
- * converted to slashes.
- */
- private String asPath( final String reference ) {
- return SEPARATOR_YAML + reference.replace( getDelimitedSeparator(),
- SEPARATOR_YAML );
- }
-
- /**
- * Sets the parent node for the entire YAML document tree.
- *
- * @param documentRoot The parent node.
- */
- private void setDocumentRoot( final ObjectNode documentRoot ) {
- this.documentRoot = documentRoot;
- }
-
- /**
- * Returns the parent node for the entire YAML document tree.
- *
- * @return The parent node.
- */
- protected JsonNode getDocumentRoot() {
- return this.documentRoot;
- }
-
- /**
- * Returns the compiled regular expression REGEX_PATTERN used to match
- * delimited references.
- *
- * @return A compiled regex for use with the Matcher.
- */
- private Pattern getPattern() {
- return REGEX_PATTERN;
- }
-
- /**
- * @return The list of references mapped to dereferenced values.
- */
- private Map<String, String> getReferences() {
- if( this.references == null ) {
- this.references = createReferences();
- }
-
- return this.references;
- }
-
- /**
- * Subclasses can override this method to insert their own map.
- *
- * @return An empty HashMap, never null.
- */
- protected Map<String, String> createReferences() {
- return new HashMap<>();
- }
-
- private final class ResolverYAMLFactory extends YAMLFactory {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected YAMLGenerator _createGenerator(
- final Writer out, final IOContext ctxt ) throws IOException {
-
- return new ResolverYAMLGenerator(
- ctxt, _generatorFeatures, _yamlGeneratorFeatures, _objectCodec,
- out, _version );
- }
- }
-
- private class ResolverYAMLGenerator extends YAMLGenerator {
-
- public ResolverYAMLGenerator(
- final IOContext ctxt,
- final int jsonFeatures,
- final int yamlFeatures,
- final ObjectCodec codec,
- final Writer out,
- final DumperOptions.Version version ) throws IOException {
-
- super( ctxt, jsonFeatures, yamlFeatures, codec, out, version );
- }
-
- @Override
- public void writeString( final String text ) throws IOException {
- super.writeString( substitute( text ) );
- }
- }
-
- private YAMLFactory getYAMLFactory() {
- return new ResolverYAMLFactory();
- }
-
- private ObjectMapper getObjectMapper() {
- return new ObjectMapper( getYAMLFactory() );
+import com.scrivenvar.definition.DocumentParser;
+import org.yaml.snakeyaml.DumperOptions;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * This program loads a YAML document into memory, scans for variable
+ * declarations, then substitutes any self-referential values back into the
+ * document. Its output is the given YAML document without any variables.
+ * Variables in the YAML document are denoted using a bracketed dollar symbol
+ * syntax. For example: $field.name$. Some nomenclature to keep from going
+ * squirrely, consider:
+ * </p>
+ *
+ * <pre>
+ * root:
+ * node:
+ * name: $field.name$
+ * field:
+ * name: Alan Turing
+ * </pre>
+ * <p>
+ * The various components of the given YAML are called:
+ *
+ * <ul>
+ * <li><code>$field.name$</code> - delimited reference</li>
+ * <li><code>field.name</code> - reference</li>
+ * <li><code>name</code> - YAML field</li>
+ * <li><code>Alan Turing</code> - (dereferenced) field value</li>
+ * </ul>
+ *
+ * @author White Magic Software, Ltd.
+ */
+public class YamlParser implements DocumentParser<JsonNode> {
+
+ /**
+ * Separates YAML variable nodes (e.g., the dots in
+ * <code>$root.node.var$</code>).
+ */
+ public static final String SEPARATOR = ".";
+
+ private final static int GROUP_DELIMITED = 1;
+ private final static int GROUP_REFERENCE = 2;
+
+ private final static VariableDecorator VARIABLE_DECORATOR
+ = new YamlVariableDecorator();
+
+ private String mError;
+
+ /**
+ * Compiled version of DEFAULT_REGEX.
+ */
+ private final static Pattern REGEX_PATTERN
+ = Pattern.compile( YamlVariableDecorator.REGEX );
+
+ /**
+ * Should be JsonPointer.SEPARATOR, but Jackson YAML uses magic values.
+ */
+ private final static char SEPARATOR_YAML = '/';
+
+ /**
+ * Start of the Universe (the YAML document node that contains all others).
+ */
+ private JsonNode documentRoot;
+
+ /**
+ * Map of references to dereferenced field values.
+ */
+ private Map<String, String> references;
+
+ public YamlParser( final InputStream in ) {
+ process( in );
+ }
+
+ /**
+ * Returns the given string with all the delimited references swapped with
+ * their recursively resolved values.
+ *
+ * @param text The text to parse with zero or more delimited references to
+ * replace.
+ * @return The substituted value.
+ */
+ public String substitute( String text ) {
+ final Matcher matcher = patternMatch( text );
+ final Map<String, String> map = getReferences();
+
+ while( matcher.find() ) {
+ final String key = matcher.group( GROUP_DELIMITED );
+ final String value = map.get( key );
+
+ if( value == null ) {
+ missing( text );
+ }
+ else {
+ text = text.replace( key, value );
+ }
+ }
+
+ return text;
+ }
+
+ /**
+ * Returns all the strings with their values resolved in a flat hierarchy.
+ * This copies all the keys and resolved values into a new map.
+ *
+ * @return The new map created with all values having been resolved,
+ * recursively.
+ */
+ public Map<String, String> createResolvedMap() {
+ final Map<String, String> map = new HashMap<>( 1024 );
+
+ resolve( parse(), "", map );
+
+ return map;
+ }
+
+ /**
+ * Iterate over a given root node (at any level of the tree) and adapt each
+ * leaf node.
+ *
+ * @param rootNode A JSON node (YAML node) to adapt.
+ * @param map Container that associates definitions with values.
+ */
+ private void resolve(
+ final JsonNode rootNode,
+ final String path,
+ final Map<String, String> map ) {
+
+ if( rootNode != null ) {
+ rootNode.fields().forEachRemaining(
+ ( Entry<String, JsonNode> leaf ) -> resolve( leaf, path, map )
+ );
+ }
+ }
+
+ /**
+ * Recursively adapt each rootNode to a corresponding rootItem.
+ *
+ * @param rootNode The node to adapt.
+ */
+ private void resolve(
+ final Entry<String, JsonNode> rootNode,
+ final String path,
+ final Map<String, String> map ) {
+
+ final JsonNode leafNode = rootNode.getValue();
+ final String key = rootNode.getKey();
+
+
+ if( leafNode.isValueNode() ) {
+ final String value;
+
+ if( leafNode instanceof NullNode ) {
+ value = "";
+ }
+ else {
+ value = rootNode.getValue().asText();
+ }
+
+ map.put( VARIABLE_DECORATOR.decorate( path + key ), substitute( value ) );
+ }
+
+ if( leafNode.isObject() ) {
+ resolve( leafNode, path + key + SEPARATOR, map );
+ }
+ }
+
+ /**
+ * Reads the first document from the given stream of YAML data and returns a
+ * corresponding object that represents the YAML hierarchy. The calling class
+ * is responsible for closing the stream. Calling classes should use
+ * <code>JsonNode.fields()</code> to walk through the YAML tree of fields.
+ *
+ * @param in The input stream containing YAML content.
+ */
+ private void process( final InputStream in ) {
+ setError( Messages.get( "Main.statusbar.state.default" ) );
+
+ try {
+ final ObjectNode root = (ObjectNode) getObjectMapper().readTree( in );
+ setDocumentRoot( root );
+ process( root );
+ } catch( final Exception e ) {
+ setDocumentRoot( new ObjectMapper().createObjectNode() );
+ setError( Messages.get( "yaml.error.open" ) );
+ }
+ }
+
+ /**
+ * Iterate over a given root node (at any level of the tree) and process each
+ * leaf node.
+ *
+ * @param root A node to process.
+ */
+ private void process( final JsonNode root ) {
+ root.fields().forEachRemaining( this::process );
+ }
+
+ /**
+ * Process the given field, which is a named node. This is where the
+ * application does the up-front work of mapping references to their fully
+ * recursively dereferenced values.
+ *
+ * @param field The named node.
+ */
+ private void process( final Entry<String, JsonNode> field ) {
+ final JsonNode node = field.getValue();
+
+ if( node.isObject() ) {
+ process( node );
+ }
+ else {
+ final JsonNode fieldValue = field.getValue();
+
+ // Only basic data types can be parsed into variable values. For
+ // node structures, YAML has a built-in mechanism.
+ if( fieldValue.isValueNode() ) {
+ try {
+ resolve( fieldValue.asText() );
+ } catch( StackOverflowError e ) {
+ final String msg = Messages.get(
+ "yaml.error.unresolvable", node.textValue(), fieldValue );
+ setError( msg );
+ }
+ }
+ }
+ }
+
+ /**
+ * Inserts the delimited references and field values into the cache. This will
+ * overwrite existing references.
+ *
+ * @param fieldValue YAML field containing zero or more delimited references.
+ * If it contains a delimited reference, the parameter is
+ * modified with the
+ * dereferenced value before it is returned.
+ * @return fieldValue without delimited references.
+ */
+ private String resolve( String fieldValue ) {
+ final Matcher matcher = patternMatch( fieldValue );
+
+ while( matcher.find() ) {
+ final String delimited = matcher.group( GROUP_DELIMITED );
+ final String reference = matcher.group( GROUP_REFERENCE );
+ final String dereference = resolve( lookup( reference ) );
+
+ fieldValue = fieldValue.replace( delimited, dereference );
+
+ // This will perform some superfluous calls by overwriting existing
+ // items in the delimited reference map.
+ put( delimited, dereference );
+ }
+
+ return fieldValue;
+ }
+
+ /**
+ * Inserts a key/value pair into the references map. The map retains
+ * references and dereferenced values found in the YAML. If the reference
+ * already exists, this will overwrite with a new value.
+ *
+ * @param delimited The variable name.
+ * @param dereferenced The resolved value.
+ */
+ private void put( String delimited, String dereferenced ) {
+ if( dereferenced.isEmpty() ) {
+ missing( delimited );
+ }
+ else {
+ getReferences().put( delimited, dereferenced );
+ }
+ }
+
+ /**
+ * Writes the modified YAML document to standard output.
+ */
+ @SuppressWarnings("unused")
+ private void writeDocument() throws IOException {
+ getObjectMapper().writeValue( System.out, parse() );
+ }
+
+ /**
+ * Called when a delimited reference is dereferenced to an empty string. This
+ * should produce a warning for the user.
+ *
+ * @param delimited Delimited reference with no derived value.
+ */
+ private void missing( final String delimited ) {
+ setError( Messages.get( "yaml.error.missing", delimited ) );
+ }
+
+ /**
+ * Returns a REGEX_PATTERN matcher for the given text.
+ *
+ * @param text The text that contains zero or more instances of a
+ * REGEX_PATTERN that can be found using the regular expression.
+ */
+ private Matcher patternMatch( String text ) {
+ return getPattern().matcher( text );
+ }
+
+ /**
+ * Finds the YAML value for a reference.
+ *
+ * @param reference References a value in the YAML document.
+ * @return The dereferenced value.
+ */
+ private String lookup( final String reference ) {
+ return parse().at( asPath( reference ) ).asText();
+ }
+
+ /**
+ * Converts a reference (not delimited) to a path that can be used to find a
+ * value that should exist inside the YAML document.
+ *
+ * @param reference The reference to convert to a YAML document path.
+ * @return The reference with a leading slash and its separator characters
+ * converted to slashes.
+ */
+ private String asPath( final String reference ) {
+ return SEPARATOR_YAML + reference.replace(
+ getDelimitedSeparator(), SEPARATOR_YAML );
+ }
+
+ /**
+ * Sets the parent node for the entire YAML document tree.
+ *
+ * @param documentRoot The parent node.
+ */
+ private void setDocumentRoot( final ObjectNode documentRoot ) {
+ this.documentRoot = documentRoot;
+ }
+
+ /**
+ * Returns the parent node for the entire YAML document tree.
+ *
+ * @return The parent node.
+ */
+ @Override
+ public JsonNode parse() {
+ return this.documentRoot;
+ }
+
+ /**
+ * Returns the compiled regular expression REGEX_PATTERN used to match
+ * delimited references.
+ *
+ * @return A compiled regex for use with the Matcher.
+ */
+ private Pattern getPattern() {
+ return REGEX_PATTERN;
+ }
+
+ /**
+ * @return The list of references mapped to dereferenced values.
+ */
+ private Map<String, String> getReferences() {
+ if( this.references == null ) {
+ this.references = createReferences();
+ }
+
+ return this.references;
+ }
+
+ /**
+ * Subclasses can override this method to insert their own map.
+ *
+ * @return An empty HashMap, never null.
+ */
+ protected Map<String, String> createReferences() {
+ return new HashMap<>();
+ }
+
+ private final class ResolverYamlFactory extends YAMLFactory {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected YAMLGenerator _createGenerator(
+ final Writer out, final IOContext ctxt ) throws IOException {
+
+ return new ResolverYamlGenerator(
+ ctxt, _generatorFeatures, _yamlGeneratorFeatures, _objectCodec,
+ out, _version );
+ }
+ }
+
+ private class ResolverYamlGenerator extends YAMLGenerator {
+
+ public ResolverYamlGenerator(
+ final IOContext ctxt,
+ final int jsonFeatures,
+ final int yamlFeatures,
+ final ObjectCodec codec,
+ final Writer out,
+ final DumperOptions.Version version ) throws IOException {
+ super( ctxt, jsonFeatures, yamlFeatures, codec, out, version );
+ }
+
+ @Override
+ public void writeString( final String text ) throws IOException {
+ super.writeString( substitute( text ) );
+ }
+ }
+
+ private YAMLFactory getYamlFactory() {
+ return new ResolverYamlFactory();
+ }
+
+ private ObjectMapper getObjectMapper() {
+ return new ObjectMapper( getYamlFactory() );
}
src/main/java/com/scrivenvar/definition/yaml/YamlTreeAdapter.java
import com.fasterxml.jackson.databind.JsonNode;
+import com.scrivenvar.definition.DocumentParser;
+import com.scrivenvar.definition.TreeAdapter;
import com.scrivenvar.definition.VariableTreeItem;
import javafx.scene.control.TreeItem;
-import javafx.scene.control.TreeView;
import java.util.Map.Entry;
/**
* Transforms a JsonNode hierarchy into a tree that can be displayed in a user
* interface.
*
* @author White Magic Software, Ltd.
*/
-public class YamlTreeAdapter {
-
- private YamlParser yamlParser;
+public class YamlTreeAdapter implements TreeAdapter {
+ private final DocumentParser<JsonNode> mParser;
- public YamlTreeAdapter( final YamlParser parser ) {
- setYamlParser( parser );
+ public YamlTreeAdapter( final DocumentParser<JsonNode> parser ) {
+ mParser = parser;
}
/**
- * Converts a YAML document to a TreeView based on the document keys. Only the
- * first document in the stream is adapted.
+ * Converts a YAML document to a {@link TreeItem} based on the document
+ * keys. Only the first document in the stream is adapted.
*
- * @param name Root TreeItem node name.
- * @return A TreeView populated with all the keys in the YAML document.
+ * @param root Root {@link TreeItem} node name.
+ * @return A {@link TreeItem} populated with all the keys in the YAML
+ * document.
*/
- public TreeView<String> adapt( final String name ) {
- final JsonNode rootNode = getYamlParser().getDocumentRoot();
- final TreeItem<String> rootItem = createTreeItem( name );
+ public TreeItem<String> adapt(
+ final String root ) {
+ final JsonNode rootNode = getParser().parse();
+ final TreeItem<String> rootItem = createTreeItem( root );
rootItem.setExpanded( true );
adapt( rootNode, rootItem );
- return new TreeView<>( rootItem );
+ return rootItem;
}
private TreeItem<String> createTreeItem( final String value ) {
return new VariableTreeItem<>( value );
- }
-
- private YamlParser getYamlParser() {
- return this.yamlParser;
}
- private void setYamlParser( final YamlParser yamlParser ) {
- this.yamlParser = yamlParser;
+ private DocumentParser<JsonNode> getParser() {
+ return mParser;
}
}
src/main/java/com/scrivenvar/definition/DocumentParser.java
+package com.scrivenvar.definition;
+
+/**
+ * Responsible for parsing structured document formats.
+ *
+ * @param <T> The type of "node" for the document's object model.
+ */
+public interface DocumentParser<T> {
+
+ /**
+ * Parses a document into a nested object hierarchy. The object returned
+ * from this call must be the root node in the document tree.
+ *
+ * @return The document's root node, which may be empty but never null.
+ */
+ T parse();
+}
src/main/java/com/scrivenvar/definition/EmptyDefinitionSource.java
import java.util.HashMap;
import java.util.Map;
-import javafx.scene.control.TreeView;
/**
* Creates a definition source that has no information to load or save.
*
* @author White Magic Software, Ltd.
*/
-public class EmptyDefinitionSource extends AbstractDefinitionSource {
+public class EmptyDefinitionSource implements DefinitionSource {
public EmptyDefinitionSource() {
+ }
+
+ @Override
+ public TreeAdapter getTreeAdapter() {
+ return new EmptyTreeAdapter();
}
@Override
public Map<String, String> getResolvedMap() {
return new HashMap<>();
}
+ /**
+ * Prevent an {@link EmptyDefinitionSource} from being saved literally as its
+ * memory reference (the default value returned by {@link Object#toString()}).
+ *
+ * @return Empty string.
+ */
@Override
- protected TreeView<String> createTreeView() {
- return new TreeView<>();
+ public String toString() {
+ return "";
}
}
src/main/java/com/scrivenvar/definition/EmptyTreeAdapter.java
+package com.scrivenvar.definition;
+
+import javafx.scene.control.TreeItem;
+
+/**
+ * Facilitates adapting empty documents into a single node object model.
+ */
+public class EmptyTreeAdapter implements TreeAdapter {
+ @Override
+ public TreeItem<String> adapt( String root ) {
+ return new TreeItem<>( root );
+ }
+}
src/main/java/com/scrivenvar/definition/FileDefinitionSource.java
* @author White Magic Software, Ltd.
*/
-public abstract class FileDefinitionSource extends AbstractDefinitionSource {
+public abstract class FileDefinitionSource implements DefinitionSource {
private final Path mPath;
}
+ /**
+ * Returns the path to the file that is to be loaded as a definition source.
+ *
+ * @return A fully qualified {@link Path} to a file.
+ */
public Path getPath() {
return mPath;
src/main/java/com/scrivenvar/definition/TreeAdapter.java
+package com.scrivenvar.definition;
+
+import javafx.scene.control.TreeItem;
+
+public interface TreeAdapter {
+ /**
+ * Adapts the document produced by the given parser into a {@link TreeItem}
+ * object that can be presented to the user within a GUI.
+ *
+ * @param root The default root node name.
+ * @return The parsed document in a {@link TreeItem} that can be displayed
+ * in a panel.
+ */
+ TreeItem<String> adapt( final String root );
+}