| Author | djarvis <email> |
|---|---|
| Date | 2016-10-18 17:32:08 GMT-0700 |
| Commit | df6f3192992ff6b9756faed65251c225e1bccc5e |
| Parent | ac6bf49 |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
| - | ||
| package org.markdownwriterfx; | ||
| import java.text.MessageFormat; | ||
| import java.util.ResourceBundle; | ||
| +import java.util.Stack; | ||
| /** | ||
| - * @author Karl Tauber | ||
| + * Recursively resolves message properties. Property values can refer | ||
| + * to other properties using a <code>${var}</code> syntax. | ||
| + * | ||
| + * @author Karl Tauber, Dave Jarvis | ||
| */ | ||
| -public class Messages | ||
| -{ | ||
| - private static final String BUNDLE_NAME = "org.markdownwriterfx.messages"; | ||
| - private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); | ||
| +public class Messages { | ||
| - private Messages() { | ||
| - } | ||
| + private static final String BUNDLE_NAME = "org.markdownwriterfx.messages"; | ||
| + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); | ||
| - public static String get(String key) { | ||
| -// try { | ||
| - return RESOURCE_BUNDLE.getString(key); | ||
| -// } catch (MissingResourceException e) { | ||
| -// return '!' + key + '!'; | ||
| -// } | ||
| - } | ||
| + private Messages() { | ||
| + } | ||
| - public static String get(String key, Object... args) { | ||
| - return MessageFormat.format(get(key), args); | ||
| - } | ||
| + /** | ||
| + * Return the value of a resource bundle value after having resolved any | ||
| + * references to other bundle variables. | ||
| + * | ||
| + * @param props The bundle containing resolvable properties. | ||
| + * @param s The value for a key to resolve. | ||
| + * | ||
| + * @return The value of the key with all references recursively dereferenced. | ||
| + */ | ||
| + private static String resolve( ResourceBundle props, String s ) { | ||
| + StringBuilder sb = new StringBuilder( 256 ); | ||
| + Stack<StringBuilder> stack = new Stack<>(); | ||
| + int len = s.length(); | ||
| + | ||
| + for( int i = 0; i < len; i++ ) { | ||
| + char c = s.charAt( i ); | ||
| + | ||
| + switch( c ) { | ||
| + case '$': { | ||
| + if( i + 1 < len && s.charAt( i + 1 ) == '{' ) { | ||
| + stack.push( sb ); | ||
| + sb = new StringBuilder( 256 ); | ||
| + i++; | ||
| + } | ||
| + break; | ||
| + } | ||
| + | ||
| + case '}': { | ||
| + if( stack.isEmpty() ) { | ||
| + throw new IllegalArgumentException( "unexpected '}'" ); | ||
| + } | ||
| + | ||
| + String name = sb.toString(); | ||
| + | ||
| + sb = stack.pop(); | ||
| + sb.append( props.getString( name ) ); | ||
| + break; | ||
| + } | ||
| + | ||
| + default: { | ||
| + sb.append( c ); | ||
| + break; | ||
| + } | ||
| + } | ||
| + } | ||
| + | ||
| + if( !stack.isEmpty() ) { | ||
| + throw new IllegalArgumentException( "missing '}'" ); | ||
| + } | ||
| + | ||
| + return sb.toString(); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the value for a key from the message bundle. | ||
| + * | ||
| + * @param key Retrieve the value for this key. | ||
| + * | ||
| + * @return The value for the key. | ||
| + */ | ||
| + public static String get( String key ) { | ||
| + return resolve( RESOURCE_BUNDLE, RESOURCE_BUNDLE.getString( key ) ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the value for a key from the message bundle with the arguments | ||
| + * replacing <code>{#}</code> placeholders. | ||
| + * | ||
| + * @param key Retrieve the value for this key. | ||
| + * @param args The values to substitute for placeholders. | ||
| + * | ||
| + * @return The value for the key. | ||
| + */ | ||
| + public static String get( String key, Object... args ) { | ||
| + return MessageFormat.format( get( key ), args ); | ||
| + } | ||
| } | ||
| -/* | ||
| - * 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 org.markdownwriterfx; | ||
| - | ||
| -import javafx.application.Application; | ||
| -import javafx.scene.Scene; | ||
| -import javafx.scene.image.Image; | ||
| -import javafx.stage.Stage; | ||
| -import org.markdownwriterfx.service.Options; | ||
| -import org.markdownwriterfx.service.Settings; | ||
| -import org.markdownwriterfx.util.StageState; | ||
| - | ||
| -/** | ||
| - * Markdown Writer FX application. | ||
| - * | ||
| - * @author Karl Tauber | ||
| - */ | ||
| -public final class Scrivano extends Application { | ||
| - | ||
| - private static Application app; | ||
| - | ||
| - private MainWindow mainWindow; | ||
| - private StageState stageState; | ||
| - private final Settings settings = Services.load( Settings.class ); | ||
| - private final Options options = Services.load( Options.class ); | ||
| - | ||
| - public static void main( String[] args ) { | ||
| - launch( args ); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Application entry point. | ||
| - * | ||
| - * @param stage The primary application stage. | ||
| - * | ||
| - * @throws Exception Could not read configuration file. | ||
| - */ | ||
| - @Override | ||
| - public void start( Stage stage ) throws Exception { | ||
| - initApplication(); | ||
| - initWindow(); | ||
| - initState( stage ); | ||
| - initStage( stage ); | ||
| - | ||
| - stage.show(); | ||
| - } | ||
| - | ||
| - private void initApplication() { | ||
| - app = this; | ||
| - } | ||
| - | ||
| - private Settings getSettings() { | ||
| - return this.settings; | ||
| - } | ||
| - | ||
| - private Options getOptions() { | ||
| - return this.options; | ||
| - } | ||
| - | ||
| - private String getApplicationTitle() { | ||
| - return getSettings().getSetting( "application.title", "Scrivano" ); | ||
| - } | ||
| - | ||
| - private void initWindow() { | ||
| - setWindow( new MainWindow() ); | ||
| - } | ||
| - | ||
| - private void initState( Stage stage ) { | ||
| - stageState = new StageState( stage, getOptions().getState() ); | ||
| - } | ||
| - | ||
| - private void initStage( Stage stage ) { | ||
| - stage.getIcons().addAll( | ||
| - new Image( "org/markdownwriterfx/markdownwriterfx16.png" ), | ||
| - new Image( "org/markdownwriterfx/markdownwriterfx32.png" ), | ||
| - new Image( "org/markdownwriterfx/markdownwriterfx128.png" ), | ||
| - new Image( "org/markdownwriterfx/markdownwriterfx256.png" ), | ||
| - new Image( "org/markdownwriterfx/markdownwriterfx512.png" ) ); | ||
| - stage.setTitle( getApplicationTitle() ); | ||
| - stage.setScene( getScene() ); | ||
| - } | ||
| - | ||
| - private Scene getScene() { | ||
| - return getMainWindow().getScene(); | ||
| - } | ||
| - | ||
| - protected MainWindow getMainWindow() { | ||
| - return this.mainWindow; | ||
| - } | ||
| - | ||
| - private void setWindow( MainWindow mainWindow ) { | ||
| - this.mainWindow = mainWindow; | ||
| - } | ||
| - | ||
| - private StageState getStageState() { | ||
| - return this.stageState; | ||
| - } | ||
| - | ||
| - private void setStageState( StageState stageState ) { | ||
| - this.stageState = stageState; | ||
| - } | ||
| - | ||
| - private static Application getApplication() { | ||
| - return app; | ||
| - } | ||
| - | ||
| - public static void showDocument( String uri ) { | ||
| - getApplication().getHostServices().showDocument( uri ); | ||
| - } | ||
| -} | ||
| +/* | ||
| + * 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 org.markdownwriterfx; | ||
| + | ||
| +import javafx.application.Application; | ||
| +import javafx.scene.Scene; | ||
| +import javafx.scene.image.Image; | ||
| +import javafx.stage.Stage; | ||
| +import org.markdownwriterfx.service.Options; | ||
| +import org.markdownwriterfx.service.Settings; | ||
| +import org.markdownwriterfx.util.StageState; | ||
| + | ||
| +/** | ||
| + * Based on the Markdown Writer FX application. | ||
| + * | ||
| + * @author Karl Tauber and White Magic Software, Ltd. | ||
| + */ | ||
| +public final class Scrivendor extends Application { | ||
| + | ||
| + private static Application app; | ||
| + | ||
| + private MainWindow mainWindow; | ||
| + private StageState stageState; | ||
| + private final Settings settings = Services.load( Settings.class ); | ||
| + private final Options options = Services.load( Options.class ); | ||
| + | ||
| + public static void main( String[] args ) { | ||
| + launch( args ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Application entry point. | ||
| + * | ||
| + * @param stage The primary application stage. | ||
| + * | ||
| + * @throws Exception Could not read configuration file. | ||
| + */ | ||
| + @Override | ||
| + public void start( Stage stage ) throws Exception { | ||
| + initApplication(); | ||
| + initWindow(); | ||
| + initState( stage ); | ||
| + initStage( stage ); | ||
| + | ||
| + stage.show(); | ||
| + } | ||
| + | ||
| + private void initApplication() { | ||
| + app = this; | ||
| + } | ||
| + | ||
| + private Settings getSettings() { | ||
| + return this.settings; | ||
| + } | ||
| + | ||
| + private Options getOptions() { | ||
| + return this.options; | ||
| + } | ||
| + | ||
| + private String getApplicationTitle() { | ||
| + return Messages.get( "Application.title" ); | ||
| + } | ||
| + | ||
| + private void initWindow() { | ||
| + setWindow( new MainWindow() ); | ||
| + } | ||
| + | ||
| + private void initState( Stage stage ) { | ||
| + stageState = new StageState( stage, getOptions().getState() ); | ||
| + } | ||
| + | ||
| + private void initStage( Stage stage ) { | ||
| + stage.getIcons().addAll( | ||
| + new Image( "org/markdownwriterfx/markdownwriterfx16.png" ), | ||
| + new Image( "org/markdownwriterfx/markdownwriterfx32.png" ), | ||
| + new Image( "org/markdownwriterfx/markdownwriterfx128.png" ), | ||
| + new Image( "org/markdownwriterfx/markdownwriterfx256.png" ), | ||
| + new Image( "org/markdownwriterfx/markdownwriterfx512.png" ) ); | ||
| + stage.setTitle( getApplicationTitle() ); | ||
| + stage.setScene( getScene() ); | ||
| + } | ||
| + | ||
| + private Scene getScene() { | ||
| + return getMainWindow().getScene(); | ||
| + } | ||
| + | ||
| + protected MainWindow getMainWindow() { | ||
| + return this.mainWindow; | ||
| + } | ||
| + | ||
| + private void setWindow( MainWindow mainWindow ) { | ||
| + this.mainWindow = mainWindow; | ||
| + } | ||
| + | ||
| + private StageState getStageState() { | ||
| + return this.stageState; | ||
| + } | ||
| + | ||
| + private void setStageState( StageState stageState ) { | ||
| + this.stageState = stageState; | ||
| + } | ||
| + | ||
| + private static Application getApplication() { | ||
| + return app; | ||
| + } | ||
| + | ||
| + public static void showDocument( String uri ) { | ||
| + getApplication().getHostServices().showDocument( uri ); | ||
| + } | ||
| +} | ||
| import javafx.beans.property.StringProperty; | ||
| import javafx.scene.control.Hyperlink; | ||
| -import org.markdownwriterfx.Scrivano; | ||
| +import org.markdownwriterfx.Scrivendor; | ||
| /** | ||
| @Override | ||
| public void fire() { | ||
| - Scrivano.showDocument(getUri()); | ||
| + Scrivendor.showDocument(getUri()); | ||
| } | ||
| # | ||
| +#---- Application ---- | ||
| + | ||
| +# The application title should exist only once in the entire code base. | ||
| +# All other references should either refer to this value via the Messages | ||
| +# class, or indirectly using ${Application.title}. | ||
| +Application.title=Scrivendor | ||
| + | ||
| #---- MainWindow ---- | ||
| MainWindow.helpMenu=_Help | ||
| -MainWindow.helpAboutAction=About Scrivano | ||
| +MainWindow.helpAboutAction=About ${Application.title} | ||
| MainWindow.about.title=_About | ||
| -MainWindow.about.headerText=Scrivano | ||
| -MainWindow.about.contentText=Copyright (c) 2016 White Magic Software, Ltd. | ||
| +MainWindow.about.headerText=${Application.title} | ||
| +MainWindow.about.contentText=Copyright 2016 White Magic Software, Ltd. | ||
| #---- FileEditor ---- | ||
| -application.title=Scrivano | ||
| - | ||
| # Comma-separated list of markdown filename extensions. | ||
| application.extensions.markdown=*.Rmd,*.md,*.txt,*.markdown |
| Delta | 232 lines added, 159 lines removed, 73-line increase |
|---|