From 66d60ab6015ffaadce3bf6c906b9463c30649b81 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 14 Aug 2012 15:11:40 +0300 Subject: Moved client compiler sources to own source folder (#9299) --- ...bstractConnectorClassBasedFactoryGenerator.java | 145 +++++++ .../AcceptCriteriaFactoryGenerator.java | 127 ++++++ .../gwt/widgetsetutils/ClassPathExplorer.java | 462 +++++++++++++++++++++ .../ConnectorStateFactoryGenerator.java | 29 ++ .../ConnectorWidgetFactoryGenerator.java | 29 ++ .../widgetsetutils/CustomWidgetMapGenerator.java | 84 ++++ .../widgetsetutils/EagerWidgetMapGenerator.java | 29 ++ .../GeneratedRpcMethodProviderGenerator.java | 211 ++++++++++ .../gwt/widgetsetutils/LazyWidgetMapGenerator.java | 23 + .../widgetsetutils/RpcProxyCreatorGenerator.java | 126 ++++++ .../gwt/widgetsetutils/RpcProxyGenerator.java | 142 +++++++ .../gwt/widgetsetutils/SerializerGenerator.java | 458 ++++++++++++++++++++ .../gwt/widgetsetutils/SerializerMapGenerator.java | 365 ++++++++++++++++ .../gwt/widgetsetutils/WidgetMapGenerator.java | 398 ++++++++++++++++++ .../gwt/widgetsetutils/WidgetSetBuilder.java | 201 +++++++++ .../src/com/vaadin/tools/WidgetsetCompiler.java | 94 +++++ ...bstractConnectorClassBasedFactoryGenerator.java | 145 ------- .../AcceptCriteriaFactoryGenerator.java | 127 ------ .../gwt/widgetsetutils/ClassPathExplorer.java | 462 --------------------- .../ConnectorStateFactoryGenerator.java | 29 -- .../ConnectorWidgetFactoryGenerator.java | 29 -- .../widgetsetutils/CustomWidgetMapGenerator.java | 84 ---- .../widgetsetutils/EagerWidgetMapGenerator.java | 29 -- .../GeneratedRpcMethodProviderGenerator.java | 211 ---------- .../gwt/widgetsetutils/LazyWidgetMapGenerator.java | 23 - .../widgetsetutils/RpcProxyCreatorGenerator.java | 126 ------ .../gwt/widgetsetutils/RpcProxyGenerator.java | 142 ------- .../gwt/widgetsetutils/SerializerGenerator.java | 458 -------------------- .../gwt/widgetsetutils/SerializerMapGenerator.java | 365 ---------------- .../gwt/widgetsetutils/WidgetMapGenerator.java | 398 ------------------ .../gwt/widgetsetutils/WidgetSetBuilder.java | 201 --------- server/src/com/vaadin/tools/WidgetsetCompiler.java | 94 ----- 32 files changed, 2923 insertions(+), 2923 deletions(-) create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java create mode 100644 client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java create mode 100755 client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java delete mode 100644 client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java delete mode 100644 server/src/com/vaadin/tools/WidgetsetCompiler.java diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java new file mode 100644 index 0000000000..3a13fceece --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java @@ -0,0 +1,145 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.Date; + +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.JMethod; +import com.google.gwt.core.ext.typeinfo.JType; +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.terminal.gwt.client.ServerConnector; +import com.vaadin.terminal.gwt.client.ui.ConnectorClassBasedFactory; +import com.vaadin.terminal.gwt.client.ui.ConnectorClassBasedFactory.Creator; + +/** + * GWT generator that creates a lookup method for + * {@link ConnectorClassBasedFactory} instances. + * + * @since 7.0 + */ +public abstract class AbstractConnectorClassBasedFactoryGenerator extends + Generator { + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + try { + // get classType and save instance variables + return generateConnectorClassBasedFactory(typeName, logger, context); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, typeName + " creation failed", e); + throw new UnableToCompleteException(); + } + } + + private String generateConnectorClassBasedFactory(String typeName, + TreeLogger logger, GeneratorContext context) + throws NotFoundException { + TypeOracle typeOracle = context.getTypeOracle(); + + JClassType classType = typeOracle.getType(typeName); + String superName = classType.getSimpleSourceName(); + String packageName = classType.getPackage().getName(); + String className = superName + "Impl"; + + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated + if (printWriter == null) { + return packageName + "." + className; + } + + Date date = new Date(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + composer.addImport(GWT.class.getName()); + composer.addImport(Creator.class.getCanonicalName()); + composer.setSuperclass(superName); + + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + // public ConnectorStateFactoryImpl() { + sourceWriter.println("public " + className + "() {"); + sourceWriter.indent(); + + JClassType serverConnectorType = typeOracle.getType(getConnectorType() + .getCanonicalName()); + for (JClassType connector : serverConnectorType.getSubtypes()) { + // addCreator(TextAreaConnector.class, new Creator() { + if (connector.isInterface() != null || connector.isAbstract()) { + continue; + } + + JClassType targetType = getTargetType(connector); + if (targetType.isAbstract()) { + continue; + } + + sourceWriter.println("addCreator(" + + connector.getQualifiedSourceName() + + ".class, new Creator<" + + targetType.getQualifiedSourceName() + ">() {"); + // public SharedState create() { + sourceWriter.println("public " + + targetType.getQualifiedSourceName() + " create() {"); + // return GWT.create(TextAreaState.class); + sourceWriter.println("return GWT.create(" + + targetType.getQualifiedSourceName() + ".class);"); + // } + sourceWriter.println("}"); + // }); + sourceWriter.println("});"); + } + + // End of constructor + sourceWriter.outdent(); + sourceWriter.println("}"); + + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + return packageName + "." + className; + + } + + protected abstract Class getConnectorType(); + + protected abstract JClassType getTargetType(JClassType connectorType); + + protected JClassType getGetterReturnType(JClassType connector, + String getterName) { + try { + JMethod getMethod = connector.getMethod(getterName, new JType[] {}); + return (JClassType) getMethod.getReturnType(); + } catch (NotFoundException e) { + return getGetterReturnType(connector.getSuperclass(), getterName); + } + + } + +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java new file mode 100644 index 0000000000..e5e2ee1f2c --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java @@ -0,0 +1,127 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.Date; + +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.TypeOracle; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.shared.ui.dd.AcceptCriterion; +import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterion; +import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory; + +/** + * GWT generator to build {@link VAcceptCriterionFactory} implementation + * dynamically based on {@link AcceptCriterion} annotations available in + * classpath. + * + */ +public class AcceptCriteriaFactoryGenerator extends Generator { + + private String packageName; + private String className; + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + try { + TypeOracle typeOracle = context.getTypeOracle(); + + // get classType and save instance variables + JClassType classType = typeOracle.getType(typeName); + packageName = classType.getPackage().getName(); + className = classType.getSimpleSourceName() + "Impl"; + // Generate class source code + generateClass(logger, context); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, + "Accept criterion factory creation failed", e); + } + // return the fully qualifed name of the class generated + return packageName + "." + className; + } + + /** + * Generate source code for WidgetMapImpl + * + * @param logger + * Logger object + * @param context + * Generator context + */ + private void generateClass(TreeLogger logger, GeneratorContext context) { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated, + // return (WidgetMap is equal to all permutations atm) + if (printWriter == null) { + return; + } + logger.log(Type.INFO, "Detecting available criteria ..."); + Date date = new Date(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + composer.addImport("com.google.gwt.core.client.GWT"); + composer.setSuperclass("com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory"); + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + + // generator constructor source code + generateInstantiatorMethod(sourceWriter, context, logger); + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + + } + + private void generateInstantiatorMethod(SourceWriter sourceWriter, + GeneratorContext context, TreeLogger logger) { + + sourceWriter.println("public VAcceptCriterion get(String name) {"); + sourceWriter.indent(); + + sourceWriter.println("name = name.intern();"); + + JClassType criteriaType = context.getTypeOracle().findType( + VAcceptCriterion.class.getName()); + for (JClassType clientClass : criteriaType.getSubtypes()) { + AcceptCriterion annotation = clientClass + .getAnnotation(AcceptCriterion.class); + if (annotation != null) { + String clientClassName = clientClass.getQualifiedSourceName(); + Class serverClass = clientClass.getAnnotation( + AcceptCriterion.class).value(); + String serverClassName = serverClass.getCanonicalName(); + logger.log(Type.INFO, "creating mapping for " + serverClassName); + sourceWriter.print("if (\""); + sourceWriter.print(serverClassName); + sourceWriter.print("\" == name) return GWT.create("); + sourceWriter.print(clientClassName); + sourceWriter.println(".class );"); + sourceWriter.print("else "); + } + } + + sourceWriter.println("return null;"); + sourceWriter.outdent(); + sourceWriter.println("}"); + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java new file mode 100644 index 0000000000..6ee30183c1 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java @@ -0,0 +1,462 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility class to collect widgetset related information from classpath. + * Utility will seek all directories from classpaths, and jar files having + * "Vaadin-Widgetsets" key in their manifest file. + *

+ * Used by WidgetMapGenerator and ide tools to implement some monkey coding for + * you. + *

+ * Developer notice: If you end up reading this comment, I guess you have faced + * a sluggish performance of widget compilation or unreliable detection of + * components in your classpaths. The thing you might be able to do is to use + * annotation processing tool like apt to generate the needed information. Then + * either use that information in {@link WidgetMapGenerator} or create the + * appropriate monkey code for gwt directly in annotation processor and get rid + * of {@link WidgetMapGenerator}. Using annotation processor might be a good + * idea when dropping Java 1.5 support (integrated to javac in 6). + * + */ +public class ClassPathExplorer { + + private static final String VAADIN_ADDON_VERSION_ATTRIBUTE = "Vaadin-Package-Version"; + + /** + * File filter that only accepts directories. + */ + private final static FileFilter DIRECTORIES_ONLY = new FileFilter() { + @Override + public boolean accept(File f) { + if (f.exists() && f.isDirectory()) { + return true; + } else { + return false; + } + } + }; + + /** + * Raw class path entries as given in the java class path string. Only + * entries that could include widgets/widgetsets are listed (primarily + * directories, Vaadin JARs and add-on JARs). + */ + private static List rawClasspathEntries = getRawClasspathEntries(); + + /** + * Map from identifiers (either a package name preceded by the path and a + * slash, or a URL for a JAR file) to the corresponding URLs. This is + * constructed from the class path. + */ + private static Map classpathLocations = getClasspathLocations(rawClasspathEntries); + + /** + * No instantiation from outside, callable methods are static. + */ + private ClassPathExplorer() { + } + + /** + * Finds the names and locations of widgetsets available on the class path. + * + * @return map from widgetset classname to widgetset location URL + */ + public static Map getAvailableWidgetSets() { + long start = System.currentTimeMillis(); + Map widgetsets = new HashMap(); + Set keySet = classpathLocations.keySet(); + for (String location : keySet) { + searchForWidgetSets(location, widgetsets); + } + long end = System.currentTimeMillis(); + + StringBuilder sb = new StringBuilder(); + sb.append("Widgetsets found from classpath:\n"); + for (String ws : widgetsets.keySet()) { + sb.append("\t"); + sb.append(ws); + sb.append(" in "); + sb.append(widgetsets.get(ws)); + sb.append("\n"); + } + final Logger logger = getLogger(); + logger.info(sb.toString()); + logger.info("Search took " + (end - start) + "ms"); + return widgetsets; + } + + /** + * Finds all GWT modules / Vaadin widgetsets in a valid location. + * + * If the location is a directory, all GWT modules (files with the + * ".gwt.xml" extension) are added to widgetsets. + * + * If the location is a JAR file, the comma-separated values of the + * "Vaadin-Widgetsets" attribute in its manifest are added to widgetsets. + * + * @param locationString + * an entry in {@link #classpathLocations} + * @param widgetsets + * a map from widgetset name (including package, with dots as + * separators) to a URL (see {@link #classpathLocations}) - new + * entries are added to this map + */ + private static void searchForWidgetSets(String locationString, + Map widgetsets) { + + URL location = classpathLocations.get(locationString); + File directory = new File(location.getFile()); + + if (directory.exists() && !directory.isHidden()) { + // Get the list of the files contained in the directory + String[] files = directory.list(); + for (int i = 0; i < files.length; i++) { + // we are only interested in .gwt.xml files + if (!files[i].endsWith(".gwt.xml")) { + continue; + } + + // remove the .gwt.xml extension + String classname = files[i].substring(0, files[i].length() - 8); + String packageName = locationString.substring(locationString + .lastIndexOf("/") + 1); + classname = packageName + "." + classname; + + if (!WidgetSetBuilder.isWidgetset(classname)) { + // Only return widgetsets and not GWT modules to avoid + // comparing modules and widgetsets + continue; + } + + if (!widgetsets.containsKey(classname)) { + String packagePath = packageName.replaceAll("\\.", "/"); + String basePath = location.getFile().replaceAll( + "/" + packagePath + "$", ""); + try { + URL url = new URL(location.getProtocol(), + location.getHost(), location.getPort(), + basePath); + widgetsets.put(classname, url); + } catch (MalformedURLException e) { + // should never happen as based on an existing URL, + // only changing end of file name/path part + getLogger().log(Level.SEVERE, + "Error locating the widgetset " + classname, e); + } + } + } + } else { + + try { + // check files in jar file, entries will list all directories + // and files in jar + + URLConnection openConnection = location.openConnection(); + if (openConnection instanceof JarURLConnection) { + JarURLConnection conn = (JarURLConnection) openConnection; + + JarFile jarFile = conn.getJarFile(); + + Manifest manifest = jarFile.getManifest(); + if (manifest == null) { + // No manifest so this is not a Vaadin Add-on + return; + } + String value = manifest.getMainAttributes().getValue( + "Vaadin-Widgetsets"); + if (value != null) { + String[] widgetsetNames = value.split(","); + for (int i = 0; i < widgetsetNames.length; i++) { + String widgetsetname = widgetsetNames[i].trim() + .intern(); + if (!widgetsetname.equals("")) { + widgetsets.put(widgetsetname, location); + } + } + } + } + } catch (IOException e) { + getLogger().log(Level.WARNING, "Error parsing jar file", e); + } + + } + } + + /** + * Splits the current class path into entries, and filters them accepting + * directories, Vaadin add-on JARs with widgetsets and Vaadin JARs. + * + * Some other non-JAR entries may also be included in the result. + * + * @return filtered list of class path entries + */ + private final static List getRawClasspathEntries() { + // try to keep the order of the classpath + List locations = new ArrayList(); + + String pathSep = System.getProperty("path.separator"); + String classpath = System.getProperty("java.class.path"); + + if (classpath.startsWith("\"")) { + classpath = classpath.substring(1); + } + if (classpath.endsWith("\"")) { + classpath = classpath.substring(0, classpath.length() - 1); + } + + getLogger().fine("Classpath: " + classpath); + + String[] split = classpath.split(pathSep); + for (int i = 0; i < split.length; i++) { + String classpathEntry = split[i]; + if (acceptClassPathEntry(classpathEntry)) { + locations.add(classpathEntry); + } + } + + return locations; + } + + /** + * Determine every URL location defined by the current classpath, and it's + * associated package name. + * + * See {@link #classpathLocations} for information on output format. + * + * @param rawClasspathEntries + * raw class path entries as split from the Java class path + * string + * @return map of classpath locations, see {@link #classpathLocations} + */ + private final static Map getClasspathLocations( + List rawClasspathEntries) { + long start = System.currentTimeMillis(); + // try to keep the order of the classpath + Map locations = new LinkedHashMap(); + for (String classpathEntry : rawClasspathEntries) { + File file = new File(classpathEntry); + include(null, file, locations); + } + long end = System.currentTimeMillis(); + Logger logger = getLogger(); + if (logger.isLoggable(Level.FINE)) { + logger.fine("getClassPathLocations took " + (end - start) + "ms"); + } + return locations; + } + + /** + * Checks a class path entry to see whether it can contain widgets and + * widgetsets. + * + * All directories are automatically accepted. JARs are accepted if they + * have the "Vaadin-Widgetsets" attribute in their manifest or the JAR file + * name contains "vaadin-" or ".vaadin.". + * + * Also other non-JAR entries may be accepted, the caller should be prepared + * to handle them. + * + * @param classpathEntry + * class path entry string as given in the Java class path + * @return true if the entry should be considered when looking for widgets + * or widgetsets + */ + private static boolean acceptClassPathEntry(String classpathEntry) { + if (!classpathEntry.endsWith(".jar")) { + // accept all non jars (practically directories) + return true; + } else { + // accepts jars that comply with vaadin-component packaging + // convention (.vaadin. or vaadin- as distribution packages), + if (classpathEntry.contains("vaadin-") + || classpathEntry.contains(".vaadin.")) { + return true; + } else { + URL url; + try { + url = new URL("file:" + + new File(classpathEntry).getCanonicalPath()); + url = new URL("jar:" + url.toExternalForm() + "!/"); + JarURLConnection conn = (JarURLConnection) url + .openConnection(); + getLogger().fine(url.toString()); + JarFile jarFile = conn.getJarFile(); + Manifest manifest = jarFile.getManifest(); + if (manifest != null) { + Attributes mainAttributes = manifest + .getMainAttributes(); + if (mainAttributes.getValue("Vaadin-Widgetsets") != null) { + return true; + } + } + } catch (MalformedURLException e) { + getLogger().log(Level.FINEST, "Failed to inspect JAR file", + e); + } catch (IOException e) { + getLogger().log(Level.FINEST, "Failed to inspect JAR file", + e); + } + + return false; + } + } + } + + /** + * Recursively add subdirectories and jar files to locations - see + * {@link #classpathLocations}. + * + * @param name + * @param file + * @param locations + */ + private final static void include(String name, File file, + Map locations) { + if (!file.exists()) { + return; + } + if (!file.isDirectory()) { + // could be a JAR file + includeJar(file, locations); + return; + } + + if (file.isHidden() || file.getPath().contains(File.separator + ".")) { + return; + } + + if (name == null) { + name = ""; + } else { + name += "."; + } + + // add all directories recursively + File[] dirs = file.listFiles(DIRECTORIES_ONLY); + for (int i = 0; i < dirs.length; i++) { + try { + // add the present directory + if (!dirs[i].isHidden() + && !dirs[i].getPath().contains(File.separator + ".")) { + String key = dirs[i].getCanonicalPath() + "/" + name + + dirs[i].getName(); + locations.put(key, + new URL("file://" + dirs[i].getCanonicalPath())); + } + } catch (Exception ioe) { + return; + } + include(name + dirs[i].getName(), dirs[i], locations); + } + } + + /** + * Add a jar file to locations - see {@link #classpathLocations}. + * + * @param name + * @param locations + */ + private static void includeJar(File file, Map locations) { + try { + URL url = new URL("file:" + file.getCanonicalPath()); + url = new URL("jar:" + url.toExternalForm() + "!/"); + JarURLConnection conn = (JarURLConnection) url.openConnection(); + JarFile jarFile = conn.getJarFile(); + if (jarFile != null) { + // the key does not matter here as long as it is unique + locations.put(url.toString(), url); + } + } catch (Exception e) { + // e.printStackTrace(); + return; + } + + } + + /** + * Find and return the default source directory where to create new + * widgetsets. + * + * Return the first directory (not a JAR file etc.) on the classpath by + * default. + * + * TODO this could be done better... + * + * @return URL + */ + public static URL getDefaultSourceDirectory() { + + final Logger logger = getLogger(); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("classpathLocations values:"); + ArrayList locations = new ArrayList( + classpathLocations.keySet()); + for (String location : locations) { + logger.fine(String.valueOf(classpathLocations.get(location))); + } + } + + Iterator it = rawClasspathEntries.iterator(); + while (it.hasNext()) { + String entry = it.next(); + + File directory = new File(entry); + if (directory.exists() && !directory.isHidden() + && directory.isDirectory()) { + try { + return new URL("file://" + directory.getCanonicalPath()); + } catch (MalformedURLException e) { + logger.log(Level.FINEST, "Ignoring exception", e); + // ignore: continue to the next classpath entry + } catch (IOException e) { + logger.log(Level.FINEST, "Ignoring exception", e); + // ignore: continue to the next classpath entry + } + } + } + return null; + } + + /** + * Test method for helper tool + */ + public static void main(String[] args) { + getLogger().info("Searching available widgetsets..."); + + Map availableWidgetSets = ClassPathExplorer + .getAvailableWidgetSets(); + for (String string : availableWidgetSets.keySet()) { + + getLogger().info(string + " in " + availableWidgetSets.get(string)); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(ClassPathExplorer.class.getName()); + } + +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java new file mode 100644 index 0000000000..33406ef85f --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java @@ -0,0 +1,29 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.vaadin.terminal.gwt.client.ServerConnector; + +/** + * GWT generator that creates a SharedState class for a given Connector class, + * based on the return type of getState() + * + * @since 7.0 + */ +public class ConnectorStateFactoryGenerator extends + AbstractConnectorClassBasedFactoryGenerator { + + @Override + protected JClassType getTargetType(JClassType connectorType) { + return getGetterReturnType(connectorType, "getState"); + } + + @Override + protected Class getConnectorType() { + return ServerConnector.class; + } + +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java new file mode 100644 index 0000000000..55a2857ce0 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java @@ -0,0 +1,29 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ServerConnector; + +/** + * GWT generator that creates a Widget class for a given Connector class, based + * on the return type of getWidget() + * + * @since 7.0 + */ +public class ConnectorWidgetFactoryGenerator extends + AbstractConnectorClassBasedFactoryGenerator { + @Override + protected JClassType getTargetType(JClassType connectorType) { + return getGetterReturnType(connectorType, "getWidget"); + } + + @Override + protected Class getConnectorType() { + return ComponentConnector.class; + } + +} \ No newline at end of file diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java new file mode 100644 index 0000000000..89045c63b2 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java @@ -0,0 +1,84 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.util.Collection; +import java.util.HashSet; + +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ServerConnector; + +/** + * An abstract helper class that can be used to easily build a widgetset with + * customized load styles for each components. In three abstract methods one can + * override the default values given in {@link Connect} annotations. + * + * @see WidgetMapGenerator + * + */ +public abstract class CustomWidgetMapGenerator extends WidgetMapGenerator { + + private Collection> eagerPaintables = new HashSet>(); + private Collection> lazyPaintables = new HashSet>(); + private Collection> deferredPaintables = new HashSet>(); + + @Override + protected LoadStyle getLoadStyle(Class connector) { + if (eagerPaintables == null) { + init(); + } + if (eagerPaintables.contains(connector)) { + return LoadStyle.EAGER; + } + if (lazyPaintables.contains(connector)) { + return LoadStyle.LAZY; + } + if (deferredPaintables.contains(connector)) { + return LoadStyle.DEFERRED; + } + return super.getLoadStyle(connector); + } + + private void init() { + Class[] eagerComponents = getEagerComponents(); + if (eagerComponents != null) { + for (Class class1 : eagerComponents) { + eagerPaintables.add(class1); + } + } + Class[] lazyComponents = getEagerComponents(); + if (lazyComponents != null) { + for (Class class1 : lazyComponents) { + lazyPaintables.add(class1); + } + } + Class[] deferredComponents = getEagerComponents(); + if (deferredComponents != null) { + for (Class class1 : deferredComponents) { + deferredPaintables.add(class1); + } + } + } + + /** + * @return an array of components whose load style should be overridden to + * {@link LoadStyle#EAGER} + */ + protected abstract Class[] getEagerComponents(); + + /** + * @return an array of components whose load style should be overridden to + * {@link LoadStyle#LAZY} + */ + protected abstract Class[] getLazyComponents(); + + /** + * @return an array of components whose load style should be overridden to + * {@link LoadStyle#DEFERRED} + */ + protected abstract Class[] getDeferredComponents(); + +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java new file mode 100644 index 0000000000..4ff0592ede --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java @@ -0,0 +1,29 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.terminal.gwt.client.ServerConnector; + +/** + * WidgetMap generator that builds a widgetset that packs all included widgets + * into a single JavaScript file loaded at application initialization. Initially + * loaded data will be relatively large, but minimal amount of server requests + * will be done. + *

+ * This is the default generator in version 6.4 and produces similar type of + * widgetset as in previous versions of Vaadin. To activate "code splitting", + * use the {@link WidgetMapGenerator} instead, that loads most components + * deferred. + * + * @see WidgetMapGenerator + * + */ +public class EagerWidgetMapGenerator extends WidgetMapGenerator { + + @Override + protected LoadStyle getLoadStyle(Class connector) { + return LoadStyle.EAGER; + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java new file mode 100644 index 0000000000..e11a12a3b5 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java @@ -0,0 +1,211 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +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.JMethod; +import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.google.gwt.core.ext.typeinfo.JType; +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.terminal.gwt.client.communication.GeneratedRpcMethodProvider; +import com.vaadin.terminal.gwt.client.communication.RpcManager; +import com.vaadin.terminal.gwt.client.communication.RpcMethod; + +/** + * GWT generator that creates an implementation for {@link RpcManager} on the + * client side classes for executing RPC calls received from the the server. + * + * @since 7.0 + */ +public class GeneratedRpcMethodProviderGenerator extends Generator { + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + String packageName = null; + String className = null; + try { + TypeOracle typeOracle = context.getTypeOracle(); + + // get classType and save instance variables + JClassType classType = typeOracle.getType(typeName); + packageName = classType.getPackage().getName(); + className = classType.getSimpleSourceName() + "Impl"; + // Generate class source code for SerializerMapImpl + generateClass(logger, context, packageName, className); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, + "SerializerMapGenerator creation failed", e); + } + // return the fully qualifed name of the class generated + return packageName + "." + className; + } + + /** + * Generate source code for RpcManagerImpl + * + * @param logger + * Logger object + * @param context + * Generator context + * @param packageName + * package name for the class to generate + * @param className + * class name for the class to generate + */ + private void generateClass(TreeLogger logger, GeneratorContext context, + String packageName, String className) { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated + if (printWriter == null) { + return; + } + logger.log(Type.INFO, + "Detecting server to client RPC interface types..."); + Date date = new Date(); + TypeOracle typeOracle = context.getTypeOracle(); + JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class + .getName()); + JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + 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 + .getName()); + composer.addImplementedInterface(GeneratedRpcMethodProvider.class + .getName()); + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + List rpcMethods = new ArrayList(); + + sourceWriter + .println("public java.util.Collection getGeneratedRpcMethods() {"); + sourceWriter.indent(); + + sourceWriter + .println("java.util.ArrayList list = new java.util.ArrayList();"); + + // iterate over RPC interfaces and create helper methods for each + // interface + for (JClassType type : rpcInterfaceSubtypes) { + if (null == type.isInterface()) { + // only interested in interfaces here, not implementations + continue; + } + + // loop over the methods of the interface and its superinterfaces + // methods + for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) { + for (JMethod method : currentType.getMethods()) { + + // RpcMethod(String interfaceName, String methodName, + // Type... parameterTypes) + sourceWriter.print("list.add(new RpcMethod(\"" + + type.getQualifiedSourceName() + "\", \"" + + method.getName() + "\""); + JType[] parameterTypes = method.getParameterTypes(); + for (JType parameter : parameterTypes) { + sourceWriter.print(", "); + writeTypeCreator(sourceWriter, parameter); + } + sourceWriter.println(") {"); + sourceWriter.indent(); + + sourceWriter + .println("public void applyInvocation(ClientRpc target, Object... parameters) {"); + sourceWriter.indent(); + + sourceWriter.print("((" + type.getQualifiedSourceName() + + ")target)." + method.getName() + "("); + for (int i = 0; i < parameterTypes.length; i++) { + JType parameterType = parameterTypes[i]; + if (i != 0) { + sourceWriter.print(", "); + } + String parameterTypeName = getBoxedTypeName(parameterType); + sourceWriter.print("(" + parameterTypeName + + ") parameters[" + i + "]"); + } + sourceWriter.println(");"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + + sourceWriter.outdent(); + sourceWriter.println("});"); + } + } + } + + sourceWriter.println("return list;"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + sourceWriter.println(); + + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + + } + + public static void writeTypeCreator(SourceWriter sourceWriter, JType type) { + String typeName = getBoxedTypeName(type); + sourceWriter.print("new Type(\"" + typeName + "\", "); + JParameterizedType parameterized = type.isParameterized(); + if (parameterized != null) { + sourceWriter.print("new Type[] {"); + JClassType[] typeArgs = parameterized.getTypeArgs(); + for (JClassType jClassType : typeArgs) { + writeTypeCreator(sourceWriter, jClassType); + sourceWriter.print(", "); + } + sourceWriter.print("}"); + } else { + sourceWriter.print("null"); + } + sourceWriter.print(")"); + } + + public static String getBoxedTypeName(JType type) { + if (type.isPrimitive() != null) { + // Used boxed types for primitives + return type.isPrimitive().getQualifiedBoxedSourceName(); + } else { + return type.getErasedType().getQualifiedSourceName(); + } + } + + private String getInvokeMethodName(JClassType type) { + return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_"); + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java new file mode 100644 index 0000000000..28f3dab482 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java @@ -0,0 +1,23 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.terminal.gwt.client.ServerConnector; + +/** + * WidgetMap generator that builds a widgetset that optimizes the transferred + * data. Widgets are loaded only when used if the widgetset is built with this + * generator. + * + * @see WidgetMapGenerator + * + */ +public class LazyWidgetMapGenerator extends WidgetMapGenerator { + @Override + protected LoadStyle getLoadStyle(Class connector) { + return LoadStyle.LAZY; + } + +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java new file mode 100644 index 0000000000..8a6c374187 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java @@ -0,0 +1,126 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.Date; + +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.TypeOracle; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.terminal.gwt.client.ServerConnector; +import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc; +import com.vaadin.terminal.gwt.client.communication.RpcProxy.RpcProxyCreator; + +public class RpcProxyCreatorGenerator extends Generator { + + @Override + public String generate(TreeLogger logger, GeneratorContext ctx, + String requestedClassName) throws UnableToCompleteException { + logger.log(TreeLogger.DEBUG, "Running RpcProxyCreatorGenerator"); + TypeOracle typeOracle = ctx.getTypeOracle(); + assert (typeOracle != null); + + JClassType requestedType = typeOracle.findType(requestedClassName); + if (requestedType == null) { + logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + + requestedClassName + "'", null); + throw new UnableToCompleteException(); + } + String packageName = requestedType.getPackage().getName(); + String className = requestedType.getSimpleSourceName() + "Impl"; + + createType(logger, ctx, packageName, className); + return packageName + "." + className; + } + + private void createType(TreeLogger logger, GeneratorContext context, + String packageName, String className) { + ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( + packageName, className); + + PrintWriter printWriter = context.tryCreate(logger, + composer.getCreatedPackage(), + composer.getCreatedClassShortName()); + if (printWriter == null) { + // print writer is null if source code has already been generated + return; + } + Date date = new Date(); + TypeOracle typeOracle = context.getTypeOracle(); + + // init composer, set class properties, create source writer + composer.addImport(GWT.class.getCanonicalName()); + composer.addImport(ServerRpc.class.getCanonicalName()); + composer.addImport(ServerConnector.class.getCanonicalName()); + composer.addImport(InitializableServerRpc.class.getCanonicalName()); + composer.addImport(IllegalArgumentException.class.getCanonicalName()); + composer.addImplementedInterface(RpcProxyCreator.class + .getCanonicalName()); + + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + sourceWriter + .println("public T create(Class rpcInterface, ServerConnector connector) {"); + sourceWriter.indent(); + + sourceWriter + .println("if (rpcInterface == null || connector == null) {"); + sourceWriter.indent(); + sourceWriter + .println("throw new IllegalArgumentException(\"RpcInterface and/or connector cannot be null\");"); + sourceWriter.outdent(); + + JClassType initializableInterface = typeOracle.findType(ServerRpc.class + .getCanonicalName()); + + for (JClassType rpcType : initializableInterface.getSubtypes()) { + String rpcClassName = rpcType.getQualifiedSourceName(); + if (InitializableServerRpc.class.getCanonicalName().equals( + rpcClassName)) { + // InitializableClientToServerRpc is a special marker interface + // that should not get a generated class + continue; + } + sourceWriter.println("} else if (rpcInterface == " + rpcClassName + + ".class) {"); + sourceWriter.indent(); + sourceWriter.println(rpcClassName + " rpc = GWT.create(" + + rpcClassName + ".class);"); + sourceWriter.println("((" + InitializableServerRpc.class.getName() + + ") rpc).initRpc(connector);"); + sourceWriter.println("return (T) rpc;"); + sourceWriter.outdent(); + } + + sourceWriter.println("} else {"); + sourceWriter.indent(); + sourceWriter + .println("throw new IllegalArgumentException(\"No RpcInterface of type \"+ rpcInterface.getName() + \" was found.\");"); + sourceWriter.outdent(); + // End of if + sourceWriter.println("}"); + // End of method + sourceWriter.println("}"); + + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, composer.getCreatedClassName() + " created in " + + (new Date().getTime() - date.getTime()) / 1000 + "seconds"); + + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java new file mode 100644 index 0000000000..7a908e5b4d --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java @@ -0,0 +1,142 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; + +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.JMethod; +import com.google.gwt.core.ext.typeinfo.JParameter; +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.MethodInvocation; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.ServerConnector; +import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc; + +/** + * GWT generator that creates client side proxy classes for making RPC calls + * from the client to the server. + * + * GWT.create() calls for interfaces extending {@link ServerRpc} are affected, + * and a proxy implementation is created. Note that the init(...) method of the + * proxy must be called before the proxy is used. + * + * @since 7.0 + */ +public class RpcProxyGenerator extends Generator { + @Override + public String generate(TreeLogger logger, GeneratorContext ctx, + String requestedClassName) throws UnableToCompleteException { + logger.log(TreeLogger.DEBUG, "Running RpcProxyGenerator", null); + + TypeOracle typeOracle = ctx.getTypeOracle(); + assert (typeOracle != null); + + JClassType requestedType = typeOracle.findType(requestedClassName); + if (requestedType == null) { + logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + + requestedClassName + "'", null); + throw new UnableToCompleteException(); + } + + String generatedClassName = "ServerRpc_" + + requestedType.getName().replaceAll("[$.]", "_"); + + JClassType initializableInterface = typeOracle + .findType(InitializableServerRpc.class.getCanonicalName()); + + ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( + requestedType.getPackage().getName(), generatedClassName); + composer.addImplementedInterface(requestedType.getQualifiedSourceName()); + composer.addImplementedInterface(initializableInterface + .getQualifiedSourceName()); + composer.addImport(MethodInvocation.class.getCanonicalName()); + + PrintWriter printWriter = ctx.tryCreate(logger, + composer.getCreatedPackage(), + composer.getCreatedClassShortName()); + if (printWriter != null) { + logger.log(Type.INFO, "Generating client proxy for RPC interface '" + + requestedType.getQualifiedSourceName() + "'"); + SourceWriter writer = composer.createSourceWriter(ctx, printWriter); + + // constructor + writer.println("public " + generatedClassName + "() {}"); + + // initialization etc. + writeCommonFieldsAndMethods(logger, writer, typeOracle); + + // actual proxy methods forwarding calls to the server + writeRemoteProxyMethods(logger, writer, typeOracle, requestedType, + requestedType.isClassOrInterface().getInheritableMethods()); + + // End of class + writer.outdent(); + writer.println("}"); + + ctx.commit(logger, printWriter); + } + + return composer.getCreatedClassName(); + } + + private void writeCommonFieldsAndMethods(TreeLogger logger, + SourceWriter writer, TypeOracle typeOracle) { + JClassType applicationConnectionClass = typeOracle + .findType(ApplicationConnection.class.getCanonicalName()); + + // fields + writer.println("private " + ServerConnector.class.getName() + + " connector;"); + + // init method from the RPC interface + writer.println("public void initRpc(" + ServerConnector.class.getName() + + " connector) {"); + writer.indent(); + writer.println("this.connector = connector;"); + writer.outdent(); + writer.println("}"); + } + + private static void writeRemoteProxyMethods(TreeLogger logger, + SourceWriter writer, TypeOracle typeOracle, + JClassType requestedType, JMethod[] methods) { + for (JMethod m : methods) { + writer.print(m.getReadableDeclaration(false, false, false, false, + true)); + writer.println(" {"); + writer.indent(); + + writer.print("this.connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(this.connector.getConnectorId(), \"" + + requestedType.getQualifiedBinaryName() + "\", \""); + writer.print(m.getName()); + writer.print("\", new Object[] {"); + // new Object[] { ... } for parameters - autoboxing etc. by the + // compiler + JParameter[] parameters = m.getParameters(); + boolean first = true; + for (JParameter p : parameters) { + if (!first) { + writer.print(", "); + } + first = false; + + writer.print(p.getName()); + } + writer.println("}), true);"); + + writer.outdent(); + writer.println("}"); + } + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java new file mode 100644 index 0000000000..1951f8ba40 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java @@ -0,0 +1,458 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +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.JArrayType; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JEnumConstant; +import com.google.gwt.core.ext.typeinfo.JEnumType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.TypeOracleException; +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONString; +import com.google.gwt.json.client.JSONValue; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer; +import com.vaadin.terminal.gwt.client.communication.JSONSerializer; +import com.vaadin.terminal.gwt.client.communication.JsonDecoder; +import com.vaadin.terminal.gwt.client.communication.JsonEncoder; +import com.vaadin.terminal.gwt.client.communication.SerializerMap; + +/** + * GWT generator for creating serializer classes for custom classes sent from + * server to client. + * + * Only fields with a correspondingly named setter are deserialized. + * + * @since 7.0 + */ +public class SerializerGenerator extends Generator { + + private static final String SUBTYPE_SEPARATOR = "___"; + private static String serializerPackageName = SerializerMap.class + .getPackage().getName(); + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + JClassType type; + try { + type = (JClassType) context.getTypeOracle().parse(typeName); + } catch (TypeOracleException e1) { + logger.log(Type.ERROR, "Could not find type " + typeName, e1); + throw new UnableToCompleteException(); + } + String serializerClassName = getSerializerSimpleClassName(type); + try { + // Generate class source code + generateClass(logger, context, type, serializerPackageName, + serializerClassName); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, "SerializerGenerator failed for " + + type.getQualifiedSourceName(), e); + throw new UnableToCompleteException(); + } + + // return the fully qualifed name of the class generated + return getFullyQualifiedSerializerClassName(type); + } + + /** + * Generate source code for a VaadinSerializer implementation. + * + * @param logger + * Logger object + * @param context + * Generator context + * @param type + * @param beanTypeName + * bean type for which the serializer is to be generated + * @param beanSerializerTypeName + * name of the serializer class to generate + * @throws UnableToCompleteException + */ + private void generateClass(TreeLogger logger, GeneratorContext context, + JClassType type, String serializerPackageName, + String serializerClassName) throws UnableToCompleteException { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, serializerPackageName, + serializerClassName); + + // print writer if null, source code has ALREADY been generated + if (printWriter == null) { + return; + } + boolean isEnum = (type.isEnum() != null); + boolean isArray = (type.isArray() != null); + + String qualifiedSourceName = type.getQualifiedSourceName(); + logger.log(Type.DEBUG, "Processing serializable type " + + qualifiedSourceName + "..."); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(serializerPackageName, + serializerClassName); + composer.addImport(GWT.class.getName()); + composer.addImport(JSONValue.class.getName()); + composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class + .getName()); + // composer.addImport(JSONObject.class.getName()); + // composer.addImport(VPaintableMap.class.getName()); + composer.addImport(JsonDecoder.class.getName()); + // composer.addImport(VaadinSerializer.class.getName()); + + if (isEnum || isArray) { + composer.addImplementedInterface(JSONSerializer.class.getName() + + "<" + qualifiedSourceName + ">"); + } else { + composer.addImplementedInterface(DiffJSONSerializer.class.getName() + + "<" + qualifiedSourceName + ">"); + } + + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + // Serializer + + // public JSONValue serialize(Object value, + // ApplicationConnection connection) { + sourceWriter.println("public " + JSONValue.class.getName() + + " serialize(" + qualifiedSourceName + " value, " + + ApplicationConnection.class.getName() + " connection) {"); + sourceWriter.indent(); + // MouseEventDetails castedValue = (MouseEventDetails) value; + sourceWriter.println(qualifiedSourceName + " castedValue = (" + + qualifiedSourceName + ") value;"); + + if (isEnum) { + writeEnumSerializer(logger, sourceWriter, type); + } else if (isArray) { + writeArraySerializer(logger, sourceWriter, type.isArray()); + } else { + writeBeanSerializer(logger, sourceWriter, type); + } + // } + sourceWriter.outdent(); + sourceWriter.println("}"); + sourceWriter.println(); + + // Updater + // public void update(T target, Type type, JSONValue jsonValue, + // ApplicationConnection connection); + if (!isEnum && !isArray) { + sourceWriter.println("public void update(" + qualifiedSourceName + + " target, Type type, " + JSONValue.class.getName() + + " jsonValue, " + ApplicationConnection.class.getName() + + " connection) {"); + sourceWriter.indent(); + + writeBeanDeserializer(logger, sourceWriter, type); + + sourceWriter.outdent(); + sourceWriter.println("}"); + } + + // Deserializer + // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection + // connection); + sourceWriter.println("public " + qualifiedSourceName + + " deserialize(Type type, " + JSONValue.class.getName() + + " jsonValue, " + ApplicationConnection.class.getName() + + " connection) {"); + sourceWriter.indent(); + + if (isEnum) { + writeEnumDeserializer(logger, sourceWriter, type.isEnum()); + } else if (isArray) { + writeArrayDeserializer(logger, sourceWriter, type.isArray()); + } else { + sourceWriter.println(qualifiedSourceName + " target = GWT.create(" + + qualifiedSourceName + ".class);"); + sourceWriter + .println("update(target, type, jsonValue, connection);"); + // return target; + sourceWriter.println("return target;"); + } + sourceWriter.outdent(); + sourceWriter.println("}"); + + // End of class + sourceWriter.outdent(); + sourceWriter.println("}"); + + // commit generated class + context.commit(logger, printWriter); + logger.log(TreeLogger.INFO, "Generated Serializer class " + + getFullyQualifiedSerializerClassName(type)); + } + + private void writeEnumDeserializer(TreeLogger logger, + SourceWriter sourceWriter, JEnumType enumType) { + sourceWriter.println("String enumIdentifier = ((" + + JSONString.class.getName() + ")jsonValue).stringValue();"); + for (JEnumConstant e : enumType.getEnumConstants()) { + sourceWriter.println("if (\"" + e.getName() + + "\".equals(enumIdentifier)) {"); + sourceWriter.indent(); + sourceWriter.println("return " + enumType.getQualifiedSourceName() + + "." + e.getName() + ";"); + sourceWriter.outdent(); + sourceWriter.println("}"); + } + sourceWriter.println("return null;"); + } + + private void writeArrayDeserializer(TreeLogger logger, + SourceWriter sourceWriter, JArrayType type) { + JType leafType = type.getLeafType(); + int rank = type.getRank(); + + sourceWriter.println(JSONArray.class.getName() + + " jsonArray = jsonValue.isArray();"); + + // Type value = new Type[jsonArray.size()][][]; + sourceWriter.print(type.getQualifiedSourceName() + " value = new " + + leafType.getQualifiedSourceName() + "[jsonArray.size()]"); + for (int i = 1; i < rank; i++) { + sourceWriter.print("[]"); + } + sourceWriter.println(";"); + + sourceWriter.println("for(int i = 0 ; i < value.length; i++) {"); + sourceWriter.indent(); + + JType componentType = type.getComponentType(); + + sourceWriter.print("value[i] = (" + + GeneratedRpcMethodProviderGenerator + .getBoxedTypeName(componentType) + ") " + + JsonDecoder.class.getName() + ".decodeValue("); + GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter, + componentType); + sourceWriter.print(", jsonArray.get(i), null, connection)"); + + sourceWriter.println(";"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + + sourceWriter.println("return value;"); + } + + private void writeBeanDeserializer(TreeLogger logger, + SourceWriter sourceWriter, JClassType beanType) { + String beanQualifiedSourceName = beanType.getQualifiedSourceName(); + + // JSONOBject json = (JSONObject)jsonValue; + sourceWriter.println(JSONObject.class.getName() + " json = (" + + JSONObject.class.getName() + ")jsonValue;"); + + for (JMethod method : getSetters(beanType)) { + String setterName = method.getName(); + String baseName = setterName.substring(3); + String fieldName = getTransportFieldName(baseName); // setZIndex() + // -> zIndex + JType setterParameterType = method.getParameterTypes()[0]; + + logger.log(Type.DEBUG, "* Processing field " + fieldName + " in " + + beanQualifiedSourceName + " (" + beanType.getName() + ")"); + + // if (json.containsKey("height")) { + sourceWriter.println("if (json.containsKey(\"" + fieldName + + "\")) {"); + sourceWriter.indent(); + String jsonFieldName = "json_" + fieldName; + // JSONValue json_Height = json.get("height"); + sourceWriter.println("JSONValue " + jsonFieldName + + " = json.get(\"" + fieldName + "\");"); + + String fieldType; + String getterName = "get" + baseName; + JPrimitiveType primitiveType = setterParameterType.isPrimitive(); + if (primitiveType != null) { + // This is a primitive type -> must used the boxed type + fieldType = primitiveType.getQualifiedBoxedSourceName(); + if (primitiveType == JPrimitiveType.BOOLEAN) { + getterName = "is" + baseName; + } + } else { + fieldType = setterParameterType.getQualifiedSourceName(); + } + + // String referenceValue = target.getHeight(); + sourceWriter.println(fieldType + " referenceValue = target." + + getterName + "();"); + + // target.setHeight((String) + // JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper, + // connection)); + sourceWriter.print("target." + setterName + "((" + fieldType + ") " + + JsonDecoder.class.getName() + ".decodeValue("); + GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter, + setterParameterType); + sourceWriter.println(", " + jsonFieldName + + ", referenceValue, connection));"); + + // } ... end of if contains + sourceWriter.outdent(); + sourceWriter.println("}"); + } + } + + private void writeEnumSerializer(TreeLogger logger, + SourceWriter sourceWriter, JClassType beanType) { + // return new JSONString(castedValue.name()); + sourceWriter.println("return new " + JSONString.class.getName() + + "(castedValue.name());"); + } + + private void writeArraySerializer(TreeLogger logger, + SourceWriter sourceWriter, JArrayType array) { + sourceWriter.println(JSONArray.class.getName() + " values = new " + + JSONArray.class.getName() + "();"); + JType componentType = array.getComponentType(); + // JPrimitiveType primitive = componentType.isPrimitive(); + sourceWriter.println("for (int i = 0; i < castedValue.length; i++) {"); + sourceWriter.indent(); + sourceWriter.print("values.set(i, "); + sourceWriter.print(JsonEncoder.class.getName() + + ".encode(castedValue[i], false, connection)"); + sourceWriter.println(");"); + sourceWriter.outdent(); + sourceWriter.println("}"); + sourceWriter.println("return values;"); + } + + private void writeBeanSerializer(TreeLogger logger, + SourceWriter sourceWriter, JClassType beanType) + throws UnableToCompleteException { + + // JSONObject json = new JSONObject(); + sourceWriter.println(JSONObject.class.getName() + " json = new " + + JSONObject.class.getName() + "();"); + + HashSet usedFieldNames = new HashSet(); + + for (JMethod setterMethod : getSetters(beanType)) { + String setterName = setterMethod.getName(); + String fieldName = getTransportFieldName(setterName.substring(3)); // setZIndex() + // -> zIndex + if (!usedFieldNames.add(fieldName)) { + logger.log( + TreeLogger.ERROR, + "Can't encode " + + beanType.getQualifiedSourceName() + + " as it has multiple fields with the name " + + fieldName.toLowerCase() + + ". This can happen if only casing distinguishes one property name from another."); + throw new UnableToCompleteException(); + } + String getterName = findGetter(beanType, setterMethod); + + if (getterName == null) { + logger.log(TreeLogger.ERROR, "No getter found for " + fieldName + + ". Serialization will likely fail"); + } + // json.put("button", + // JsonEncoder.encode(castedValue.getButton(), false, idMapper, + // connection)); + sourceWriter.println("json.put(\"" + fieldName + "\", " + + JsonEncoder.class.getName() + ".encode(castedValue." + + getterName + "(), false, connection));"); + } + // return json; + sourceWriter.println("return json;"); + + } + + private static String getTransportFieldName(String baseName) { + return Character.toLowerCase(baseName.charAt(0)) + + baseName.substring(1); + } + + private String findGetter(JClassType beanType, JMethod setterMethod) { + JType setterParameterType = setterMethod.getParameterTypes()[0]; + String fieldName = setterMethod.getName().substring(3); + if (setterParameterType.getQualifiedSourceName().equals( + boolean.class.getName())) { + return "is" + fieldName; + } else { + return "get" + fieldName; + } + } + + /** + * Returns a list of all setters found in the beanType or its parent class + * + * @param beanType + * The type to check + * @return A list of setter methods from the class and its parents + */ + protected static List getSetters(JClassType beanType) { + + List setterMethods = new ArrayList(); + + while (beanType != null + && !beanType.getQualifiedSourceName().equals( + Object.class.getName())) { + for (JMethod method : beanType.getMethods()) { + // Process all setters that have corresponding fields + if (!method.isPublic() || method.isStatic() + || !method.getName().startsWith("set") + || method.getParameterTypes().length != 1) { + // Not setter, skip to next method + continue; + } + setterMethods.add(method); + } + beanType = beanType.getSuperclass(); + } + + return setterMethods; + } + + private static String getSerializerSimpleClassName(JClassType beanType) { + return getSimpleClassName(beanType) + "_Serializer"; + } + + private static String getSimpleClassName(JType type) { + JArrayType arrayType = type.isArray(); + if (arrayType != null) { + return "Array" + getSimpleClassName(arrayType.getComponentType()); + } + JClassType classType = type.isClass(); + if (classType != null && classType.isMemberType()) { + // Assumed to be static sub class + String baseName = getSimpleClassName(classType.getEnclosingType()); + String name = baseName + SUBTYPE_SEPARATOR + + type.getSimpleSourceName(); + return name; + } + return type.getSimpleSourceName(); + } + + public static String getFullyQualifiedSerializerClassName(JClassType type) { + return serializerPackageName + "." + getSerializerSimpleClassName(type); + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java new file mode 100644 index 0000000000..3f1ad24066 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java @@ -0,0 +1,365 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.JArrayType; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.NotFoundException; +import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.json.client.JSONValue; +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.communication.SharedState; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.communication.JSONSerializer; +import com.vaadin.terminal.gwt.client.communication.SerializerMap; + +/** + * GWT generator that creates a {@link SerializerMap} implementation (mapper + * from type string to serializer instance) and serializer classes for all + * subclasses of {@link SharedState}. + * + * @since 7.0 + */ +public class SerializerMapGenerator extends Generator { + + private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; + private String packageName; + private String className; + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + try { + TypeOracle typeOracle = context.getTypeOracle(); + Set typesNeedingSerializers = findTypesNeedingSerializers( + typeOracle, logger); + checkForUnserializableTypes(typesNeedingSerializers, typeOracle, + logger); + Set typesWithExistingSerializers = findTypesWithExistingSerializers( + typeOracle, logger); + Set serializerMappings = new HashSet(); + serializerMappings.addAll(typesNeedingSerializers); + serializerMappings.addAll(typesWithExistingSerializers); + // get classType and save instance variables + JClassType classType = typeOracle.getType(typeName); + packageName = classType.getPackage().getName(); + className = classType.getSimpleSourceName() + "Impl"; + // Generate class source code for SerializerMapImpl + generateSerializerMap(serializerMappings, logger, context); + + SerializerGenerator sg = new SerializerGenerator(); + for (JClassType type : typesNeedingSerializers) { + sg.generate(logger, context, type.getQualifiedSourceName()); + } + } catch (Exception e) { + logger.log(TreeLogger.ERROR, + "SerializerMapGenerator creation failed", e); + throw new UnableToCompleteException(); + } + // return the fully qualifed name of the class generated + return packageName + "." + className; + } + + /** + * Emits a warning for all classes that are used in communication but do not + * implement java.io.Serializable. Implementing java.io.Serializable is not + * needed for communication but for the server side Application to be + * serializable i.e. work in GAE for instance. + * + * @param typesNeedingSerializers + * @param typeOracle + * @param logger + * @throws UnableToCompleteException + */ + private void checkForUnserializableTypes( + Set typesNeedingSerializers, TypeOracle typeOracle, + TreeLogger logger) throws UnableToCompleteException { + JClassType javaSerializable = typeOracle.findType(Serializable.class + .getName()); + for (JClassType type : typesNeedingSerializers) { + if (type.isArray() != null) { + // Don't check for arrays + continue; + } + boolean serializable = type.isAssignableTo(javaSerializable); + if (!serializable) { + boolean abortCompile = "true".equals(System + .getProperty(FAIL_IF_NOT_SERIALIZABLE)); + logger.log( + abortCompile ? Type.ERROR : Type.WARN, + type + + " is used in RPC or shared state but does not implement " + + Serializable.class.getName() + + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. " + + "If the system property " + + FAIL_IF_NOT_SERIALIZABLE + + " is set to \"true\", this causes the compilation to fail instead of just emitting a warning."); + if (abortCompile) { + throw new UnableToCompleteException(); + } + } + } + } + + private Set findTypesWithExistingSerializers( + TypeOracle typeOracle, TreeLogger logger) + throws UnableToCompleteException { + JClassType serializerInterface = typeOracle + .findType(JSONSerializer.class.getName()); + JType[] deserializeParamTypes = new JType[] { + typeOracle + .findType(com.vaadin.terminal.gwt.client.communication.Type.class + .getName()), + typeOracle.findType(JSONValue.class.getName()), + typeOracle.findType(ApplicationConnection.class.getName()) }; + String deserializeMethodName = "deserialize"; + try { + serializerInterface.getMethod(deserializeMethodName, + deserializeParamTypes); + } catch (NotFoundException e) { + logger.log(Type.ERROR, "Could not find " + deserializeMethodName + + " in " + serializerInterface); + throw new UnableToCompleteException(); + } + + Set types = new HashSet(); + for (JClassType serializer : serializerInterface.getSubtypes()) { + JMethod deserializeMethod = serializer.findMethod( + deserializeMethodName, deserializeParamTypes); + if (deserializeMethod == null) { + logger.log(Type.DEBUG, "Could not find " + + deserializeMethodName + " in " + serializer); + continue; + } + JType returnType = deserializeMethod.getReturnType(); + logger.log(Type.DEBUG, "Found " + deserializeMethodName + + " with return type " + returnType + " in " + serializer); + + types.add(returnType.isClass()); + } + return types; + } + + /** + * Generate source code for SerializerMapImpl + * + * @param typesNeedingSerializers + * + * @param logger + * Logger object + * @param context + * Generator context + */ + private void generateSerializerMap(Set typesNeedingSerializers, + TreeLogger logger, GeneratorContext context) { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated + if (printWriter == null) { + return; + } + Date date = new Date(); + TypeOracle typeOracle = context.getTypeOracle(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + composer.addImport("com.google.gwt.core.client.GWT"); + composer.addImplementedInterface(SerializerMap.class.getName()); + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + sourceWriter.indent(); + + sourceWriter.println("public " + JSONSerializer.class.getName() + + " getSerializer(String type) {"); + sourceWriter.indent(); + + // TODO cache serializer instances in a map + for (JClassType type : typesNeedingSerializers) { + sourceWriter.print("if (type.equals(\"" + + type.getQualifiedSourceName() + "\")"); + if (type instanceof JArrayType) { + // Also add binary name to support encoding based on + // object.getClass().getName() + sourceWriter.print("||type.equals(\"" + type.getJNISignature() + + "\")"); + } + sourceWriter.println(") {"); + sourceWriter.indent(); + String serializerName = SerializerGenerator + .getFullyQualifiedSerializerClassName(type); + sourceWriter.println("return GWT.create(" + serializerName + + ".class);"); + sourceWriter.outdent(); + sourceWriter.println("}"); + logger.log(Type.INFO, "Configured serializer (" + serializerName + + ") for " + type.getName()); + } + sourceWriter + .println("throw new RuntimeException(\"No serializer found for class \"+type);"); + sourceWriter.outdent(); + sourceWriter.println("}"); + + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + + } + + public Set findTypesNeedingSerializers(TypeOracle typeOracle, + TreeLogger logger) { + logger.log(Type.DEBUG, "Detecting serializable data types..."); + + HashSet types = new HashSet(); + + // Generate serializer classes for each subclass of SharedState + JClassType serializerType = typeOracle.findType(SharedState.class + .getName()); + types.add(serializerType); + JClassType[] serializerSubtypes = serializerType.getSubtypes(); + for (JClassType type : serializerSubtypes) { + types.add(type); + } + + // Serializer classes might also be needed for RPC methods + for (Class cls : new Class[] { ServerRpc.class, ClientRpc.class }) { + JClassType rpcType = typeOracle.findType(cls.getName()); + JClassType[] serverRpcSubtypes = rpcType.getSubtypes(); + for (JClassType type : serverRpcSubtypes) { + addMethodParameterTypes(type, types, logger); + } + } + + // Add all types used from/in the types + for (Object t : types.toArray()) { + findSubTypesNeedingSerializers((JClassType) t, types); + } + logger.log(Type.DEBUG, "Serializable data types: " + types.toString()); + + return types; + } + + private void addMethodParameterTypes(JClassType classContainingMethods, + Set types, TreeLogger logger) { + for (JMethod method : classContainingMethods.getMethods()) { + if (method.getName().equals("initRpc")) { + continue; + } + for (JType type : method.getParameterTypes()) { + addTypeIfNeeded(types, type); + } + } + } + + public void findSubTypesNeedingSerializers(JClassType type, + Set serializableTypes) { + // Find all setters and look at their parameter type to determine if a + // new serializer is needed + for (JMethod setterMethod : SerializerGenerator.getSetters(type)) { + // The one and only parameter for the setter + JType setterType = setterMethod.getParameterTypes()[0]; + addTypeIfNeeded(serializableTypes, setterType); + } + } + + private void addTypeIfNeeded(Set serializableTypes, JType type) { + if (serializableTypes.contains(type)) { + return; + } + JParameterizedType parametrized = type.isParameterized(); + if (parametrized != null) { + for (JClassType parameterType : parametrized.getTypeArgs()) { + addTypeIfNeeded(serializableTypes, parameterType); + } + } + + if (serializationHandledByFramework(type)) { + return; + } + + if (serializableTypes.contains(type)) { + return; + } + + JClassType typeClass = type.isClass(); + if (typeClass != null) { + // setterTypeClass is null at least for List. It is + // possible that we need to handle the cases somehow, for + // instance for List. + serializableTypes.add(typeClass); + findSubTypesNeedingSerializers(typeClass, serializableTypes); + } + + // Generate (n-1)-dimensional array serializer for n-dimensional array + JArrayType arrayType = type.isArray(); + if (arrayType != null) { + serializableTypes.add(arrayType); + addTypeIfNeeded(serializableTypes, arrayType.getComponentType()); + } + + } + + Set> frameworkHandledTypes = new HashSet>(); + { + frameworkHandledTypes.add(String.class); + frameworkHandledTypes.add(Boolean.class); + frameworkHandledTypes.add(Integer.class); + frameworkHandledTypes.add(Float.class); + frameworkHandledTypes.add(Double.class); + frameworkHandledTypes.add(Long.class); + frameworkHandledTypes.add(Enum.class); + frameworkHandledTypes.add(String[].class); + frameworkHandledTypes.add(Object[].class); + frameworkHandledTypes.add(Map.class); + frameworkHandledTypes.add(List.class); + frameworkHandledTypes.add(Set.class); + frameworkHandledTypes.add(Byte.class); + frameworkHandledTypes.add(Character.class); + + } + + private boolean serializationHandledByFramework(JType setterType) { + // Some types are handled by the framework at the moment. See #8449 + // This method should be removed at some point. + if (setterType.isPrimitive() != null) { + return true; + } + + String qualifiedName = setterType.getQualifiedSourceName(); + for (Class cls : frameworkHandledTypes) { + if (qualifiedName.equals(cls.getName())) { + return true; + } + } + + return false; + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java new file mode 100644 index 0000000000..0d062ec4ff --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java @@ -0,0 +1,398 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.TreeSet; + +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.TypeOracle; +import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.shared.Connector; +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.ui.UnknownComponentConnector; +import com.vaadin.terminal.gwt.client.ui.root.RootConnector; +import com.vaadin.terminal.gwt.server.ClientConnector; + +/** + * WidgetMapGenerator's are GWT generator to build WidgetMapImpl dynamically + * based on {@link Connect} annotations available in workspace. By modifying the + * generator it is possible to do some fine tuning for the generated widgetset + * (aka client side engine). The components to be included in the client side + * engine can modified be overriding {@link #getUsedConnectors()}. + *

+ * The generator also decides how the client side component implementations are + * loaded to the browser. The default generator is + * {@link EagerWidgetMapGenerator} that builds a monolithic client side engine + * that loads all widget implementation on application initialization. This has + * been the only option until Vaadin 6.4. + *

+ * This generator uses the loadStyle hints from the {@link Connect} annotations. + * Depending on the {@link LoadStyle} used, the widget may be included in the + * initially loaded JavaScript, loaded when the application has started and + * there is no communication to server or lazy loaded when the implementation is + * absolutely needed. + *

+ * The GWT module description file of the widgetset ( + * ...Widgetset.gwt.xml) can be used to define the + * WidgetMapGenarator. An example that defines this generator to be used: + * + *

+ * 
+ * <generate-with
+ *           class="com.vaadin.terminal.gwt.widgetsetutils.MyWidgetMapGenerator">
+ *          <when-type-is class="com.vaadin.terminal.gwt.client.WidgetMap" />
+ * </generate-with>
+ * 
+ * 
+ * 
+ * + *

+ * Vaadin package also includes {@link LazyWidgetMapGenerator}, which is a good + * option if the transferred data should be minimized, and + * {@link CustomWidgetMapGenerator} for easy overriding of loading strategies. + * + */ +public class WidgetMapGenerator extends Generator { + + private static String serverConnectorClassName = ServerConnector.class + .getName(); + + private String packageName; + private String className; + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + + try { + TypeOracle typeOracle = context.getTypeOracle(); + + // get classType and save instance variables + JClassType classType = typeOracle.getType(typeName); + packageName = classType.getPackage().getName(); + className = classType.getSimpleSourceName() + "Impl"; + // Generate class source code + generateClass(logger, context); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, "WidgetMap creation failed", e); + } + // return the fully qualifed name of the class generated + return packageName + "." + className; + } + + /** + * Generate source code for WidgetMapImpl + * + * @param logger + * Logger object + * @param context + * Generator context + */ + private void generateClass(TreeLogger logger, GeneratorContext context) { + // get print writer that receives the source code + PrintWriter printWriter = null; + printWriter = context.tryCreate(logger, packageName, className); + // print writer if null, source code has ALREADY been generated, + // return (WidgetMap is equal to all permutations atm) + if (printWriter == null) { + return; + } + logger.log(Type.INFO, + "Detecting Vaadin connectors in classpath to generate WidgetMapImpl.java ..."); + Date date = new Date(); + + // init composer, set class properties, create source writer + ClassSourceFileComposerFactory composer = null; + composer = new ClassSourceFileComposerFactory(packageName, className); + composer.addImport("com.google.gwt.core.client.GWT"); + composer.addImport("java.util.HashMap"); + composer.addImport("com.google.gwt.core.client.RunAsyncCallback"); + composer.setSuperclass("com.vaadin.terminal.gwt.client.WidgetMap"); + SourceWriter sourceWriter = composer.createSourceWriter(context, + printWriter); + + Collection> connectors = getUsedConnectors(context + .getTypeOracle()); + + validateConnectors(logger, connectors); + logConnectors(logger, context, connectors); + + // generator constructor source code + generateImplementationDetector(sourceWriter, connectors); + generateInstantiatorMethod(sourceWriter, connectors); + // close generated class + sourceWriter.outdent(); + sourceWriter.println("}"); + // commit generated class + context.commit(logger, printWriter); + logger.log(Type.INFO, + "Done. (" + (new Date().getTime() - date.getTime()) / 1000 + + "seconds)"); + + } + + private void validateConnectors(TreeLogger logger, + Collection> connectors) { + + Iterator> iter = connectors.iterator(); + while (iter.hasNext()) { + Class connectorClass = iter.next(); + Connect annotation = connectorClass.getAnnotation(Connect.class); + if (!ClientConnector.class.isAssignableFrom(annotation.value())) { + logger.log( + Type.WARN, + "Connector class " + + annotation.value().getName() + + " defined in @Connect annotation is not a subclass of " + + ClientConnector.class.getName() + + ". The component connector " + + connectorClass.getName() + + " will not be included in the widgetset."); + iter.remove(); + } + } + + } + + private void logConnectors(TreeLogger logger, GeneratorContext context, + Collection> connectors) { + logger.log(Type.INFO, + "Widget set will contain implementations for following component connectors: "); + + TreeSet classNames = new TreeSet(); + HashMap loadStyle = new HashMap(); + for (Class connectorClass : connectors) { + String className = connectorClass.getCanonicalName(); + classNames.add(className); + if (getLoadStyle(connectorClass) == LoadStyle.DEFERRED) { + loadStyle.put(className, "DEFERRED"); + } else if (getLoadStyle(connectorClass) == LoadStyle.LAZY) { + loadStyle.put(className, "LAZY"); + } + + } + for (String className : classNames) { + String msg = className; + if (loadStyle.containsKey(className)) { + msg += " (load style: " + loadStyle.get(className) + ")"; + } + logger.log(Type.INFO, "\t" + msg); + } + } + + /** + * This method is protected to allow creation of optimized widgetsets. The + * Widgetset will contain only implementation returned by this function. If + * one knows which widgets are needed for the application, returning only + * them here will significantly optimize the size of the produced JS. + * + * @return a collections of Vaadin components that will be added to + * widgetset + */ + @SuppressWarnings("unchecked") + private Collection> getUsedConnectors( + TypeOracle typeOracle) { + JClassType connectorType = typeOracle.findType(Connector.class + .getName()); + Collection> connectors = new HashSet>(); + for (JClassType jClassType : connectorType.getSubtypes()) { + Connect annotation = jClassType.getAnnotation(Connect.class); + if (annotation != null) { + try { + Class clazz = (Class) Class + .forName(jClassType.getQualifiedSourceName()); + connectors.add(clazz); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + return connectors; + } + + /** + * Returns true if the widget for given component will be lazy loaded by the + * client. The default implementation reads the information from the + * {@link Connect} annotation. + *

+ * The method can be overridden to optimize the widget loading mechanism. If + * the Widgetset is wanted to be optimized for a network with a high latency + * or for a one with a very fast throughput, it may be good to return false + * for every component. + * + * @param connector + * @return true iff the widget for given component should be lazy loaded by + * the client side engine + */ + protected LoadStyle getLoadStyle(Class connector) { + Connect annotation = connector.getAnnotation(Connect.class); + return annotation.loadStyle(); + } + + private void generateInstantiatorMethod( + SourceWriter sourceWriter, + Collection> connectorsHavingComponentAnnotation) { + + Collection> deferredWidgets = new LinkedList>(); + + // TODO detect if it would be noticably faster to instantiate with a + // lookup with index than with the hashmap + + sourceWriter.println("public void ensureInstantiator(Class classType) {"); + sourceWriter.println("if(!instmap.containsKey(classType)){"); + boolean first = true; + + ArrayList> lazyLoadedConnectors = new ArrayList>(); + + HashSet> connectorsWithInstantiator = new HashSet>(); + + for (Class class1 : connectorsHavingComponentAnnotation) { + Class clientClass = class1; + if (connectorsWithInstantiator.contains(clientClass)) { + continue; + } + if (clientClass == RootConnector.class) { + // Roots are not instantiated by widgetset + continue; + } + if (!first) { + sourceWriter.print(" else "); + } else { + first = false; + } + sourceWriter.print("if( classType == " + clientClass.getName() + + ".class) {"); + + String instantiator = "new WidgetInstantiator() {\n public " + + serverConnectorClassName + + " get() {\n return GWT.create(" + clientClass.getName() + + ".class );\n}\n}\n"; + + LoadStyle loadStyle = getLoadStyle(class1); + + if (loadStyle != LoadStyle.EAGER) { + sourceWriter + .print("ApplicationConfiguration.startWidgetLoading();\n" + + "GWT.runAsync( \n" + + "new WidgetLoader() { void addInstantiator() {instmap.put(" + + clientClass.getName() + + ".class," + + instantiator + ");}});\n"); + lazyLoadedConnectors.add(class1); + + if (loadStyle == LoadStyle.DEFERRED) { + deferredWidgets.add(class1); + } + + } else { + // widget implementation in initially loaded js script + sourceWriter.print("instmap.put("); + sourceWriter.print(clientClass.getName()); + sourceWriter.print(".class, "); + sourceWriter.print(instantiator); + sourceWriter.print(");"); + } + sourceWriter.print("}"); + connectorsWithInstantiator.add(clientClass); + } + + sourceWriter.println("}"); + + sourceWriter.println("}"); + + sourceWriter.println("public Class[] getDeferredLoadedConnectors() {"); + + sourceWriter.println("return new Class[] {"); + first = true; + for (Class class2 : deferredWidgets) { + if (!first) { + sourceWriter.println(","); + } + first = false; + sourceWriter.print(class2.getName() + ".class"); + } + + sourceWriter.println("};"); + sourceWriter.println("}"); + + // in constructor add a "thread" that lazyly loads lazy loaded widgets + // if communication to server idles + + // TODO an array of lazy loaded widgets + + // TODO an index of last ensured widget in array + + sourceWriter.println("public " + serverConnectorClassName + + " instantiate(Class classType) {"); + sourceWriter.indent(); + sourceWriter.println(serverConnectorClassName + + " p = super.instantiate(classType); if(p!= null) return p;"); + sourceWriter.println("return instmap.get(classType).get();"); + + sourceWriter.outdent(); + sourceWriter.println("}"); + + } + + /** + * + * @param sourceWriter + * Source writer to output source code + * @param paintablesHavingWidgetAnnotation + */ + private void generateImplementationDetector( + SourceWriter sourceWriter, + Collection> paintablesHavingWidgetAnnotation) { + sourceWriter + .println("public Class " + + "getConnectorClassForServerSideClassName(String fullyQualifiedName) {"); + sourceWriter.indent(); + sourceWriter + .println("fullyQualifiedName = fullyQualifiedName.intern();"); + + for (Class connectorClass : paintablesHavingWidgetAnnotation) { + Class clientConnectorClass = getClientConnectorClass(connectorClass); + sourceWriter.print("if ( fullyQualifiedName == \""); + sourceWriter.print(clientConnectorClass.getName()); + sourceWriter.print("\" ) { ensureInstantiator(" + + connectorClass.getName() + ".class); return "); + sourceWriter.print(connectorClass.getName()); + sourceWriter.println(".class;}"); + sourceWriter.print("else "); + } + sourceWriter.println("return " + + UnknownComponentConnector.class.getName() + ".class;"); + sourceWriter.outdent(); + sourceWriter.println("}"); + + } + + private static Class getClientConnectorClass( + Class connectorClass) { + Connect annotation = connectorClass.getAnnotation(Connect.class); + return (Class) annotation.value(); + } +} diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java new file mode 100644 index 0000000000..4c6e334a33 --- /dev/null +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java @@ -0,0 +1,201 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.widgetsetutils; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Reader; +import java.net.URL; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Helper class to update widgetsets GWT module configuration file. Can be used + * command line or via IDE tools. + * + *

+ * If module definition file contains text "WS Compiler: manually edited", tool + * will skip editing file. + * + */ +public class WidgetSetBuilder { + + public static void main(String[] args) throws IOException { + if (args.length == 0) { + printUsage(); + } else { + String widgetsetname = args[0]; + updateWidgetSet(widgetsetname); + + } + } + + public static void updateWidgetSet(final String widgetset) + throws IOException, FileNotFoundException { + boolean changed = false; + + Map availableWidgetSets = ClassPathExplorer + .getAvailableWidgetSets(); + + URL sourceUrl = availableWidgetSets.get(widgetset); + if (sourceUrl == null) { + // find first/default source directory + sourceUrl = ClassPathExplorer.getDefaultSourceDirectory(); + } + + String widgetsetfilename = sourceUrl.getFile() + "/" + + widgetset.replace(".", "/") + ".gwt.xml"; + + File widgetsetFile = new File(widgetsetfilename); + if (!widgetsetFile.exists()) { + // create empty gwt module file + File parent = widgetsetFile.getParentFile(); + if (parent != null && !parent.exists()) { + if (!parent.mkdirs()) { + throw new IOException( + "Could not create directory for the widgetset: " + + parent.getPath()); + } + } + widgetsetFile.createNewFile(); + PrintStream printStream = new PrintStream(new FileOutputStream( + widgetsetFile)); + printStream.print("\n" + + "\n"); + printStream.print("\n"); + printStream + .print(" \n" + + " \n"); + printStream.print("\n\n"); + printStream.close(); + changed = true; + } + + String content = readFile(widgetsetFile); + if (isEditable(content)) { + String originalContent = content; + + Collection oldInheritedWidgetsets = getCurrentInheritedWidgetsets(content); + + // add widgetsets that do not exist + Iterator i = availableWidgetSets.keySet().iterator(); + while (i.hasNext()) { + String ws = i.next(); + if (ws.equals(widgetset)) { + // do not inherit the module itself + continue; + } + if (!oldInheritedWidgetsets.contains(ws)) { + content = addWidgetSet(ws, content); + } + } + + for (String ws : oldInheritedWidgetsets) { + if (!availableWidgetSets.containsKey(ws)) { + // widgetset not available in classpath + content = removeWidgetSet(ws, content); + } + } + + changed = changed || !content.equals(originalContent); + if (changed) { + commitChanges(widgetsetfilename, content); + } + } else { + System.out + .println("Widgetset is manually edited. Skipping updates."); + } + } + + private static boolean isEditable(String content) { + return !content.contains("WS Compiler: manually edited"); + } + + private static String removeWidgetSet(String ws, String content) { + return content.replaceFirst("", ""); + } + + private static void commitChanges(String widgetsetfilename, String content) + throws IOException { + BufferedWriter bufferedWriter = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(widgetsetfilename))); + bufferedWriter.write(content); + bufferedWriter.close(); + } + + private static String addWidgetSet(String ws, String content) { + return content.replace("", "\n " + "\n"); + } + + private static Collection getCurrentInheritedWidgetsets( + String content) { + HashSet hashSet = new HashSet(); + Pattern inheritsPattern = Pattern.compile(" name=\"([^\"]*)\""); + + Matcher matcher = inheritsPattern.matcher(content); + + while (matcher.find()) { + String gwtModule = matcher.group(1); + if (isWidgetset(gwtModule)) { + hashSet.add(gwtModule); + } + } + return hashSet; + } + + static boolean isWidgetset(String gwtModuleName) { + return gwtModuleName.toLowerCase().contains("widgetset"); + } + + private static String readFile(File widgetsetFile) throws IOException { + Reader fi = new FileReader(widgetsetFile); + BufferedReader bufferedReader = new BufferedReader(fi); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line); + sb.append("\n"); + } + fi.close(); + return sb.toString(); + } + + private static void printUsage() { + PrintStream o = System.out; + o.println(WidgetSetBuilder.class.getSimpleName() + " usage:"); + o.println(" 1. Set the same classpath as you will " + + "have for the GWT compiler."); + o.println(" 2. Give the widgetsetname (to be created or updated)" + + " as first parameter"); + o.println(); + o.println("All found vaadin widgetsets will be inherited in given widgetset"); + + } + +} diff --git a/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java b/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java new file mode 100755 index 0000000000..476136a584 --- /dev/null +++ b/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java @@ -0,0 +1,94 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.tools; + +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.terminal.gwt.widgetsetutils.WidgetSetBuilder; + +/** + * A wrapper for the GWT 1.6 compiler that runs the compiler in a new thread. + * + * This allows circumventing a J2SE 5.0 bug (6316197) that prevents setting the + * stack size for the main thread. Thus, larger widgetsets can be compiled. + * + * This class takes the same command line arguments as the + * com.google.gwt.dev.GWTCompiler class. The old and deprecated compiler is used + * for compatibility with GWT 1.5. + * + * A typical invocation would use e.g. the following arguments + * + * "-out WebContent/VAADIN/widgetsets com.vaadin.terminal.gwt.DefaultWidgetSet" + * + * In addition, larger memory usage settings for the VM should be used, e.g. + * + * "-Xms256M -Xmx512M -Xss8M" + * + * The source directory containing widgetset and related classes must be + * included in the classpath, as well as the gwt-dev-[platform].jar and other + * relevant JARs. + * + * @deprecated with Java 6, can use com.google.gwt.dev.Compiler directly (also + * in Eclipse plug-in etc.) + */ +@Deprecated +public class WidgetsetCompiler { + + /** + * @param args + * same arguments as for com.google.gwt.dev.Compiler + */ + public static void main(final String[] args) { + try { + // run the compiler in a different thread to enable using the + // user-set stack size + + // on Windows, the default stack size is too small for the main + // thread and cannot be changed in JRE 1.5 (see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6316197) + + Runnable runCompiler = new Runnable() { + @Override + public void run() { + try { + // GWTCompiler.main(args); + // avoid warnings + + String wsname = args[args.length - 1]; + + // TODO expecting this is launched via eclipse WTP + // project + System.out + .println("Updating GWT module description file..."); + WidgetSetBuilder.updateWidgetSet(wsname); + System.out.println("Done."); + + System.out.println("Starting GWT compiler"); + System.setProperty("gwt.nowarn.legacy.tools", "true"); + Class compilerClass = Class + .forName("com.google.gwt.dev.GWTCompiler"); + Method method = compilerClass.getDeclaredMethod("main", + String[].class); + method.invoke(null, new Object[] { args }); + } catch (Throwable thr) { + getLogger().log(Level.SEVERE, + "Widgetset compilation failed", thr); + } + } + }; + Thread runThread = new Thread(runCompiler); + runThread.start(); + runThread.join(); + System.out.println("Widgetset compilation finished"); + } catch (Throwable thr) { + getLogger().log(Level.SEVERE, "Widgetset compilation failed", thr); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(WidgetsetCompiler.class.getName()); + } +} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java deleted file mode 100644 index 3a13fceece..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/AbstractConnectorClassBasedFactoryGenerator.java +++ /dev/null @@ -1,145 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.Date; - -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.JMethod; -import com.google.gwt.core.ext.typeinfo.JType; -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.terminal.gwt.client.ServerConnector; -import com.vaadin.terminal.gwt.client.ui.ConnectorClassBasedFactory; -import com.vaadin.terminal.gwt.client.ui.ConnectorClassBasedFactory.Creator; - -/** - * GWT generator that creates a lookup method for - * {@link ConnectorClassBasedFactory} instances. - * - * @since 7.0 - */ -public abstract class AbstractConnectorClassBasedFactoryGenerator extends - Generator { - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - try { - // get classType and save instance variables - return generateConnectorClassBasedFactory(typeName, logger, context); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, typeName + " creation failed", e); - throw new UnableToCompleteException(); - } - } - - private String generateConnectorClassBasedFactory(String typeName, - TreeLogger logger, GeneratorContext context) - throws NotFoundException { - TypeOracle typeOracle = context.getTypeOracle(); - - JClassType classType = typeOracle.getType(typeName); - String superName = classType.getSimpleSourceName(); - String packageName = classType.getPackage().getName(); - String className = superName + "Impl"; - - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated - if (printWriter == null) { - return packageName + "." + className; - } - - Date date = new Date(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - composer.addImport(GWT.class.getName()); - composer.addImport(Creator.class.getCanonicalName()); - composer.setSuperclass(superName); - - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - // public ConnectorStateFactoryImpl() { - sourceWriter.println("public " + className + "() {"); - sourceWriter.indent(); - - JClassType serverConnectorType = typeOracle.getType(getConnectorType() - .getCanonicalName()); - for (JClassType connector : serverConnectorType.getSubtypes()) { - // addCreator(TextAreaConnector.class, new Creator() { - if (connector.isInterface() != null || connector.isAbstract()) { - continue; - } - - JClassType targetType = getTargetType(connector); - if (targetType.isAbstract()) { - continue; - } - - sourceWriter.println("addCreator(" - + connector.getQualifiedSourceName() - + ".class, new Creator<" - + targetType.getQualifiedSourceName() + ">() {"); - // public SharedState create() { - sourceWriter.println("public " - + targetType.getQualifiedSourceName() + " create() {"); - // return GWT.create(TextAreaState.class); - sourceWriter.println("return GWT.create(" - + targetType.getQualifiedSourceName() + ".class);"); - // } - sourceWriter.println("}"); - // }); - sourceWriter.println("});"); - } - - // End of constructor - sourceWriter.outdent(); - sourceWriter.println("}"); - - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - return packageName + "." + className; - - } - - protected abstract Class getConnectorType(); - - protected abstract JClassType getTargetType(JClassType connectorType); - - protected JClassType getGetterReturnType(JClassType connector, - String getterName) { - try { - JMethod getMethod = connector.getMethod(getterName, new JType[] {}); - return (JClassType) getMethod.getReturnType(); - } catch (NotFoundException e) { - return getGetterReturnType(connector.getSuperclass(), getterName); - } - - } - -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java deleted file mode 100644 index e5e2ee1f2c..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/AcceptCriteriaFactoryGenerator.java +++ /dev/null @@ -1,127 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.Date; - -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.TypeOracle; -import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.shared.ui.dd.AcceptCriterion; -import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterion; -import com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory; - -/** - * GWT generator to build {@link VAcceptCriterionFactory} implementation - * dynamically based on {@link AcceptCriterion} annotations available in - * classpath. - * - */ -public class AcceptCriteriaFactoryGenerator extends Generator { - - private String packageName; - private String className; - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - try { - TypeOracle typeOracle = context.getTypeOracle(); - - // get classType and save instance variables - JClassType classType = typeOracle.getType(typeName); - packageName = classType.getPackage().getName(); - className = classType.getSimpleSourceName() + "Impl"; - // Generate class source code - generateClass(logger, context); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, - "Accept criterion factory creation failed", e); - } - // return the fully qualifed name of the class generated - return packageName + "." + className; - } - - /** - * Generate source code for WidgetMapImpl - * - * @param logger - * Logger object - * @param context - * Generator context - */ - private void generateClass(TreeLogger logger, GeneratorContext context) { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated, - // return (WidgetMap is equal to all permutations atm) - if (printWriter == null) { - return; - } - logger.log(Type.INFO, "Detecting available criteria ..."); - Date date = new Date(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - composer.addImport("com.google.gwt.core.client.GWT"); - composer.setSuperclass("com.vaadin.terminal.gwt.client.ui.dd.VAcceptCriterionFactory"); - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - - // generator constructor source code - generateInstantiatorMethod(sourceWriter, context, logger); - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - - } - - private void generateInstantiatorMethod(SourceWriter sourceWriter, - GeneratorContext context, TreeLogger logger) { - - sourceWriter.println("public VAcceptCriterion get(String name) {"); - sourceWriter.indent(); - - sourceWriter.println("name = name.intern();"); - - JClassType criteriaType = context.getTypeOracle().findType( - VAcceptCriterion.class.getName()); - for (JClassType clientClass : criteriaType.getSubtypes()) { - AcceptCriterion annotation = clientClass - .getAnnotation(AcceptCriterion.class); - if (annotation != null) { - String clientClassName = clientClass.getQualifiedSourceName(); - Class serverClass = clientClass.getAnnotation( - AcceptCriterion.class).value(); - String serverClassName = serverClass.getCanonicalName(); - logger.log(Type.INFO, "creating mapping for " + serverClassName); - sourceWriter.print("if (\""); - sourceWriter.print(serverClassName); - sourceWriter.print("\" == name) return GWT.create("); - sourceWriter.print(clientClassName); - sourceWriter.println(".class );"); - sourceWriter.print("else "); - } - } - - sourceWriter.println("return null;"); - sourceWriter.outdent(); - sourceWriter.println("}"); - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java deleted file mode 100644 index 6ee30183c1..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java +++ /dev/null @@ -1,462 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Utility class to collect widgetset related information from classpath. - * Utility will seek all directories from classpaths, and jar files having - * "Vaadin-Widgetsets" key in their manifest file. - *

- * Used by WidgetMapGenerator and ide tools to implement some monkey coding for - * you. - *

- * Developer notice: If you end up reading this comment, I guess you have faced - * a sluggish performance of widget compilation or unreliable detection of - * components in your classpaths. The thing you might be able to do is to use - * annotation processing tool like apt to generate the needed information. Then - * either use that information in {@link WidgetMapGenerator} or create the - * appropriate monkey code for gwt directly in annotation processor and get rid - * of {@link WidgetMapGenerator}. Using annotation processor might be a good - * idea when dropping Java 1.5 support (integrated to javac in 6). - * - */ -public class ClassPathExplorer { - - private static final String VAADIN_ADDON_VERSION_ATTRIBUTE = "Vaadin-Package-Version"; - - /** - * File filter that only accepts directories. - */ - private final static FileFilter DIRECTORIES_ONLY = new FileFilter() { - @Override - public boolean accept(File f) { - if (f.exists() && f.isDirectory()) { - return true; - } else { - return false; - } - } - }; - - /** - * Raw class path entries as given in the java class path string. Only - * entries that could include widgets/widgetsets are listed (primarily - * directories, Vaadin JARs and add-on JARs). - */ - private static List rawClasspathEntries = getRawClasspathEntries(); - - /** - * Map from identifiers (either a package name preceded by the path and a - * slash, or a URL for a JAR file) to the corresponding URLs. This is - * constructed from the class path. - */ - private static Map classpathLocations = getClasspathLocations(rawClasspathEntries); - - /** - * No instantiation from outside, callable methods are static. - */ - private ClassPathExplorer() { - } - - /** - * Finds the names and locations of widgetsets available on the class path. - * - * @return map from widgetset classname to widgetset location URL - */ - public static Map getAvailableWidgetSets() { - long start = System.currentTimeMillis(); - Map widgetsets = new HashMap(); - Set keySet = classpathLocations.keySet(); - for (String location : keySet) { - searchForWidgetSets(location, widgetsets); - } - long end = System.currentTimeMillis(); - - StringBuilder sb = new StringBuilder(); - sb.append("Widgetsets found from classpath:\n"); - for (String ws : widgetsets.keySet()) { - sb.append("\t"); - sb.append(ws); - sb.append(" in "); - sb.append(widgetsets.get(ws)); - sb.append("\n"); - } - final Logger logger = getLogger(); - logger.info(sb.toString()); - logger.info("Search took " + (end - start) + "ms"); - return widgetsets; - } - - /** - * Finds all GWT modules / Vaadin widgetsets in a valid location. - * - * If the location is a directory, all GWT modules (files with the - * ".gwt.xml" extension) are added to widgetsets. - * - * If the location is a JAR file, the comma-separated values of the - * "Vaadin-Widgetsets" attribute in its manifest are added to widgetsets. - * - * @param locationString - * an entry in {@link #classpathLocations} - * @param widgetsets - * a map from widgetset name (including package, with dots as - * separators) to a URL (see {@link #classpathLocations}) - new - * entries are added to this map - */ - private static void searchForWidgetSets(String locationString, - Map widgetsets) { - - URL location = classpathLocations.get(locationString); - File directory = new File(location.getFile()); - - if (directory.exists() && !directory.isHidden()) { - // Get the list of the files contained in the directory - String[] files = directory.list(); - for (int i = 0; i < files.length; i++) { - // we are only interested in .gwt.xml files - if (!files[i].endsWith(".gwt.xml")) { - continue; - } - - // remove the .gwt.xml extension - String classname = files[i].substring(0, files[i].length() - 8); - String packageName = locationString.substring(locationString - .lastIndexOf("/") + 1); - classname = packageName + "." + classname; - - if (!WidgetSetBuilder.isWidgetset(classname)) { - // Only return widgetsets and not GWT modules to avoid - // comparing modules and widgetsets - continue; - } - - if (!widgetsets.containsKey(classname)) { - String packagePath = packageName.replaceAll("\\.", "/"); - String basePath = location.getFile().replaceAll( - "/" + packagePath + "$", ""); - try { - URL url = new URL(location.getProtocol(), - location.getHost(), location.getPort(), - basePath); - widgetsets.put(classname, url); - } catch (MalformedURLException e) { - // should never happen as based on an existing URL, - // only changing end of file name/path part - getLogger().log(Level.SEVERE, - "Error locating the widgetset " + classname, e); - } - } - } - } else { - - try { - // check files in jar file, entries will list all directories - // and files in jar - - URLConnection openConnection = location.openConnection(); - if (openConnection instanceof JarURLConnection) { - JarURLConnection conn = (JarURLConnection) openConnection; - - JarFile jarFile = conn.getJarFile(); - - Manifest manifest = jarFile.getManifest(); - if (manifest == null) { - // No manifest so this is not a Vaadin Add-on - return; - } - String value = manifest.getMainAttributes().getValue( - "Vaadin-Widgetsets"); - if (value != null) { - String[] widgetsetNames = value.split(","); - for (int i = 0; i < widgetsetNames.length; i++) { - String widgetsetname = widgetsetNames[i].trim() - .intern(); - if (!widgetsetname.equals("")) { - widgetsets.put(widgetsetname, location); - } - } - } - } - } catch (IOException e) { - getLogger().log(Level.WARNING, "Error parsing jar file", e); - } - - } - } - - /** - * Splits the current class path into entries, and filters them accepting - * directories, Vaadin add-on JARs with widgetsets and Vaadin JARs. - * - * Some other non-JAR entries may also be included in the result. - * - * @return filtered list of class path entries - */ - private final static List getRawClasspathEntries() { - // try to keep the order of the classpath - List locations = new ArrayList(); - - String pathSep = System.getProperty("path.separator"); - String classpath = System.getProperty("java.class.path"); - - if (classpath.startsWith("\"")) { - classpath = classpath.substring(1); - } - if (classpath.endsWith("\"")) { - classpath = classpath.substring(0, classpath.length() - 1); - } - - getLogger().fine("Classpath: " + classpath); - - String[] split = classpath.split(pathSep); - for (int i = 0; i < split.length; i++) { - String classpathEntry = split[i]; - if (acceptClassPathEntry(classpathEntry)) { - locations.add(classpathEntry); - } - } - - return locations; - } - - /** - * Determine every URL location defined by the current classpath, and it's - * associated package name. - * - * See {@link #classpathLocations} for information on output format. - * - * @param rawClasspathEntries - * raw class path entries as split from the Java class path - * string - * @return map of classpath locations, see {@link #classpathLocations} - */ - private final static Map getClasspathLocations( - List rawClasspathEntries) { - long start = System.currentTimeMillis(); - // try to keep the order of the classpath - Map locations = new LinkedHashMap(); - for (String classpathEntry : rawClasspathEntries) { - File file = new File(classpathEntry); - include(null, file, locations); - } - long end = System.currentTimeMillis(); - Logger logger = getLogger(); - if (logger.isLoggable(Level.FINE)) { - logger.fine("getClassPathLocations took " + (end - start) + "ms"); - } - return locations; - } - - /** - * Checks a class path entry to see whether it can contain widgets and - * widgetsets. - * - * All directories are automatically accepted. JARs are accepted if they - * have the "Vaadin-Widgetsets" attribute in their manifest or the JAR file - * name contains "vaadin-" or ".vaadin.". - * - * Also other non-JAR entries may be accepted, the caller should be prepared - * to handle them. - * - * @param classpathEntry - * class path entry string as given in the Java class path - * @return true if the entry should be considered when looking for widgets - * or widgetsets - */ - private static boolean acceptClassPathEntry(String classpathEntry) { - if (!classpathEntry.endsWith(".jar")) { - // accept all non jars (practically directories) - return true; - } else { - // accepts jars that comply with vaadin-component packaging - // convention (.vaadin. or vaadin- as distribution packages), - if (classpathEntry.contains("vaadin-") - || classpathEntry.contains(".vaadin.")) { - return true; - } else { - URL url; - try { - url = new URL("file:" - + new File(classpathEntry).getCanonicalPath()); - url = new URL("jar:" + url.toExternalForm() + "!/"); - JarURLConnection conn = (JarURLConnection) url - .openConnection(); - getLogger().fine(url.toString()); - JarFile jarFile = conn.getJarFile(); - Manifest manifest = jarFile.getManifest(); - if (manifest != null) { - Attributes mainAttributes = manifest - .getMainAttributes(); - if (mainAttributes.getValue("Vaadin-Widgetsets") != null) { - return true; - } - } - } catch (MalformedURLException e) { - getLogger().log(Level.FINEST, "Failed to inspect JAR file", - e); - } catch (IOException e) { - getLogger().log(Level.FINEST, "Failed to inspect JAR file", - e); - } - - return false; - } - } - } - - /** - * Recursively add subdirectories and jar files to locations - see - * {@link #classpathLocations}. - * - * @param name - * @param file - * @param locations - */ - private final static void include(String name, File file, - Map locations) { - if (!file.exists()) { - return; - } - if (!file.isDirectory()) { - // could be a JAR file - includeJar(file, locations); - return; - } - - if (file.isHidden() || file.getPath().contains(File.separator + ".")) { - return; - } - - if (name == null) { - name = ""; - } else { - name += "."; - } - - // add all directories recursively - File[] dirs = file.listFiles(DIRECTORIES_ONLY); - for (int i = 0; i < dirs.length; i++) { - try { - // add the present directory - if (!dirs[i].isHidden() - && !dirs[i].getPath().contains(File.separator + ".")) { - String key = dirs[i].getCanonicalPath() + "/" + name - + dirs[i].getName(); - locations.put(key, - new URL("file://" + dirs[i].getCanonicalPath())); - } - } catch (Exception ioe) { - return; - } - include(name + dirs[i].getName(), dirs[i], locations); - } - } - - /** - * Add a jar file to locations - see {@link #classpathLocations}. - * - * @param name - * @param locations - */ - private static void includeJar(File file, Map locations) { - try { - URL url = new URL("file:" + file.getCanonicalPath()); - url = new URL("jar:" + url.toExternalForm() + "!/"); - JarURLConnection conn = (JarURLConnection) url.openConnection(); - JarFile jarFile = conn.getJarFile(); - if (jarFile != null) { - // the key does not matter here as long as it is unique - locations.put(url.toString(), url); - } - } catch (Exception e) { - // e.printStackTrace(); - return; - } - - } - - /** - * Find and return the default source directory where to create new - * widgetsets. - * - * Return the first directory (not a JAR file etc.) on the classpath by - * default. - * - * TODO this could be done better... - * - * @return URL - */ - public static URL getDefaultSourceDirectory() { - - final Logger logger = getLogger(); - - if (logger.isLoggable(Level.FINE)) { - logger.fine("classpathLocations values:"); - ArrayList locations = new ArrayList( - classpathLocations.keySet()); - for (String location : locations) { - logger.fine(String.valueOf(classpathLocations.get(location))); - } - } - - Iterator it = rawClasspathEntries.iterator(); - while (it.hasNext()) { - String entry = it.next(); - - File directory = new File(entry); - if (directory.exists() && !directory.isHidden() - && directory.isDirectory()) { - try { - return new URL("file://" + directory.getCanonicalPath()); - } catch (MalformedURLException e) { - logger.log(Level.FINEST, "Ignoring exception", e); - // ignore: continue to the next classpath entry - } catch (IOException e) { - logger.log(Level.FINEST, "Ignoring exception", e); - // ignore: continue to the next classpath entry - } - } - } - return null; - } - - /** - * Test method for helper tool - */ - public static void main(String[] args) { - getLogger().info("Searching available widgetsets..."); - - Map availableWidgetSets = ClassPathExplorer - .getAvailableWidgetSets(); - for (String string : availableWidgetSets.keySet()) { - - getLogger().info(string + " in " + availableWidgetSets.get(string)); - } - } - - private static final Logger getLogger() { - return Logger.getLogger(ClassPathExplorer.class.getName()); - } - -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java deleted file mode 100644 index 33406ef85f..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorStateFactoryGenerator.java +++ /dev/null @@ -1,29 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.vaadin.terminal.gwt.client.ServerConnector; - -/** - * GWT generator that creates a SharedState class for a given Connector class, - * based on the return type of getState() - * - * @since 7.0 - */ -public class ConnectorStateFactoryGenerator extends - AbstractConnectorClassBasedFactoryGenerator { - - @Override - protected JClassType getTargetType(JClassType connectorType) { - return getGetterReturnType(connectorType, "getState"); - } - - @Override - protected Class getConnectorType() { - return ServerConnector.class; - } - -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java deleted file mode 100644 index 55a2857ce0..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorWidgetFactoryGenerator.java +++ /dev/null @@ -1,29 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.vaadin.terminal.gwt.client.ComponentConnector; -import com.vaadin.terminal.gwt.client.ServerConnector; - -/** - * GWT generator that creates a Widget class for a given Connector class, based - * on the return type of getWidget() - * - * @since 7.0 - */ -public class ConnectorWidgetFactoryGenerator extends - AbstractConnectorClassBasedFactoryGenerator { - @Override - protected JClassType getTargetType(JClassType connectorType) { - return getGetterReturnType(connectorType, "getWidget"); - } - - @Override - protected Class getConnectorType() { - return ComponentConnector.class; - } - -} \ No newline at end of file diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java deleted file mode 100644 index 89045c63b2..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/CustomWidgetMapGenerator.java +++ /dev/null @@ -1,84 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.util.Collection; -import java.util.HashSet; - -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.terminal.gwt.client.ComponentConnector; -import com.vaadin.terminal.gwt.client.ServerConnector; - -/** - * An abstract helper class that can be used to easily build a widgetset with - * customized load styles for each components. In three abstract methods one can - * override the default values given in {@link Connect} annotations. - * - * @see WidgetMapGenerator - * - */ -public abstract class CustomWidgetMapGenerator extends WidgetMapGenerator { - - private Collection> eagerPaintables = new HashSet>(); - private Collection> lazyPaintables = new HashSet>(); - private Collection> deferredPaintables = new HashSet>(); - - @Override - protected LoadStyle getLoadStyle(Class connector) { - if (eagerPaintables == null) { - init(); - } - if (eagerPaintables.contains(connector)) { - return LoadStyle.EAGER; - } - if (lazyPaintables.contains(connector)) { - return LoadStyle.LAZY; - } - if (deferredPaintables.contains(connector)) { - return LoadStyle.DEFERRED; - } - return super.getLoadStyle(connector); - } - - private void init() { - Class[] eagerComponents = getEagerComponents(); - if (eagerComponents != null) { - for (Class class1 : eagerComponents) { - eagerPaintables.add(class1); - } - } - Class[] lazyComponents = getEagerComponents(); - if (lazyComponents != null) { - for (Class class1 : lazyComponents) { - lazyPaintables.add(class1); - } - } - Class[] deferredComponents = getEagerComponents(); - if (deferredComponents != null) { - for (Class class1 : deferredComponents) { - deferredPaintables.add(class1); - } - } - } - - /** - * @return an array of components whose load style should be overridden to - * {@link LoadStyle#EAGER} - */ - protected abstract Class[] getEagerComponents(); - - /** - * @return an array of components whose load style should be overridden to - * {@link LoadStyle#LAZY} - */ - protected abstract Class[] getLazyComponents(); - - /** - * @return an array of components whose load style should be overridden to - * {@link LoadStyle#DEFERRED} - */ - protected abstract Class[] getDeferredComponents(); - -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java deleted file mode 100644 index 4ff0592ede..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/EagerWidgetMapGenerator.java +++ /dev/null @@ -1,29 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.terminal.gwt.client.ServerConnector; - -/** - * WidgetMap generator that builds a widgetset that packs all included widgets - * into a single JavaScript file loaded at application initialization. Initially - * loaded data will be relatively large, but minimal amount of server requests - * will be done. - *

- * This is the default generator in version 6.4 and produces similar type of - * widgetset as in previous versions of Vaadin. To activate "code splitting", - * use the {@link WidgetMapGenerator} instead, that loads most components - * deferred. - * - * @see WidgetMapGenerator - * - */ -public class EagerWidgetMapGenerator extends WidgetMapGenerator { - - @Override - protected LoadStyle getLoadStyle(Class connector) { - return LoadStyle.EAGER; - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java deleted file mode 100644 index e11a12a3b5..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/GeneratedRpcMethodProviderGenerator.java +++ /dev/null @@ -1,211 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -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.JMethod; -import com.google.gwt.core.ext.typeinfo.JParameterizedType; -import com.google.gwt.core.ext.typeinfo.JType; -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.terminal.gwt.client.communication.GeneratedRpcMethodProvider; -import com.vaadin.terminal.gwt.client.communication.RpcManager; -import com.vaadin.terminal.gwt.client.communication.RpcMethod; - -/** - * GWT generator that creates an implementation for {@link RpcManager} on the - * client side classes for executing RPC calls received from the the server. - * - * @since 7.0 - */ -public class GeneratedRpcMethodProviderGenerator extends Generator { - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - String packageName = null; - String className = null; - try { - TypeOracle typeOracle = context.getTypeOracle(); - - // get classType and save instance variables - JClassType classType = typeOracle.getType(typeName); - packageName = classType.getPackage().getName(); - className = classType.getSimpleSourceName() + "Impl"; - // Generate class source code for SerializerMapImpl - generateClass(logger, context, packageName, className); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, - "SerializerMapGenerator creation failed", e); - } - // return the fully qualifed name of the class generated - return packageName + "." + className; - } - - /** - * Generate source code for RpcManagerImpl - * - * @param logger - * Logger object - * @param context - * Generator context - * @param packageName - * package name for the class to generate - * @param className - * class name for the class to generate - */ - private void generateClass(TreeLogger logger, GeneratorContext context, - String packageName, String className) { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated - if (printWriter == null) { - return; - } - logger.log(Type.INFO, - "Detecting server to client RPC interface types..."); - Date date = new Date(); - TypeOracle typeOracle = context.getTypeOracle(); - JClassType serverToClientRpcType = typeOracle.findType(ClientRpc.class - .getName()); - JClassType[] rpcInterfaceSubtypes = serverToClientRpcType.getSubtypes(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - 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 - .getName()); - composer.addImplementedInterface(GeneratedRpcMethodProvider.class - .getName()); - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - List rpcMethods = new ArrayList(); - - sourceWriter - .println("public java.util.Collection getGeneratedRpcMethods() {"); - sourceWriter.indent(); - - sourceWriter - .println("java.util.ArrayList list = new java.util.ArrayList();"); - - // iterate over RPC interfaces and create helper methods for each - // interface - for (JClassType type : rpcInterfaceSubtypes) { - if (null == type.isInterface()) { - // only interested in interfaces here, not implementations - continue; - } - - // loop over the methods of the interface and its superinterfaces - // methods - for (JClassType currentType : type.getFlattenedSupertypeHierarchy()) { - for (JMethod method : currentType.getMethods()) { - - // RpcMethod(String interfaceName, String methodName, - // Type... parameterTypes) - sourceWriter.print("list.add(new RpcMethod(\"" - + type.getQualifiedSourceName() + "\", \"" - + method.getName() + "\""); - JType[] parameterTypes = method.getParameterTypes(); - for (JType parameter : parameterTypes) { - sourceWriter.print(", "); - writeTypeCreator(sourceWriter, parameter); - } - sourceWriter.println(") {"); - sourceWriter.indent(); - - sourceWriter - .println("public void applyInvocation(ClientRpc target, Object... parameters) {"); - sourceWriter.indent(); - - sourceWriter.print("((" + type.getQualifiedSourceName() - + ")target)." + method.getName() + "("); - for (int i = 0; i < parameterTypes.length; i++) { - JType parameterType = parameterTypes[i]; - if (i != 0) { - sourceWriter.print(", "); - } - String parameterTypeName = getBoxedTypeName(parameterType); - sourceWriter.print("(" + parameterTypeName - + ") parameters[" + i + "]"); - } - sourceWriter.println(");"); - - sourceWriter.outdent(); - sourceWriter.println("}"); - - sourceWriter.outdent(); - sourceWriter.println("});"); - } - } - } - - sourceWriter.println("return list;"); - - sourceWriter.outdent(); - sourceWriter.println("}"); - sourceWriter.println(); - - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - - } - - public static void writeTypeCreator(SourceWriter sourceWriter, JType type) { - String typeName = getBoxedTypeName(type); - sourceWriter.print("new Type(\"" + typeName + "\", "); - JParameterizedType parameterized = type.isParameterized(); - if (parameterized != null) { - sourceWriter.print("new Type[] {"); - JClassType[] typeArgs = parameterized.getTypeArgs(); - for (JClassType jClassType : typeArgs) { - writeTypeCreator(sourceWriter, jClassType); - sourceWriter.print(", "); - } - sourceWriter.print("}"); - } else { - sourceWriter.print("null"); - } - sourceWriter.print(")"); - } - - public static String getBoxedTypeName(JType type) { - if (type.isPrimitive() != null) { - // Used boxed types for primitives - return type.isPrimitive().getQualifiedBoxedSourceName(); - } else { - return type.getErasedType().getQualifiedSourceName(); - } - } - - private String getInvokeMethodName(JClassType type) { - return "invoke" + type.getQualifiedSourceName().replaceAll("\\.", "_"); - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java deleted file mode 100644 index 28f3dab482..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/LazyWidgetMapGenerator.java +++ /dev/null @@ -1,23 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.terminal.gwt.client.ServerConnector; - -/** - * WidgetMap generator that builds a widgetset that optimizes the transferred - * data. Widgets are loaded only when used if the widgetset is built with this - * generator. - * - * @see WidgetMapGenerator - * - */ -public class LazyWidgetMapGenerator extends WidgetMapGenerator { - @Override - protected LoadStyle getLoadStyle(Class connector) { - return LoadStyle.LAZY; - } - -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java deleted file mode 100644 index 8a6c374187..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyCreatorGenerator.java +++ /dev/null @@ -1,126 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.Date; - -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.TypeOracle; -import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ServerConnector; -import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc; -import com.vaadin.terminal.gwt.client.communication.RpcProxy.RpcProxyCreator; - -public class RpcProxyCreatorGenerator extends Generator { - - @Override - public String generate(TreeLogger logger, GeneratorContext ctx, - String requestedClassName) throws UnableToCompleteException { - logger.log(TreeLogger.DEBUG, "Running RpcProxyCreatorGenerator"); - TypeOracle typeOracle = ctx.getTypeOracle(); - assert (typeOracle != null); - - JClassType requestedType = typeOracle.findType(requestedClassName); - if (requestedType == null) { - logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" - + requestedClassName + "'", null); - throw new UnableToCompleteException(); - } - String packageName = requestedType.getPackage().getName(); - String className = requestedType.getSimpleSourceName() + "Impl"; - - createType(logger, ctx, packageName, className); - return packageName + "." + className; - } - - private void createType(TreeLogger logger, GeneratorContext context, - String packageName, String className) { - ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( - packageName, className); - - PrintWriter printWriter = context.tryCreate(logger, - composer.getCreatedPackage(), - composer.getCreatedClassShortName()); - if (printWriter == null) { - // print writer is null if source code has already been generated - return; - } - Date date = new Date(); - TypeOracle typeOracle = context.getTypeOracle(); - - // init composer, set class properties, create source writer - composer.addImport(GWT.class.getCanonicalName()); - composer.addImport(ServerRpc.class.getCanonicalName()); - composer.addImport(ServerConnector.class.getCanonicalName()); - composer.addImport(InitializableServerRpc.class.getCanonicalName()); - composer.addImport(IllegalArgumentException.class.getCanonicalName()); - composer.addImplementedInterface(RpcProxyCreator.class - .getCanonicalName()); - - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - sourceWriter - .println("public T create(Class rpcInterface, ServerConnector connector) {"); - sourceWriter.indent(); - - sourceWriter - .println("if (rpcInterface == null || connector == null) {"); - sourceWriter.indent(); - sourceWriter - .println("throw new IllegalArgumentException(\"RpcInterface and/or connector cannot be null\");"); - sourceWriter.outdent(); - - JClassType initializableInterface = typeOracle.findType(ServerRpc.class - .getCanonicalName()); - - for (JClassType rpcType : initializableInterface.getSubtypes()) { - String rpcClassName = rpcType.getQualifiedSourceName(); - if (InitializableServerRpc.class.getCanonicalName().equals( - rpcClassName)) { - // InitializableClientToServerRpc is a special marker interface - // that should not get a generated class - continue; - } - sourceWriter.println("} else if (rpcInterface == " + rpcClassName - + ".class) {"); - sourceWriter.indent(); - sourceWriter.println(rpcClassName + " rpc = GWT.create(" - + rpcClassName + ".class);"); - sourceWriter.println("((" + InitializableServerRpc.class.getName() - + ") rpc).initRpc(connector);"); - sourceWriter.println("return (T) rpc;"); - sourceWriter.outdent(); - } - - sourceWriter.println("} else {"); - sourceWriter.indent(); - sourceWriter - .println("throw new IllegalArgumentException(\"No RpcInterface of type \"+ rpcInterface.getName() + \" was found.\");"); - sourceWriter.outdent(); - // End of if - sourceWriter.println("}"); - // End of method - sourceWriter.println("}"); - - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, composer.getCreatedClassName() + " created in " - + (new Date().getTime() - date.getTime()) / 1000 + "seconds"); - - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java deleted file mode 100644 index 7a908e5b4d..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java +++ /dev/null @@ -1,142 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; - -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.JMethod; -import com.google.gwt.core.ext.typeinfo.JParameter; -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.MethodInvocation; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.ServerConnector; -import com.vaadin.terminal.gwt.client.communication.InitializableServerRpc; - -/** - * GWT generator that creates client side proxy classes for making RPC calls - * from the client to the server. - * - * GWT.create() calls for interfaces extending {@link ServerRpc} are affected, - * and a proxy implementation is created. Note that the init(...) method of the - * proxy must be called before the proxy is used. - * - * @since 7.0 - */ -public class RpcProxyGenerator extends Generator { - @Override - public String generate(TreeLogger logger, GeneratorContext ctx, - String requestedClassName) throws UnableToCompleteException { - logger.log(TreeLogger.DEBUG, "Running RpcProxyGenerator", null); - - TypeOracle typeOracle = ctx.getTypeOracle(); - assert (typeOracle != null); - - JClassType requestedType = typeOracle.findType(requestedClassName); - if (requestedType == null) { - logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" - + requestedClassName + "'", null); - throw new UnableToCompleteException(); - } - - String generatedClassName = "ServerRpc_" - + requestedType.getName().replaceAll("[$.]", "_"); - - JClassType initializableInterface = typeOracle - .findType(InitializableServerRpc.class.getCanonicalName()); - - ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( - requestedType.getPackage().getName(), generatedClassName); - composer.addImplementedInterface(requestedType.getQualifiedSourceName()); - composer.addImplementedInterface(initializableInterface - .getQualifiedSourceName()); - composer.addImport(MethodInvocation.class.getCanonicalName()); - - PrintWriter printWriter = ctx.tryCreate(logger, - composer.getCreatedPackage(), - composer.getCreatedClassShortName()); - if (printWriter != null) { - logger.log(Type.INFO, "Generating client proxy for RPC interface '" - + requestedType.getQualifiedSourceName() + "'"); - SourceWriter writer = composer.createSourceWriter(ctx, printWriter); - - // constructor - writer.println("public " + generatedClassName + "() {}"); - - // initialization etc. - writeCommonFieldsAndMethods(logger, writer, typeOracle); - - // actual proxy methods forwarding calls to the server - writeRemoteProxyMethods(logger, writer, typeOracle, requestedType, - requestedType.isClassOrInterface().getInheritableMethods()); - - // End of class - writer.outdent(); - writer.println("}"); - - ctx.commit(logger, printWriter); - } - - return composer.getCreatedClassName(); - } - - private void writeCommonFieldsAndMethods(TreeLogger logger, - SourceWriter writer, TypeOracle typeOracle) { - JClassType applicationConnectionClass = typeOracle - .findType(ApplicationConnection.class.getCanonicalName()); - - // fields - writer.println("private " + ServerConnector.class.getName() - + " connector;"); - - // init method from the RPC interface - writer.println("public void initRpc(" + ServerConnector.class.getName() - + " connector) {"); - writer.indent(); - writer.println("this.connector = connector;"); - writer.outdent(); - writer.println("}"); - } - - private static void writeRemoteProxyMethods(TreeLogger logger, - SourceWriter writer, TypeOracle typeOracle, - JClassType requestedType, JMethod[] methods) { - for (JMethod m : methods) { - writer.print(m.getReadableDeclaration(false, false, false, false, - true)); - writer.println(" {"); - writer.indent(); - - writer.print("this.connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(this.connector.getConnectorId(), \"" - + requestedType.getQualifiedBinaryName() + "\", \""); - writer.print(m.getName()); - writer.print("\", new Object[] {"); - // new Object[] { ... } for parameters - autoboxing etc. by the - // compiler - JParameter[] parameters = m.getParameters(); - boolean first = true; - for (JParameter p : parameters) { - if (!first) { - writer.print(", "); - } - first = false; - - writer.print(p.getName()); - } - writer.println("}), true);"); - - writer.outdent(); - writer.println("}"); - } - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java deleted file mode 100644 index 1951f8ba40..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java +++ /dev/null @@ -1,458 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -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.JArrayType; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JEnumConstant; -import com.google.gwt.core.ext.typeinfo.JEnumType; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JPrimitiveType; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.core.ext.typeinfo.TypeOracleException; -import com.google.gwt.json.client.JSONArray; -import com.google.gwt.json.client.JSONObject; -import com.google.gwt.json.client.JSONString; -import com.google.gwt.json.client.JSONValue; -import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer; -import com.vaadin.terminal.gwt.client.communication.JSONSerializer; -import com.vaadin.terminal.gwt.client.communication.JsonDecoder; -import com.vaadin.terminal.gwt.client.communication.JsonEncoder; -import com.vaadin.terminal.gwt.client.communication.SerializerMap; - -/** - * GWT generator for creating serializer classes for custom classes sent from - * server to client. - * - * Only fields with a correspondingly named setter are deserialized. - * - * @since 7.0 - */ -public class SerializerGenerator extends Generator { - - private static final String SUBTYPE_SEPARATOR = "___"; - private static String serializerPackageName = SerializerMap.class - .getPackage().getName(); - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - JClassType type; - try { - type = (JClassType) context.getTypeOracle().parse(typeName); - } catch (TypeOracleException e1) { - logger.log(Type.ERROR, "Could not find type " + typeName, e1); - throw new UnableToCompleteException(); - } - String serializerClassName = getSerializerSimpleClassName(type); - try { - // Generate class source code - generateClass(logger, context, type, serializerPackageName, - serializerClassName); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, "SerializerGenerator failed for " - + type.getQualifiedSourceName(), e); - throw new UnableToCompleteException(); - } - - // return the fully qualifed name of the class generated - return getFullyQualifiedSerializerClassName(type); - } - - /** - * Generate source code for a VaadinSerializer implementation. - * - * @param logger - * Logger object - * @param context - * Generator context - * @param type - * @param beanTypeName - * bean type for which the serializer is to be generated - * @param beanSerializerTypeName - * name of the serializer class to generate - * @throws UnableToCompleteException - */ - private void generateClass(TreeLogger logger, GeneratorContext context, - JClassType type, String serializerPackageName, - String serializerClassName) throws UnableToCompleteException { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, serializerPackageName, - serializerClassName); - - // print writer if null, source code has ALREADY been generated - if (printWriter == null) { - return; - } - boolean isEnum = (type.isEnum() != null); - boolean isArray = (type.isArray() != null); - - String qualifiedSourceName = type.getQualifiedSourceName(); - logger.log(Type.DEBUG, "Processing serializable type " - + qualifiedSourceName + "..."); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(serializerPackageName, - serializerClassName); - composer.addImport(GWT.class.getName()); - composer.addImport(JSONValue.class.getName()); - composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class - .getName()); - // composer.addImport(JSONObject.class.getName()); - // composer.addImport(VPaintableMap.class.getName()); - composer.addImport(JsonDecoder.class.getName()); - // composer.addImport(VaadinSerializer.class.getName()); - - if (isEnum || isArray) { - composer.addImplementedInterface(JSONSerializer.class.getName() - + "<" + qualifiedSourceName + ">"); - } else { - composer.addImplementedInterface(DiffJSONSerializer.class.getName() - + "<" + qualifiedSourceName + ">"); - } - - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - // Serializer - - // public JSONValue serialize(Object value, - // ApplicationConnection connection) { - sourceWriter.println("public " + JSONValue.class.getName() - + " serialize(" + qualifiedSourceName + " value, " - + ApplicationConnection.class.getName() + " connection) {"); - sourceWriter.indent(); - // MouseEventDetails castedValue = (MouseEventDetails) value; - sourceWriter.println(qualifiedSourceName + " castedValue = (" - + qualifiedSourceName + ") value;"); - - if (isEnum) { - writeEnumSerializer(logger, sourceWriter, type); - } else if (isArray) { - writeArraySerializer(logger, sourceWriter, type.isArray()); - } else { - writeBeanSerializer(logger, sourceWriter, type); - } - // } - sourceWriter.outdent(); - sourceWriter.println("}"); - sourceWriter.println(); - - // Updater - // public void update(T target, Type type, JSONValue jsonValue, - // ApplicationConnection connection); - if (!isEnum && !isArray) { - sourceWriter.println("public void update(" + qualifiedSourceName - + " target, Type type, " + JSONValue.class.getName() - + " jsonValue, " + ApplicationConnection.class.getName() - + " connection) {"); - sourceWriter.indent(); - - writeBeanDeserializer(logger, sourceWriter, type); - - sourceWriter.outdent(); - sourceWriter.println("}"); - } - - // Deserializer - // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection - // connection); - sourceWriter.println("public " + qualifiedSourceName - + " deserialize(Type type, " + JSONValue.class.getName() - + " jsonValue, " + ApplicationConnection.class.getName() - + " connection) {"); - sourceWriter.indent(); - - if (isEnum) { - writeEnumDeserializer(logger, sourceWriter, type.isEnum()); - } else if (isArray) { - writeArrayDeserializer(logger, sourceWriter, type.isArray()); - } else { - sourceWriter.println(qualifiedSourceName + " target = GWT.create(" - + qualifiedSourceName + ".class);"); - sourceWriter - .println("update(target, type, jsonValue, connection);"); - // return target; - sourceWriter.println("return target;"); - } - sourceWriter.outdent(); - sourceWriter.println("}"); - - // End of class - sourceWriter.outdent(); - sourceWriter.println("}"); - - // commit generated class - context.commit(logger, printWriter); - logger.log(TreeLogger.INFO, "Generated Serializer class " - + getFullyQualifiedSerializerClassName(type)); - } - - private void writeEnumDeserializer(TreeLogger logger, - SourceWriter sourceWriter, JEnumType enumType) { - sourceWriter.println("String enumIdentifier = ((" - + JSONString.class.getName() + ")jsonValue).stringValue();"); - for (JEnumConstant e : enumType.getEnumConstants()) { - sourceWriter.println("if (\"" + e.getName() - + "\".equals(enumIdentifier)) {"); - sourceWriter.indent(); - sourceWriter.println("return " + enumType.getQualifiedSourceName() - + "." + e.getName() + ";"); - sourceWriter.outdent(); - sourceWriter.println("}"); - } - sourceWriter.println("return null;"); - } - - private void writeArrayDeserializer(TreeLogger logger, - SourceWriter sourceWriter, JArrayType type) { - JType leafType = type.getLeafType(); - int rank = type.getRank(); - - sourceWriter.println(JSONArray.class.getName() - + " jsonArray = jsonValue.isArray();"); - - // Type value = new Type[jsonArray.size()][][]; - sourceWriter.print(type.getQualifiedSourceName() + " value = new " - + leafType.getQualifiedSourceName() + "[jsonArray.size()]"); - for (int i = 1; i < rank; i++) { - sourceWriter.print("[]"); - } - sourceWriter.println(";"); - - sourceWriter.println("for(int i = 0 ; i < value.length; i++) {"); - sourceWriter.indent(); - - JType componentType = type.getComponentType(); - - sourceWriter.print("value[i] = (" - + GeneratedRpcMethodProviderGenerator - .getBoxedTypeName(componentType) + ") " - + JsonDecoder.class.getName() + ".decodeValue("); - GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter, - componentType); - sourceWriter.print(", jsonArray.get(i), null, connection)"); - - sourceWriter.println(";"); - - sourceWriter.outdent(); - sourceWriter.println("}"); - - sourceWriter.println("return value;"); - } - - private void writeBeanDeserializer(TreeLogger logger, - SourceWriter sourceWriter, JClassType beanType) { - String beanQualifiedSourceName = beanType.getQualifiedSourceName(); - - // JSONOBject json = (JSONObject)jsonValue; - sourceWriter.println(JSONObject.class.getName() + " json = (" - + JSONObject.class.getName() + ")jsonValue;"); - - for (JMethod method : getSetters(beanType)) { - String setterName = method.getName(); - String baseName = setterName.substring(3); - String fieldName = getTransportFieldName(baseName); // setZIndex() - // -> zIndex - JType setterParameterType = method.getParameterTypes()[0]; - - logger.log(Type.DEBUG, "* Processing field " + fieldName + " in " - + beanQualifiedSourceName + " (" + beanType.getName() + ")"); - - // if (json.containsKey("height")) { - sourceWriter.println("if (json.containsKey(\"" + fieldName - + "\")) {"); - sourceWriter.indent(); - String jsonFieldName = "json_" + fieldName; - // JSONValue json_Height = json.get("height"); - sourceWriter.println("JSONValue " + jsonFieldName - + " = json.get(\"" + fieldName + "\");"); - - String fieldType; - String getterName = "get" + baseName; - JPrimitiveType primitiveType = setterParameterType.isPrimitive(); - if (primitiveType != null) { - // This is a primitive type -> must used the boxed type - fieldType = primitiveType.getQualifiedBoxedSourceName(); - if (primitiveType == JPrimitiveType.BOOLEAN) { - getterName = "is" + baseName; - } - } else { - fieldType = setterParameterType.getQualifiedSourceName(); - } - - // String referenceValue = target.getHeight(); - sourceWriter.println(fieldType + " referenceValue = target." - + getterName + "();"); - - // target.setHeight((String) - // JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper, - // connection)); - sourceWriter.print("target." + setterName + "((" + fieldType + ") " - + JsonDecoder.class.getName() + ".decodeValue("); - GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter, - setterParameterType); - sourceWriter.println(", " + jsonFieldName - + ", referenceValue, connection));"); - - // } ... end of if contains - sourceWriter.outdent(); - sourceWriter.println("}"); - } - } - - private void writeEnumSerializer(TreeLogger logger, - SourceWriter sourceWriter, JClassType beanType) { - // return new JSONString(castedValue.name()); - sourceWriter.println("return new " + JSONString.class.getName() - + "(castedValue.name());"); - } - - private void writeArraySerializer(TreeLogger logger, - SourceWriter sourceWriter, JArrayType array) { - sourceWriter.println(JSONArray.class.getName() + " values = new " - + JSONArray.class.getName() + "();"); - JType componentType = array.getComponentType(); - // JPrimitiveType primitive = componentType.isPrimitive(); - sourceWriter.println("for (int i = 0; i < castedValue.length; i++) {"); - sourceWriter.indent(); - sourceWriter.print("values.set(i, "); - sourceWriter.print(JsonEncoder.class.getName() - + ".encode(castedValue[i], false, connection)"); - sourceWriter.println(");"); - sourceWriter.outdent(); - sourceWriter.println("}"); - sourceWriter.println("return values;"); - } - - private void writeBeanSerializer(TreeLogger logger, - SourceWriter sourceWriter, JClassType beanType) - throws UnableToCompleteException { - - // JSONObject json = new JSONObject(); - sourceWriter.println(JSONObject.class.getName() + " json = new " - + JSONObject.class.getName() + "();"); - - HashSet usedFieldNames = new HashSet(); - - for (JMethod setterMethod : getSetters(beanType)) { - String setterName = setterMethod.getName(); - String fieldName = getTransportFieldName(setterName.substring(3)); // setZIndex() - // -> zIndex - if (!usedFieldNames.add(fieldName)) { - logger.log( - TreeLogger.ERROR, - "Can't encode " - + beanType.getQualifiedSourceName() - + " as it has multiple fields with the name " - + fieldName.toLowerCase() - + ". This can happen if only casing distinguishes one property name from another."); - throw new UnableToCompleteException(); - } - String getterName = findGetter(beanType, setterMethod); - - if (getterName == null) { - logger.log(TreeLogger.ERROR, "No getter found for " + fieldName - + ". Serialization will likely fail"); - } - // json.put("button", - // JsonEncoder.encode(castedValue.getButton(), false, idMapper, - // connection)); - sourceWriter.println("json.put(\"" + fieldName + "\", " - + JsonEncoder.class.getName() + ".encode(castedValue." - + getterName + "(), false, connection));"); - } - // return json; - sourceWriter.println("return json;"); - - } - - private static String getTransportFieldName(String baseName) { - return Character.toLowerCase(baseName.charAt(0)) - + baseName.substring(1); - } - - private String findGetter(JClassType beanType, JMethod setterMethod) { - JType setterParameterType = setterMethod.getParameterTypes()[0]; - String fieldName = setterMethod.getName().substring(3); - if (setterParameterType.getQualifiedSourceName().equals( - boolean.class.getName())) { - return "is" + fieldName; - } else { - return "get" + fieldName; - } - } - - /** - * Returns a list of all setters found in the beanType or its parent class - * - * @param beanType - * The type to check - * @return A list of setter methods from the class and its parents - */ - protected static List getSetters(JClassType beanType) { - - List setterMethods = new ArrayList(); - - while (beanType != null - && !beanType.getQualifiedSourceName().equals( - Object.class.getName())) { - for (JMethod method : beanType.getMethods()) { - // Process all setters that have corresponding fields - if (!method.isPublic() || method.isStatic() - || !method.getName().startsWith("set") - || method.getParameterTypes().length != 1) { - // Not setter, skip to next method - continue; - } - setterMethods.add(method); - } - beanType = beanType.getSuperclass(); - } - - return setterMethods; - } - - private static String getSerializerSimpleClassName(JClassType beanType) { - return getSimpleClassName(beanType) + "_Serializer"; - } - - private static String getSimpleClassName(JType type) { - JArrayType arrayType = type.isArray(); - if (arrayType != null) { - return "Array" + getSimpleClassName(arrayType.getComponentType()); - } - JClassType classType = type.isClass(); - if (classType != null && classType.isMemberType()) { - // Assumed to be static sub class - String baseName = getSimpleClassName(classType.getEnclosingType()); - String name = baseName + SUBTYPE_SEPARATOR - + type.getSimpleSourceName(); - return name; - } - return type.getSimpleSourceName(); - } - - public static String getFullyQualifiedSerializerClassName(JClassType type) { - return serializerPackageName + "." + getSerializerSimpleClassName(type); - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java deleted file mode 100644 index 3f1ad24066..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java +++ /dev/null @@ -1,365 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ - -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.io.Serializable; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -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.JArrayType; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JParameterizedType; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.core.ext.typeinfo.NotFoundException; -import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.json.client.JSONValue; -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.communication.SharedState; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.communication.JSONSerializer; -import com.vaadin.terminal.gwt.client.communication.SerializerMap; - -/** - * GWT generator that creates a {@link SerializerMap} implementation (mapper - * from type string to serializer instance) and serializer classes for all - * subclasses of {@link SharedState}. - * - * @since 7.0 - */ -public class SerializerMapGenerator extends Generator { - - private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; - private String packageName; - private String className; - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - try { - TypeOracle typeOracle = context.getTypeOracle(); - Set typesNeedingSerializers = findTypesNeedingSerializers( - typeOracle, logger); - checkForUnserializableTypes(typesNeedingSerializers, typeOracle, - logger); - Set typesWithExistingSerializers = findTypesWithExistingSerializers( - typeOracle, logger); - Set serializerMappings = new HashSet(); - serializerMappings.addAll(typesNeedingSerializers); - serializerMappings.addAll(typesWithExistingSerializers); - // get classType and save instance variables - JClassType classType = typeOracle.getType(typeName); - packageName = classType.getPackage().getName(); - className = classType.getSimpleSourceName() + "Impl"; - // Generate class source code for SerializerMapImpl - generateSerializerMap(serializerMappings, logger, context); - - SerializerGenerator sg = new SerializerGenerator(); - for (JClassType type : typesNeedingSerializers) { - sg.generate(logger, context, type.getQualifiedSourceName()); - } - } catch (Exception e) { - logger.log(TreeLogger.ERROR, - "SerializerMapGenerator creation failed", e); - throw new UnableToCompleteException(); - } - // return the fully qualifed name of the class generated - return packageName + "." + className; - } - - /** - * Emits a warning for all classes that are used in communication but do not - * implement java.io.Serializable. Implementing java.io.Serializable is not - * needed for communication but for the server side Application to be - * serializable i.e. work in GAE for instance. - * - * @param typesNeedingSerializers - * @param typeOracle - * @param logger - * @throws UnableToCompleteException - */ - private void checkForUnserializableTypes( - Set typesNeedingSerializers, TypeOracle typeOracle, - TreeLogger logger) throws UnableToCompleteException { - JClassType javaSerializable = typeOracle.findType(Serializable.class - .getName()); - for (JClassType type : typesNeedingSerializers) { - if (type.isArray() != null) { - // Don't check for arrays - continue; - } - boolean serializable = type.isAssignableTo(javaSerializable); - if (!serializable) { - boolean abortCompile = "true".equals(System - .getProperty(FAIL_IF_NOT_SERIALIZABLE)); - logger.log( - abortCompile ? Type.ERROR : Type.WARN, - type - + " is used in RPC or shared state but does not implement " - + Serializable.class.getName() - + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. " - + "If the system property " - + FAIL_IF_NOT_SERIALIZABLE - + " is set to \"true\", this causes the compilation to fail instead of just emitting a warning."); - if (abortCompile) { - throw new UnableToCompleteException(); - } - } - } - } - - private Set findTypesWithExistingSerializers( - TypeOracle typeOracle, TreeLogger logger) - throws UnableToCompleteException { - JClassType serializerInterface = typeOracle - .findType(JSONSerializer.class.getName()); - JType[] deserializeParamTypes = new JType[] { - typeOracle - .findType(com.vaadin.terminal.gwt.client.communication.Type.class - .getName()), - typeOracle.findType(JSONValue.class.getName()), - typeOracle.findType(ApplicationConnection.class.getName()) }; - String deserializeMethodName = "deserialize"; - try { - serializerInterface.getMethod(deserializeMethodName, - deserializeParamTypes); - } catch (NotFoundException e) { - logger.log(Type.ERROR, "Could not find " + deserializeMethodName - + " in " + serializerInterface); - throw new UnableToCompleteException(); - } - - Set types = new HashSet(); - for (JClassType serializer : serializerInterface.getSubtypes()) { - JMethod deserializeMethod = serializer.findMethod( - deserializeMethodName, deserializeParamTypes); - if (deserializeMethod == null) { - logger.log(Type.DEBUG, "Could not find " - + deserializeMethodName + " in " + serializer); - continue; - } - JType returnType = deserializeMethod.getReturnType(); - logger.log(Type.DEBUG, "Found " + deserializeMethodName - + " with return type " + returnType + " in " + serializer); - - types.add(returnType.isClass()); - } - return types; - } - - /** - * Generate source code for SerializerMapImpl - * - * @param typesNeedingSerializers - * - * @param logger - * Logger object - * @param context - * Generator context - */ - private void generateSerializerMap(Set typesNeedingSerializers, - TreeLogger logger, GeneratorContext context) { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated - if (printWriter == null) { - return; - } - Date date = new Date(); - TypeOracle typeOracle = context.getTypeOracle(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - composer.addImport("com.google.gwt.core.client.GWT"); - composer.addImplementedInterface(SerializerMap.class.getName()); - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - sourceWriter.indent(); - - sourceWriter.println("public " + JSONSerializer.class.getName() - + " getSerializer(String type) {"); - sourceWriter.indent(); - - // TODO cache serializer instances in a map - for (JClassType type : typesNeedingSerializers) { - sourceWriter.print("if (type.equals(\"" - + type.getQualifiedSourceName() + "\")"); - if (type instanceof JArrayType) { - // Also add binary name to support encoding based on - // object.getClass().getName() - sourceWriter.print("||type.equals(\"" + type.getJNISignature() - + "\")"); - } - sourceWriter.println(") {"); - sourceWriter.indent(); - String serializerName = SerializerGenerator - .getFullyQualifiedSerializerClassName(type); - sourceWriter.println("return GWT.create(" + serializerName - + ".class);"); - sourceWriter.outdent(); - sourceWriter.println("}"); - logger.log(Type.INFO, "Configured serializer (" + serializerName - + ") for " + type.getName()); - } - sourceWriter - .println("throw new RuntimeException(\"No serializer found for class \"+type);"); - sourceWriter.outdent(); - sourceWriter.println("}"); - - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - - } - - public Set findTypesNeedingSerializers(TypeOracle typeOracle, - TreeLogger logger) { - logger.log(Type.DEBUG, "Detecting serializable data types..."); - - HashSet types = new HashSet(); - - // Generate serializer classes for each subclass of SharedState - JClassType serializerType = typeOracle.findType(SharedState.class - .getName()); - types.add(serializerType); - JClassType[] serializerSubtypes = serializerType.getSubtypes(); - for (JClassType type : serializerSubtypes) { - types.add(type); - } - - // Serializer classes might also be needed for RPC methods - for (Class cls : new Class[] { ServerRpc.class, ClientRpc.class }) { - JClassType rpcType = typeOracle.findType(cls.getName()); - JClassType[] serverRpcSubtypes = rpcType.getSubtypes(); - for (JClassType type : serverRpcSubtypes) { - addMethodParameterTypes(type, types, logger); - } - } - - // Add all types used from/in the types - for (Object t : types.toArray()) { - findSubTypesNeedingSerializers((JClassType) t, types); - } - logger.log(Type.DEBUG, "Serializable data types: " + types.toString()); - - return types; - } - - private void addMethodParameterTypes(JClassType classContainingMethods, - Set types, TreeLogger logger) { - for (JMethod method : classContainingMethods.getMethods()) { - if (method.getName().equals("initRpc")) { - continue; - } - for (JType type : method.getParameterTypes()) { - addTypeIfNeeded(types, type); - } - } - } - - public void findSubTypesNeedingSerializers(JClassType type, - Set serializableTypes) { - // Find all setters and look at their parameter type to determine if a - // new serializer is needed - for (JMethod setterMethod : SerializerGenerator.getSetters(type)) { - // The one and only parameter for the setter - JType setterType = setterMethod.getParameterTypes()[0]; - addTypeIfNeeded(serializableTypes, setterType); - } - } - - private void addTypeIfNeeded(Set serializableTypes, JType type) { - if (serializableTypes.contains(type)) { - return; - } - JParameterizedType parametrized = type.isParameterized(); - if (parametrized != null) { - for (JClassType parameterType : parametrized.getTypeArgs()) { - addTypeIfNeeded(serializableTypes, parameterType); - } - } - - if (serializationHandledByFramework(type)) { - return; - } - - if (serializableTypes.contains(type)) { - return; - } - - JClassType typeClass = type.isClass(); - if (typeClass != null) { - // setterTypeClass is null at least for List. It is - // possible that we need to handle the cases somehow, for - // instance for List. - serializableTypes.add(typeClass); - findSubTypesNeedingSerializers(typeClass, serializableTypes); - } - - // Generate (n-1)-dimensional array serializer for n-dimensional array - JArrayType arrayType = type.isArray(); - if (arrayType != null) { - serializableTypes.add(arrayType); - addTypeIfNeeded(serializableTypes, arrayType.getComponentType()); - } - - } - - Set> frameworkHandledTypes = new HashSet>(); - { - frameworkHandledTypes.add(String.class); - frameworkHandledTypes.add(Boolean.class); - frameworkHandledTypes.add(Integer.class); - frameworkHandledTypes.add(Float.class); - frameworkHandledTypes.add(Double.class); - frameworkHandledTypes.add(Long.class); - frameworkHandledTypes.add(Enum.class); - frameworkHandledTypes.add(String[].class); - frameworkHandledTypes.add(Object[].class); - frameworkHandledTypes.add(Map.class); - frameworkHandledTypes.add(List.class); - frameworkHandledTypes.add(Set.class); - frameworkHandledTypes.add(Byte.class); - frameworkHandledTypes.add(Character.class); - - } - - private boolean serializationHandledByFramework(JType setterType) { - // Some types are handled by the framework at the moment. See #8449 - // This method should be removed at some point. - if (setterType.isPrimitive() != null) { - return true; - } - - String qualifiedName = setterType.getQualifiedSourceName(); - for (Class cls : frameworkHandledTypes) { - if (qualifiedName.equals(cls.getName())) { - return true; - } - } - - return false; - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java deleted file mode 100644 index 0d062ec4ff..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java +++ /dev/null @@ -1,398 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.TreeSet; - -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.TypeOracle; -import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.shared.Connector; -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.ui.UnknownComponentConnector; -import com.vaadin.terminal.gwt.client.ui.root.RootConnector; -import com.vaadin.terminal.gwt.server.ClientConnector; - -/** - * WidgetMapGenerator's are GWT generator to build WidgetMapImpl dynamically - * based on {@link Connect} annotations available in workspace. By modifying the - * generator it is possible to do some fine tuning for the generated widgetset - * (aka client side engine). The components to be included in the client side - * engine can modified be overriding {@link #getUsedConnectors()}. - *

- * The generator also decides how the client side component implementations are - * loaded to the browser. The default generator is - * {@link EagerWidgetMapGenerator} that builds a monolithic client side engine - * that loads all widget implementation on application initialization. This has - * been the only option until Vaadin 6.4. - *

- * This generator uses the loadStyle hints from the {@link Connect} annotations. - * Depending on the {@link LoadStyle} used, the widget may be included in the - * initially loaded JavaScript, loaded when the application has started and - * there is no communication to server or lazy loaded when the implementation is - * absolutely needed. - *

- * The GWT module description file of the widgetset ( - * ...Widgetset.gwt.xml) can be used to define the - * WidgetMapGenarator. An example that defines this generator to be used: - * - *

- * 
- * <generate-with
- *           class="com.vaadin.terminal.gwt.widgetsetutils.MyWidgetMapGenerator">
- *          <when-type-is class="com.vaadin.terminal.gwt.client.WidgetMap" />
- * </generate-with>
- * 
- * 
- * 
- * - *

- * Vaadin package also includes {@link LazyWidgetMapGenerator}, which is a good - * option if the transferred data should be minimized, and - * {@link CustomWidgetMapGenerator} for easy overriding of loading strategies. - * - */ -public class WidgetMapGenerator extends Generator { - - private static String serverConnectorClassName = ServerConnector.class - .getName(); - - private String packageName; - private String className; - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - - try { - TypeOracle typeOracle = context.getTypeOracle(); - - // get classType and save instance variables - JClassType classType = typeOracle.getType(typeName); - packageName = classType.getPackage().getName(); - className = classType.getSimpleSourceName() + "Impl"; - // Generate class source code - generateClass(logger, context); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, "WidgetMap creation failed", e); - } - // return the fully qualifed name of the class generated - return packageName + "." + className; - } - - /** - * Generate source code for WidgetMapImpl - * - * @param logger - * Logger object - * @param context - * Generator context - */ - private void generateClass(TreeLogger logger, GeneratorContext context) { - // get print writer that receives the source code - PrintWriter printWriter = null; - printWriter = context.tryCreate(logger, packageName, className); - // print writer if null, source code has ALREADY been generated, - // return (WidgetMap is equal to all permutations atm) - if (printWriter == null) { - return; - } - logger.log(Type.INFO, - "Detecting Vaadin connectors in classpath to generate WidgetMapImpl.java ..."); - Date date = new Date(); - - // init composer, set class properties, create source writer - ClassSourceFileComposerFactory composer = null; - composer = new ClassSourceFileComposerFactory(packageName, className); - composer.addImport("com.google.gwt.core.client.GWT"); - composer.addImport("java.util.HashMap"); - composer.addImport("com.google.gwt.core.client.RunAsyncCallback"); - composer.setSuperclass("com.vaadin.terminal.gwt.client.WidgetMap"); - SourceWriter sourceWriter = composer.createSourceWriter(context, - printWriter); - - Collection> connectors = getUsedConnectors(context - .getTypeOracle()); - - validateConnectors(logger, connectors); - logConnectors(logger, context, connectors); - - // generator constructor source code - generateImplementationDetector(sourceWriter, connectors); - generateInstantiatorMethod(sourceWriter, connectors); - // close generated class - sourceWriter.outdent(); - sourceWriter.println("}"); - // commit generated class - context.commit(logger, printWriter); - logger.log(Type.INFO, - "Done. (" + (new Date().getTime() - date.getTime()) / 1000 - + "seconds)"); - - } - - private void validateConnectors(TreeLogger logger, - Collection> connectors) { - - Iterator> iter = connectors.iterator(); - while (iter.hasNext()) { - Class connectorClass = iter.next(); - Connect annotation = connectorClass.getAnnotation(Connect.class); - if (!ClientConnector.class.isAssignableFrom(annotation.value())) { - logger.log( - Type.WARN, - "Connector class " - + annotation.value().getName() - + " defined in @Connect annotation is not a subclass of " - + ClientConnector.class.getName() - + ". The component connector " - + connectorClass.getName() - + " will not be included in the widgetset."); - iter.remove(); - } - } - - } - - private void logConnectors(TreeLogger logger, GeneratorContext context, - Collection> connectors) { - logger.log(Type.INFO, - "Widget set will contain implementations for following component connectors: "); - - TreeSet classNames = new TreeSet(); - HashMap loadStyle = new HashMap(); - for (Class connectorClass : connectors) { - String className = connectorClass.getCanonicalName(); - classNames.add(className); - if (getLoadStyle(connectorClass) == LoadStyle.DEFERRED) { - loadStyle.put(className, "DEFERRED"); - } else if (getLoadStyle(connectorClass) == LoadStyle.LAZY) { - loadStyle.put(className, "LAZY"); - } - - } - for (String className : classNames) { - String msg = className; - if (loadStyle.containsKey(className)) { - msg += " (load style: " + loadStyle.get(className) + ")"; - } - logger.log(Type.INFO, "\t" + msg); - } - } - - /** - * This method is protected to allow creation of optimized widgetsets. The - * Widgetset will contain only implementation returned by this function. If - * one knows which widgets are needed for the application, returning only - * them here will significantly optimize the size of the produced JS. - * - * @return a collections of Vaadin components that will be added to - * widgetset - */ - @SuppressWarnings("unchecked") - private Collection> getUsedConnectors( - TypeOracle typeOracle) { - JClassType connectorType = typeOracle.findType(Connector.class - .getName()); - Collection> connectors = new HashSet>(); - for (JClassType jClassType : connectorType.getSubtypes()) { - Connect annotation = jClassType.getAnnotation(Connect.class); - if (annotation != null) { - try { - Class clazz = (Class) Class - .forName(jClassType.getQualifiedSourceName()); - connectors.add(clazz); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - } - return connectors; - } - - /** - * Returns true if the widget for given component will be lazy loaded by the - * client. The default implementation reads the information from the - * {@link Connect} annotation. - *

- * The method can be overridden to optimize the widget loading mechanism. If - * the Widgetset is wanted to be optimized for a network with a high latency - * or for a one with a very fast throughput, it may be good to return false - * for every component. - * - * @param connector - * @return true iff the widget for given component should be lazy loaded by - * the client side engine - */ - protected LoadStyle getLoadStyle(Class connector) { - Connect annotation = connector.getAnnotation(Connect.class); - return annotation.loadStyle(); - } - - private void generateInstantiatorMethod( - SourceWriter sourceWriter, - Collection> connectorsHavingComponentAnnotation) { - - Collection> deferredWidgets = new LinkedList>(); - - // TODO detect if it would be noticably faster to instantiate with a - // lookup with index than with the hashmap - - sourceWriter.println("public void ensureInstantiator(Class classType) {"); - sourceWriter.println("if(!instmap.containsKey(classType)){"); - boolean first = true; - - ArrayList> lazyLoadedConnectors = new ArrayList>(); - - HashSet> connectorsWithInstantiator = new HashSet>(); - - for (Class class1 : connectorsHavingComponentAnnotation) { - Class clientClass = class1; - if (connectorsWithInstantiator.contains(clientClass)) { - continue; - } - if (clientClass == RootConnector.class) { - // Roots are not instantiated by widgetset - continue; - } - if (!first) { - sourceWriter.print(" else "); - } else { - first = false; - } - sourceWriter.print("if( classType == " + clientClass.getName() - + ".class) {"); - - String instantiator = "new WidgetInstantiator() {\n public " - + serverConnectorClassName - + " get() {\n return GWT.create(" + clientClass.getName() - + ".class );\n}\n}\n"; - - LoadStyle loadStyle = getLoadStyle(class1); - - if (loadStyle != LoadStyle.EAGER) { - sourceWriter - .print("ApplicationConfiguration.startWidgetLoading();\n" - + "GWT.runAsync( \n" - + "new WidgetLoader() { void addInstantiator() {instmap.put(" - + clientClass.getName() - + ".class," - + instantiator + ");}});\n"); - lazyLoadedConnectors.add(class1); - - if (loadStyle == LoadStyle.DEFERRED) { - deferredWidgets.add(class1); - } - - } else { - // widget implementation in initially loaded js script - sourceWriter.print("instmap.put("); - sourceWriter.print(clientClass.getName()); - sourceWriter.print(".class, "); - sourceWriter.print(instantiator); - sourceWriter.print(");"); - } - sourceWriter.print("}"); - connectorsWithInstantiator.add(clientClass); - } - - sourceWriter.println("}"); - - sourceWriter.println("}"); - - sourceWriter.println("public Class[] getDeferredLoadedConnectors() {"); - - sourceWriter.println("return new Class[] {"); - first = true; - for (Class class2 : deferredWidgets) { - if (!first) { - sourceWriter.println(","); - } - first = false; - sourceWriter.print(class2.getName() + ".class"); - } - - sourceWriter.println("};"); - sourceWriter.println("}"); - - // in constructor add a "thread" that lazyly loads lazy loaded widgets - // if communication to server idles - - // TODO an array of lazy loaded widgets - - // TODO an index of last ensured widget in array - - sourceWriter.println("public " + serverConnectorClassName - + " instantiate(Class classType) {"); - sourceWriter.indent(); - sourceWriter.println(serverConnectorClassName - + " p = super.instantiate(classType); if(p!= null) return p;"); - sourceWriter.println("return instmap.get(classType).get();"); - - sourceWriter.outdent(); - sourceWriter.println("}"); - - } - - /** - * - * @param sourceWriter - * Source writer to output source code - * @param paintablesHavingWidgetAnnotation - */ - private void generateImplementationDetector( - SourceWriter sourceWriter, - Collection> paintablesHavingWidgetAnnotation) { - sourceWriter - .println("public Class " - + "getConnectorClassForServerSideClassName(String fullyQualifiedName) {"); - sourceWriter.indent(); - sourceWriter - .println("fullyQualifiedName = fullyQualifiedName.intern();"); - - for (Class connectorClass : paintablesHavingWidgetAnnotation) { - Class clientConnectorClass = getClientConnectorClass(connectorClass); - sourceWriter.print("if ( fullyQualifiedName == \""); - sourceWriter.print(clientConnectorClass.getName()); - sourceWriter.print("\" ) { ensureInstantiator(" - + connectorClass.getName() + ".class); return "); - sourceWriter.print(connectorClass.getName()); - sourceWriter.println(".class;}"); - sourceWriter.print("else "); - } - sourceWriter.println("return " - + UnknownComponentConnector.class.getName() + ".class;"); - sourceWriter.outdent(); - sourceWriter.println("}"); - - } - - private static Class getClientConnectorClass( - Class connectorClass) { - Connect annotation = connectorClass.getAnnotation(Connect.class); - return (Class) annotation.value(); - } -} diff --git a/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java b/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java deleted file mode 100644 index 4c6e334a33..0000000000 --- a/client/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetSetBuilder.java +++ /dev/null @@ -1,201 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.widgetsetutils; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintStream; -import java.io.Reader; -import java.net.URL; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Helper class to update widgetsets GWT module configuration file. Can be used - * command line or via IDE tools. - * - *

- * If module definition file contains text "WS Compiler: manually edited", tool - * will skip editing file. - * - */ -public class WidgetSetBuilder { - - public static void main(String[] args) throws IOException { - if (args.length == 0) { - printUsage(); - } else { - String widgetsetname = args[0]; - updateWidgetSet(widgetsetname); - - } - } - - public static void updateWidgetSet(final String widgetset) - throws IOException, FileNotFoundException { - boolean changed = false; - - Map availableWidgetSets = ClassPathExplorer - .getAvailableWidgetSets(); - - URL sourceUrl = availableWidgetSets.get(widgetset); - if (sourceUrl == null) { - // find first/default source directory - sourceUrl = ClassPathExplorer.getDefaultSourceDirectory(); - } - - String widgetsetfilename = sourceUrl.getFile() + "/" - + widgetset.replace(".", "/") + ".gwt.xml"; - - File widgetsetFile = new File(widgetsetfilename); - if (!widgetsetFile.exists()) { - // create empty gwt module file - File parent = widgetsetFile.getParentFile(); - if (parent != null && !parent.exists()) { - if (!parent.mkdirs()) { - throw new IOException( - "Could not create directory for the widgetset: " - + parent.getPath()); - } - } - widgetsetFile.createNewFile(); - PrintStream printStream = new PrintStream(new FileOutputStream( - widgetsetFile)); - printStream.print("\n" - + "\n"); - printStream.print("\n"); - printStream - .print(" \n" - + " \n"); - printStream.print("\n\n"); - printStream.close(); - changed = true; - } - - String content = readFile(widgetsetFile); - if (isEditable(content)) { - String originalContent = content; - - Collection oldInheritedWidgetsets = getCurrentInheritedWidgetsets(content); - - // add widgetsets that do not exist - Iterator i = availableWidgetSets.keySet().iterator(); - while (i.hasNext()) { - String ws = i.next(); - if (ws.equals(widgetset)) { - // do not inherit the module itself - continue; - } - if (!oldInheritedWidgetsets.contains(ws)) { - content = addWidgetSet(ws, content); - } - } - - for (String ws : oldInheritedWidgetsets) { - if (!availableWidgetSets.containsKey(ws)) { - // widgetset not available in classpath - content = removeWidgetSet(ws, content); - } - } - - changed = changed || !content.equals(originalContent); - if (changed) { - commitChanges(widgetsetfilename, content); - } - } else { - System.out - .println("Widgetset is manually edited. Skipping updates."); - } - } - - private static boolean isEditable(String content) { - return !content.contains("WS Compiler: manually edited"); - } - - private static String removeWidgetSet(String ws, String content) { - return content.replaceFirst("", ""); - } - - private static void commitChanges(String widgetsetfilename, String content) - throws IOException { - BufferedWriter bufferedWriter = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(widgetsetfilename))); - bufferedWriter.write(content); - bufferedWriter.close(); - } - - private static String addWidgetSet(String ws, String content) { - return content.replace("", "\n " + "\n"); - } - - private static Collection getCurrentInheritedWidgetsets( - String content) { - HashSet hashSet = new HashSet(); - Pattern inheritsPattern = Pattern.compile(" name=\"([^\"]*)\""); - - Matcher matcher = inheritsPattern.matcher(content); - - while (matcher.find()) { - String gwtModule = matcher.group(1); - if (isWidgetset(gwtModule)) { - hashSet.add(gwtModule); - } - } - return hashSet; - } - - static boolean isWidgetset(String gwtModuleName) { - return gwtModuleName.toLowerCase().contains("widgetset"); - } - - private static String readFile(File widgetsetFile) throws IOException { - Reader fi = new FileReader(widgetsetFile); - BufferedReader bufferedReader = new BufferedReader(fi); - StringBuilder sb = new StringBuilder(); - String line; - while ((line = bufferedReader.readLine()) != null) { - sb.append(line); - sb.append("\n"); - } - fi.close(); - return sb.toString(); - } - - private static void printUsage() { - PrintStream o = System.out; - o.println(WidgetSetBuilder.class.getSimpleName() + " usage:"); - o.println(" 1. Set the same classpath as you will " - + "have for the GWT compiler."); - o.println(" 2. Give the widgetsetname (to be created or updated)" - + " as first parameter"); - o.println(); - o.println("All found vaadin widgetsets will be inherited in given widgetset"); - - } - -} diff --git a/server/src/com/vaadin/tools/WidgetsetCompiler.java b/server/src/com/vaadin/tools/WidgetsetCompiler.java deleted file mode 100644 index ecc1946e60..0000000000 --- a/server/src/com/vaadin/tools/WidgetsetCompiler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.tools; - -import java.lang.reflect.Method; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.terminal.gwt.widgetsetutils.WidgetSetBuilder; - -/** - * A wrapper for the GWT 1.6 compiler that runs the compiler in a new thread. - * - * This allows circumventing a J2SE 5.0 bug (6316197) that prevents setting the - * stack size for the main thread. Thus, larger widgetsets can be compiled. - * - * This class takes the same command line arguments as the - * com.google.gwt.dev.GWTCompiler class. The old and deprecated compiler is used - * for compatibility with GWT 1.5. - * - * A typical invocation would use e.g. the following arguments - * - * "-out WebContent/VAADIN/widgetsets com.vaadin.terminal.gwt.DefaultWidgetSet" - * - * In addition, larger memory usage settings for the VM should be used, e.g. - * - * "-Xms256M -Xmx512M -Xss8M" - * - * The source directory containing widgetset and related classes must be - * included in the classpath, as well as the gwt-dev-[platform].jar and other - * relevant JARs. - * - * @deprecated with Java 6, can use com.google.gwt.dev.Compiler directly (also - * in Eclipse plug-in etc.) - */ -@Deprecated -public class WidgetsetCompiler { - - /** - * @param args - * same arguments as for com.google.gwt.dev.Compiler - */ - public static void main(final String[] args) { - try { - // run the compiler in a different thread to enable using the - // user-set stack size - - // on Windows, the default stack size is too small for the main - // thread and cannot be changed in JRE 1.5 (see - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6316197) - - Runnable runCompiler = new Runnable() { - @Override - public void run() { - try { - // GWTCompiler.main(args); - // avoid warnings - - String wsname = args[args.length - 1]; - - // TODO expecting this is launched via eclipse WTP - // project - System.out - .println("Updating GWT module description file..."); - WidgetSetBuilder.updateWidgetSet(wsname); - System.out.println("Done."); - - System.out.println("Starting GWT compiler"); - System.setProperty("gwt.nowarn.legacy.tools", "true"); - Class compilerClass = Class - .forName("com.google.gwt.dev.GWTCompiler"); - Method method = compilerClass.getDeclaredMethod("main", - String[].class); - method.invoke(null, new Object[] { args }); - } catch (Throwable thr) { - getLogger().log(Level.SEVERE, - "Widgetset compilation failed", thr); - } - } - }; - Thread runThread = new Thread(runCompiler); - runThread.start(); - runThread.join(); - System.out.println("Widgetset compilation finished"); - } catch (Throwable thr) { - getLogger().log(Level.SEVERE, "Widgetset compilation failed", thr); - } - } - - private static final Logger getLogger() { - return Logger.getLogger(WidgetsetCompiler.class.getName()); - } -} -- cgit v1.2.3