]> source.dussan.org Git - vaadin-framework.git/commitdiff
Add @JavaScript and @StyleSheet and remove @LoadScripts (#9044)
authorLeif Åstrand <leif@vaadin.com>
Wed, 27 Jun 2012 13:52:09 +0000 (16:52 +0300)
committerLeif Åstrand <leif@vaadin.com>
Wed, 27 Jun 2012 13:52:29 +0000 (16:52 +0300)
src/com/vaadin/annotations/JavaScript.java [new file with mode: 0644]
src/com/vaadin/annotations/LoadScripts.java [deleted file]
src/com/vaadin/annotations/StyleSheet.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
src/com/vaadin/terminal/gwt/client/ResourceLoader.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
src/com/vaadin/terminal/gwt/server/BootstrapHandler.java
src/com/vaadin/terminal/gwt/server/CommunicationManager.java
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java
tests/testbench/com/vaadin/tests/extensions/SimpleJavaScriptExtensionTest.java

diff --git a/src/com/vaadin/annotations/JavaScript.java b/src/com/vaadin/annotations/JavaScript.java
new file mode 100644 (file)
index 0000000..8e0bc09
--- /dev/null
@@ -0,0 +1,16 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface JavaScript {
+    public String[] value();
+}
diff --git a/src/com/vaadin/annotations/LoadScripts.java b/src/com/vaadin/annotations/LoadScripts.java
deleted file mode 100644 (file)
index 84ac2d2..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* 
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Temporary hack used for ensuring external javascript libraries are included.
- * To add a javascript, add this annotation to your Root class.
- * 
- * @deprecated Will be removed in favor of a more robust solution before version
- *             7.0.0
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-@Deprecated
-public @interface LoadScripts {
-    public String[] value();
-
-}
diff --git a/src/com/vaadin/annotations/StyleSheet.java b/src/com/vaadin/annotations/StyleSheet.java
new file mode 100644 (file)
index 0000000..40f7551
--- /dev/null
@@ -0,0 +1,16 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface StyleSheet {
+    public String[] value();
+}
index f0470c8ee8b425ee86f04439442883e17de74c99..3ec4233c59b858f9d13fc35592a98c1752980d14 100644 (file)
@@ -40,6 +40,8 @@ import com.google.gwt.user.client.Timer;
 import com.google.gwt.user.client.ui.HasWidgets;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadEvent;
+import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadListener;
 import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper;
 import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
 import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
@@ -1067,6 +1069,14 @@ public class ApplicationConnection {
                     json.getValueMap("typeMappings"), widgetSet);
         }
 
