boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 12 Dec 2013 23:54:32 +0100
changeset 349 53634fd10e30
parent 323 86aabecda7a3
child 350 25abd193f145
child 358 80702021b851
permissions -rw-r--r--
Load global scripts when first method in the class is called
     1 /**
     2  * HTML via Java(tm) Language Bindings
     3  * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details. apidesign.org
    13  * designates this particular file as subject to the
    14  * "Classpath" exception as provided by apidesign.org
    15  * in the License file that accompanied this code.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program. Look for COPYING file in the top folder.
    19  * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    20  */
    21 package org.apidesign.html.boot.spi;
    22 
    23 import java.io.Closeable;
    24 import java.io.InputStream;
    25 import java.io.InputStreamReader;
    26 import java.io.Reader;
    27 import java.net.URL;
    28 import java.util.HashMap;
    29 import java.util.HashSet;
    30 import java.util.Map;
    31 import java.util.Set;
    32 import java.util.WeakHashMap;
    33 import net.java.html.js.JavaScriptBody;
    34 import org.apidesign.html.boot.impl.FnContext;
    35 
    36 /** Represents single JavaScript function that can be invoked. 
    37  * Created via {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
    38  *
    39  * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    40  */
    41 public abstract class Fn {
    42     private final Presenter presenter;
    43     
    44     /**
    45      * @deprecated Ineffective as of 0.6. 
    46      * Provide a presenter via {@link #Fn(org.apidesign.html.boot.spi.Fn.Presenter)}
    47      * constructor
    48      */
    49     @Deprecated
    50     protected Fn() {
    51         this(null);
    52     }
    53     
    54     /** Creates new function object and associates it with given presenter.
    55      * 
    56      * @param presenter the browser presenter associated with this function
    57      * @since 0.6 
    58      */
    59     protected Fn(Presenter presenter) {
    60         this.presenter = presenter;
    61     }
    62 
    63     /** True, if currently active presenter is the same as presenter this
    64      * function has been created for via {@link #Fn(org.apidesign.html.boot.spi.Fn.Presenter)}.
    65      * 
    66      * @return true, if proper presenter is used
    67      */
    68     public final boolean isValid() {
    69         return FnContext.currentPresenter() == presenter;
    70     }
    71     
    72     /** Helper method to check if the provided instance is valid function.
    73      * Checks if the parameter is non-null and if so, does {@link #isValid()}
    74      * check.
    75      * 
    76      * @param fnOrNull function or <code>null</code>
    77      * @return true if the parameter is non-null and valid
    78      * @since 0.7
    79      */
    80     public static boolean isValid(Fn fnOrNull) {
    81         return fnOrNull != null && fnOrNull.isValid();
    82     }
    83 
    84     /** Helper method to find current presenter and ask it to define new
    85      * function by calling {@link Presenter#defineFn(java.lang.String, java.lang.String...)}.
    86      * 
    87      * @param caller the class who wishes to define the function
    88      * @param code the body of the function (can reference <code>this</code> and <code>names</code> variables)
    89      * @param names names of individual parameters
    90      * @return the function object that can be {@link Fn#invoke(java.lang.Object, java.lang.Object...) invoked}
    91      * @since 0.7
    92      */
    93     public static Fn define(Class<?> caller, String code, String... names) {
    94         return FnContext.currentPresenter().defineFn(code, names);
    95     }
    96     
    97     private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
    98     public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
    99         return new Fn() {
   100             @Override
   101             public Object invoke(Object thiz, Object... args) throws Exception {
   102                 final Presenter p = FnContext.currentPresenter();
   103                 Set<Presenter> there = LOADED.get(resource);
   104                 if (there == null) {
   105                     there = new HashSet<Presenter>();
   106                     LOADED.put(resource, there);
   107                 }
   108                 if (there.add(p)) {
   109                     InputStream is = caller.getClassLoader().getResourceAsStream(resource);
   110                     try {
   111                         InputStreamReader r = new InputStreamReader(is, "UTF-8");
   112                         p.loadScript(r);
   113                     } finally {
   114                         is.close();
   115                     }
   116                 }
   117                 return fn.invoke(thiz, args);
   118             }
   119         };
   120     }
   121     
   122     /** The currently active presenter.
   123      * 
   124      * @return the currently active presenter or <code>null</code>
   125      * @since 0.7
   126      */
   127     public static Presenter activePresenter() {
   128         return FnContext.currentPresenter();
   129     }
   130     
   131     /** Activates given presenter. Used by the code generated by 
   132      * {@link JavaScriptBody} annotation: 
   133      * <pre>
   134      * try ({@link Closeable} c = Fn.activate(presenter)) {
   135      *   doCallsInPresenterContext();
   136      * }
   137      * </pre>
   138      * 
   139      * @param p the presenter that should be active until closable is closed
   140      * @return the closable to close
   141      * @since 0.7
   142      */
   143     public static Closeable activate(Presenter p) {
   144         return FnContext.activate(p);
   145     }
   146     
   147     /** Invokes the defined function with specified <code>this</code> and
   148      * appropriate arguments.
   149      * 
   150      * @param thiz the meaning of <code>this</code> inside of the JavaScript
   151      *   function - can be <code>null</code>
   152      * @param args arguments for the function
   153      * @return return value from the function
   154      * @throws Exception if something goes wrong, as exception may be thrown
   155      */
   156     public abstract Object invoke(Object thiz, Object... args) throws Exception;
   157 
   158     /** The representation of a <em>presenter</em> - usually a browser window.
   159      * Should be provided by a library included in the application and registered
   160      * in <code>META-INF/services</code>, for example with
   161      * <code>@ServiceProvider(service = Fn.Presenter.class)</code> annotation.
   162      */
   163     public interface Presenter {
   164         /** Creates new function with given parameter names and provided body.
   165          * 
   166          * @param code the body of the function. Can refer to variables named
   167          *   as <code>names</code>
   168          * @param names names of parameters of the function - these will be 
   169          *   available when the <code>code</code> body executes
   170          * 
   171          * @return function that can be later invoked
   172          */
   173         public Fn defineFn(String code, String... names);
   174         
   175         /** Opens the browser, loads provided page and when the
   176          * page is ready, it calls back to the provider runnable.
   177          * 
   178          * @param page the URL for the page to display
   179          * @param onPageLoad callback when the page is ready
   180          */
   181         public void displayPage(URL page, Runnable onPageLoad);
   182         
   183         /** Loads a script into the browser JavaScript interpreter and 
   184          * executes it.
   185          * @param code the script to execute
   186          * @throws Exception if something goes wrong, throw an exception
   187          */
   188         public void loadScript(Reader code) throws Exception;
   189     }
   190 }