]> source.dussan.org Git - vaadin-framework.git/commitdiff
Initial ConnectorBundle implementation (#9371)
authorLeif Åstrand <leif@vaadin.com>
Fri, 17 Aug 2012 15:11:54 +0000 (18:11 +0300)
committerLeif Åstrand <leif@vaadin.com>
Wed, 22 Aug 2012 16:25:30 +0000 (19:25 +0300)
27 files changed:
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorInitVisitor.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/TypeVisitor.java [new file with mode: 0644]
client/src/com/vaadin/Vaadin.gwt.xml
client/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java
client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
client/src/com/vaadin/terminal/gwt/client/WidgetSet.java
client/src/com/vaadin/terminal/gwt/client/communication/DiffJSONSerializer.java
client/src/com/vaadin/terminal/gwt/client/communication/JSONSerializer.java
client/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
client/src/com/vaadin/terminal/gwt/client/communication/RpcManager.java
client/src/com/vaadin/terminal/gwt/client/communication/RpcMethod.java
client/src/com/vaadin/terminal/gwt/client/communication/URLReference_Serializer.java
client/src/com/vaadin/terminal/gwt/client/metadata/AsyncBundleLoader.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/BundleLoadCallback.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/ConnectorBundleLoader.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/Invoker.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/Method.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/Property.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/Type.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/TypeData.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataBundle.java [new file with mode: 0644]
client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataStore.java [new file with mode: 0644]

diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
new file mode 100644 (file)
index 0000000..cb967df
--- /dev/null
@@ -0,0 +1,280 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.shared.communication.ClientRpc;
+import com.vaadin.shared.communication.ServerRpc;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.Connect.LoadStyle;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader;
+import com.vaadin.terminal.gwt.client.metadata.TypeDataBundle;
+import com.vaadin.terminal.gwt.client.metadata.TypeDataStore;
+import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorBundle;
+import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorInitVisitor;
+import com.vaadin.terminal.gwt.widgetsetutils.metadata.TypeVisitor;
+
+public class ConnectorBundleLoaderFactory extends Generator {
+
+    @Override
+    public String generate(TreeLogger logger, GeneratorContext context,
+            String typeName) throws UnableToCompleteException {
+        TypeOracle typeOracle = context.getTypeOracle();
+
+        try {
+            JClassType classType = typeOracle.getType(typeName);
+            String packageName = classType.getPackage().getName();
+            String className = classType.getSimpleSourceName() + "Impl";
+
+            generateClass(logger, context, packageName, className, typeName);
+
+            return packageName + "." + className;
+        } catch (UnableToCompleteException e) {
+            // Just rethrow
+            throw e;
+        } catch (Exception e) {
+            logger.log(Type.ERROR, getClass() + " failed", e);
+            throw new UnableToCompleteException();
+        }
+
+    }
+
+    private void generateClass(TreeLogger logger, GeneratorContext context,
+            String packageName, String className, String requestedType)
+            throws Exception {
+        PrintWriter printWriter = context.tryCreate(logger, packageName,
+                className);
+        if (printWriter == null) {
+            return;
+        }
+
+        List<ConnectorBundle> bundles = buildBundles(logger,
+                context.getTypeOracle());
+
+        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
+                packageName, className);
+        composer.setSuperclass(requestedType);
+
+        SourceWriter w = composer.createSourceWriter(context, printWriter);
+
+        w.println("public void init() {");
+        w.indent();
+
+        for (ConnectorBundle bundle : bundles) {
+            String name = bundle.getName();
+            boolean isEager = name
+                    .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME);
+
+            w.print("addAsyncBlockLoader(new AsyncBundleLoader(\"");
+            w.print(escape(name));
+            w.print("\", ");
+
+            w.print("new String[] {");
+            for (Entry<JClassType, Set<String>> entry : bundle.getIdentifiers()
+                    .entrySet()) {
+                Set<String> identifiers = entry.getValue();
+                for (String id : identifiers) {
+                    w.print("\"");
+                    w.print(escape(id));
+                    w.print("\",");
+                }
+            }
+            w.println("}) {");
+            w.indent();
+
+            w.print("protected void load(final ");
+            w.print(TypeDataStore.class.getName());
+            w.println(" store) {");
+            w.indent();
+
+            if (!isEager) {
+                w.print(GWT.class.getName());
+                w.print(".runAsync(");
+            }
+
+            w.print("new ");
+            w.print(TypeDataBundle.class.getName());
+            w.println("(getName()) {");
+            w.indent();
+
+            w.println("public void load() {");
+            w.indent();
+
+            printBundleData(w, bundle);
+
+            // Close load method
+            w.outdent();
+            w.println("}");
+
+            // Close new TypeDataBundle() {}
+            w.outdent();
+            w.print("}");
+
+            if (isEager) {
+                w.println(".onSuccess();");
+            } else {
+                w.println(");");
+            }
+
+            // Close load method
+            w.outdent();
+            w.println("}");
+
+            // Close add(new ...
+            w.outdent();
+            w.println("});");
+        }
+
+        w.outdent();
+        w.println("}");
+
+        w.commit(logger);
+    }
+
+    private void printBundleData(SourceWriter w, ConnectorBundle bundle) {
+        writeIdentifiers(w, bundle);
+        writeGwtConstructors(w, bundle);
+    }
+
+    private void writeGwtConstructors(SourceWriter w, ConnectorBundle bundle) {
+        Set<JClassType> constructors = bundle.getGwtConstructors();
+        for (JClassType type : constructors) {
+            w.print("store.setConstructor(");
+            printClassLiteral(w, type);
+            w.print(", new Invoker() {");
+            w.indent();
+
+            w.println("public Object invoke(Object target, Object[] params) {");
+            w.indent();
+
+            w.print("return ");
+            w.print(GWT.class.getName());
+            w.print(".create(");
+            printClassLiteral(w, type);
+            w.println(");");
+
+            w.outdent();
+            w.println("}");
+
+            w.outdent();
+            w.println("});");
+        }
+    }
+
+    private void printClassLiteral(SourceWriter w, JClassType type) {
+        w.print(type.getQualifiedSourceName());
+        w.print(".class");
+    }
+
+    private void writeIdentifiers(SourceWriter w, ConnectorBundle bundle) {
+        Map<JClassType, Set<String>> identifiers = bundle.getIdentifiers();
+        for (Entry<JClassType, Set<String>> entry : identifiers.entrySet()) {
+            Set<String> ids = entry.getValue();
+            JClassType type = entry.getKey();
+            for (String id : ids) {
+                w.print("store.setClass(\"");
+                w.print(escape(id));
+                w.print("\", ");
+                printClassLiteral(w, type);
+                w.println(");");
+            }
+        }
+    }
+
+    private List<ConnectorBundle> buildBundles(TreeLogger logger,
+            TypeOracle typeOracle) throws NotFoundException {
+
+        Map<LoadStyle, Collection<JClassType>> connectorsByLoadStyle = new HashMap<LoadStyle, Collection<JClassType>>();
+        for (LoadStyle loadStyle : LoadStyle.values()) {
+            connectorsByLoadStyle.put(loadStyle, new ArrayList<JClassType>());
+        }
+
+        JClassType connectorType = typeOracle.getType(ServerConnector.class
+                .getName());
+        JClassType[] subtypes = connectorType.getSubtypes();
+        for (JClassType connectorSubtype : subtypes) {
+            if (!connectorSubtype.isAnnotationPresent(Connect.class)) {
+                continue;
+            }
+            LoadStyle loadStyle = getLoadStyle(connectorSubtype);
+            if (loadStyle != null) {
+                connectorsByLoadStyle.get(loadStyle).add(connectorSubtype);
+            }
+        }
+
+        List<ConnectorBundle> bundles = new ArrayList<ConnectorBundle>();
+
+        Collection<TypeVisitor> visitors = getVisitors(typeOracle);
+
+        ConnectorBundle eagerBundle = new ConnectorBundle(
+                ConnectorBundleLoader.EAGER_BUNDLE_NAME, null);
+
+        // Eager connectors and all RPC interfaces are loaded by default
+        eagerBundle.visitTypes(connectorsByLoadStyle.get(LoadStyle.EAGER),
+                visitors);
+        eagerBundle.visitSubTypes(
+                typeOracle.getType(ClientRpc.class.getName()), visitors);
+        eagerBundle.visitSubTypes(
+                typeOracle.getType(ServerRpc.class.getName()), visitors);
+
+        bundles.add(eagerBundle);
+
+        ConnectorBundle deferredBundle = new ConnectorBundle(
+                ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle);
+        deferredBundle.visitTypes(
+                connectorsByLoadStyle.get(LoadStyle.DEFERRED), visitors);
+
+        bundles.add(deferredBundle);
+
+        Collection<JClassType> lazy = connectorsByLoadStyle.get(LoadStyle.LAZY);
+        for (JClassType type : lazy) {
+            ConnectorBundle bundle = new ConnectorBundle(type.getName(),
+                    deferredBundle);
+            bundle.visitTypes(Collections.singleton(type), visitors);
+
+            bundles.add(bundle);
+        }
+
+        return bundles;
+    }
+
+    private Collection<TypeVisitor> getVisitors(TypeOracle oracle)
+            throws NotFoundException {
+        List<TypeVisitor> visitors = Arrays
+                .<TypeVisitor> asList(new ConnectorInitVisitor());
+        for (TypeVisitor typeVisitor : visitors) {
+            typeVisitor.init(oracle);
+        }
+        return visitors;
+    }
+
+    protected LoadStyle getLoadStyle(JClassType connectorType) {
+        Connect annotation = connectorType.getAnnotation(Connect.class);
+        return annotation.loadStyle();
+    }
+
+}
index 47062d9abba067d1a76ed96a8aa5d31a0b6ad95d..c6cf28db28a66548a1771ebc0619cebb32f89bdf 100644 (file)
@@ -104,7 +104,7 @@ public class GeneratedRpcMethodProviderGenerator extends Generator {
         composer.addImport("com.google.gwt.core.client.GWT");
         composer.addImport(RpcMethod.class.getName());
         composer.addImport(ClientRpc.class.getName());
-        composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class
+        composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class
                 .getName());
         composer.addImplementedInterface(GeneratedRpcMethodProvider.class
                 .getName());