+        VConsole.log("Handling resource dependencies");
+        if (json.containsKey("scriptDependencies")) {
+            loadScriptDependencies(json.getJSStringArray("scriptDependencies"));
+        }
+        if (json.containsKey("styleDependencies")) {
+            loadStyleDependencies(json.getJSStringArray("styleDependencies"));
+        }
+
         handleUIDLDuration.logDuration(
                 " * Handling type mappings from server completed", 10);
         /*
@@ -1611,6 +1621,51 @@ public class ApplicationConnection {
         ApplicationConfiguration.runWhenWidgetsLoaded(c);
     }
 
+    private static void loadStyleDependencies(JsArrayString dependencies) {
+        // Assuming no reason to interpret in a defined order
+        ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
+            public void onResourceLoad(ResourceLoadEvent event) {
+                ApplicationConfiguration.endWidgetLoading();
+            }
+        };
+        ResourceLoader loader = ResourceLoader.get();
+        for (int i = 0; i < dependencies.length(); i++) {
+            ApplicationConfiguration.startWidgetLoading();
+            loader.loadStylesheet(dependencies.get(i), resourceLoadListener);
+        }
+    }
+
+    private static void loadScriptDependencies(final JsArrayString dependencies) {
+        if (dependencies.length() == 0) {
+            return;
+        }
+
+        // Listener that loads the next when one is completed
+        ResourceLoadListener resourceLoadListener = new ResourceLoadListener() {
+            public void onResourceLoad(ResourceLoadEvent event) {
+                if (dependencies.length() != 0) {
+                    ApplicationConfiguration.startWidgetLoading();
+                    // Load next in chain (hopefully already preloaded)
+                    event.getResourceLoader().loadScript(dependencies.shift(),
+                            this);
+                }
+                // Call start for next before calling end for current
+                ApplicationConfiguration.endWidgetLoading();
+            }
+        };
+
+        ResourceLoader loader = ResourceLoader.get();
+
+        // Start chain by loading first
+        ApplicationConfiguration.startWidgetLoading();
+        loader.loadScript(dependencies.shift(), resourceLoadListener);
+
+        // Preload all remaining
+        for (int i = 0; i < dependencies.length(); i++) {
+            loader.loadScript(dependencies.get(i), null);
+        }
+    }
+
     // Redirect browser, null reloads current page
     private static native void redirect(String url)
     /*-{
diff --git a/src/com/vaadin/terminal/gwt/client/ResourceLoader.java b/src/com/vaadin/terminal/gwt/client/ResourceLoader.java
new file mode 100644 (file)
index 0000000..7abafbc
--- /dev/null
@@ -0,0 +1,401 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.RepeatingCommand;
+import com.google.gwt.dom.client.AnchorElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.LinkElement;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.dom.client.ObjectElement;
+import com.google.gwt.dom.client.ScriptElement;
+
+/**
+ * ResourceLoader lets you dynamically include external scripts and styles on
+ * the page and lets you know when the resource has been loaded.
+ * 
+ * You can also preload resources, allowing them to get cached by the browser
+ * without being evaluated. This enables downloading multiple resources at once
+ * while still controlling in which order e.g. scripts are executed.
+ * 
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ */
+public class ResourceLoader {
+    /**
+     * Event fired when a resource has been loaded.
+     */
+    public static class ResourceLoadEvent {
+        private ResourceLoader loader;
+        private String resourceUrl;
+        private final boolean preload;
+
+        /**
+         * Creates a new event.
+         * 
+         * @param loader
+         *            the resource loader that has loaded the resource
+         * @param resourceUrl
+         *            the url of the loaded resource
+         * @param preload
+         *            true if the resource has only been preloaded, false if
+         *            it's fully loaded
+         */
+        public ResourceLoadEvent(ResourceLoader loader, String resourceUrl,
+                boolean preload) {
+            this.loader = loader;
+            this.resourceUrl = resourceUrl;
+            this.preload = preload;
+        }
+
+        /**
+         * Gets the resource loader that has fired this event
+         * 
+         * @return the resource loader
+         */
+        public ResourceLoader getResourceLoader() {
+            return loader;
+        }
+
+        /**
+         * Gets the absolute url of the loaded resource.
+         * 
+         * @return the absolute url of the loaded resource
+         */
+        public String getResourceUrl() {
+            return resourceUrl;
+        }
+
+        /**
+         * Returns true if the resource has been preloaded, false if it's fully
+         * loaded
+         * 
+         * @see ResourceLoader#preloadResource(String, ResourceLoadListener)
+         * 
+         * @return true if the resource has been preloaded, false if it's fully
+         *         loaded
+         */
+        public boolean isPreload() {
+            return preload;
+        }
+    }
+
+    /**
+     * Event listener that gets notified when a resource has been loaded
+     */
+    public interface ResourceLoadListener {
+        /**
+         * Notified this ResourceLoadListener that a resource has been loaded
+         * 
+         * @see ResourceLoadEvent
+         * 
+         * @param event
+         *            a resource load event with information about the loaded
+         *            resource
+         */
+        public void onResourceLoad(ResourceLoadEvent event);
+    }
+
+    private static final ResourceLoader INSTANCE = GWT
+            .create(ResourceLoader.class);
+
+    private ApplicationConnection connection;
+
+    private final Set<String> loadedResources = new HashSet<String>();
+    private final Set<String> preloadedResources = new HashSet<String>();
+
+    private final Map<String, Collection<ResourceLoadListener>> loadListeners = new HashMap<String, Collection<ResourceLoadListener>>();
+    private final Map<String, Collection<ResourceLoadListener>> preloadListeners = new HashMap<String, Collection<ResourceLoadListener>>();
+
+    private final Element head;
+
+    /**
+     * Creates a new resource loader. You should generally not create you own
+     * resource loader, but instead use {@link ResourceLoader#get()} to get an
+     * instance.
+     */
+    protected ResourceLoader() {
+        Document document = Document.get();
+        head = document.getElementsByTagName("head").getItem(0);
+
+        // detect already loaded scripts and stylesheets
+        NodeList<Element> scripts = document.getElementsByTagName("script");
+        for (int i = 0; i < scripts.getLength(); i++) {
+            ScriptElement element = ScriptElement.as(scripts.getItem(i));
+            String src = element.getSrc();
+            if (src != null && src.length() != 0) {
+                loadedResources.add(src);
+            }
+        }
+
+        NodeList<Element> links = document.getElementsByTagName("link");
+        for (int i = 0; i < links.getLength(); i++) {
+            LinkElement linkElement = LinkElement.as(links.getItem(i));
+            String rel = linkElement.getRel();
+            String href = linkElement.getHref();
+            if ("stylesheet".equalsIgnoreCase(rel) && href != null
+                    && href.length() != 0) {
+                loadedResources.add(href);
+            }
+        }
+    }
+
+    /**
+     * Returns the default ResourceLoader
+     * 
+     * @return the default ResourceLoader
+     */
+    public static ResourceLoader get() {
+        return INSTANCE;
+    }
+
+    /**
+     * Load a script and notify a listener when the script is loaded. Calling
+     * this method when the script is currently loading or already loaded
+     * doesn't cause the script to be loaded again, but the listener will still
+     * be notified when appropriate.
+     * 
+     * 
+     * @param scriptUrl
+     *            the url of the script to load
+     * @param resourceLoadListener
+     *            the listener that will get notified when the script is loaded
+     */
+    public void loadScript(final String scriptUrl,
+            final ResourceLoadListener resourceLoadListener) {
+        final String url = getAbsoluteUrl(scriptUrl);
+        if (loadedResources.contains(url)) {
+            if (resourceLoadListener != null) {
+                resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this,
+                        url, false));
+            }
+            return;
+        }
+
+        if (preloadListeners.containsKey(url)) {
+            preloadResource(url, new ResourceLoadListener() {
+                public void onResourceLoad(ResourceLoadEvent event) {
+                    loadScript(url, resourceLoadListener);
+                }
+            });
+            return;
+        }
+
+        if (addListener(url, resourceLoadListener, loadListeners)) {
+            ScriptElement scriptTag = Document.get().createScriptElement();
+            scriptTag.setSrc(url);
+            scriptTag.setType("text/javascript");
+            addOnloadHandler(scriptTag, url, false);
+            head.appendChild(scriptTag);
+        }
+    }
+
+    private static String getAbsoluteUrl(String url) {
+        AnchorElement a = Document.get().createAnchorElement();
+        a.setHref(url);
+        return a.getHref();
+    }
+
+    /**
+     * Download a resource and notify a listener when the resource is loaded
+     * without attempting to interpret the resource. When a resource has been
+     * preloaded, it will be present in the browser's cache (provided the HTTP
+     * headers allow caching), making a subsequent load operation complete
+     * without having to wait for the resource to be downloaded again.
+     * 
+     * Calling this method when the resource is currently loading, currently
+     * preloading, already preloaded or already loaded doesn't cause the
+     * resource to be preloaded again, but the listener will still be notified
+     * when appropriate.
+     * 
+     * @param url
+     *            the url of the resource to preload
+     * @param resourceLoadListener
+     *            the listener that will get notified when the resource is
+     *            preloaded
+     */
+    public void preloadResource(String url,
+            ResourceLoadListener resourceLoadListener) {
+        url = getAbsoluteUrl(url);
+        if (loadedResources.contains(url) || preloadedResources.contains(url)) {
+            if (resourceLoadListener != null) {
+                resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this,
+                        url, !loadedResources.contains(url)));
+            }
+            return;
+        }
+
+        if (addListener(url, resourceLoadListener, preloadListeners)
+                && !loadListeners.containsKey(url)) {
+            // Inject loader element if this is the first time this is preloaded
+            // AND the resources isn't already being loaded in the normal way
+
+            Element element = getPreloadElement(url);
+            addOnloadHandler(element, url, true);
+
+            // TODO Remove object when loaded (without causing spinner in FF)
+            Document.get().getBody().appendChild(element);
+        }
+    }
+
+    private static Element getPreloadElement(String url) {
+        if (BrowserInfo.get().isIE()) {
+            ScriptElement element = Document.get().createScriptElement();
+            element.setSrc(url);
+            element.setType("text/cache");
+            return element;
+        } else {
+            ObjectElement element = Document.get().createObjectElement();
+            element.setData(url);
+            element.setType("text/plain");
+            element.setHeight("0px");
+            element.setWidth("0px");
+            return element;
+        }
+    }
+
+    private native void addOnloadHandler(Element element, String url,
+            boolean preload)
+    /*-{
+        var self = this;
+        var done = $entry(function() {
+            element.onloadDone = true;
+            element.onload = null;
+            element.onreadystatechange = null;
+            self.@com.vaadin.terminal.gwt.client.ResourceLoader::onResourceLoad(Ljava/lang/String;Z)(url, preload);
+        });
+        element.onload = function() {
+            if (!element.onloadDone) {
+                done(); 
+            }
+        };
+        element.onreadystatechange = function() { 
+            if (("loaded" === element.readyState || "complete" === element.readyState) && !element.onloadDone ) {
+                done();
+            }
+        };       
+    }-*/;
+
+    /**
+     * Load a stylesheet and notify a listener when the stylesheet is loaded.
+     * Calling this method when the stylesheet is currently loading or already
+     * loaded doesn't cause the stylesheet to be loaded again, but the listener
+     * will still be notified when appropriate.
+     * 
+     * @param stylesheetUrl
+     *            the url of the stylesheet to load
+     * @param resourceLoadListener
+     *            the listener that will get notified when the stylesheet is
+     *            loaded
+     */
+    public void loadStylesheet(final String stylesheetUrl,
+            final ResourceLoadListener resourceLoadListener) {
+        final String url = getAbsoluteUrl(stylesheetUrl);
+        if (loadedResources.contains(url)) {
+            if (resourceLoadListener != null) {
+                resourceLoadListener.onResourceLoad(new ResourceLoadEvent(this,
+                        url, false));
+            }
+            return;
+        }
+
+        if (preloadListeners.containsKey(url)) {
+            preloadResource(url, new ResourceLoadListener() {
+                public void onResourceLoad(ResourceLoadEvent event) {
+                    loadStylesheet(url, resourceLoadListener);
+                }
+            });
+            return;
+        }
+
+        if (addListener(url, resourceLoadListener, loadListeners)) {
+            LinkElement linkElement = Document.get().createLinkElement();
+            linkElement.setRel("stylesheet");
+            linkElement.setType("text/css");
+            linkElement.setHref(url);
+
+            if (BrowserInfo.get().isSafari()) {
+                // Safari doesn't fire onload events for link elements
+                // See http://www.phpied.com/when-is-a-stylesheet-really-loaded/
+                // TODO Stop checking after some timeout
+                Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() {
+                    public boolean execute() {
+                        if (isStyleSheetPresent(url)) {
+                            onResourceLoad(url, false);
+                            return false; // Stop repeating
+                        } else {
+                            return true; // Continue repeating
+                        }
+                    }
+                }, 10);
+            } else {
+                addOnloadHandler(linkElement, url, false);
+            }
+
+            head.appendChild(linkElement);
+        }
+    }
+
+    private static native boolean isStyleSheetPresent(String url)
+    /*-{
+        for(var i = 0; i < $doc.styleSheets.length; i++) {
+            if ($doc.styleSheets[i].href === url) {
+                return true;
+            }
+        }
+        return false;
+    }-*/;
+
+    private static boolean addListener(String url,
+            ResourceLoadListener listener,
+            Map<String, Collection<ResourceLoadListener>> listenerMap) {
+        Collection<ResourceLoadListener> listeners = listenerMap.get(url);
+        if (listeners == null) {
+            listeners = new HashSet<ResourceLoader.ResourceLoadListener>();
+            listeners.add(listener);
+            listenerMap.put(url, listeners);
+            return true;
+        } else {
+            listeners.add(listener);
+            return false;
+        }
+    }
+
+    private void onResourceLoad(String resource, boolean preload) {
+        Collection<ResourceLoadListener> listeners;
+        if (preload) {
+            preloadedResources.add(resource);
+            listeners = preloadListeners.remove(resource);
+        } else {
+            if (preloadListeners.containsKey(resource)) {
+                // Also fire preload events for potential listeners
+                onResourceLoad(resource, true);
+            }
+            preloadedResources.remove(resource);
+            loadedResources.add(resource);
+            listeners = loadListeners.remove(resource);
+        }
+        if (listeners != null && !listeners.isEmpty()) {
+            ResourceLoadEvent event = new ResourceLoadEvent(this, resource,
+                    preload);
+            for (ResourceLoadListener listener : listeners) {
+                if (listener != null) {
+                    listener.onResourceLoad(event);
+                }
+            }
+        }
+    }
+
+}
index 7cad8e3a33692f5b38bf663c8acc18ccf0dc73b4..c65b8947d6c74f09993a3fb5e60eeb9e548fbc12 100644 (file)
@@ -25,6 +25,7 @@ import java.text.DateFormatSymbols;
 import java.text.SimpleDateFormat;
 import java.text.StringCharacterIterator;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
