--- /dev/null
+/*
+@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();
+ }
+
+}
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());
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());
.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()) };
--- /dev/null
+/*
+@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
--- /dev/null
+/*
+@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);
+ }
+ }
+
+}
--- /dev/null
+/*
+@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);
+}
<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" />
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 {
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;
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) {
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);
}
}
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() {
return;
}
registerCallback(GWT.getModuleName());
- deferredWidgetLoader = new DeferredWidgetLoader();
}
/**
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;
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}.
/*
* 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);
* @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();
+ }
+ });
+ }
}
}
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> {
/**
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
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
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
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;
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> {
--- /dev/null
+/*
+@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;
+ }
+
+}
--- /dev/null
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public interface BundleLoadCallback {
+ public void loaded();
+
+ public void failed(Throwable reason);
+}
--- /dev/null
+/*
+@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();
+
+}
--- /dev/null
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.metadata;
+
+public interface Invoker {
+ public Object invoke(Object target, Object[] params);
+}
--- /dev/null
+/*
+@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();
+ }
+
+}
--- /dev/null
+/*
+@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();
+ }
+
+}
--- /dev/null
+/*
+@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();
+ }
+
+}
--- /dev/null
+/*
+@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);
+ }
+}
--- /dev/null
+/*
+@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;
+ }
+}
--- /dev/null
+/*
+@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);
+ }
+}