index cc92551846b814bf0bd7ca1a41615fe475d01c6c..c7cc7bf7cbd097193005056932e9f64bb982d7aa 100644 (file)
@@ -125,7 +125,7 @@ public class SerializerGenerator extends Generator {
                 serializerClassName);
         composer.addImport(GWT.class.getName());
         composer.addImport(JSONValue.class.getName());
-        composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class
+        composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class
                 .getName());
         // composer.addImport(JSONObject.class.getName());
         // composer.addImport(VPaintableMap.class.getName());
index 9e91763b9f3318358601c443013bac8a3690bc80..ac22aa2e8a97454f45966062c507a6ef0a857b8b 100644 (file)
@@ -142,7 +142,7 @@ public class SerializerMapGenerator extends Generator {
                 .findType(JSONSerializer.class.getName());
         JType[] deserializeParamTypes = new JType[] {
                 typeOracle
-                        .findType(com.vaadin.terminal.gwt.client.communication.Type.class
+                        .findType(com.vaadin.terminal.gwt.client.metadata.Type.class
                                 .getName()),
                 typeOracle.findType(JSONValue.class.getName()),
                 typeOracle.findType(ApplicationConnection.class.getName()) };
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
new file mode 100644 (file)
index 0000000..3155ab4
--- /dev/null
@@ -0,0 +1,116 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+
+public class ConnectorBundle {
+    private final String name;
+    private final ConnectorBundle previousBundle;
+
+    private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>();
+    private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>();
+    private final Set<JClassType> visitedTypes = new HashSet<JClassType>();
+    private final Set<JClassType> visitQueue = new HashSet<JClassType>();
+
+    public ConnectorBundle(String name, ConnectorBundle previousBundle) {
+        this.name = name;
+        this.previousBundle = previousBundle;
+    }
+
+    public void setNeedsGwtConstructor(JClassType type) {
+        if (!needsGwtConstructor(type)) {
+            needsGwtConstructor.add(type);
+        }
+    }
+
+    private boolean needsGwtConstructor(JClassType type) {
+        if (needsGwtConstructor.contains(type)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.needsGwtConstructor(type);
+        }
+    }
+
+    public void setIdentifier(JClassType type, String identifier) {
+        if (!hasIdentifier(type, identifier)) {
+            Set<String> set = identifiers.get(type);
+            if (set == null) {
+                set = new HashSet<String>();
+                identifiers.put(type, set);
+            }
+            set.add(identifier);
+        }
+    }
+
+    private boolean hasIdentifier(JClassType type, String identifier) {
+        if (identifiers.containsKey(type)
+                && identifiers.get(type).contains(identifier)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.hasIdentifier(type, identifier);
+        }
+    }
+
+    public ConnectorBundle getPreviousBundle() {
+        return previousBundle;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Map<JClassType, Set<String>> getIdentifiers() {
+        return Collections.unmodifiableMap(identifiers);
+    }
+
+    public Set<JClassType> getGwtConstructors() {
+        return Collections.unmodifiableSet(needsGwtConstructor);
+    }
+
+    public void visitTypes(Collection<JClassType> types,
+            Collection<TypeVisitor> visitors) {
+        for (JClassType type : types) {
+            if (!isTypeVisited(type)) {
+                visitQueue.add(type);
+            }
+        }
+        visitQueue(visitors);
+    }
+
+    private boolean isTypeVisited(JClassType type) {
+        if (visitedTypes.contains(type)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.isTypeVisited(type);
+        }
+    }
+
+    private void visitQueue(Collection<TypeVisitor> visitors) {
+        while (!visitQueue.isEmpty()) {
+            JClassType type = visitQueue.iterator().next();
+            for (TypeVisitor typeVisitor : visitors) {
+                typeVisitor.visit(type, this);
+            }
+            visitQueue.remove(type);
+            visitedTypes.add(type);
+        }
+    }
+
+    public void visitSubTypes(JClassType type, Collection<TypeVisitor> visitors) {
+        visitTypes(Arrays.asList(type.getSubtypes()), visitors);
+    }
+}
\ No newline at end of file
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorInitVisitor.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorInitVisitor.java
new file mode 100644 (file)
index 0000000..a6e71c7
--- /dev/null
@@ -0,0 +1,32 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+
+public class ConnectorInitVisitor implements TypeVisitor {
+
+    private JClassType serverConnector;
+
+    @Override
+    public void init(TypeOracle oracle) throws NotFoundException {
+        serverConnector = oracle.getType(ServerConnector.class.getName());
+    }
+
+    @Override
+    public void visit(JClassType type, ConnectorBundle bundle) {
+        Connect connectAnnotation = type.getAnnotation(Connect.class);
+        if (connectAnnotation != null && serverConnector.isAssignableFrom(type)) {
+            bundle.setIdentifier(type, connectAnnotation.value()
+                    .getCanonicalName());
+            bundle.setNeedsGwtConstructor(type);
+        }
+    }
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/TypeVisitor.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/TypeVisitor.java
new file mode 100644 (file)
index 0000000..6ed791f
--- /dev/null
@@ -0,0 +1,15 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+public interface TypeVisitor {
+    public void init(TypeOracle oracle) throws NotFoundException;
+
+    public void visit(JClassType type, ConnectorBundle bundle);
+}
index 07d7c941e69bbcd2aceb1edc80ded7b07fb66637..5a9909a29c5864280095aa1249aa2b4023e8b203 100644 (file)
                <when-type-is class="com.vaadin.terminal.gwt.client.Console" />
        </replace-with>
 
-       <generate-with
-               class="com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator">
-               <when-type-is class="com.vaadin.terminal.gwt.client.WidgetMap" />
-       </generate-with>
-
        <generate-with
                class="com.vaadin.terminal.gwt.widgetsetutils.AcceptCriteriaFactoryGenerator">
                <when-type-is
                <when-type-assignable
                        class="com.vaadin.terminal.gwt.client.ui.ConnectorStateFactory" />
        </generate-with>
+       
+       <generate-with class="com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory">
+               <when-type-assignable class="com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader" />
+       </generate-with>
 
        <!-- Use the new cross site linker to get a nocache.js without document.write -->
        <add-linker name="xsiframe" />
index eea60b04ea3bfdf98b26aaf5dc87ff0f88555c12..8f6697288c444afc6243e92c07daaccf7bb67184 100644 (file)
@@ -29,9 +29,11 @@ import com.google.gwt.core.client.JsArrayString;
 import com.google.gwt.core.client.Scheduler;
 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Timer;
 import com.google.gwt.user.client.Window;
 import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.terminal.gwt.client.metadata.BundleLoadCallback;
+import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader;
+import com.vaadin.terminal.gwt.client.metadata.TypeData;
 import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
 
 public class ApplicationConfiguration implements EntryPoint {
@@ -207,7 +209,7 @@ public class ApplicationConfiguration implements EntryPoint {
 
     private HashMap<Integer, String> unknownComponents;
 
-    private Class<? extends ServerConnector>[] classes = new Class[1024];
+    private Map<Integer, Class<? extends ServerConnector>> classes = new HashMap<Integer, Class<? extends ServerConnector>>();
 
     private boolean browserDetailsSent = false;
     private boolean widgetsetVersionSent = false;
@@ -390,12 +392,26 @@ public class ApplicationConfiguration implements EntryPoint {
 
     public Class<? extends ServerConnector> getConnectorClassByEncodedTag(
             int tag) {
-        try {
-            return classes[tag];
-        } catch (Exception e) {
-            // component was not present in mappings
-            return UnknownComponentConnector.class;
+        Class<? extends ServerConnector> type = classes.get(tag);
+        if (type == null && !classes.containsKey(tag)) {
+            // Initialize if not already loaded
+            Integer currentTag = Integer.valueOf(tag);
+            while (type == null && currentTag != null) {
+                String serverSideClassNameForTag = getServerSideClassNameForTag(currentTag);
+                type = (Class<? extends ServerConnector>) TypeData
+                        .getClass(serverSideClassNameForTag);
+                currentTag = getParentTag(currentTag.intValue());
+            }
+            if (type == null) {
+                type = UnknownComponentConnector.class;
+                if (unknownComponents == null) {
+                    unknownComponents = new HashMap<Integer, String>();
+                }
+                unknownComponents.put(tag, getServerSideClassNameForTag(tag));
+            }
+            classes.put(tag, type);
         }
+        return type;
     }
 
     public void addComponentInheritanceInfo(ValueMap valueMap) {
@@ -418,13 +434,7 @@ public class ApplicationConfiguration implements EntryPoint {
         for (int i = 0; i < keyArray.length(); i++) {
             String key = keyArray.get(i).intern();
             int value = valueMap.getInt(key);
-            classes[value] = widgetSet.getConnectorClassByTag(value, this);
-            if (classes[value] == UnknownComponentConnector.class) {
-                if (unknownComponents == null) {
-                    unknownComponents = new HashMap<Integer, String>();
-                }
-                unknownComponents.put(value, key);
-            }
+            widgetSet.ensureConnectorLoaded(value, this);
         }
     }
 
@@ -466,86 +476,25 @@ public class ApplicationConfiguration implements EntryPoint {
                 cmd.execute();
             }
             callbacks.clear();
-        } else if (dependenciesLoading == 0 && deferredWidgetLoader != null) {
-            deferredWidgetLoader.trigger();
+        } else if (dependenciesLoading == 0
+                && !ConnectorBundleLoader.get().isBundleLoaded(
+                        ConnectorBundleLoader.DEFERRED_BUNDLE_NAME)) {
+            ConnectorBundleLoader.get().loadBundle(
+                    ConnectorBundleLoader.DEFERRED_BUNDLE_NAME,
+                    new BundleLoadCallback() {
+                        @Override
+                        public void loaded() {
+                            // Nothing to do
+                        }
+
+                        @Override
+                        public void failed(Throwable reason) {
+                            VConsole.error(reason);
+                        }
+                    });
         }
-
     }
 
-    /*
-     * This loop loads widget implementation that should be loaded deferred.
-     */
-    static class DeferredWidgetLoader extends Timer {
-        private static final int FREE_LIMIT = 4;
-        private static final int FREE_CHECK_TIMEOUT = 100;
-
-        int communicationFree = 0;
-        int nextWidgetIndex = 0;
-        private boolean pending;
-
-        public DeferredWidgetLoader() {
-            schedule(5000);
-        }
-
-        public void trigger() {
-            if (!pending) {
-                schedule(FREE_CHECK_TIMEOUT);
-            }
-        }
-
-        @Override
-        public void schedule(int delayMillis) {
-            super.schedule(delayMillis);
-            pending = true;
-        }
-
-        @Override
-        public void run() {
-            pending = false;
-            if (!isBusy()) {
-                Class<? extends ServerConnector> nextType = getNextType();
-                if (nextType == null) {
-                    // ensured that all widgets are loaded
-                    deferredWidgetLoader = null;
-                } else {
-                    communicationFree = 0;
-                    widgetSet.loadImplementation(nextType);
-                }
-            } else {
-                schedule(FREE_CHECK_TIMEOUT);
-            }
-        }
-
-        private Class<? extends ServerConnector> getNextType() {
-            Class<? extends ServerConnector>[] deferredLoadedConnectors = widgetSet
-                    .getDeferredLoadedConnectors();
-            if (deferredLoadedConnectors.length <= nextWidgetIndex) {
-                return null;
-            } else {
-                return deferredLoadedConnectors[nextWidgetIndex++];
-            }
-        }
-
-        private boolean isBusy() {
-            if (dependenciesLoading > 0) {
-                communicationFree = 0;
-                return true;
-            }
-            for (ApplicationConnection app : runningApplications) {
-                if (app.hasActiveRequest()) {
-                    // if an UIDL request or widget loading is active, mark as
-                    // busy
-                    communicationFree = 0;
-                    return true;
-                }
-            }
-            communicationFree++;
-            return communicationFree < FREE_LIMIT;
-        }
-    }
-
-    private static DeferredWidgetLoader deferredWidgetLoader;
-
     @Override
     public void onModuleLoad() {
 
@@ -582,7 +531,6 @@ public class ApplicationConfiguration implements EntryPoint {
             return;
         }
         registerCallback(GWT.getModuleName());
-        deferredWidgetLoader = new DeferredWidgetLoader();
     }
 
     /**
index 32b15d6d87ea7bf53b63f558dd14a9811268d358..9a14e5434e87ac137078d64857f6fc8bba1455bc 100644 (file)
@@ -67,8 +67,8 @@ import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
 import com.vaadin.terminal.gwt.client.communication.RpcManager;
 import com.vaadin.terminal.gwt.client.communication.SerializerMap;
 import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
-import com.vaadin.terminal.gwt.client.communication.Type;
 import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector;
 import com.vaadin.terminal.gwt.client.ui.VContextMenu;
 import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
index fbcfbb68d9c56990b25a6d273a150bf0ff63e2e2..776436f5f04ba6273e8ac7d7718b60ad448f4d58 100644 (file)
@@ -18,17 +18,12 @@ package com.vaadin.terminal.gwt.client;
 
 import com.google.gwt.core.client.GWT;
 import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper;
+import com.vaadin.terminal.gwt.client.metadata.BundleLoadCallback;
+import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader;
+import com.vaadin.terminal.gwt.client.metadata.TypeData;
 import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector;
 
 public class WidgetSet {
-
-    /**
-     * WidgetSet (and its extensions) delegate instantiation of widgets and
-     * client-server matching to WidgetMap. The actual implementations are
-     * generated with gwts generators/deferred binding.
-     */
-    private WidgetMap widgetMap = GWT.create(WidgetMap.class);
-
     /**
      * Create an uninitialized connector that best matches given UIDL. The
      * connector must implement {@link ServerConnector}.
@@ -65,7 +60,8 @@ public class WidgetSet {
             /*
              * let the auto generated code instantiate this type
              */
-            ServerConnector connector = widgetMap.instantiate(classType);
+            ServerConnector connector = (ServerConnector) TypeData.getType(
+                    classType).createInstance();
             if (connector instanceof HasJavaScriptConnectorHelper) {
                 ((HasJavaScriptConnectorHelper) connector)
                         .getJavascriptConnectorHelper().setTag(tag);
@@ -102,26 +98,32 @@ public class WidgetSet {
      * @param applicationConfiguration
      * @return
      */
-    public Class<? extends ServerConnector> getConnectorClassByTag(int tag,
-            ApplicationConfiguration conf) {
-        Class<? extends ServerConnector> connectorClass = null;
+    public void ensureConnectorLoaded(int tag, ApplicationConfiguration conf) {
+        ConnectorBundleLoader loader = ConnectorBundleLoader.get();
+        String bundleName = null;
         Integer t = tag;
         do {
             String serverSideClassName = conf.getServerSideClassNameForTag(t);
-            connectorClass = widgetMap
-                    .getConnectorClassForServerSideClassName(serverSideClassName);
-            t = conf.getParentTag(t);
-        } while (connectorClass == UnknownComponentConnector.class && t != null);
+            bundleName = loader.getBundleForIdentifier(serverSideClassName);
 
-        return connectorClass;
-    }
+            t = conf.getParentTag(t);
+        } while (bundleName == null && t != null);
 
-    public Class<? extends ServerConnector>[] getDeferredLoadedConnectors() {
-        return widgetMap.getDeferredLoadedConnectors();
-    }
+        if (bundleName != null && !loader.isBundleLoaded(bundleName)) {
+            ApplicationConfiguration.startDependencyLoading();
+            loader.loadBundle(bundleName, new BundleLoadCallback() {
+                @Override
+                public void loaded() {
+                    ApplicationConfiguration.endDependencyLoading();
+                }
 
-    public void loadImplementation(Class<? extends ServerConnector> nextType) {
-        widgetMap.ensureInstantiator(nextType);
+                @Override
+                public void failed(Throwable reason) {
+                    VConsole.error(reason);
+                    ApplicationConfiguration.endDependencyLoading();
+                }
+            });
+        }
     }
 
 }
index a3b96a6cb26ff7db53ee995f346207928e581441..1d5415263f68937a95c44ea8421539ce166fad47 100644 (file)
@@ -17,6 +17,7 @@ package com.vaadin.terminal.gwt.client.communication;
 
 import com.google.gwt.json.client.JSONValue;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 public interface DiffJSONSerializer<T> extends JSONSerializer<T> {
     /**
index a8fe2c7ccce0af54bfdd62ff240d8386332c370d..c6b814a5c11273d01c0dc854b493d3732295708d 100644 (file)
@@ -20,6 +20,7 @@ import com.google.gwt.json.client.JSONObject;
 import com.google.gwt.json.client.JSONValue;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 /**
  * Implementors of this interface knows how to serialize an Object of a given
index 7d2046982c234fc29ec5b30139aba74c3875825e..ef5090ec18289b2d53f46a697f9b7b58b6a9c810 100644 (file)
@@ -31,6 +31,7 @@ import com.google.gwt.json.client.JSONValue;
 import com.vaadin.shared.Connector;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 /**
  * Client side decoder for decodeing shared state and other values from JSON
index 04d0e3f56f57a7e1d4b4beaa2a4e7151fa7134e4..537cc341857325775066bd6c603eac25ab27816c 100644 (file)
@@ -29,6 +29,7 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
 import com.vaadin.terminal.gwt.client.ServerConnector;
 import com.vaadin.terminal.gwt.client.VConsole;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 /**
  * Client side RPC manager that can invoke methods based on RPC calls received
index a47fa5eab2c85e01a52a429d0aa8acefefd82caa..1759fbb97f141a2ad94ee1bf5d319013fb2911b6 100644 (file)
@@ -16,6 +16,7 @@
 package com.vaadin.terminal.gwt.client.communication;
 
 import com.vaadin.shared.communication.ClientRpc;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 public abstract class RpcMethod {
     private String interfaceName;
index f77553d3c00723ce28db7cf5dcdc48434cf10b69..3d2e4f38049ab59407ae5812db921c8543d62d68 100644 (file)
@@ -20,6 +20,7 @@ import com.google.gwt.json.client.JSONObject;
 import com.google.gwt.json.client.JSONValue;
 import com.vaadin.shared.communication.URLReference;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 public class URLReference_Serializer implements JSONSerializer<URLReference> {
 
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/AsyncBundleLoader.java b/client/src/com/vaadin/terminal/gwt/client/metadata/AsyncBundleLoader.java
new file mode 100644 (file)
index 0000000..e92e51b
--- /dev/null
@@ -0,0 +1,86 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class AsyncBundleLoader {
+    public enum State {
+        NOT_STARTED, LOADING, LOADED, ERROR;
+    }
+
+    private State state = State.NOT_STARTED;
+
+    private Throwable error = null;
+
+    private List<BundleLoadCallback> callbacks = new ArrayList<BundleLoadCallback>();
+
+    private final String packageName;
+
+    private final String[] indentifiers;
+
+    public AsyncBundleLoader(String packageName, String[] indentifiers) {
+        this.packageName = packageName;
+        this.indentifiers = indentifiers;
+    }
+
+    protected abstract void load(TypeDataStore store);
+
+    public List<BundleLoadCallback> setError(Throwable error) {
+        assert state == State.LOADING;
+        state = State.ERROR;
+        this.error = error;
+
+        return clearCallbacks();
+    }
+
+    public Throwable getError() {
+        return error;
+    }
+
+    public State getState() {
+        return state;
+    }
+
+    public List<BundleLoadCallback> getCallback() {
+        return Collections.unmodifiableList(callbacks);
+    }
+
+    public void load(BundleLoadCallback callback, TypeDataStore store) {
+        assert state == State.NOT_STARTED;
+        state = State.LOADING;
+        callbacks.add(callback);
+        load(store);
+    }
+
+    public void addCallback(BundleLoadCallback callback) {
+        assert state == State.LOADING;
+        callbacks.add(callback);
+    }
+
+    public List<BundleLoadCallback> setLoaded() {
+        assert state == State.LOADING;
+        state = State.LOADED;
+
+        return clearCallbacks();
+    }
+
+    private List<BundleLoadCallback> clearCallbacks() {
+        List<BundleLoadCallback> callbacks = this.callbacks;
+        this.callbacks = null;
+        return callbacks;
+    }
+
+    public String getName() {
+        return packageName;
+    }
+
+    public String[] getIndentifiers() {
+        return indentifiers;
+    }
+
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/BundleLoadCallback.java b/client/src/com/vaadin/terminal/gwt/client/metadata/BundleLoadCallback.java
new file mode 100644 (file)
index 0000000..c7fc735
--- /dev/null
@@ -0,0 +1,11 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public interface BundleLoadCallback {
+    public void loaded();
+
+    public void failed(Throwable reason);
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/ConnectorBundleLoader.java b/client/src/com/vaadin/terminal/gwt/client/metadata/ConnectorBundleLoader.java
new file mode 100644 (file)
index 0000000..48a8430
--- /dev/null
@@ -0,0 +1,97 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.core.shared.GWT;
+import com.vaadin.terminal.gwt.client.metadata.AsyncBundleLoader.State;
+
+public abstract class ConnectorBundleLoader {
+    public static final String EAGER_BUNDLE_NAME = "__eager";
+    public static final String DEFERRED_BUNDLE_NAME = "__deferred";
+
+    private static ConnectorBundleLoader impl;
+
+    private Map<String, AsyncBundleLoader> asyncBlockLoaders = new HashMap<String, AsyncBundleLoader>();
+    private Map<String, String> identifierToBundle = new HashMap<String, String>();
+
+    private final TypeDataStore datStore = new TypeDataStore();
+
+    public ConnectorBundleLoader() {
+        init();
+    }
+
+    public TypeDataStore getTypeDataStore() {
+        return datStore;
+    }
+
+    public static ConnectorBundleLoader get() {
+        if (impl == null) {
+            impl = GWT.create(ConnectorBundleLoader.class);
+        }
+        return impl;
+    }
+
+    public void loadBundle(String packageName, BundleLoadCallback callback) {
+        AsyncBundleLoader loader = asyncBlockLoaders.get(packageName);
+        switch (loader.getState()) {
+        case NOT_STARTED:
+            loader.load(callback, getTypeDataStore());
+            break;
+        case LOADING:
+            loader.addCallback(callback);
+            break;
+        case LOADED:
+            callback.loaded();
+            break;
+        case ERROR:
+            callback.failed(loader.getError());
+        }
+    }
+
+    public boolean isBundleLoaded(String bundleName) {
+        AsyncBundleLoader loader = asyncBlockLoaders.get(bundleName);
+        if (loader == null) {
+            throw new IllegalArgumentException("Bundle " + bundleName
+                    + " not recognized");
+        }
+        return loader.getState() == State.LOADED;
+    }
+
+    public void setLoaded(String packageName) {
+        List<BundleLoadCallback> callbacks = asyncBlockLoaders.get(packageName)
+                .setLoaded();
+        for (BundleLoadCallback callback : callbacks) {
+            callback.loaded();
+        }
+    }
+
+    public void setLoadFailure(String bundleName, Throwable reason) {
+        List<BundleLoadCallback> callbacks = asyncBlockLoaders.get(bundleName)
+                .setError(reason);
+        for (BundleLoadCallback callback : callbacks) {
+            callback.failed(reason);
+        }
+    }
+
+    public String getBundleForIdentifier(String identifier) {
+        return identifierToBundle.get(identifier);
+    }
+
+    protected void addAsyncBlockLoader(AsyncBundleLoader loader) {
+        String name = loader.getName();
+        asyncBlockLoaders.put(name, loader);
+        String[] indentifiers = loader.getIndentifiers();
+        for (String identifier : indentifiers) {
+            identifierToBundle.put(identifier, name);
+        }
+    }
+
+    public abstract void init();
+
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/Invoker.java b/client/src/com/vaadin/terminal/gwt/client/metadata/Invoker.java
new file mode 100644 (file)
index 0000000..5f6a839
--- /dev/null
@@ -0,0 +1,9 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public interface Invoker {
+    public Object invoke(Object target, Object[] params);
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/Method.java b/client/src/com/vaadin/terminal/gwt/client/metadata/Method.java
new file mode 100644 (file)
index 0000000..f164bc4
--- /dev/null
@@ -0,0 +1,54 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public class Method {
+
+    private final Type type;
+    private final String name;
+
+    public Method(Type type, String name) {
+        this.type = type;
+        this.name = name;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Type getReturnType() {
+        return TypeDataStore.getReturnType(this);
+    }
+
+    public void invoke(Object target, Object... params) {
+        TypeDataStore.getInvoker(this).invoke(target, params);
+    }
+
+    public String getSignature() {
+        return type.toString() + "." + name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof Method) {
+            Method other = (Method) obj;
+            return other.getSignature().equals(getSignature());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return getSignature().hashCode();
+    }
+
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/Property.java b/client/src/com/vaadin/terminal/gwt/client/metadata/Property.java
new file mode 100644 (file)
index 0000000..5f2b1ff
--- /dev/null
@@ -0,0 +1,53 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public class Property {
+    private final Type type;
+    private final String name;
+
+    public Property(Type type, String name) {
+        this.type = type;
+        this.name = name;
+    }
+
+    public Object getValue(Object bean) {
+        return TypeDataStore.getGetter(this).invoke(bean, null);
+    }
+
+    public String getDelegateToWidgetMethod() {
+        String value = TypeDataStore.getDelegateToWidget(this);
+        if (value == null) {
+            return null;
+        } else if (value.isEmpty()) {
+            return "set" + Character.toUpperCase(value.charAt(0))
+                    + value.substring(1);
+        } else {
+            return value;
+        }
+    }
+
+    public String getSignature() {
+        return type.toString() + "." + name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj instanceof Property) {
+            Property other = (Property) obj;
+            return getSignature().equals(other.getSignature());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return getSignature().hashCode();
+    }
+
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/Type.java b/client/src/com/vaadin/terminal/gwt/client/metadata/Type.java
new file mode 100644 (file)
index 0000000..4fab296
--- /dev/null
@@ -0,0 +1,79 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.terminal.gwt.client.metadata;
+
+public class Type {
+    private final String name;
+    private final Type[] parameterTypes;
+
+    public Type(Class<?> clazz) {
+        name = clazz.getName();
+        parameterTypes = null;
+    }
+
+    public Type(String baseTypeName, Type[] parameterTypes) {
+        name = baseTypeName;
+        this.parameterTypes = parameterTypes;
+    }
+
+    public String getBaseTypeName() {
+        return name;
+    }
+
+    public Type[] getParameterTypes() {
+        return parameterTypes;
+    }
+
+    public Object createInstance() {
+        Invoker invoker = TypeDataStore.getConstructor(this);
+        return invoker.invoke(null, null);
+    }
+
+    public Method getMethod(String name) {
+        return new Method(this, name);
+    }
+
+    public Property getProperty(String propertyName) {
+        return new Property(this, propertyName);
+    }
+
+    public String getSignature() {
+        String string = name;
+        if (parameterTypes != null) {
+            string += '<';
+            for (int i = 0; i < parameterTypes.length; i++) {
+                if (i != 0) {
+                    string += ',';
+                }
+                string += parameterTypes[i].toString();
+            }
+            string += '>';
+        }
+
+        return string;
+    }
+
+    @Override
+    public String toString() {
+        return getSignature();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof Type) {
+            Type other = (Type) obj;
+            return other.getSignature().equals(getSignature());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return getSignature().hashCode();
+    }
+
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/TypeData.java b/client/src/com/vaadin/terminal/gwt/client/metadata/TypeData.java
new file mode 100644 (file)
index 0000000..6ee0b4e
--- /dev/null
@@ -0,0 +1,20 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public class TypeData {
+
+    public static Type getType(Class<?> type) {
+        return TypeDataStore.getType(type);
+    }
+
+    public static Type getType(String identifier) {
+        return TypeDataStore.getType(getClass(identifier));
+    }
+
+    public static Class<?> getClass(String identifier) {
+        return TypeDataStore.getClass(identifier);
+    }
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataBundle.java b/client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataBundle.java
new file mode 100644 (file)
index 0000000..cbde338
--- /dev/null
@@ -0,0 +1,33 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+import com.google.gwt.core.client.RunAsyncCallback;
+
+public abstract class TypeDataBundle implements RunAsyncCallback {
+    private final String name;
+
+    public TypeDataBundle(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public void onSuccess() {
+        ConnectorBundleLoader loader = ConnectorBundleLoader.get();
+        load();
+        loader.setLoaded(getName());
+    }
+
+    @Override
+    public void onFailure(Throwable reason) {
+        ConnectorBundleLoader.get().setLoadFailure(getName(), reason);
+    }
+
+    public abstract void load();
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataStore.java b/client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataStore.java
new file mode 100644 (file)
index 0000000..4b22472
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TypeDataStore {
+    private static final String CONSTRUCTOR_NAME = "!new";
+
+    private final Map<String, Class<?>> identifiers = new HashMap<String, Class<?>>();
+
+    private final Map<Method, Type> returnTypes = new HashMap<Method, Type>();
+    private final Map<Method, Invoker> invokers = new HashMap<Method, Invoker>();
+
+    private final Map<Property, Invoker> getters = new HashMap<Property, Invoker>();
+    private final Map<Property, String> delegateToWidget = new HashMap<Property, String>();
+
+    public static TypeDataStore get() {
+        return ConnectorBundleLoader.get().getTypeDataStore();
+    }
+
+    public void setClass(String identifier, Class<?> type) {
+        identifiers.put(identifier, type);
+    }
+
+    public static Class<?> getClass(String identifier) {
+        return get().identifiers.get(identifier);
+    }
+
+    public static Type getType(Class<?> clazz) {
+        return new Type(clazz);
+    }
+
+    public static Type getReturnType(Method method) {
+        return get().returnTypes.get(method);
+    }
+
+    public static Invoker getInvoker(Method method) {
+        return get().invokers.get(method);
+    }
+
+    public static Invoker getConstructor(Type type) {
+        return get().invokers.get(new Method(type, CONSTRUCTOR_NAME));
+    }
+
+    public static Invoker getGetter(Property property) {
+        return get().getters.get(property);
+    }
+
+    public static String getDelegateToWidget(Property property) {
+        return get().delegateToWidget.get(property);
+    }
+
+    public void setReturnType(Class<?> type, String methodName, Type returnType) {
+        returnTypes.put(new Method(getType(type), methodName), returnType);
+    }
+
+    public void setConstructor(Class<?> type, Invoker constructor) {
+        invokers.put(new Method(getType(type), CONSTRUCTOR_NAME), constructor);
+    }
+}