@@ -46,6 +47,8 @@ import com.vaadin.Application;
 import com.vaadin.Application.SystemMessages;
 import com.vaadin.RootRequiresMoreInformationException;
 import com.vaadin.Version;
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.StyleSheet;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
@@ -497,10 +500,11 @@ public abstract class AbstractCommunicationManager implements Serializable {
      *            found
      * @throws IOException
      * @throws InvalidUIDLSecurityKeyException
+     * @throws JSONException
      */
     public void handleUidlRequest(WrappedRequest request,
             WrappedResponse response, Callback callback, Root root)
-            throws IOException, InvalidUIDLSecurityKeyException {
+            throws IOException, InvalidUIDLSecurityKeyException, JSONException {
 
         checkWidgetsetVersion(request);
         requestThemeName = request.getParameter("theme");
@@ -696,11 +700,12 @@ public abstract class AbstractCommunicationManager implements Serializable {
      * @param analyzeLayouts
      * @throws PaintException
      * @throws IOException
+     * @throws JSONException
      */
     private void paintAfterVariableChanges(WrappedRequest request,
             WrappedResponse response, Callback callback, boolean repaintAll,
             final PrintWriter outWriter, Root root, boolean analyzeLayouts)
-            throws PaintException, IOException {
+            throws PaintException, IOException, JSONException {
 
         // Removes application if it has stopped during variable changes
         if (!application.isRunning()) {
@@ -764,7 +769,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
     @SuppressWarnings("unchecked")
     public void writeUidlResponse(WrappedRequest request, boolean repaintAll,
             final PrintWriter outWriter, Root root, boolean analyzeLayouts)
-            throws PaintException {
+            throws PaintException, JSONException {
         ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
         Application application = root.getApplication();
         // Paints components
@@ -1095,10 +1100,14 @@ public abstract class AbstractCommunicationManager implements Serializable {
         boolean typeMappingsOpen = false;
         ClientCache clientCache = getClientCache(root);
 
+        List<Class<? extends ClientConnector>> newConnectorTypes = new ArrayList<Class<? extends ClientConnector>>();
+
         for (Class<? extends ClientConnector> class1 : usedClientConnectors) {
             if (clientCache.cache(class1)) {
                 // client does not know the mapping key for this type, send
                 // mapping to client
+                newConnectorTypes.add(class1);
+
                 if (!typeMappingsOpen) {
                     typeMappingsOpen = true;
                     outWriter.print(", \"typeMappings\" : { ");
@@ -1142,6 +1151,54 @@ public abstract class AbstractCommunicationManager implements Serializable {
             }
         }
 
+        /*
+         * Ensure super classes come before sub classes to get script dependency
+         * order right. Sub class @JavaScript might assume that @JavaScript
+         * defined by super class is already loaded.
+         */
+        Collections.sort(newConnectorTypes, new Comparator<Class<?>>() {
+            public int compare(Class<?> o1, Class<?> o2) {
+                // TODO optimize using Class.isAssignableFrom?
+                return hierarchyDepth(o1) - hierarchyDepth(o2);
+            }
+
+            private int hierarchyDepth(Class<?> type) {
+                if (type == Object.class) {
+                    return 0;
+                } else {
+                    return hierarchyDepth(type.getSuperclass()) + 1;
+                }
+            }
+        });
+
+        List<String> scriptDependencies = new ArrayList<String>();
+        List<String> styleDependencies = new ArrayList<String>();
+
+        for (Class<? extends ClientConnector> class1 : newConnectorTypes) {
+            JavaScript jsAnnotation = class1.getAnnotation(JavaScript.class);
+            if (jsAnnotation != null) {
+                scriptDependencies.addAll(Arrays.asList(jsAnnotation.value()));
+            }
+
+            StyleSheet styleAnnotation = class1.getAnnotation(StyleSheet.class);
+            if (styleAnnotation != null) {
+                styleDependencies
+                        .addAll(Arrays.asList(styleAnnotation.value()));
+            }
+        }
+
+        // Include script dependencies in output if there are any
+        if (!scriptDependencies.isEmpty()) {
+            outWriter.print(", \"scriptDependencies\": "
+                    + new JSONArray(scriptDependencies).toString());
+        }
+
+        // Include style dependencies in output if there are any
+        if (!styleDependencies.isEmpty()) {
+            outWriter.print(", \"styleDependencies\": "
+                    + new JSONArray(styleDependencies).toString());
+        }
+
         // add any pending locale definitions requested by the client
         printLocaleDeclarations(outWriter);
 
@@ -1380,7 +1437,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
     private boolean handleVariables(WrappedRequest request,
             WrappedResponse response, Callback callback,
             Application application2, Root root) throws IOException,
-            InvalidUIDLSecurityKeyException {
+            InvalidUIDLSecurityKeyException, JSONException {
         boolean success = true;
 
         String changes = getRequestPayload(request);
@@ -2256,9 +2313,11 @@ public abstract class AbstractCommunicationManager implements Serializable {
      * @return a string with the initial UIDL message
      * @throws PaintException
      *             if an exception occurs while painting
+     * @throws JSONException
+     *             if an exception occurs while encoding output
      */
     protected String getInitialUIDL(WrappedRequest request, Root root)
-            throws PaintException {
+            throws PaintException, JSONException {
         // TODO maybe unify writeUidlResponse()?
         StringWriter sWriter = new StringWriter();
         PrintWriter pWriter = new PrintWriter(sWriter);
index 69f033c8cd14d28dc948600e894425dfa3bb59df..d32fa325f45834c7625eaf3c2b193d37cb351896 100644 (file)
@@ -9,10 +9,6 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Serializable;
 import java.io.Writer;
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
@@ -20,7 +16,6 @@ import javax.servlet.http.HttpServletResponse;
 import com.vaadin.Application;
 import com.vaadin.RootRequiresMoreInformationException;
 import com.vaadin.Version;
-import com.vaadin.annotations.LoadScripts;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
 import com.vaadin.terminal.DeploymentConfiguration;
@@ -490,50 +485,6 @@ public abstract class BootstrapHandler implements RequestHandler {
         page.write("<title>"
                 + AbstractApplicationServlet.safeEscapeForHtml(title)
                 + "</title>\n");
-
-        if (root != null) {
-            List<LoadScripts> loadScriptsAnnotations = getAnnotationsFor(
-                    root.getClass(), LoadScripts.class);
-            Collections.reverse(loadScriptsAnnotations);
-            // Begin from the end as a class might requests scripts that depend
-            // on script loaded by a super class
-            for (int i = loadScriptsAnnotations.size() - 1; i >= 0; i--) {
-                LoadScripts loadScripts = loadScriptsAnnotations.get(i);
-                String[] value = loadScripts.value();
-                if (value != null) {
-                    for (String script : value) {
-                        page.write("<script type='text/javascript' src='");
-                        page.write(script);
-                        page.write("'></script>\n");
-                    }
-                }
-            }
-
-        }
-    }
-
-    private static <T extends Annotation> List<T> getAnnotationsFor(
-            Class<?> type, Class<T> annotationType) {
-        List<T> list = new ArrayList<T>();
-        // Find from the class hierarchy
-        Class<?> currentType = type;
-        while (currentType != Object.class) {
-            T annotation = currentType.getAnnotation(annotationType);
-            if (annotation != null) {
-                list.add(annotation);
-            }
-            currentType = currentType.getSuperclass();
-        }
-
-        // Find from an implemented interface
-        for (Class<?> iface : type.getInterfaces()) {
-            T annotation = iface.getAnnotation(annotationType);
-            if (annotation != null) {
-                list.add(annotation);
-            }
-        }
-
-        return list;
     }
 
     /**
@@ -645,8 +596,10 @@ public abstract class BootstrapHandler implements RequestHandler {
      * @return a string with the initial UIDL message
      * @throws PaintException
      *             if an exception occurs while painting the components
+     * @throws JSONException
+     *             if an exception occurs while formatting the output
      */
     protected abstract String getInitialUIDL(WrappedRequest request, Root root)
-            throws PaintException;
+            throws PaintException, JSONException;
 
 }
index cc2981dc45bc26c21c6e8a62d34bc6a246c1062b..2d2888e03476942be1a25ad81420dde09877a538 100644 (file)
@@ -15,6 +15,7 @@ import java.util.UUID;
 import javax.servlet.ServletContext;
 
 import com.vaadin.Application;
+import com.vaadin.external.json.JSONException;
 import com.vaadin.terminal.PaintException;
 import com.vaadin.terminal.StreamVariable;
 import com.vaadin.terminal.WrappedRequest;
@@ -245,7 +246,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
 
             @Override
             protected String getInitialUIDL(WrappedRequest request, Root root)
-                    throws PaintException {
+                    throws PaintException, JSONException {
                 return CommunicationManager.this.getInitialUIDL(request, root);
             }
         };
index d3fbf4d988b13e6a8d2cb75e9e22b2b02bbd9749..7398315ee25a08d654267e80fccf99e2259932cb 100644 (file)
@@ -104,7 +104,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
     @Override
     public void handleUidlRequest(WrappedRequest request,
             WrappedResponse response, Callback callback, Root root)
-            throws IOException, InvalidUIDLSecurityKeyException {
+            throws IOException, InvalidUIDLSecurityKeyException, JSONException {
         setCurrentMimeReponse(response);
         super.handleUidlRequest(request, response, callback, root);
         currentMimeResponse = null;
@@ -253,7 +253,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
 
             @Override
             protected String getInitialUIDL(WrappedRequest request, Root root)
-                    throws PaintException {
+                    throws PaintException, JSONException {
                 return PortletCommunicationManager.this.getInitialUIDL(request,
                         root);
             }
index 5f2f945c8b4e138d9316159393ee04469b765941..15c5f6b6427a0d7ecf7bb12e9e5eaa1845e66dc2 100644 (file)
@@ -6,7 +6,7 @@ package com.vaadin.tests.components.javascriptcomponent;
 import java.util.Arrays;
 import java.util.List;
 
-import com.vaadin.annotations.LoadScripts;
+import com.vaadin.annotations.JavaScript;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.terminal.WrappedRequest;
@@ -17,7 +17,6 @@ import com.vaadin.ui.AbstractJavaScriptComponent;
 import com.vaadin.ui.JavaScriptCallback;
 import com.vaadin.ui.Notification;
 
-@LoadScripts({ "/statictestfiles/jsconnector.js" })
 public class BasicJavaScriptComponent extends AbstractTestRoot {
 
     public interface ExampleClickRpc extends ServerRpc {
@@ -36,6 +35,7 @@ public class BasicJavaScriptComponent extends AbstractTestRoot {
         }
     }
 
+    @JavaScript("/statictestfiles/jsconnector.js")
     public static class ExampleWidget extends AbstractJavaScriptComponent {
         public ExampleWidget() {
             registerRpc(new ExampleClickRpc() {
index bbfe3f0f46e83e79c8e138690bd3ae24382644f3..72168c08dfd0f18b2d0434c294e7e1cbac83a512 100644 (file)
@@ -4,7 +4,8 @@
 
 package com.vaadin.tests.extensions;
 
-import com.vaadin.annotations.LoadScripts;
+import com.vaadin.annotations.JavaScript;
+import com.vaadin.annotations.StyleSheet;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.terminal.AbstractJavaScriptExtension;
@@ -18,7 +19,6 @@ import com.vaadin.ui.Button.ClickEvent;
 import com.vaadin.ui.JavaScriptCallback;
 import com.vaadin.ui.Notification;
 
-@LoadScripts({ "/statictestfiles/jsextension.js" })
 public class SimpleJavaScriptExtensionTest extends AbstractTestRoot {
 
     public static class SimpleJavaScriptExtensionState extends
@@ -44,6 +44,8 @@ public class SimpleJavaScriptExtensionTest extends AbstractTestRoot {
         public void greet(String message);
     }
 
+    @JavaScript("/statictestfiles/jsextension.js")
+    @StyleSheet("/VAADIN/external1.css")
     public static class SimpleJavascriptExtension extends
             AbstractJavaScriptExtension {