Dave Jarvis' Repositories

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

Added superscript, subscript, and strike markdown delimiters. Finished first draft of Renjin R engine integration.

Authordjarvis <email>
Date2016-12-24 22:04:37 GMT-0800
Commit0788de0bfd32e9ecc44776de0c9b9094d6d97141
Parentb3625c2
Delta311 lines added, 155 lines removed, 156-line increase
src/main/java/com/scrivenvar/processors/text/TextReplacementFactory.java
/**
* Convenience method to instantiate a suitable text replacer algorithm and
- * perform a replacement using the given map.
+ * perform a replacement using the given map. At this point, the values should
+ * be already dereferenced and ready to be substituted verbatim; any
+ * recursively defined values must have been interpolated previously.
*
* @param text The text containing zero or more variables to replace.
* @param map The map of variables to their dereferenced values.
*
* @return The text with all variables replaced.
*/
- public static String replace( final String text, final Map<String, String> map ) {
+ public static String replace(
+ final String text, final Map<String, String> map ) {
return getTextReplacer( text.length() ).replace( text, map );
}
src/main/java/com/scrivenvar/processors/InlineRProcessor.java
+/*
+ * Copyright 2016 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.scrivenvar.processors;
+
+import static com.scrivenvar.decorators.RVariableDecorator.PREFIX;
+import static com.scrivenvar.decorators.RVariableDecorator.SUFFIX;
+import static com.scrivenvar.processors.text.TextReplacementFactory.replace;
+import static java.lang.Math.min;
+import java.nio.file.Path;
+import java.util.Map;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+/**
+ * Transforms a document containing R statements into Markdown.
+ *
+ * @author White Magic Software, Ltd.
+ */
+public final class InlineRProcessor extends DefaultVariableProcessor {
+
+ private ScriptEngine engine;
+
+ /**
+ * Constructs a processor capable of evaluating R statements.
+ *
+ * @param processor Subsequent link in the processing chain.
+ * @param map Resolved definitions map.
+ * @param path Path to the file being edited so that its working directory can
+ * be extracted. Must not be null.
+ */
+ public InlineRProcessor(
+ final Processor<String> processor,
+ final Map<String, String> map,
+ final Path path ) {
+ super( processor, map );
+ init( path.getParent() );
+ }
+
+ public void init( final Path workingDirectory ) {
+ eval( replace( ""
+ + "assign( 'anchor', as.Date( '$date.anchor$', format='%Y-%m-%d' ), envir = .GlobalEnv );"
+ + "setwd( '" + workingDirectory + "' );"
+ + "source( '../bin/pluralize.R' );"
+ + "source( '../bin/common.R' )", getDefinitions() ) );
+ }
+
+ @Override
+ public String processLink( final String text ) {
+ final int length = text.length();
+ final int prefixLength = PREFIX.length();
+
+ // Pre-allocate the same amount of space. A calculation is longer to write
+ // than its computed value inserted into the text.
+ final StringBuilder sb = new StringBuilder( length );
+
+ int prevIndex = 0;
+ int currIndex = text.indexOf( PREFIX );
+
+ while( currIndex >= 0 ) {
+ // Copy everything up to, but not including, an R statement (`r#).
+ sb.append( text.substring( prevIndex, currIndex ) );
+
+ prevIndex = currIndex + prefixLength;
+
+ // Find the statement ending (`), without indexing past the text boundary.
+ currIndex = text.indexOf( SUFFIX, min( currIndex + 1, length ) );
+
+ // Only evalutate inline R statements that have end delimiters.
+ if( currIndex > 1 ) {
+ // Extract the inline R statement to be evaluated.
+ final String r = text.substring( prevIndex, currIndex );
+
+ // Pass the R statement into the R engine for evaluation.
+ final Object result = eval( r );
+
+ // Append the string representation of the result into the text.
+ sb.append( result );
+
+ // Retain the R statement's ending position in the text.
+ prevIndex = currIndex + 1;
+
+ } else {
+ // There was a starting prefix but no ending suffix. Ignore the
+ // problem, copy to the end, and exit the loop.
+// sb.append()
+
+ }
+
+ // Find the start of the next inline R statement.
+ currIndex = text.indexOf( PREFIX, min( currIndex + 1, length ) );
+ }
+
+ // Copy from the previous index to the end of the string.
+ sb.append( text.substring( min( prevIndex, length ) ) );
+
+ return sb.toString();
+ }
+
+ /**
+ * Evaluate an R expression and return the resulting object.
+ *
+ * @param r The expression to evaluate.
+ *
+ * @return The object resulting from the evaluation.
+ */
+ private Object eval( final String r ) {
+ try {
+ return getScriptEngine().eval( r );
+ } catch( final ScriptException ex ) {
+ problem( ex );
+ }
+
+ return "";
+ }
+
+ private synchronized ScriptEngine getScriptEngine() {
+ if( this.engine == null ) {
+ this.engine = (new ScriptEngineManager()).getEngineByName( "Renjin" );
+ }
+
+ return this.engine;
+ }
+
+ /**
+ * Notify the user (passively) of the problem.
+ *
+ * @param ex A problem parsing the text.
+ */
+ private void problem( final Exception ex ) {
+ // TODO: Use the notify service to warn the user that there's an issue.
+ System.out.println( ex );
+ }
+}
src/main/java/com/scrivenvar/processors/MarkdownProcessor.java
import com.vladsch.flexmark.Extension;
import com.vladsch.flexmark.ast.Node;
+import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughSubscriptExtension;
import com.vladsch.flexmark.ext.gfm.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
+import com.vladsch.flexmark.superscript.SuperscriptExtension;
import java.util.ArrayList;
import java.util.List;
final List<Extension> result = new ArrayList<>();
result.add( TablesExtension.create() );
+ result.add( SuperscriptExtension.create() );
+ result.add( StrikethroughSubscriptExtension.create() );
return result;
}
src/main/java/com/scrivenvar/processors/MarkdownVariableProcessor.java
-/*
- * Copyright 2016 White Magic Software, Ltd.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * o Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * o Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.scrivenvar.processors;
-
-import static com.scrivenvar.processors.text.TextReplacementFactory.replace;
-import java.util.Map;
-
-/**
- * Processes variables in the document and inserts their values into the
- * post-processed text.
- *
- * @author White Magic Software, Ltd.
- */
-public class MarkdownVariableProcessor extends AbstractProcessor<String> {
-
- private Map<String, String> definitions;
-
- /**
- * Constructs a new Markdown processor that can create HTML documents.
- *
- * @param successor Usually the HTML Preview Processor.
- */
- private MarkdownVariableProcessor( final Processor<String> successor ) {
- super( successor );
- }
-
- public MarkdownVariableProcessor(
- final Processor<String> successor, final Map<String, String> map ) {
- this( successor );
- setDefinitions( map );
- }
-
- /**
- *
- * @param text The document text that includes variables that should be
- * replaced with values when rendered as HTML.
- *
- * @return The text with all variables replaced.
- */
- @Override
- public String processLink( final String text ) {
- return replace( text, getDefinitions() );
- }
-
- private Map<String, String> getDefinitions() {
- return this.definitions;
- }
-
- private void setDefinitions( final Map<String, String> definitions ) {
- this.definitions = definitions;
- }
-}
src/main/java/com/scrivenvar/processors/ProcessorFactory.java
switch( fileType ) {
case RMARKDOWN:
- processor = createRMarkdownProcessor( tab );
+ processor = createRProcessor( tab );
break;
case MARKDOWN:
processor = createMarkdownProcessor( tab );
break;
case XML:
processor = createXMLProcessor( tab );
+ break;
+
+ case RXML:
+ processor = createRXMLProcessor( tab );
break;
* @return Processors at the end of the processing chain.
*/
- private Processor<String> getTerminalProcessChain() {
+ private Processor<String> getCommonProcessor() {
if( this.terminalProcessChain == null ) {
- this.terminalProcessChain = createCommonChain();
+ this.terminalProcessChain = createCommonProcessor();
}
* @return A markdown, caret replacement, and preview pane processor chain.
*/
- private Processor<String> createCommonChain() {
+ private Processor<String> createCommonProcessor() {
final Processor<String> hpp = new HTMLPreviewProcessor( getPreviewPane() );
final Processor<String> mcrp = new CaretReplacementProcessor( hpp );
final Processor<String> mpp = new MarkdownProcessor( mcrp );
return mpp;
- }
-
- private Processor<String> createInsertionProcessor(
- final Processor<String> tpc, final ObservableValue<Integer> caret ) {
- return new MarkdownCaretInsertionProcessor( tpc, caret );
}
protected Processor<String> createMarkdownProcessor( final FileEditorTab tab ) {
final ObservableValue<Integer> caret = tab.caretPositionProperty();
- final Processor<String> tpc = getTerminalProcessChain();
+ final Processor<String> tpc = getCommonProcessor();
final Processor<String> cip = createInsertionProcessor( tpc, caret );
- final Processor<String> vp = new MarkdownVariableProcessor( cip, getResolvedMap() );
+ final Processor<String> dvp = new DefaultVariableProcessor( cip, getResolvedMap() );
- return vp;
+ return dvp;
}
- protected Processor<String> createRMarkdownProcessor( final FileEditorTab tab ) {
+ protected Processor<String> createRProcessor( final FileEditorTab tab ) {
final ObservableValue<Integer> caret = tab.caretPositionProperty();
- final Processor<String> tpc = getTerminalProcessChain();
+ final Processor<String> tpc = getCommonProcessor();
final Processor<String> cip = createInsertionProcessor( tpc, caret );
- final Processor<String> rp = new RProcessor( cip );
-
- return rp;
+ final Processor<String> rp = new InlineRProcessor( cip, getResolvedMap(), tab.getPath() );
+ final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() );
+
+ return rvp;
}
protected Processor<String> createXMLProcessor( final FileEditorTab tab ) {
- final Processor<String> tpc = getTerminalProcessChain();
+ final ObservableValue<Integer> caret = tab.caretPositionProperty();
+ final Processor<String> tpc = getCommonProcessor();
final Processor<String> xmlp = new XMLProcessor( tpc, tab.getPath() );
- final Processor<String> xcip = new XMLCaretInsertionProcessor( xmlp, tab.caretPositionProperty() );
- final Processor<String> vp = new MarkdownVariableProcessor( xcip, getResolvedMap() );
+ final Processor<String> xcip = createXMLInsertionProcessor( xmlp, caret );
+ final Processor<String> dvp = new DefaultVariableProcessor( xcip, getResolvedMap() );
- return vp;
+ return dvp;
+ }
+
+ protected Processor<String> createRXMLProcessor( final FileEditorTab tab ) {
+ final ObservableValue<Integer> caret = tab.caretPositionProperty();
+ final Processor<String> tpc = getCommonProcessor();
+ final Processor<String> xmlp = new XMLProcessor( tpc, tab.getPath() );
+ final Processor<String> xcip = createXMLInsertionProcessor( xmlp, caret );
+ final Processor<String> rp = new InlineRProcessor( xcip, getResolvedMap(), tab.getPath() );
+ final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() );
+
+ return rvp;
+ }
+
+ private Processor<String> createInsertionProcessor(
+ final Processor<String> tpc, final ObservableValue<Integer> caret ) {
+ return new MarkdownCaretInsertionProcessor( tpc, caret );
+ }
+
+ private Processor<String> createXMLInsertionProcessor(
+ final Processor<String> tpc, final ObservableValue<Integer> caret ) {
+ return new XMLCaretInsertionProcessor( tpc, caret );
}
src/main/java/com/scrivenvar/processors/RProcessor.java
-/*
- * Copyright 2016 White Magic Software, Ltd.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * o Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * o Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.scrivenvar.processors;
-
-import javax.script.ScriptEngine;
-import org.renjin.script.RenjinScriptEngineFactory;
-
-/**
- * Transforms an R document into markdown using knitr:
- *
- * @author White Magic Software, Ltd.
- */
-public class RProcessor extends AbstractProcessor<String> {
-
- public RProcessor( Processor<String> processor ) {
- super( processor );
- }
-
- @Override
- public String processLink( final String text ) {
- System.out.println( "Renjin + Knitr Smackdown" );
-
- RenjinScriptEngineFactory factory = new RenjinScriptEngineFactory();
- ScriptEngine engine = factory.getScriptEngine();
-
- return text;
- }
-}
src/main/java/com/scrivenvar/processors/RVariableProcessor.java
+/*
+ * Copyright 2016 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.scrivenvar.processors;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Converts the keys of the resolved map from default form to R form, then
+ * performs a substitution on the text. The default R variable syntax is
+ * <code>v$tree$leaf</code>.
+ *
+ * @author White Magic Software, Ltd.
+ */
+class RVariableProcessor extends DefaultVariableProcessor {
+
+ public RVariableProcessor(
+ final Processor<String> rp, final Map<String, String> map ) {
+ super( rp, map );
+ }
+
+ /**
+ * Returns the R-based version of the interpolated variable definitions.
+ *
+ * @return Variable names transmogrified from the default syntax to R syntax.
+ */
+ @Override
+ protected Map<String, String> getDefinitions() {
+ return toR( super.getDefinitions() );
+ }
+
+ /**
+ * Converts the given map from regular variables to R variables.
+ *
+ * @param map Map of variable names to values.
+ *
+ * @return
+ */
+ private Map<String, String> toR( final Map<String, String> map ) {
+ final Map<String, String> rMap = new HashMap<>( map.size() );
+
+ for( final String key : map.keySet() ) {
+ rMap.put( toR( key ), '\'' + map.get( key ) + '\'' );
+ }
+
+ return rMap;
+ }
+
+ /**
+ * Transforms a variable name from $tree.branch.leaf$ to v$tree$branch$leaf
+ * form.
+ *
+ * @param key The variable name to transform, can be empty but not null.
+ *
+ * @return The transformed variable name.
+ */
+ private String toR( final String key ) {
+ // Replace all the periods with dollar symbols.
+ final StringBuilder sb = new StringBuilder( 'v' + key );
+ final int length = sb.length();
+
+ // Replace all periods with dollar symbols. Normally we'd check i >= 0,
+ // but the prepended 'v' is always going to be a 'v', not a dot.
+ for( int i = length - 1; i > 0; i-- ) {
+ if( sb.charAt( i ) == '.' ) {
+ sb.setCharAt( i, '$' );
+ }
+ }
+
+ // The length is always at least 1 (the 'v'), so bounds aren't broken here.
+ sb.setLength( length - 1 );
+
+ return sb.toString();
+ }
+}
src/main/java/com/scrivenvar/processors/XMLProcessor.java
}
}
-
- sr.close();
}