Dave Jarvis' Repositories

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

Extract interpolation behaviour, rename events

AuthorDaveJarvis <email>
Date2021-12-05 11:52:46 GMT-0800
Commit079c4fc7d6a7171583974b6859f035f7ed0fc216
Parent3ead2c2
Delta127 lines added, 30 lines removed, 97-line increase
src/main/java/com/keenwrite/util/InterpolatingMap.java
+package com.keenwrite.util;
+
+import com.keenwrite.sigils.SigilOperator;
+import com.keenwrite.sigils.Sigils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import static java.lang.String.format;
+import static java.util.regex.Pattern.compile;
+import static java.util.regex.Pattern.quote;
+
+public class InterpolatingMap extends ConcurrentHashMap<String, String> {
+ private static final int GROUP_DELIMITED = 1;
+
+ /**
+ * Used to override the default initial capacity in {@link HashMap}.
+ */
+ private static final int INITIAL_CAPACITY = 1 << 8;
+
+ public InterpolatingMap() {
+ super( INITIAL_CAPACITY );
+ }
+
+ /**
+ * Interpolates all values in the map that reference other values by way
+ * of key names. Performs a non-greedy match of key names delimited by
+ * definition tokens. This operation modifies the map directly.
+ *
+ * @param operator Contains the opening and closing sigils that mark
+ * where variable names begin and end.
+ * @return {@code this}
+ */
+ public Map<String, String> interpolate( final SigilOperator operator ) {
+ sigilize( operator );
+ interpolate( operator.getSigils() );
+ return this;
+ }
+
+ /**
+ * Wraps each key in this map with the starting and ending sigils provided
+ * by the given {@link SigilOperator}. This operation modifies the map
+ * directly.
+ *
+ * @param operator Container for starting and ending sigils.
+ */
+ private void sigilize( final SigilOperator operator ) {
+ forEach( ( k, v ) -> put( operator.entoken( k ), v ) );
+ }
+
+ /**
+ * Interpolates all values in the map that reference other values by way
+ * of key names. Performs a non-greedy match of key names delimited by
+ * definition tokens. This operation modifies the map directly.
+ *
+ * @param sigils Contains the opening and closing sigils that mark
+ * where variable names begin and end.
+ */
+ private void interpolate( final Sigils sigils ) {
+ final var pattern = compile(
+ format(
+ "(%s.*?%s)", quote( sigils.getBegan() ), quote( sigils.getEnded() )
+ )
+ );
+
+ replaceAll( ( k, v ) -> resolve( v, pattern ) );
+ }
+
+ /**
+ * Given a value with zero or more key references, this will resolve all
+ * the values, recursively. If a key cannot be de-referenced, the value will
+ * contain the key name.
+ *
+ * @param value Value containing zero or more key references.
+ * @param pattern The regular expression pattern to match variable key names.
+ * @return The given value with all embedded key references interpolated.
+ */
+ private String resolve( String value, final Pattern pattern ) {
+ final var matcher = pattern.matcher( value );
+
+ while( matcher.find() ) {
+ final var keyName = matcher.group( GROUP_DELIMITED );
+ final var mapValue = get( keyName );
+ final var keyValue = mapValue == null
+ ? keyName
+ : resolve( mapValue, pattern );
+
+ value = value.replace( keyName, keyValue );
+ }
+
+ return value;
+ }
+}
src/main/java/com/keenwrite/sigils/SigilOperator.java
}
+ public Sigils getSigils() {
+ return mSigils;
+ }
+
/**
* Wraps the given key in the began and ended tokens. This may perform any
src/main/java/com/keenwrite/sigils/YamlSigilOperator.java
*/
public final class YamlSigilOperator extends SigilOperator {
- public static final char KEY_SEPARATOR_DEF = '.';
-
public YamlSigilOperator( final Sigils sigils ) {
super( sigils );
src/main/java/com/keenwrite/events/ExportFailedEvent.java
*/
public class ExportFailedEvent implements AppEvent {
- public static void fireExportFailedEvent() {
- new ExportFailedEvent().fire();
+ public static void fire() {
+ new ExportFailedEvent().publish();
}
}
src/main/java/com/keenwrite/events/FileOpenEvent.java
* @param uri The instance of {@link URI} to open as a file in a text editor.
*/
- public static void fireFileOpenEvent( final URI uri ) {
- new FileOpenEvent( uri ).fire();
+ public static void fire( final URI uri ) {
+ new FileOpenEvent( uri ).publish();
}
src/main/java/com/keenwrite/events/HyperlinkOpenEvent.java
* @param uri The location to open.
*/
- public static void fireHyperlinkOpenEvent( final URI uri )
+ public static void fire( final URI uri )
throws IOException {
- new HyperlinkOpenEvent( uri ).fire();
+ new HyperlinkOpenEvent( uri ).publish();
}
/**
* Requests to open the default browser at the given location.
*
* @param uri The location to open.
*/
- public static void fireHyperlinkOpenEvent( final String uri ) {
+ public static void fire( final String uri ) {
try {
- fireHyperlinkOpenEvent( new URI( uri ) );
+ fire( new URI( uri ) );
} catch( final Exception ex ) {
clue( ex );
src/main/java/com/keenwrite/events/ParseHeadingEvent.java
*/
public static void fireNewOutlineEvent() {
- new ParseHeadingEvent( NEW_OUTLINE_LEVEL, "Document", 0 ).fire();
+ new ParseHeadingEvent( NEW_OUTLINE_LEVEL, "Document", 0 ).publish();
}
/**
* Call to indicate that a new heading must be added to the document outline.
*
* @param text The heading text (parsed and processed).
* @param level A value between 1 and 6.
* @param offset Absolute offset into document where heading is found.
*/
- public static void fireNewHeadingEvent(
+ public static void fire(
final int level, final String text, final int offset ) {
assert text != null;
assert 1 <= level && level <= 6;
assert 0 <= offset;
- new ParseHeadingEvent( level, text, offset ).fire();
+ new ParseHeadingEvent( level, text, offset ).publish();
}
src/main/java/com/keenwrite/events/ScrollLockEvent.java
private static void fire( final boolean locked ) {
- new ScrollLockEvent( locked ).fire();
+ new ScrollLockEvent( locked ).publish();
}
src/main/java/com/keenwrite/events/StatusEvent.java
*/
public static void clue() {
- fireStatusEvent( get( STATUS_BAR_OK, "OK" ) );
+ fire( get( STATUS_BAR_OK, "OK" ) );
}
/**
* Notifies listeners of a series of messages. This is useful when providing
* users feedback of how third-party executables have failed.
*
* @param messages The lines of text to display.
*/
public static void clue( final List<String> messages ) {
- messages.forEach( StatusEvent::fireStatusEvent );
+ messages.forEach( StatusEvent::fire );
}
/**
* Notifies listeners of an error.
*
* @param key The message bundle key to look up.
* @param t The exception that caused the error.
*/
public static void clue( final String key, final Throwable t ) {
- fireStatusEvent( get( key ), t );
+ fire( get( key ), t );
}
/**
* Notifies listeners of a custom message.
*
* @param key The property key having a value to populate with arguments.
* @param args The placeholder values to substitute into the key's value.
*/
public static void clue( final String key, final Object... args ) {
- fireStatusEvent( get( key, args ) );
+ fire( get( key, args ) );
}
/**
* Notifies listeners of an exception occurs that warrants the user's
* attention.
*
* @param problem The exception with a message to display to the user.
*/
public static void clue( final Throwable problem ) {
- fireStatusEvent( problem );
+ fire( problem );
}
- private static void fireStatusEvent( final String message ) {
- new StatusEvent( message ).fire();
+ private static void fire( final String message ) {
+ new StatusEvent( message ).publish();
}
- private static void fireStatusEvent( final Throwable problem ) {
- new StatusEvent( problem ).fire();
+ private static void fire( final Throwable problem ) {
+ new StatusEvent( problem ).publish();
}
- private static void fireStatusEvent(
+ private static void fire(
final String message, final Throwable problem ) {
- new StatusEvent( message, problem ).fire();
+ new StatusEvent( message, problem ).publish();
}
}
src/main/java/com/keenwrite/events/TextDefinitionFocusEvent.java
* @param editor The instance of editor that has gained input focus.
*/
- public static void fireTextDefinitionFocus( final TextDefinition editor ) {
- new TextDefinitionFocusEvent( editor ).fire();
+ public static void fire( final TextDefinition editor ) {
+ new TextDefinitionFocusEvent( editor ).publish();
}
}
src/main/java/com/keenwrite/events/TextEditorFocusEvent.java
*/
public static void fireTextEditorFocus( final TextEditor editor ) {
- new TextEditorFocusEvent( editor ).fire();
+ new TextEditorFocusEvent( editor ).publish();
}
}
src/main/java/com/keenwrite/events/WordCountEvent.java
* @param count The approximate number of words in the document.
*/
- public static void fireWordCountEvent( final int count ) {
- new WordCountEvent( count ).fire();
+ public static void fire( final int count ) {
+ new WordCountEvent( count ).publish();
}