| Author | djarvis <email> |
|---|---|
| Date | 2016-12-24 22:04:37 GMT-0800 |
| Commit | 0788de0bfd32e9ecc44776de0c9b9094d6d97141 |
| Parent | b3625c2 |
| Delta | 311 lines added, 155 lines removed, 156-line increase |
| /** | ||
| * 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 ); | ||
| } |
| +/* | ||
| + * 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 ); | ||
| + } | ||
| +} | ||
| 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; | ||
| } | ||
| -/* | ||
| - * 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; | ||
| - } | ||
| -} | ||
| 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 ); | ||
| } | ||
| -/* | ||
| - * 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; | ||
| - } | ||
| -} | ||
| +/* | ||
| + * 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(); | ||
| + } | ||
| +} | ||
| } | ||
| } | ||
| - | ||
| - sr.close(); | ||
| } | ||