| Author | DaveJarvis <email> |
|---|---|
| Date | 2020-05-16 14:32:10 GMT-0700 |
| Commit | 23afe9c20f1af71d3cc1ed02f37ab314f53721af |
| Parent | a492c56 |
| Delta | 75 lines added, 239 lines removed, 164-line decrease |
| -/* | ||
| - * Copyright (c) 2015 Karl Tauber <karl at jformdesigner dot com> | ||
| - * 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.util; | ||
| - | ||
| -/** | ||
| - * Simple item for a ChoiceBox, ComboBox or ListView. Consists of a string name | ||
| - * and a value object. toString() returns the name. equals() compares the value | ||
| - * and hashCode() returns the hash code of the value. | ||
| - * | ||
| - * @author Karl Tauber | ||
| - * @param <V> The type of item value. | ||
| - */ | ||
| -public class Item<V> { | ||
| - | ||
| - public final String name; | ||
| - public final V value; | ||
| - | ||
| - public Item( final String name, final V value ) { | ||
| - this.name = name; | ||
| - this.value = value; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public boolean equals( final Object obj ) { | ||
| - if( this == obj ) { | ||
| - return true; | ||
| - } | ||
| - if( !(obj instanceof Item) ) { | ||
| - return false; | ||
| - } | ||
| - return Utils.safeEquals( value, ((Item<?>)obj).value ); | ||
| - } | ||
| - | ||
| - @Override | ||
| - public int hashCode() { | ||
| - return (value != null) ? value.hashCode() : 0; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public String toString() { | ||
| - return name; | ||
| - } | ||
| -} | ||
| /** | ||
| - * @author Karl Tauber | ||
| + * @author Karl Tauber and White Magic Software, Ltd. | ||
| */ | ||
| public class Utils { | ||
| - | ||
| - public static boolean safeEquals( final Object o1, final Object o2 ) { | ||
| - if( o1 == o2 ) { | ||
| - return true; | ||
| - } | ||
| - if( o1 == null || o2 == null ) { | ||
| - return false; | ||
| - } | ||
| - return o1.equals( o2 ); | ||
| - } | ||
| - | ||
| - public static boolean isNullOrEmpty( final String s ) { | ||
| - return s == null || s.isEmpty(); | ||
| - } | ||
| public static String ltrim( final String s ) { | ||
| return s.substring( 0, i + 1 ); | ||
| - } | ||
| - | ||
| - public static void putPrefs( Preferences prefs, String key, String value, String def ) { | ||
| - if( value != def && !value.equals( def ) ) { | ||
| - prefs.put( key, value ); | ||
| - } else { | ||
| - prefs.remove( key ); | ||
| - } | ||
| - } | ||
| - | ||
| - public static void putPrefsInt( Preferences prefs, String key, int value, int def ) { | ||
| - if( value != def ) { | ||
| - prefs.putInt( key, value ); | ||
| - } else { | ||
| - prefs.remove( key ); | ||
| - } | ||
| - } | ||
| - | ||
| - public static void putPrefsBoolean( Preferences prefs, String key, boolean value, boolean def ) { | ||
| - if( value != def ) { | ||
| - prefs.putBoolean( key, value ); | ||
| - } else { | ||
| - prefs.remove( key ); | ||
| - } | ||
| } | ||
| - public static String[] getPrefsStrings( final Preferences prefs, String key ) { | ||
| + public static String[] getPrefsStrings( final Preferences prefs, | ||
| + String key ) { | ||
| final ArrayList<String> arr = new ArrayList<>( 256 ); | ||
| } | ||
| - return arr.toArray( new String[ arr.size() ] ); | ||
| + return arr.toArray( new String[ 0 ] ); | ||
| } | ||
| - public static void putPrefsStrings( Preferences prefs, String key, String[] strings ) { | ||
| + public static void putPrefsStrings( Preferences prefs, String key, | ||
| + String[] strings ) { | ||
| for( int i = 0; i < strings.length; i++ ) { | ||
| prefs.put( key + (i + 1), strings[ i ] ); | ||
| } | ||
| - for( int i = strings.length; prefs.get( key + (i + 1), null ) != null; i++ ) { | ||
| + for( int i = strings.length; prefs.get( key + (i + 1), | ||
| + null ) != null; i++ ) { | ||
| prefs.remove( key + (i + 1) ); | ||
| } | ||
| package com.scrivenvar.service.impl; | ||
| -import static com.scrivenvar.Constants.APP_WATCHDOG_TIMEOUT; | ||
| import com.scrivenvar.service.Snitch; | ||
| + | ||
| import java.io.IOException; | ||
| -import java.nio.file.FileSystem; | ||
| -import java.nio.file.FileSystems; | ||
| -import java.nio.file.Files; | ||
| -import java.nio.file.Path; | ||
| -import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; | ||
| -import java.nio.file.WatchEvent; | ||
| -import java.nio.file.WatchKey; | ||
| -import java.nio.file.WatchService; | ||
| +import java.nio.file.*; | ||
| import java.util.Collections; | ||
| import java.util.Map; | ||
| import java.util.Observable; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| + | ||
| +import static com.scrivenvar.Constants.APP_WATCHDOG_TIMEOUT; | ||
| +import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; | ||
| /** | ||
| * | ||
| * @param file Path to a file to watch for changes. | ||
| - * | ||
| * @throws IOException The file could not be monitored. | ||
| */ | ||
| * | ||
| * @param path The file to return as a directory, which should always be the | ||
| - * case. | ||
| - * | ||
| + * case. | ||
| * @return The given path as a directory, if a file, otherwise the path | ||
| * itself. | ||
| */ | ||
| private Path toDirectory( final Path path ) { | ||
| return Files.isDirectory( path ) | ||
| - ? path | ||
| - : path.toFile().getParentFile().toPath(); | ||
| + ? path | ||
| + : path.toFile().getParentFile().toPath(); | ||
| } | ||
| */ | ||
| @Override | ||
| - @SuppressWarnings( "SleepWhileInLoop" ) | ||
| + @SuppressWarnings("BusyWait") | ||
| public void run() { | ||
| setListening( true ); | ||
| for( final WatchEvent<?> event : key.pollEvents() ) { | ||
| - final Path changed = path.resolve( (Path)event.context() ); | ||
| + final Path changed = path.resolve( (Path) event.context() ); | ||
| if( event.kind() == ENTRY_MODIFY && isListening( changed ) ) { | ||
| * | ||
| * @param file Path to a system file. | ||
| - * | ||
| * @return true The given file is being monitored for changes. | ||
| */ | ||
| * | ||
| * @param key The key to lookup its corresponding path. | ||
| - * | ||
| * @return The path for the given key. | ||
| */ | ||
| * | ||
| * @return A valid WatchService instance, never null. | ||
| - * | ||
| * @throws IOException Could not create a new watch service. | ||
| */ | ||
| -/* | ||
| - * To change this license header, choose License Headers in Project Properties. | ||
| - * To change this template file, choose Tools | Templates | ||
| - * and open the template in the editor. | ||
| - */ | ||
| -package com.scrivenvar.service.events.impl; | ||
| - | ||
| -/** | ||
| - * Lists known file types for creating document processors via the factory. | ||
| - * | ||
| - * @author White Magic Software, Ltd. | ||
| - */ | ||
| -public enum FileType { | ||
| - MARKDOWN("md", "markdown", "mkdown", "mdown", "mkdn", "mkd", "mdwn", "mdtxt", "mdtext", "text", "txt"), | ||
| - R_MARKDOWN("Rmd"), | ||
| - XML("xml"); | ||
| - | ||
| - private final String[] extensions; | ||
| - | ||
| - private FileType(final String... extensions) { | ||
| - this.extensions = extensions; | ||
| - } | ||
| - | ||
| - /** | ||
| - * Returns true if the given file type aligns with the extension for this | ||
| - * enumeration. | ||
| - * | ||
| - * @param filetype The file extension to compare against the internal list. | ||
| - * @return true The given filetype equals (case insensitive) the internal | ||
| - * type. | ||
| - */ | ||
| - public boolean isType(final String filetype) { | ||
| - boolean result = false; | ||
| - | ||
| - for (final String extension : this.extensions) { | ||
| - if (extension.equalsIgnoreCase(filetype)) { | ||
| - result = true; | ||
| - break; | ||
| - } | ||
| - } | ||
| - | ||
| - return result; | ||
| - } | ||
| -} | ||
| * | ||
| * @param text The text that contains zero or more keys. | ||
| - * @param map The set of keys mapped to replacement values. | ||
| - * | ||
| + * @param map The set of keys mapped to replacement values. | ||
| * @return The given text with all keys replaced with corresponding values. | ||
| */ | ||
| - public String replace( String text, Map<String, String> map ); | ||
| + String replace( String text, Map<String, String> map ); | ||
| } | ||
| static { | ||
| - final Collection<Extension> extensions = new ArrayList<Extension>(); | ||
| + final Collection<Extension> extensions = new ArrayList<>(); | ||
| extensions.add( TablesExtension.create() ); | ||
| extensions.add( SuperscriptExtension.create() ); | ||
| * | ||
| * @param markdown The string to convert from Markdown to HTML. | ||
| - * | ||
| * @return The HTML representation of the Markdown document. | ||
| */ | ||
| * | ||
| * @param markdown The markdown to convert into an AST. | ||
| - * | ||
| * @return The markdown AST for the given text (usually a paragraph). | ||
| */ | ||
| * | ||
| * @param markdown The markdown to parse. | ||
| - * | ||
| * @return The root node of the markdown tree. | ||
| */ | ||
| * | ||
| * @param markdown The markdown text to convert to HTML, must not be null. | ||
| - * | ||
| * @return The markdown rendered as an HTML document. | ||
| */ | ||
| * Responsible for processing documents from one known format to another. | ||
| * | ||
| - * @author White Magic Software, Ltd. | ||
| * @param <T> The type of processor to create. | ||
| + * @author White Magic Software, Ltd. | ||
| */ | ||
| public interface Processor<T> { | ||
| - | ||
| + | ||
| /** | ||
| * Provided so that the chain can be invoked from any link using a given | ||
| * value. This should be called automatically by a superclass so that | ||
| * the links in the chain need only implement the processLink method. | ||
| - * | ||
| + * | ||
| * @param t The value to pass along to each link in the chain. | ||
| */ | ||
| - public void processChain( T t ); | ||
| + void processChain( T t ); | ||
| /** | ||
| * Processes the given content providing a transformation from one document | ||
| * format into another. For example, this could convert from XML to text using | ||
| * an XSLT processor, or from markdown to HTML. | ||
| * | ||
| * @param t The type of object to process. | ||
| - * | ||
| * @return The post-processed document, or null if processing should stop. | ||
| */ | ||
| - public T processLink( T t ); | ||
| + T processLink( T t ); | ||
| /** | ||
| * Adds a document processor to call after this processor finishes processing | ||
| * the document given to the process method. | ||
| * | ||
| * @return The processor that should transform the document after this | ||
| * instance has finished processing. | ||
| */ | ||
| - public Processor<T> next(); | ||
| + Processor<T> next(); | ||
| } | ||
| import com.scrivenvar.FileEditorTab; | ||
| import com.scrivenvar.preview.HTMLPreviewPane; | ||
| +import javafx.beans.value.ObservableValue; | ||
| + | ||
| import java.nio.file.Path; | ||
| import java.util.Map; | ||
| -import javafx.beans.value.ObservableValue; | ||
| /** | ||
| * text and caret processing to generate a final preview. | ||
| * | ||
| - * @param previewPane | ||
| - * @param resolvedMap | ||
| + * @param previewPane Where the final output is rendered. | ||
| + * @param resolvedMap Map of definitions to replace before final render. | ||
| */ | ||
| public ProcessorFactory( | ||
| - final HTMLPreviewPane previewPane, | ||
| - final Map<String, String> resolvedMap ) { | ||
| + final HTMLPreviewPane previewPane, | ||
| + final Map<String, String> resolvedMap ) { | ||
| this.previewPane = previewPane; | ||
| this.resolvedMap = resolvedMap; | ||
| } | ||
| /** | ||
| * Creates a processor suitable for parsing and rendering the file opened at | ||
| * the given tab. | ||
| * | ||
| * @param tab The tab containing a text editor, path, and caret position. | ||
| - * | ||
| * @return A processor that can render the given tab's text. | ||
| */ | ||
| default: | ||
| - processor = createIdentityProcessor( tab ); | ||
| + processor = createIdentityProcessor(); | ||
| break; | ||
| } | ||
| final Processor<String> hpp = new HTMLPreviewProcessor( getPreviewPane() ); | ||
| final Processor<String> mcrp = new CaretReplacementProcessor( hpp ); | ||
| - final Processor<String> mpp = new MarkdownProcessor( mcrp ); | ||
| - return mpp; | ||
| + return new MarkdownProcessor( mcrp ); | ||
| } | ||
| - | ||
| - protected Processor<String> createIdentityProcessor( final FileEditorTab tab ) { | ||
| + | ||
| + protected Processor<String> createIdentityProcessor() { | ||
| final Processor<String> hpp = new HTMLPreviewProcessor( getPreviewPane() ); | ||
| - final Processor<String> ip = new IdentityProcessor( hpp ); | ||
| - | ||
| - return ip; | ||
| + | ||
| + return new IdentityProcessor( hpp ); | ||
| } | ||
| - protected Processor<String> createMarkdownProcessor( final FileEditorTab tab ) { | ||
| + protected Processor<String> createMarkdownProcessor( | ||
| + final FileEditorTab tab ) { | ||
| final ObservableValue<Integer> caret = tab.caretPositionProperty(); | ||
| final Processor<String> tpc = getCommonProcessor(); | ||
| - final Processor<String> cip = createMarkdownInsertionProcessor( tpc, caret ); | ||
| - final Processor<String> dvp = new DefaultVariableProcessor( cip, getResolvedMap() ); | ||
| + final Processor<String> cip = createMarkdownInsertionProcessor( | ||
| + tpc, caret ); | ||
| - return dvp; | ||
| + return new DefaultVariableProcessor( cip, getResolvedMap() ); | ||
| } | ||
| protected Processor<String> createXMLProcessor( 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> dvp = new DefaultVariableProcessor( xmlp, getResolvedMap() ); | ||
| - final Processor<String> xcip = createXMLInsertionProcessor( dvp, caret ); | ||
| + final Processor<String> dvp = new DefaultVariableProcessor( | ||
| + xmlp, getResolvedMap() ); | ||
| - return xcip; | ||
| + return createXMLInsertionProcessor( dvp, caret ); | ||
| } | ||
| protected Processor<String> createRProcessor( final FileEditorTab tab ) { | ||
| final ObservableValue<Integer> caret = tab.caretPositionProperty(); | ||
| final Processor<String> tpc = getCommonProcessor(); | ||
| - final Processor<String> rp = new InlineRProcessor( tpc, getResolvedMap(), tab.getPath() ); | ||
| - final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() ); | ||
| - final Processor<String> cip = createRInsertionProcessor( rvp, caret ); | ||
| + final Processor<String> rp = new InlineRProcessor( | ||
| + tpc, getResolvedMap(), tab.getPath() ); | ||
| + final Processor<String> rvp = new RVariableProcessor( | ||
| + rp, getResolvedMap() ); | ||
| - return cip; | ||
| + return createRInsertionProcessor( rvp, caret ); | ||
| } | ||
| 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> rp = new InlineRProcessor( xmlp, getResolvedMap(), tab.getPath() ); | ||
| - final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() ); | ||
| - final Processor<String> xcip = createXMLInsertionProcessor( rvp, caret ); | ||
| + final Processor<String> rp = new InlineRProcessor( | ||
| + xmlp, getResolvedMap(), tab.getPath() ); | ||
| + final Processor<String> rvp = new RVariableProcessor( | ||
| + rp, getResolvedMap() ); | ||
| - return xcip; | ||
| + return createXMLInsertionProcessor( rvp, caret ); | ||
| } | ||
| private Processor<String> createMarkdownInsertionProcessor( | ||
| - final Processor<String> tpc, final ObservableValue<Integer> caret ) { | ||
| + final Processor<String> tpc, final ObservableValue<Integer> caret ) { | ||
| return new MarkdownCaretInsertionProcessor( tpc, caret ); | ||
| } | ||
| /** | ||
| * Create an insertion processor that is aware of R statements and will insert | ||
| * a caret outside of any statement the caret falls within. | ||
| * | ||
| * @param processor Another link in the processor chain. | ||
| - * @param caret The caret insertion point. | ||
| - * | ||
| + * @param caret The caret insertion point. | ||
| * @return A processor that can insert a caret token without disturbing any R | ||
| * code. | ||
| */ | ||
| private Processor<String> createRInsertionProcessor( | ||
| - final Processor<String> processor, final ObservableValue<Integer> caret ) { | ||
| + final Processor<String> processor, | ||
| + final ObservableValue<Integer> caret ) { | ||
| return new RMarkdownCaretInsertionProcessor( processor, caret ); | ||
| } | ||
| private Processor<String> createXMLInsertionProcessor( | ||
| - final Processor<String> tpc, final ObservableValue<Integer> caret ) { | ||
| + final Processor<String> tpc, final ObservableValue<Integer> caret ) { | ||
| return new XMLCaretInsertionProcessor( tpc, caret ); | ||
| } | ||
| import static com.scrivenvar.decorators.RVariableDecorator.SUFFIX; | ||
| import static java.lang.Integer.max; | ||
| + | ||
| import javafx.beans.value.ObservableValue; | ||
| /** | ||
| * Responsible for inserting a caret position token into an R document. | ||
| * | ||
| * @author White Magic Software, Ltd. | ||
| */ | ||
| public class RMarkdownCaretInsertionProcessor | ||
| - extends MarkdownCaretInsertionProcessor { | ||
| + extends MarkdownCaretInsertionProcessor { | ||
| /** | ||
| * Constructs a processor capable of inserting a caret marker into Markdown. | ||
| * | ||
| * @param processor The next processor in the chain. | ||
| - * @param position The caret's current position in the text. | ||
| + * @param position The caret's current position in the text. | ||
| */ | ||
| public RMarkdownCaretInsertionProcessor( | ||
| - final Processor<String> processor, | ||
| - final ObservableValue<Integer> position ) { | ||
| + final Processor<String> processor, | ||
| + final ObservableValue<Integer> position ) { | ||
| super( processor, position ); | ||
| } | ||
| /** | ||
| * Changes the text to insert a "caret" at the caret position. This will | ||
| * insert the unique key of Constants.MD_CARET_POSITION into the document. | ||
| * | ||
| * @param text The text document to process. | ||
| - * | ||
| * @return The text with the caret position token inserted at the caret | ||
| * position. | ||
| // insertion point. | ||
| final boolean between = isBetween( offset, rPrefix, rSuffix ); | ||
| - | ||
| + | ||
| // Insert the caret marker at the start of the R statement. | ||
| if( between ) { | ||
| public RVariableProcessor( | ||
| - final Processor<String> rp, final Map<String, String> map ) { | ||
| + final Processor<String> rp, final Map<String, String> map ) { | ||
| super( rp, map ); | ||
| } | ||
| * | ||
| * @param map Map of variable names to values. | ||
| - * | ||
| - * @return | ||
| + * @return Map of R variables. | ||
| */ | ||
| private Map<String, String> toR( final Map<String, String> map ) { | ||
| * | ||
| * @param key The variable name to transform, can be empty but not null. | ||
| - * | ||
| * @return The transformed variable name. | ||
| */ | ||
| /** | ||
| * TODO: Make generic method for replacing text. | ||
| - * | ||
| - * @see CaretReplacementProcessor.replace | ||
| * | ||
| * @param haystack Search this string for the needle, must not be null. | ||
| - * @param needle The character to find in the haystack. | ||
| - * @param thread Replace the needle with this text, if the needle is found. | ||
| - * | ||
| + * @param needle The character to find in the haystack. | ||
| + * @param thread Replace the needle with this text, if the needle is found. | ||
| * @return The haystack with the all instances of needle replaced with thread. | ||
| */ | ||
| + @SuppressWarnings("SameParameterValue") | ||
| private String escape( | ||
| - final String haystack, final char needle, final String thread ) { | ||
| + final String haystack, final char needle, final String thread ) { | ||
| int end = haystack.indexOf( needle ); | ||
| while( end >= 0 ) { | ||
| - sb.append( haystack.substring( start, end ) ).append( thread ); | ||
| + sb.append( haystack, start, end ).append( thread ); | ||
| start = end + 1; | ||
| end = haystack.indexOf( needle, start ); | ||