From 521ec24c62a1ba476795369a42114fede7043f44 Mon Sep 17 00:00:00 2001 From: elmot Date: Fri, 18 Mar 2016 17:29:27 +0200 Subject: Build client-compiler with maven Change-Id: Ic86a95ce44cc37c8d34d607b39f2aec4cc922ce2 --- .classpath | 4 +- .gitignore | 4 + client-compiled/ivy.xml | 2 +- client-compiler/pom.xml | 327 +++++ .../src/com/vaadin/sass/linker/SassLinker.java | 233 ---- .../AcceptCriteriaFactoryGenerator.java | 143 --- .../ConnectorBundleLoaderFactory.java | 1306 -------------------- .../widgetsetutils/metadata/ArraySerializer.java | 95 -- .../widgetsetutils/metadata/ClientRpcVisitor.java | 84 -- .../widgetsetutils/metadata/ConnectorBundle.java | 737 ----------- .../metadata/ConnectorInitVisitor.java | 40 - .../widgetsetutils/metadata/CustomSerializer.java | 43 - .../widgetsetutils/metadata/EnumSerializer.java | 58 - .../widgetsetutils/metadata/FieldProperty.java | 93 -- .../metadata/GeneratedSerializer.java | 26 - .../widgetsetutils/metadata/JsonSerializer.java | 88 -- .../widgetsetutils/metadata/MethodProperty.java | 144 --- .../metadata/OnStateChangeVisitor.java | 54 - .../server/widgetsetutils/metadata/Property.java | 127 -- .../widgetsetutils/metadata/RendererVisitor.java | 119 -- .../widgetsetutils/metadata/ServerRpcVisitor.java | 70 -- .../widgetsetutils/metadata/StateInitVisitor.java | 37 - .../widgetsetutils/metadata/TypeVisitor.java | 44 - .../widgetsetutils/metadata/WidgetInitVisitor.java | 105 -- .../src/com/vaadin/tools/CvalAddonsChecker.java | 193 --- .../src/com/vaadin/tools/CvalChecker.java | 510 -------- .../src/com/vaadin/tools/CvalChecker.properties | 13 - .../src/com/vaadin/tools/WidgetsetCompiler.java | 99 -- .../java/com/vaadin/sass/linker/SassLinker.java | 233 ++++ .../AcceptCriteriaFactoryGenerator.java | 143 +++ .../ConnectorBundleLoaderFactory.java | 1306 ++++++++++++++++++++ .../widgetsetutils/metadata/ArraySerializer.java | 95 ++ .../widgetsetutils/metadata/ClientRpcVisitor.java | 84 ++ .../widgetsetutils/metadata/ConnectorBundle.java | 737 +++++++++++ .../metadata/ConnectorInitVisitor.java | 40 + .../widgetsetutils/metadata/CustomSerializer.java | 43 + .../widgetsetutils/metadata/EnumSerializer.java | 58 + .../widgetsetutils/metadata/FieldProperty.java | 93 ++ .../metadata/GeneratedSerializer.java | 26 + .../widgetsetutils/metadata/JsonSerializer.java | 88 ++ .../widgetsetutils/metadata/MethodProperty.java | 144 +++ .../metadata/OnStateChangeVisitor.java | 54 + .../server/widgetsetutils/metadata/Property.java | 127 ++ .../widgetsetutils/metadata/RendererVisitor.java | 119 ++ .../widgetsetutils/metadata/ServerRpcVisitor.java | 70 ++ .../widgetsetutils/metadata/StateInitVisitor.java | 37 + .../widgetsetutils/metadata/TypeVisitor.java | 44 + .../widgetsetutils/metadata/WidgetInitVisitor.java | 105 ++ .../java/com/vaadin/tools/CvalAddonsChecker.java | 193 +++ .../main/java/com/vaadin/tools/CvalChecker.java | 510 ++++++++ .../java/com/vaadin/tools/WidgetsetCompiler.java | 99 ++ .../resources/com/google/gwt/dev/About.properties | 2 + .../com/vaadin/tools/CvalChecker.properties | 13 + .../com/vaadin/tools/CvalAddonsCheckerTest.java | 190 +++ .../tools/CvalAddonstCheckerUseCasesTest.java | 273 ++++ .../java/com/vaadin/tools/CvalCheckerTest.java | 495 ++++++++ .../com/vaadin/tools/CvalAddonsCheckerTest.java | 184 --- .../tools/CvalAddonstCheckerUseCasesTest.java | 217 ---- .../src/com/vaadin/tools/CvalCheckerTest.java | 495 -------- ivysettings.xml | 2 +- pom.xml | 2 + uitest/ivy.xml | 2 +- widgets/ivy.xml | 2 +- 63 files changed, 5760 insertions(+), 5363 deletions(-) create mode 100644 client-compiler/pom.xml delete mode 100644 client-compiler/src/com/vaadin/sass/linker/SassLinker.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/Property.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java delete mode 100644 client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java delete mode 100644 client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java delete mode 100644 client-compiler/src/com/vaadin/tools/CvalChecker.java delete mode 100644 client-compiler/src/com/vaadin/tools/CvalChecker.properties delete mode 100755 client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java create mode 100644 client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/Property.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java create mode 100644 client-compiler/src/main/java/com/vaadin/tools/CvalAddonsChecker.java create mode 100644 client-compiler/src/main/java/com/vaadin/tools/CvalChecker.java create mode 100755 client-compiler/src/main/java/com/vaadin/tools/WidgetsetCompiler.java create mode 100644 client-compiler/src/main/resources/com/google/gwt/dev/About.properties create mode 100644 client-compiler/src/main/resources/com/vaadin/tools/CvalChecker.properties create mode 100644 client-compiler/src/test/java/com/vaadin/tools/CvalAddonsCheckerTest.java create mode 100644 client-compiler/src/test/java/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java create mode 100644 client-compiler/src/test/java/com/vaadin/tools/CvalCheckerTest.java delete mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java delete mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java delete mode 100644 client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java diff --git a/.classpath b/.classpath index 2cd430abfe..f68a533702 100644 --- a/.classpath +++ b/.classpath @@ -5,8 +5,8 @@ - - + + diff --git a/.gitignore b/.gitignore index 2b15b7e5b0..7c5b461de9 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,10 @@ push/target/ shared/target/ server/target/ client/target/ +client-compiler/target/ .sass-cache phantomjsdriver.log + +*.iml +.idea \ No newline at end of file diff --git a/client-compiled/ivy.xml b/client-compiled/ivy.xml index dc7a396a4e..2fed37467a 100644 --- a/client-compiled/ivy.xml +++ b/client-compiled/ivy.xml @@ -26,7 +26,7 @@ + rev="${vaadin.version}" conf="compile-module-> default" /> diff --git a/client-compiler/pom.xml b/client-compiler/pom.xml new file mode 100644 index 0000000000..a37155a738 --- /dev/null +++ b/client-compiler/pom.xml @@ -0,0 +1,327 @@ + + + 4.0.0 + + com.vaadin + vaadin-root + 7.7.0-SNAPSHOT + + vaadin-client-compiler + vaadin-client-compiler + jar + + + Vaadin Ltd + + https://vaadin.com/ + Vaadin client compiler + + + + com.vaadin + vaadin-shared + ${project.version} + + + com.vaadin + vaadin-server + ${project.version} + + + com.vaadin + vaadin-client + ${project.version} + + + com.vaadin + vaadin-sass-compiler + ${vaadin.sass.version} + + + + net.sourceforge.cssparser + cssparser + + + + + + commons-collections + commons-collections + 3.1 + + + commons-logging + commons-logging + 1.1.3 + + + net.sourceforge.cssparser + cssparser + 0.9.11 + + + ant + ant + 1.6.5 + + + ant + ant-launcher + 1.6.5 + + + org.ow2.asm + asm + 5.0.3 + + + org.ow2.asm + asm-util + 5.0.3 + + + org.ow2.asm + asm-commons + 5.0.3 + + + org.eclipse.jetty + jetty-annotations + 8.1.12.v20130726 + + + org.eclipse.jetty + orbit + + + + + org.eclipse.jetty + jetty-servlets + 8.1.12.v20130726 + + + org.eclipse.jetty + orbit + + + + + + org.eclipse.jetty + jetty-util + 8.1.12.v20130726 + + + org.jdesktop + swing-worker + 1.1 + + + commons-codec + commons-codec + 1.8 + + + commons-io + commons-io + ${commons-io.version} + + + org.apache.commons + commons-lang3 + 3.1 + + + org.apache.james + apache-mime4j + 0.6 + + + org.apache.httpcomponents + httpclient + 4.3.1 + + + org.apache.httpcomponents + httpcore + 4.3 + + + org.apache.httpcomponents + httpmime + 4.3.1 + + + net.sourceforge.nekohtml + nekohtml + 1.9.19 + + + xalan + serializer + 2.7.1 + + + xerces + xercesImpl + 2.11.0 + + + xml-apis + xml-apis + 1.4.01 + + + com.ibm.icu + icu4j + 50.1.1 + + + com.vaadin + vaadin-client-compiler-deps + 1.2.0 + + + com.vaadin.external.gwt + gwt-dev + ${vaadin.gwt.version} + provided + + + + junit + junit + ${junit.version} + test + + + + + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + process-classes + + unpack + + + + + com.vaadin.external.gwt + gwt-dev + + about_files/**, + com/google/gwt/core/**, + com/google/gwt/dev/**, + com/google/gwt/lang/**, + com/google/gwt/soyc/**, + com/google/gwt/thirdparty/json/**, + com/google/gwt/util/**, + license/LICENSE.sax.txt, + netscape/**, + org/apache/COPYING, + org/apache/jasper/**, + org/apache/commons/el/**, + org/apache/commons/lang3/**, + templates/*, + about.html, + jetty-dir.css, + plugin.properties + + + com/google/gwt/dev/protobuf/**, + com/google/gwt/dev/About.properties + + + + ${project.build.directory}/classes + false + true + + + + + + maven-resources-plugin + + + + copy-sources + + prepare-package + + copy-resources + + + ${project.build.directory}/classes + + + src/main/java + false + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + JavaSE-1.6 + com.vaadin.*;version="${project.version}",com.google.gwt.*;version="${project.version}" + + + + + + + bundle-manifest + prepare-package + + manifest + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + + + + + diff --git a/client-compiler/src/com/vaadin/sass/linker/SassLinker.java b/client-compiler/src/com/vaadin/sass/linker/SassLinker.java deleted file mode 100644 index dda6733384..0000000000 --- a/client-compiler/src/com/vaadin/sass/linker/SassLinker.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.sass.linker; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.w3c.css.sac.CSSException; - -import com.google.gwt.core.ext.LinkerContext; -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.linker.AbstractLinker; -import com.google.gwt.core.ext.linker.ArtifactSet; -import com.google.gwt.core.ext.linker.EmittedArtifact; -import com.google.gwt.core.ext.linker.LinkerOrder; -import com.google.gwt.core.ext.linker.LinkerOrder.Order; -import com.google.gwt.core.ext.linker.Shardable; -import com.vaadin.sass.internal.ScssStylesheet; - -/** - * Pre-linker that checks for the existence of SASS files in public folders, - * compiles them to CSS files with the SassCompiler from Vaadin and adds the CSS - * back into the artifact. - * - */ -@LinkerOrder(Order.PRE) -@Shardable -public class SassLinker extends AbstractLinker { - - @Override - public String getDescription() { - return "Compiling SCSS files in public folders to standard CSS"; - } - - @Override - public ArtifactSet link(TreeLogger logger, LinkerContext context, - ArtifactSet artifacts, boolean onePermutation) - throws UnableToCompleteException { - - if (!onePermutation) { - // The artifact to return - ArtifactSet toReturn = new ArtifactSet(artifacts); - - // The temporary scss files provided from the artefacts - List scssFiles = new ArrayList(); - - // The public files are provided as inputstream, but the compiler - // needs real files, as they can contain references to other - // files. They will be stored here, with their relative paths intact - String tempFolderName = new Date().getTime() + File.separator; - File tempFolder = createTempDir(tempFolderName); - - // Can't search here specifically for public resources, as the type - // is different during compilation. This means we have to loop - // through all the artifacts - for (EmittedArtifact resource : artifacts - .find(EmittedArtifact.class)) { - - // Create the temporary files. - String partialPath = resource.getPartialPath(); - if (partialPath.endsWith(".scss")) { - // In my opinion, the SCSS file does not need to be - // output to the web content folder, as they can't - // be used there - toReturn.remove(resource); - - String fileName = partialPath; - File path = tempFolder; - - int separatorIndex = fileName.lastIndexOf(File.separator); - if (-1 != separatorIndex) { - fileName = fileName.substring(separatorIndex + 1); - - String filePath = partialPath.substring(0, - separatorIndex); - path = createTempDir(tempFolderName + filePath); - } - - File tempfile = new File(path, fileName); - try { - boolean fileCreated = tempfile.createNewFile(); - if (fileCreated) { - - // write the received inputstream to the temp file - writeFromInputStream(resource.getContents(logger), - tempfile); - - // Store the file info for the compilation - scssFiles.add(new FileInfo(tempfile, partialPath)); - } else { - logger.log(TreeLogger.WARN, "Duplicate file " - + tempfile.getPath()); - } - } catch (IOException e) { - logger.log(TreeLogger.ERROR, - "Could not write temporary file " + fileName, e); - } - } - } - - // Compile the files and store them in the artifact - logger.log(TreeLogger.INFO, "Processing " + scssFiles.size() - + " Sass file(s)"); - for (FileInfo fileInfo : scssFiles) { - logger.log(TreeLogger.INFO, " " + fileInfo.originalScssPath - + " -> " + fileInfo.getOriginalCssPath()); - - try { - ScssStylesheet scss = ScssStylesheet.get(fileInfo - .getAbsolutePath()); - if (!fileInfo.isMixin()) { - scss.compile(); - InputStream is = new ByteArrayInputStream(scss - .printState().getBytes()); - - toReturn.add(this.emitInputStream(logger, is, - fileInfo.getOriginalCssPath())); - } - - fileInfo.getFile().delete(); - } catch (CSSException e) { - logger.log(TreeLogger.ERROR, "SCSS compilation failed for " - + fileInfo.getOriginalCssPath(), e); - } catch (IOException e) { - logger.log( - TreeLogger.ERROR, - "Could not write CSS file for " - + fileInfo.getOriginalCssPath(), e); - } catch (Exception e) { - logger.log(TreeLogger.ERROR, "SCSS compilation failed for " - + fileInfo.getOriginalCssPath(), e); - } - } - - return toReturn; - } - - return artifacts; - } - - /** - * Writes the contents of an InputStream out to a file. - * - * @param contents - * @param tempfile - * @throws IOException - */ - private void writeFromInputStream(InputStream contents, File tempfile) - throws IOException { - // write the inputStream to a FileOutputStream - OutputStream out = new FileOutputStream(tempfile); - - int read = 0; - byte[] bytes = new byte[1024]; - - while ((read = contents.read(bytes)) != -1) { - out.write(bytes, 0, read); - } - - contents.close(); - out.flush(); - out.close(); - } - - /** - * Create folder in temporary space on disk. - * - * @param partialPath - * @return - */ - private File createTempDir(String partialPath) { - String baseTempPath = System.getProperty("java.io.tmpdir"); - - File tempDir = new File(baseTempPath + File.separator + partialPath); - if (!tempDir.exists()) { - tempDir.mkdirs(); - } - - tempDir.deleteOnExit(); - return tempDir; - } - - /** - * Temporal storage for file info from Artifact. - */ - private class FileInfo { - private String originalScssPath; - private File file; - - public FileInfo(File file, String originalScssPath) { - this.file = file; - this.originalScssPath = originalScssPath; - } - - public boolean isMixin() { - return file.getName().startsWith("_"); - } - - public String getAbsolutePath() { - return file.getAbsolutePath(); - } - - public String getOriginalCssPath() { - return originalScssPath.substring(0, originalScssPath.length() - 5) - + ".css"; - } - - public File getFile() { - return file; - } - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java b/client-compiler/src/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java deleted file mode 100644 index 2d08329e9a..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils; - -import java.io.PrintWriter; -import java.util.Arrays; -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.client.ui.dd.VAcceptCriterion; -import com.vaadin.client.ui.dd.VAcceptCriterionFactory; -import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; -import com.vaadin.shared.ui.dd.AcceptCriterion; - -/** - * 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.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()); - JClassType[] subtypes = criteriaType.getSubtypes(); - Arrays.sort(subtypes, ConnectorBundle.jClassComparator); - for (JClassType clientClass : subtypes) { - 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/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java deleted file mode 100644 index 2b8ccc87d0..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java +++ /dev/null @@ -1,1306 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.RunAsyncCallback; -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.JPrimitiveType; -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.client.JsArrayObject; -import com.vaadin.client.ServerConnector; -import com.vaadin.client.annotations.OnStateChange; -import com.vaadin.client.communication.JsonDecoder; -import com.vaadin.client.metadata.ConnectorBundleLoader; -import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; -import com.vaadin.client.metadata.InvokationHandler; -import com.vaadin.client.metadata.OnStateChangeMethod; -import com.vaadin.client.metadata.ProxyHandler; -import com.vaadin.client.metadata.TypeData; -import com.vaadin.client.metadata.TypeDataStore; -import com.vaadin.client.metadata.TypeDataStore.MethodAttribute; -import com.vaadin.client.ui.UnknownComponentConnector; -import com.vaadin.server.widgetsetutils.metadata.ClientRpcVisitor; -import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; -import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor; -import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer; -import com.vaadin.server.widgetsetutils.metadata.OnStateChangeVisitor; -import com.vaadin.server.widgetsetutils.metadata.Property; -import com.vaadin.server.widgetsetutils.metadata.RendererVisitor; -import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor; -import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor; -import com.vaadin.server.widgetsetutils.metadata.TypeVisitor; -import com.vaadin.server.widgetsetutils.metadata.WidgetInitVisitor; -import com.vaadin.shared.annotations.DelegateToWidget; -import com.vaadin.shared.annotations.NoLayout; -import com.vaadin.shared.communication.ClientRpc; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.tools.CvalAddonsChecker; -import com.vaadin.tools.CvalChecker; -import com.vaadin.tools.CvalChecker.InvalidCvalException; - -public class ConnectorBundleLoaderFactory extends Generator { - /** - * Special SourceWriter that approximates the number of written bytes to - * support splitting long methods into shorter chunks to avoid hitting the - * 65535 byte limit. - */ - private class SplittingSourceWriter implements SourceWriter { - private final SourceWriter target; - private final String baseName; - private final int splitSize; - private final List methodNames; - - // Seems to be undercounted by about 15% - private int approximateChars = 0; - private int wrapCount = 0; - - public SplittingSourceWriter(SourceWriter target, String baseName, - int splitSize) { - this.target = target; - this.baseName = baseName; - this.splitSize = splitSize; - methodNames = new ArrayList(); - methodNames.add(baseName); - } - - @Override - public void beginJavaDocComment() { - target.beginJavaDocComment(); - addChars(10); - } - - private void addChars(int i) { - approximateChars += i; - } - - private void addChars(String s) { - addChars(s.length()); - } - - private void addChars(String s, Object[] args) { - addChars(String.format(s, args)); - } - - @Override - public void commit(TreeLogger logger) { - target.commit(logger); - } - - @Override - public void endJavaDocComment() { - target.endJavaDocComment(); - addChars(10); - } - - @Override - public void indent() { - target.indent(); - addChars(10); - } - - @Override - public void indentln(String s) { - target.indentln(s); - addChars(s); - } - - @Override - public void indentln(String s, Object... args) { - target.indentln(s, args); - addChars(s, args); - } - - @Override - public void outdent() { - target.outdent(); - } - - @Override - public void print(String s) { - target.print(s); - addChars(s); - } - - @Override - public void print(String s, Object... args) { - target.print(s, args); - addChars(s, args); - } - - @Override - public void println() { - target.println(); - addChars(5); - } - - @Override - public void println(String s) { - target.println(s); - addChars(s); - } - - @Override - public void println(String s, Object... args) { - target.println(s, args); - addChars(s, args); - } - - public void splitIfNeeded() { - splitIfNeeded(false, null); - } - - public void splitIfNeeded(boolean isNative, String params) { - if (approximateChars > splitSize) { - String newMethod = baseName + wrapCount++; - String args = params == null ? "" : params; - if (isNative) { - outdent(); - println("}-*/;"); - // To support fields of type long (#13692) - println("@com.google.gwt.core.client.UnsafeNativeLong"); - println("private native void %s(%s) /*-{", newMethod, args); - } else { - println("%s();", newMethod); - outdent(); - println("}"); - println("private void %s(%s) {", newMethod, args); - } - methodNames.add(newMethod); - indent(); - - approximateChars = 0; - } - } - - public List getMethodNames() { - return Collections.unmodifiableList(methodNames); - } - - } - - private CvalAddonsChecker cvalChecker = new CvalAddonsChecker(); - - @Override - public String generate(TreeLogger logger, GeneratorContext context, - String typeName) throws UnableToCompleteException { - TypeOracle typeOracle = context.getTypeOracle(); - - try { - JClassType classType = typeOracle.getType(typeName); - String packageName = classType.getPackage().getName(); - String className = classType.getSimpleSourceName() + "Impl"; - - generateClass(logger, context, packageName, className, typeName); - - return packageName + "." + className; - } catch (UnableToCompleteException e) { - // Just rethrow - throw e; - } catch (Exception e) { - logger.log(Type.ERROR, getClass() + " failed", e); - throw new UnableToCompleteException(); - } - } - - private void generateClass(TreeLogger logger, GeneratorContext context, - String packageName, String className, String requestedType) - throws Exception { - PrintWriter printWriter = context.tryCreate(logger, packageName, - className); - if (printWriter == null) { - return; - } - - List cvalInfos = null; - try { - if (cvalChecker != null) { - cvalInfos = cvalChecker.run(); - // Don't run twice - cvalChecker = null; - } - } catch (InvalidCvalException e) { - System.err.println("\n\n\n\n" + CvalChecker.LINE); - for (String line : e.getMessage().split("\n")) { - System.err.println(line); - } - System.err.println(CvalChecker.LINE + "\n\n\n\n"); - System.exit(1); - throw new UnableToCompleteException(); - } - - List bundles = buildBundles(logger, - context.getTypeOracle()); - - ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( - packageName, className); - composer.setSuperclass(requestedType); - - SourceWriter w = composer.createSourceWriter(context, printWriter); - - w.println("public void init() {"); - w.indent(); - - for (ConnectorBundle bundle : bundles) { - detectBadProperties(bundle, logger); - - String name = bundle.getName(); - boolean isEager = name - .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME); - - w.print("addAsyncBlockLoader(new AsyncBundleLoader(\""); - w.print(escape(name)); - w.print("\", "); - - w.print("new String[] {"); - for (Entry> entry : bundle.getIdentifiers() - .entrySet()) { - Set identifiers = entry.getValue(); - for (String id : identifiers) { - w.print("\""); - w.print(escape(id)); - w.print("\","); - } - } - w.println("}) {"); - w.indent(); - - w.print("protected void load(final "); - w.print(TypeDataStore.class.getName()); - w.println(" store) {"); - w.indent(); - - if (!isEager) { - w.print(GWT.class.getName()); - w.print(".runAsync("); - } - - w.println("new %s() {", RunAsyncCallback.class.getName()); - w.indent(); - - w.println("public void onSuccess() {"); - w.indent(); - - w.println("load();"); - w.println("%s.get().setLoaded(getName());", - ConnectorBundleLoader.class.getName()); - - // Close onSuccess method - w.outdent(); - w.println("}"); - - w.println("private void load() {"); - w.indent(); - - String loadNativeJsBundle = "loadJsBundle"; - printBundleData(logger, w, bundle, loadNativeJsBundle); - - // Close load method - w.outdent(); - w.println("}"); - - // Separate method for loading native JS stuff (e.g. callbacks) - String loadNativeJsMethodName = "loadNativeJs"; - // To support fields of type long (#13692) - w.println("@com.google.gwt.core.client.UnsafeNativeLong"); - w.println("private native void %s(%s store) /*-{", - loadNativeJsMethodName, TypeDataStore.class.getName()); - w.indent(); - List jsMethodNames = printJsBundleData(logger, w, bundle, - loadNativeJsMethodName); - - w.outdent(); - w.println("}-*/;"); - - // Call all generated native method inside one Java method to avoid - // refercences inside native methods to each other - w.println("private void %s(%s store) {", loadNativeJsBundle, - TypeDataStore.class.getName()); - w.indent(); - printLoadJsBundleData(w, loadNativeJsBundle, jsMethodNames); - w.outdent(); - w.println("}"); - - // onFailure method declaration starts - w.println("public void onFailure(Throwable reason) {"); - w.indent(); - - w.println("%s.get().setLoadFailure(getName(), reason);", - ConnectorBundleLoader.class.getName()); - - w.outdent(); - w.println("}"); - - // Close new RunAsyncCallback() {} - w.outdent(); - w.print("}"); - - if (isEager) { - w.println(".onSuccess();"); - } else { - w.println(");"); - } - - // Close load method - w.outdent(); - w.println("}"); - - // Close add(new ... - w.outdent(); - w.println("});"); - } - - if (cvalInfos != null && !cvalInfos.isEmpty()) { - w.println("{"); - for (CValUiInfo c : cvalInfos) { - if ("evaluation".equals(c.type)) { - w.println("cvals.add(new CValUiInfo(\"" + c.product - + "\", \"" + c.version + "\", \"" + c.widgetset - + "\", null));"); - } - } - w.println("}"); - } - - w.outdent(); - w.println("}"); - - w.commit(logger); - } - - private void printLoadJsBundleData(SourceWriter w, String methodName, - List methods) { - SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, - 30000); - - for (String method : methods) { - writer.println("%s(store);", method); - writer.splitIfNeeded(); - } - } - - private void detectBadProperties(ConnectorBundle bundle, TreeLogger logger) - throws UnableToCompleteException { - Map> definedProperties = new HashMap>(); - - for (Property property : bundle.getNeedsProperty()) { - JClassType beanType = property.getBeanType(); - Set usedPropertyNames = definedProperties.get(beanType); - if (usedPropertyNames == null) { - usedPropertyNames = new HashSet(); - definedProperties.put(beanType, usedPropertyNames); - } - - String name = property.getName(); - if (!usedPropertyNames.add(name)) { - logger.log(Type.ERROR, beanType.getQualifiedSourceName() - + " has multiple properties with the name " + name - + ". This can happen if there are multiple " - + "setters with identical names ignoring case."); - throw new UnableToCompleteException(); - } - if (!property.hasAccessorMethods()) { - logger.log(Type.ERROR, beanType.getQualifiedSourceName() - + " has the property '" + name - + "' without getter defined."); - throw new UnableToCompleteException(); - } - } - } - - private List printJsBundleData(TreeLogger logger, SourceWriter w, - ConnectorBundle bundle, String methodName) { - SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, - 30000); - Set needsProperty = bundle.getNeedsProperty(); - for (Property property : needsProperty) { - writer.println("var data = {"); - writer.indent(); - - if (property.getAnnotation(NoLayout.class) != null) { - writer.println("noLayout: 1, "); - } - - writer.println("setter: function(bean, value) {"); - writer.indent(); - property.writeSetterBody(logger, writer, "bean", "value"); - writer.outdent(); - writer.println("},"); - - writer.println("getter: function(bean) {"); - writer.indent(); - property.writeGetterBody(logger, writer, "bean"); - writer.outdent(); - writer.println("}"); - - writer.outdent(); - writer.println("};"); - - // Method declaration - writer.print( - "store.@%s::setPropertyData(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)", - TypeDataStore.class.getName()); - writer.println("(@%s::class, '%s', data);", property.getBeanType() - .getQualifiedSourceName(), property.getName()); - writer.println(); - writer.splitIfNeeded(true, - String.format("%s store", TypeDataStore.class.getName())); - } - return writer.getMethodNames(); - } - - private void printBundleData(TreeLogger logger, SourceWriter sourceWriter, - ConnectorBundle bundle, String loadNativeJsMethodName) - throws UnableToCompleteException { - // Split into new load method when reaching approximately 30000 bytes - SplittingSourceWriter w = new SplittingSourceWriter(sourceWriter, - "load", 30000); - - writeSuperClasses(w, bundle); - writeIdentifiers(w, bundle); - writeGwtConstructors(w, bundle); - writeReturnTypes(w, bundle); - writeInvokers(logger, w, bundle); - writeParamTypes(w, bundle); - writeProxys(w, bundle); - writeMethodAttributes(logger, w, bundle); - - w.println("%s(store);", loadNativeJsMethodName); - - // Must use Java code to generate Type data (because of Type[]), doing - // this after the JS property data has been initialized - writePropertyTypes(logger, w, bundle); - writeSerializers(logger, w, bundle); - writePresentationTypes(w, bundle); - writeDelegateToWidget(logger, w, bundle); - writeOnStateChangeHandlers(logger, w, bundle); - } - - private void writeOnStateChangeHandlers(TreeLogger logger, - SplittingSourceWriter w, ConnectorBundle bundle) - throws UnableToCompleteException { - Map> needsOnStateChangeHandler = bundle - .getNeedsOnStateChangeHandler(); - for (Entry> entry : needsOnStateChangeHandler - .entrySet()) { - JClassType connector = entry.getKey(); - - TreeLogger typeLogger = logger.branch( - Type.DEBUG, - "Generating @OnStateChange support for " - + connector.getName()); - - // Build map to speed up error checking - HashMap stateProperties = new HashMap(); - JClassType stateType = ConnectorBundle - .findInheritedMethod(connector, "getState").getReturnType() - .isClassOrInterface(); - for (Property property : bundle.getProperties(stateType)) { - stateProperties.put(property.getName(), property); - } - - for (JMethod method : entry.getValue()) { - TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, - "Processing method " + method.getName()); - - if (method.isPublic() || method.isProtected()) { - methodLogger - .log(Type.ERROR, - "@OnStateChange is only supported for methods with private or default visibility."); - throw new UnableToCompleteException(); - } - - OnStateChange onStateChange = method - .getAnnotation(OnStateChange.class); - - String[] properties = onStateChange.value(); - - if (properties.length == 0) { - methodLogger.log(Type.ERROR, - "There are no properties to listen to"); - throw new UnableToCompleteException(); - } - - // Verify that all properties do exist - for (String propertyName : properties) { - if (!stateProperties.containsKey(propertyName)) { - methodLogger.log(Type.ERROR, - "State class has no property named " - + propertyName); - throw new UnableToCompleteException(); - } - } - - if (method.getParameters().length != 0) { - methodLogger.log(Type.ERROR, - "Method should accept zero parameters"); - throw new UnableToCompleteException(); - } - - // new OnStateChangeMethod(Class declaringClass, String - // methodName, String[], properties) - w.print("store.addOnStateChangeMethod(%s, new %s(", - getClassLiteralString(connector), - OnStateChangeMethod.class.getName()); - if (!connector.equals(method.getEnclosingType())) { - w.print("%s, ", - getClassLiteralString(method.getEnclosingType())); - } - w.print("\"%s\", ", method.getName()); - - w.print("new String[] {"); - for (String propertyName : properties) { - w.print("\"%s\", ", propertyName); - } - w.print("}"); - - w.println("));"); - - w.splitIfNeeded(); - } - } - } - - private void writeSuperClasses(SplittingSourceWriter w, - ConnectorBundle bundle) { - List needsSuperclass = new ArrayList( - bundle.getNeedsSuperclass()); - // Emit in hierarchy order to ensure superclass is defined when - // referenced - Collections.sort(needsSuperclass, new Comparator() { - - @Override - public int compare(JClassType type1, JClassType type2) { - int depthDiff = getDepth(type1) - getDepth(type2); - if (depthDiff != 0) { - return depthDiff; - } else { - // Just something to get a stable compare - return type1.getName().compareTo(type2.getName()); - } - } - - private int getDepth(JClassType type) { - int depth = 0; - while (type != null) { - depth++; - type = type.getSuperclass(); - } - return depth; - } - }); - - for (JClassType jClassType : needsSuperclass) { - JClassType superclass = jClassType.getSuperclass(); - while (superclass != null && !superclass.isPublic()) { - superclass = superclass.getSuperclass(); - } - String classLiteralString; - if (superclass == null) { - classLiteralString = "null"; - } else { - classLiteralString = getClassLiteralString(superclass); - } - w.println("store.setSuperClass(%s, %s);", - getClassLiteralString(jClassType), classLiteralString); - } - } - - private void writeDelegateToWidget(TreeLogger logger, - SplittingSourceWriter w, ConnectorBundle bundle) { - Map> needsDelegateToWidget = bundle - .getNeedsDelegateToWidget(); - for (Entry> entry : needsDelegateToWidget - .entrySet()) { - JClassType beanType = entry.getKey(); - for (Property property : entry.getValue()) { - w.println( - "store.setDelegateToWidget(%s, \"%s\", \"%s\");", - getClassLiteralString(beanType),// property.getBeanType()), - property.getName(), - property.getAnnotation(DelegateToWidget.class).value()); - } - w.splitIfNeeded(); - } - } - - private void writeSerializers(TreeLogger logger, SplittingSourceWriter w, - ConnectorBundle bundle) throws UnableToCompleteException { - Map serializers = bundle.getSerializers(); - for (Entry entry : serializers.entrySet()) { - JType type = entry.getKey(); - GeneratedSerializer serializer = entry.getValue(); - - w.print("store.setSerializerFactory("); - writeClassLiteral(w, type); - w.print(", "); - w.println("new Invoker() {"); - w.indent(); - - w.println("public Object invoke(Object target, Object[] params) {"); - w.indent(); - - serializer.writeSerializerInstantiator(logger, w); - - w.outdent(); - w.println("}"); - - w.outdent(); - w.print("}"); - w.println(");"); - - w.splitIfNeeded(); - } - } - - private void writePresentationTypes(SplittingSourceWriter w, - ConnectorBundle bundle) { - Map presentationTypes = bundle - .getPresentationTypes(); - for (Entry entry : presentationTypes.entrySet()) { - - w.print("store.setPresentationType("); - writeClassLiteral(w, entry.getKey()); - w.print(", "); - writeClassLiteral(w, entry.getValue()); - w.println(");"); - w.splitIfNeeded(); - } - } - - private void writePropertyTypes(TreeLogger logger, SplittingSourceWriter w, - ConnectorBundle bundle) { - Set properties = bundle.getNeedsProperty(); - for (Property property : properties) { - w.print("store.setPropertyType("); - writeClassLiteral(w, property.getBeanType()); - w.print(", \""); - w.print(escape(property.getName())); - w.print("\", "); - writeTypeCreator(w, property.getPropertyType()); - w.println(");"); - - w.splitIfNeeded(); - } - } - - private void writeMethodAttributes(TreeLogger logger, - SplittingSourceWriter w, ConnectorBundle bundle) { - for (Entry>> typeEntry : bundle - .getMethodAttributes().entrySet()) { - JClassType type = typeEntry.getKey(); - for (Entry> methodEntry : typeEntry - .getValue().entrySet()) { - JMethod method = methodEntry.getKey(); - Set attributes = methodEntry.getValue(); - for (MethodAttribute attribute : attributes) { - w.println("store.setMethodAttribute(%s, \"%s\", %s.%s);", - getClassLiteralString(type), method.getName(), - MethodAttribute.class.getCanonicalName(), - attribute.name()); - w.splitIfNeeded(); - } - } - } - } - - private void writeProxys(SplittingSourceWriter w, ConnectorBundle bundle) { - Set needsProxySupport = bundle.getNeedsProxySupport(); - for (JClassType type : needsProxySupport) { - w.print("store.setProxyHandler("); - writeClassLiteral(w, type); - w.print(", new "); - w.print(ProxyHandler.class.getCanonicalName()); - w.println("() {"); - w.indent(); - - w.println("public Object createProxy(final " - + InvokationHandler.class.getName() + " handler) {"); - w.indent(); - - w.print("return new "); - w.print(type.getQualifiedSourceName()); - w.println("() {"); - w.indent(); - - JMethod[] methods = type.getOverridableMethods(); - for (JMethod method : methods) { - if (method.isAbstract()) { - w.print("public "); - w.print(method.getReturnType().getQualifiedSourceName()); - w.print(" "); - w.print(method.getName()); - w.print("("); - - JType[] types = method.getParameterTypes(); - for (int i = 0; i < types.length; i++) { - if (i != 0) { - w.print(", "); - } - w.print(types[i].getQualifiedSourceName()); - w.print(" p"); - w.print(Integer.toString(i)); - } - - w.println(") {"); - w.indent(); - - if (!method.getReturnType().getQualifiedSourceName() - .equals("void")) { - w.print("return "); - } - - w.print("handler.invoke(this, "); - w.print(TypeData.class.getCanonicalName()); - w.print(".getType("); - writeClassLiteral(w, type); - w.print(").getMethod(\""); - w.print(escape(method.getName())); - w.print("\"), new Object [] {"); - for (int i = 0; i < types.length; i++) { - w.print("p" + i + ", "); - } - w.println("});"); - - w.outdent(); - w.println("}"); - } - } - - w.outdent(); - w.println("};"); - - w.outdent(); - w.println("}"); - - w.outdent(); - w.println("});"); - - w.splitIfNeeded(); - } - } - - private void writeParamTypes(SplittingSourceWriter w, ConnectorBundle bundle) { - Map> needsParamTypes = bundle - .getNeedsParamTypes(); - for (Entry> entry : needsParamTypes.entrySet()) { - JClassType type = entry.getKey(); - - Set methods = entry.getValue(); - for (JMethod method : methods) { - w.print("store.setParamTypes("); - writeClassLiteral(w, type); - w.print(", \""); - w.print(escape(method.getName())); - w.print("\", new Type[] {"); - - for (JType parameter : method.getParameterTypes()) { - ConnectorBundleLoaderFactory.writeTypeCreator(w, parameter); - w.print(", "); - } - - w.println("});"); - - w.splitIfNeeded(); - } - } - } - - private void writeInvokers(TreeLogger logger, SplittingSourceWriter w, - ConnectorBundle bundle) throws UnableToCompleteException { - Map> needsInvoker = bundle.getNeedsInvoker(); - for (Entry> entry : needsInvoker.entrySet()) { - JClassType type = entry.getKey(); - - TreeLogger typeLogger = logger.branch(Type.DEBUG, - "Creating invokers for " + type); - - Set methods = entry.getValue(); - for (JMethod method : methods) { - w.print("store.setInvoker("); - writeClassLiteral(w, type); - w.print(", \""); - w.print(escape(method.getName())); - w.print("\","); - - if (method.isPublic()) { - typeLogger.log(Type.DEBUG, "Invoking " + method.getName() - + " using java"); - - writeJavaInvoker(w, type, method); - } else { - TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, - "Invoking " + method.getName() + " using jsni"); - // Must use JSNI to access non-public methods - writeJsniInvoker(methodLogger, w, type, method); - } - - w.println(");"); - - w.splitIfNeeded(); - } - } - } - - private void writeJsniInvoker(TreeLogger logger, SplittingSourceWriter w, - JClassType type, JMethod method) throws UnableToCompleteException { - w.println("new JsniInvoker() {"); - w.indent(); - - w.println( - "protected native Object jsniInvoke(Object target, %s params) /*-{ ", - JsArrayObject.class.getName()); - w.indent(); - - JType returnType = method.getReturnType(); - boolean hasReturnType = !"void".equals(returnType - .getQualifiedSourceName()); - - // Note that void is also a primitive type - boolean hasPrimitiveReturnType = hasReturnType - && returnType.isPrimitive() != null; - - if (hasReturnType) { - w.print("return "); - - if (hasPrimitiveReturnType) { - // Integer.valueOf(expression); - w.print("@%s::valueOf(%s)(", returnType.isPrimitive() - .getQualifiedBoxedSourceName(), returnType - .getJNISignature()); - - // Implementation tested briefly, but I don't dare leave it - // enabled since we are not using it in the framework and we - // have not tests for it. - logger.log(Type.ERROR, - "JSNI invocation is not yet supported for methods with " - + "primitive return types. Change your method " - + "to public to be able to use conventional" - + " Java invoking instead."); - throw new UnableToCompleteException(); - } - } - - JType[] parameterTypes = method.getParameterTypes(); - - w.print("target.@%s::" + method.getName() + "(*)(", method - .getEnclosingType().getQualifiedSourceName()); - for (int i = 0; i < parameterTypes.length; i++) { - if (i != 0) { - w.print(", "); - } - - w.print("params[" + i + "]"); - - JPrimitiveType primitive = parameterTypes[i].isPrimitive(); - if (primitive != null) { - // param.intValue(); - w.print(".@%s::%sValue()()", - primitive.getQualifiedBoxedSourceName(), - primitive.getQualifiedSourceName()); - } - } - - if (hasPrimitiveReturnType) { - assert hasReturnType; - w.print(")"); - } - - w.println(");"); - - if (!hasReturnType) { - w.println("return null;"); - } - - w.outdent(); - w.println("}-*/;"); - - w.outdent(); - w.print("}"); - } - - private void writeJavaInvoker(SplittingSourceWriter w, JClassType type, - JMethod method) { - w.println("new Invoker() {"); - w.indent(); - - w.println("public Object invoke(Object target, Object[] params) {"); - w.indent(); - - JType returnType = method.getReturnType(); - boolean hasReturnType = !"void".equals(returnType - .getQualifiedSourceName()); - if (hasReturnType) { - w.print("return "); - } - - JType[] parameterTypes = method.getParameterTypes(); - - w.print("((" + type.getQualifiedSourceName() + ") target)." - + method.getName() + "("); - for (int i = 0; i < parameterTypes.length; i++) { - JType parameterType = parameterTypes[i]; - if (i != 0) { - w.print(", "); - } - String parameterTypeName = getBoxedTypeName(parameterType); - - if (parameterTypeName.startsWith("elemental.json.Json")) { - // Need to pass through native method to allow casting Object to - // JSO if the value is a string - w.print("%s.<%s>obj2jso(params[%d])", - JsonDecoder.class.getCanonicalName(), - parameterTypeName, i); - } else { - w.print("(" + parameterTypeName + ") params[" + i + "]"); - } - } - w.println(");"); - - if (!hasReturnType) { - w.println("return null;"); - } - - w.outdent(); - w.println("}"); - - w.outdent(); - w.print("}"); - } - - private void writeReturnTypes(SplittingSourceWriter w, - ConnectorBundle bundle) { - Map> methodReturnTypes = bundle - .getMethodReturnTypes(); - for (Entry> entry : methodReturnTypes - .entrySet()) { - JClassType type = entry.getKey(); - - Set methods = entry.getValue(); - for (JMethod method : methods) { - // setReturnType(Class type, String methodName, Type - // returnType) - w.print("store.setReturnType("); - writeClassLiteral(w, type); - w.print(", \""); - w.print(escape(method.getName())); - w.print("\", "); - writeTypeCreator(w, method.getReturnType()); - w.println(");"); - - w.splitIfNeeded(); - } - } - } - - private void writeGwtConstructors(SplittingSourceWriter w, - ConnectorBundle bundle) { - Set constructors = bundle.getGwtConstructors(); - for (JClassType type : constructors) { - w.print("store.setConstructor("); - writeClassLiteral(w, type); - w.println(", new Invoker() {"); - w.indent(); - - w.println("public Object invoke(Object target, Object[] params) {"); - w.indent(); - - w.print("return "); - w.print(GWT.class.getName()); - w.print(".create("); - writeClassLiteral(w, type); - w.println(");"); - - w.outdent(); - w.println("}"); - - w.outdent(); - w.println("});"); - - w.splitIfNeeded(); - } - } - - public static void writeClassLiteral(SourceWriter w, JType type) { - w.print(getClassLiteralString(type)); - } - - public static String getClassLiteralString(JType type) { - return type.getQualifiedSourceName() + ".class"; - } - - private void writeIdentifiers(SplittingSourceWriter w, - ConnectorBundle bundle) { - Map> identifiers = bundle.getIdentifiers(); - for (Entry> entry : identifiers.entrySet()) { - Set ids = entry.getValue(); - JClassType type = entry.getKey(); - for (String id : ids) { - w.print("store.setClass(\""); - w.print(escape(id)); - w.print("\", "); - writeClassLiteral(w, type); - w.println(");"); - - w.splitIfNeeded(); - } - } - } - - private List buildBundles(TreeLogger logger, - TypeOracle typeOracle) throws NotFoundException, - UnableToCompleteException { - - Map> connectorsByLoadStyle = new HashMap>(); - for (LoadStyle loadStyle : LoadStyle.values()) { - connectorsByLoadStyle.put(loadStyle, new ArrayList()); - } - - // Find all types with a valid mapping - Collection selectedTypes = getConnectorsForWidgetset( - logger, typeOracle); - - // Group by load style - for (JClassType connectorSubtype : selectedTypes) { - LoadStyle loadStyle = getLoadStyle(connectorSubtype); - if (loadStyle != null) { - connectorsByLoadStyle.get(loadStyle).add(connectorSubtype); - } - } - - List bundles = new ArrayList(); - - Collection visitors = getVisitors(typeOracle); - - ConnectorBundle eagerBundle = new ConnectorBundle( - ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle); - TreeLogger eagerLogger = logger.branch(Type.TRACE, - "Populating eager bundle"); - - // Eager connectors and all RPC interfaces are loaded by default - eagerBundle.processTypes(eagerLogger, - connectorsByLoadStyle.get(LoadStyle.EAGER)); - eagerBundle.processType(eagerLogger, typeOracle - .findType(UnknownComponentConnector.class.getCanonicalName())); - eagerBundle.processSubTypes(eagerLogger, - typeOracle.getType(ClientRpc.class.getName())); - eagerBundle.processSubTypes(eagerLogger, - typeOracle.getType(ServerRpc.class.getName())); - - bundles.add(eagerBundle); - - ConnectorBundle deferredBundle = new ConnectorBundle( - ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle); - TreeLogger deferredLogger = logger.branch(Type.TRACE, - "Populating deferred bundle"); - deferredBundle.processTypes(deferredLogger, - connectorsByLoadStyle.get(LoadStyle.DEFERRED)); - - bundles.add(deferredBundle); - - Collection lazy = connectorsByLoadStyle.get(LoadStyle.LAZY); - for (JClassType type : lazy) { - ConnectorBundle bundle = new ConnectorBundle(type.getName(), - eagerBundle); - TreeLogger subLogger = logger.branch(Type.TRACE, "Populating " - + type.getName() + " bundle"); - bundle.processType(subLogger, type); - - bundles.add(bundle); - } - - return bundles; - } - - /** - * Returns the connector types that should be included in the widgetset. - * This method can be overridden to create a widgetset only containing - * selected connectors. - *

- * The default implementation finds all type implementing - * {@link ServerConnector} that have a @{@link Connect} annotation. It also - * checks that multiple connectors aren't connected to the same server-side - * class. - * - * @param logger - * the logger to which information can be logged - * @param typeOracle - * the type oracle that can be used for finding types - * @return a collection of all the connector types that should be included - * in the widgetset - * @throws UnableToCompleteException - * if the operation fails - */ - protected Collection getConnectorsForWidgetset( - TreeLogger logger, TypeOracle typeOracle) - throws UnableToCompleteException { - JClassType serverConnectorType; - try { - serverConnectorType = typeOracle.getType(ServerConnector.class - .getName()); - } catch (NotFoundException e) { - logger.log(Type.ERROR, - "Can't find " + ServerConnector.class.getName()); - throw new UnableToCompleteException(); - } - - JClassType[] types = serverConnectorType.getSubtypes(); - - Map mappings = new TreeMap(); - - // Keep track of what has happened to avoid logging intermediate state - Map> replaced = new TreeMap>( - ConnectorBundle.jClassComparator); - - for (JClassType type : types) { - Connect connectAnnotation = type.getAnnotation(Connect.class); - if (connectAnnotation == null) { - continue; - } - - String identifier = connectAnnotation.value().getCanonicalName(); - - JClassType previousMapping = mappings.put(identifier, type); - if (previousMapping != null) { - // There are multiple mappings, pick the subclass - JClassType subclass; - JClassType superclass; - if (previousMapping.isAssignableFrom(type)) { - subclass = type; - superclass = previousMapping; - } else if (type.isAssignableFrom(previousMapping)) { - subclass = previousMapping; - superclass = type; - } else { - // Neither inherits from the other - this is a conflict - logger.log( - Type.ERROR, - "Conflicting @Connect mappings detected for " - + identifier - + ": " - + type.getQualifiedSourceName() - + " and " - + previousMapping.getQualifiedSourceName() - + ". There can only be multiple @Connect mappings for the same server-side type if one is the subclass of the other."); - throw new UnableToCompleteException(); - } - - mappings.put(identifier, subclass); - - // Inherit any previous replacements - List previousReplacements = replaced - .remove(superclass); - if (previousReplacements == null) { - previousReplacements = new ArrayList(); - } - - previousReplacements.add(superclass); - replaced.put(subclass, previousReplacements); - } - } - - // Log the final set of replacements - for (Entry> entry : replaced.entrySet()) { - String msg = entry.getKey().getQualifiedSourceName() + " replaces "; - - List list = entry.getValue(); - for (int i = 0; i < list.size(); i++) { - if (i != 0) { - msg += ", "; - } - msg += list.get(i).getQualifiedSourceName(); - } - - logger.log(Type.INFO, msg); - } - - // Return the types of the final mapping - return mappings.values(); - } - - private Collection getVisitors(TypeOracle oracle) - throws NotFoundException { - List visitors = Arrays. asList( - new ConnectorInitVisitor(), new StateInitVisitor(), - new WidgetInitVisitor(), new RendererVisitor(), - new ClientRpcVisitor(), new ServerRpcVisitor(), - new OnStateChangeVisitor()); - for (TypeVisitor typeVisitor : visitors) { - typeVisitor.init(oracle); - } - return visitors; - } - - protected LoadStyle getLoadStyle(JClassType connectorType) { - Connect annotation = connectorType.getAnnotation(Connect.class); - return annotation.loadStyle(); - } - - public static String getBoxedTypeName(JType type) { - if (type.isPrimitive() != null) { - // Used boxed types for primitives - return type.isPrimitive().getQualifiedBoxedSourceName(); - } else { - return type.getErasedType().getQualifiedSourceName(); - } - } - - public static void writeTypeCreator(SourceWriter sourceWriter, JType type) { - String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type); - JParameterizedType parameterized = type.isParameterized(); - if (parameterized != null) { - sourceWriter.print("new Type(\"" + typeName + "\", "); - sourceWriter.print("new Type[] {"); - JClassType[] typeArgs = parameterized.getTypeArgs(); - for (JClassType jClassType : typeArgs) { - writeTypeCreator(sourceWriter, jClassType); - sourceWriter.print(", "); - } - sourceWriter.print("}"); - } else { - sourceWriter.print("new Type(" + typeName + ".class"); - } - sourceWriter.print(")"); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java deleted file mode 100644 index 0049ae9b50..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.JArrayType; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.client.communication.JsonDecoder; -import com.vaadin.client.communication.JsonEncoder; -import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory; - -import elemental.json.Json; -import elemental.json.JsonArray; - -public class ArraySerializer extends JsonSerializer { - - private final JArrayType arrayType; - - public ArraySerializer(JArrayType arrayType) { - super(arrayType); - this.arrayType = arrayType; - } - - @Override - protected void printDeserializerBody(TreeLogger logger, SourceWriter w, - String type, String jsonValue, String connection) { - JType leafType = arrayType.getLeafType(); - int rank = arrayType.getRank(); - - w.println(JsonArray.class.getName() + " jsonArray = (" - + JsonArray.class.getName() + ")" + jsonValue + ";"); - - // Type value = new Type[jsonArray.size()][][]; - w.print(arrayType.getQualifiedSourceName() + " value = new " - + leafType.getQualifiedSourceName() + "[jsonArray.length()]"); - for (int i = 1; i < rank; i++) { - w.print("[]"); - } - w.println(";"); - - w.println("for(int i = 0 ; i < value.length; i++) {"); - w.indent(); - - JType componentType = arrayType.getComponentType(); - - w.print("value[i] = (" - + ConnectorBundleLoaderFactory.getBoxedTypeName(componentType) - + ") " + JsonDecoder.class.getName() + ".decodeValue("); - ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType); - w.print(", jsonArray.get(i), null, " + connection + ")"); - - w.println(";"); - - w.outdent(); - w.println("}"); - - w.println("return value;"); - } - - @Override - protected void printSerializerBody(TreeLogger logger, SourceWriter w, - String value, String applicationConnection) { - JType componentType = arrayType.getComponentType(); - - w.println(JsonArray.class.getName() + " values = " - + Json.class.getName() + ".createArray();"); - // JPrimitiveType primitive = componentType.isPrimitive(); - w.println("for (int i = 0; i < " + value + ".length; i++) {"); - w.indent(); - w.print("values.set(i, "); - w.print(JsonEncoder.class.getName() + ".encode(" + value + "[i],"); - ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType); - w.print(", " + applicationConnection + ")"); - w.println(");"); - w.outdent(); - w.println("}"); - w.println("return values;"); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java deleted file mode 100644 index 992a012005..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import java.util.Set; - -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.vaadin.client.metadata.TypeDataStore.MethodAttribute; -import com.vaadin.shared.annotations.NoLayout; - -public class ClientRpcVisitor extends TypeVisitor { - @Override - public void visitClientRpc(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - checkGenericType(logger, type); - Set hierarchy = type - .getFlattenedSupertypeHierarchy(); - for (JClassType subType : hierarchy) { - JMethod[] methods = subType.getMethods(); - for (JMethod method : methods) { - checkReturnType(logger, method); - - bundle.setNeedsInvoker(type, method); - bundle.setNeedsParamTypes(type, method); - if (method.getAnnotation(NoLayout.class) != null) { - bundle.setMethodAttribute(type, method, - MethodAttribute.NO_LAYOUT); - } - - JType[] parameterTypes = method.getParameterTypes(); - for (JType paramType : parameterTypes) { - bundle.setNeedsSerialize(paramType); - } - } - } - } - - public static void checkGenericType(TreeLogger logger, JClassType type) - throws UnableToCompleteException { - if (type.isGenericType() != null) { - logger.log(Type.ERROR, - "Type " + type.getParameterizedQualifiedSourceName() - + "is parameterizied generic. RPC proxy " - + "for parameterizied types is not supported."); - throw new UnableToCompleteException(); - } - } - - public static void checkReturnType(TreeLogger logger, JMethod method) - throws UnableToCompleteException { - if (!method.getReturnType().getQualifiedSourceName().equals("void")) { - logger.log( - Type.ERROR, - "The method " - + method.getEnclosingType() - .getQualifiedSourceName() - + "." - + method.getName() - + " returns " - + method.getReturnType().getQualifiedSourceName() - + " but only void is supported for methods in RPC interfaces."); - throw new UnableToCompleteException(); - } - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java deleted file mode 100644 index b4531eb08e..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -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.JEnumType; -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.vaadin.client.ApplicationConnection; -import com.vaadin.client.ComponentConnector; -import com.vaadin.client.ServerConnector; -import com.vaadin.client.communication.JSONSerializer; -import com.vaadin.client.connectors.AbstractRendererConnector; -import com.vaadin.client.metadata.TypeDataStore.MethodAttribute; -import com.vaadin.client.ui.UnknownComponentConnector; -import com.vaadin.shared.communication.ClientRpc; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.shared.ui.Connect; - -import elemental.json.JsonValue; - -public class ConnectorBundle { - private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; - - public static final Comparator jClassComparator = new Comparator() { - @Override - public int compare(JClassType o1, JClassType o2) { - return o1.getQualifiedSourceName().compareTo( - o2.getQualifiedSourceName()); - } - }; - - public static final Comparator jMethodComparator = new Comparator() { - @Override - public int compare(JMethod o1, JMethod o2) { - return o1.getReadableDeclaration().compareTo( - o2.getReadableDeclaration()); - } - }; - - private final String name; - private final ConnectorBundle previousBundle; - private final Collection visitors; - private final Map customSerializers; - - private final Set hasSerializeSupport = new HashSet(); - private final Set needsSerializeSupport = new HashSet(); - - private final Map serializers = new TreeMap( - new Comparator() { - @Override - public int compare(JType o1, JType o2) { - return o1.toString().compareTo(o2.toString()); - } - }); - - private final Map>> methodAttributes = new TreeMap>>( - jClassComparator); - private final Set needsSuperClass = new TreeSet( - jClassComparator); - private final Set needsGwtConstructor = new TreeSet( - jClassComparator); - private final Set visitedTypes = new HashSet(); - - private final Set needsProxySupport = new TreeSet( - jClassComparator); - - private final Map presentationTypes = new TreeMap( - jClassComparator); - private final Map> identifiers = new TreeMap>( - jClassComparator); - private final Map> needsReturnType = new TreeMap>( - jClassComparator); - private final Map> needsInvoker = new TreeMap>( - jClassComparator); - private final Map> needsParamTypes = new TreeMap>( - jClassComparator); - private final Map> needsOnStateChange = new TreeMap>( - jClassComparator); - - private final Set needsProperty = new TreeSet(); - private final Map> needsDelegateToWidget = new TreeMap>( - jClassComparator); - - private ConnectorBundle(String name, ConnectorBundle previousBundle, - Collection visitors, - Map customSerializers) { - this.name = name; - this.previousBundle = previousBundle; - this.visitors = visitors; - this.customSerializers = customSerializers; - } - - public ConnectorBundle(String name, ConnectorBundle previousBundle) { - this(name, previousBundle, previousBundle.visitors, - previousBundle.customSerializers); - } - - public ConnectorBundle(String name, Collection visitors, - TypeOracle oracle) throws NotFoundException { - this(name, null, visitors, findCustomSerializers(oracle)); - } - - private static Map findCustomSerializers( - TypeOracle oracle) throws NotFoundException { - Map serializers = new HashMap(); - - JClassType serializerInterface = oracle.findType(JSONSerializer.class - .getName()); - JType[] deserializeParamTypes = new JType[] { - oracle.findType(com.vaadin.client.metadata.Type.class.getName()), - oracle.findType(JsonValue.class.getName()), - oracle.findType(ApplicationConnection.class.getName()) }; - String deserializeMethodName = "deserialize"; - // Just test that the method exists - serializerInterface.getMethod(deserializeMethodName, - deserializeParamTypes); - - for (JClassType serializer : serializerInterface.getSubtypes()) { - JMethod deserializeMethod = serializer.findMethod( - deserializeMethodName, deserializeParamTypes); - if (deserializeMethod == null) { - continue; - } - JType returnType = deserializeMethod.getReturnType(); - - serializers.put(returnType, serializer); - } - return serializers; - } - - public void setNeedsGwtConstructor(JClassType type) { - if (!needsGwtConstructor(type)) { - needsGwtConstructor.add(type); - } - } - - private boolean needsGwtConstructor(JClassType type) { - if (needsGwtConstructor.contains(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.needsGwtConstructor(type); - } - } - - public void setIdentifier(JClassType type, String identifier) { - if (!hasIdentifier(type, identifier)) { - addMapping(identifiers, type, identifier); - } - } - - private boolean hasIdentifier(JClassType type, String identifier) { - if (hasMapping(identifiers, type, identifier)) { - return true; - } else { - return previousBundle != null - && previousBundle.hasIdentifier(type, identifier); - } - } - - public ConnectorBundle getPreviousBundle() { - return previousBundle; - } - - public String getName() { - return name; - } - - public Map> getIdentifiers() { - return Collections.unmodifiableMap(identifiers); - } - - public Set getGwtConstructors() { - return Collections.unmodifiableSet(needsGwtConstructor); - } - - public void processTypes(TreeLogger logger, Collection types) - throws UnableToCompleteException { - for (JClassType type : types) { - processType(logger, type); - } - } - - public void processType(TreeLogger logger, JClassType type) - throws UnableToCompleteException { - if (!isTypeVisited(type)) { - for (TypeVisitor typeVisitor : visitors) { - invokeVisitor(logger, type, typeVisitor); - } - visitedTypes.add(type); - purgeSerializeSupportQueue(logger); - } - } - - private boolean isTypeVisited(JClassType type) { - if (visitedTypes.contains(type)) { - return true; - } else { - return previousBundle != null && previousBundle.isTypeVisited(type); - } - } - - private void purgeSerializeSupportQueue(TreeLogger logger) - throws UnableToCompleteException { - while (!needsSerializeSupport.isEmpty()) { - Iterator iterator = needsSerializeSupport.iterator(); - JType type = iterator.next(); - iterator.remove(); - - if (hasSerializeSupport(type)) { - continue; - } - - addSerializeSupport(logger, type); - } - } - - private void addSerializeSupport(TreeLogger logger, JType type) - throws UnableToCompleteException { - hasSerializeSupport.add(type); - - JParameterizedType parametrized = type.isParameterized(); - if (parametrized != null) { - for (JClassType parameterType : parametrized.getTypeArgs()) { - setNeedsSerialize(parameterType); - } - } - - if (serializationHandledByFramework(type)) { - return; - } - - JClassType customSerializer = customSerializers.get(type); - JClassType typeAsClass = type.isClass(); - JEnumType enumType = type.isEnum(); - JArrayType arrayType = type.isArray(); - - if (customSerializer != null) { - logger.log(Type.INFO, "Will serialize " + type + " using " - + customSerializer.getName()); - setSerializer(type, new CustomSerializer(customSerializer)); - } else if (arrayType != null) { - logger.log(Type.INFO, "Will serialize " + type + " as an array"); - setSerializer(type, new ArraySerializer(arrayType)); - setNeedsSerialize(arrayType.getComponentType()); - } else if (enumType != null) { - logger.log(Type.INFO, "Will serialize " + type + " as an enum"); - setSerializer(type, new EnumSerializer(enumType)); - } else if (typeAsClass != null) { - // Bean - checkSerializable(logger, typeAsClass); - - logger.log(Type.INFO, "Will serialize " + type + " as a bean"); - - JClassType needsSuperClass = typeAsClass; - while (needsSuperClass != null) { - if (needsSuperClass.isPublic()) { - setNeedsSuperclass(needsSuperClass); - } - needsSuperClass = needsSuperClass.getSuperclass(); - } - - setNeedsGwtConstructor(typeAsClass); - - for (Property property : getProperties(typeAsClass)) { - setNeedsProperty(property); - - JType propertyType = property.getPropertyType(); - setNeedsSerialize(propertyType); - } - } - } - - private void checkSerializable(TreeLogger logger, JClassType type) - throws UnableToCompleteException { - JClassType javaSerializable = type.getOracle().findType( - Serializable.class.getName()); - 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 void setSerializer(JType type, GeneratedSerializer serializer) { - if (!hasSerializer(type)) { - serializers.put(type, serializer); - } - } - - private boolean hasSerializer(JType type) { - if (serializers.containsKey(type)) { - return true; - } else { - return previousBundle != null && previousBundle.hasSerializer(type); - } - } - - public Map getSerializers() { - return Collections.unmodifiableMap(serializers); - } - - public void setPresentationType(JClassType type, JType presentationType) { - if (!hasPresentationType(type)) { - presentationTypes.put(type, presentationType); - } - } - - private boolean hasPresentationType(JClassType type) { - if (presentationTypes.containsKey(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.hasPresentationType(type); - } - } - - public Map getPresentationTypes() { - return Collections.unmodifiableMap(presentationTypes); - } - - private void setNeedsSuperclass(JClassType typeAsClass) { - if (!isNeedsSuperClass(typeAsClass)) { - needsSuperClass.add(typeAsClass); - } - } - - private boolean isNeedsSuperClass(JClassType typeAsClass) { - if (needsSuperClass.contains(typeAsClass)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsSuperClass(typeAsClass); - } - } - - public Set getNeedsSuperclass() { - return Collections.unmodifiableSet(needsSuperClass); - } - - private void setNeedsProperty(Property property) { - if (!isNeedsProperty(property)) { - needsProperty.add(property); - } - } - - private boolean isNeedsProperty(Property property) { - if (needsProperty.contains(property)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsProperty(property); - } - } - - public Set getNeedsProperty() { - return Collections.unmodifiableSet(needsProperty); - } - - public Collection getProperties(JClassType type) { - Set properties = new TreeSet(); - - properties.addAll(MethodProperty.findProperties(type)); - properties.addAll(FieldProperty.findProperties(type)); - - return properties; - } - - private void invokeVisitor(TreeLogger logger, JClassType type, - TypeVisitor typeVisitor) throws UnableToCompleteException { - TreeLogger subLogger = logger.branch(Type.TRACE, - "Visiting " + type.getName() + " with " - + typeVisitor.getClass().getSimpleName()); - if (isConnectedConnector(type)) { - typeVisitor.visitConnector(subLogger, type, this); - } - if (isClientRpc(type)) { - typeVisitor.visitClientRpc(subLogger, type, this); - } - if (isServerRpc(type)) { - typeVisitor.visitServerRpc(subLogger, type, this); - } - } - - public void processSubTypes(TreeLogger logger, JClassType type) - throws UnableToCompleteException { - processTypes(logger, Arrays.asList(type.getSubtypes())); - } - - public void setNeedsReturnType(JClassType type, JMethod method) { - if (!isNeedsReturnType(type, method)) { - addMapping(needsReturnType, type, method); - } - } - - private boolean isNeedsReturnType(JClassType type, JMethod method) { - if (hasMapping(needsReturnType, type, method)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsReturnType(type, method); - } - } - - public Map> getMethodReturnTypes() { - return Collections.unmodifiableMap(needsReturnType); - } - - private static boolean isClientRpc(JClassType type) { - return isInterfaceType(type, ClientRpc.class); - } - - private static boolean isServerRpc(JClassType type) { - return isInterfaceType(type, ServerRpc.class); - } - - public static boolean isConnectedConnector(JClassType type) { - return isConnected(type) && isType(type, ServerConnector.class); - } - - private static boolean isConnected(JClassType type) { - return type.isAnnotationPresent(Connect.class) - || type.getQualifiedSourceName().equals( - UnknownComponentConnector.class.getCanonicalName()); - } - - public static boolean isConnectedComponentConnector(JClassType type) { - return isConnected(type) && isType(type, ComponentConnector.class); - } - - public static boolean isConnectedRendererConnector(JClassType type) { - return isConnected(type) - && isType(type, AbstractRendererConnector.class); - } - - private static boolean isInterfaceType(JClassType type, Class class1) { - return type.isInterface() != null && isType(type, class1); - } - - private static boolean isType(JClassType type, Class class1) { - try { - return type.getOracle().getType(class1.getName()) - .isAssignableFrom(type); - } catch (NotFoundException e) { - throw new RuntimeException("Could not find " + class1.getName(), e); - } - } - - public void setNeedsInvoker(JClassType type, JMethod method) { - if (!isNeedsInvoker(type, method)) { - addMapping(needsInvoker, type, method); - } - } - - private void addMapping(Map> map, K key, String value) { - Set set = map.get(key); - if (set == null) { - set = new TreeSet(); - map.put(key, set); - } - set.add(value); - } - - private void addMapping(Map> map, K key, JMethod value) { - Set set = map.get(key); - if (set == null) { - set = new TreeSet(jMethodComparator); - map.put(key, set); - } - set.add(value); - } - - private boolean hasMapping(Map> map, K key, V value) { - return map.containsKey(key) && map.get(key).contains(value); - } - - private boolean isNeedsInvoker(JClassType type, JMethod method) { - if (hasMapping(needsInvoker, type, method)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsInvoker(type, method); - } - } - - public Map> getNeedsInvoker() { - return Collections.unmodifiableMap(needsInvoker); - } - - public void setNeedsParamTypes(JClassType type, JMethod method) { - if (!isNeedsParamTypes(type, method)) { - addMapping(needsParamTypes, type, method); - } - } - - private boolean isNeedsParamTypes(JClassType type, JMethod method) { - if (hasMapping(needsParamTypes, type, method)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsParamTypes(type, method); - } - } - - public Map> getNeedsParamTypes() { - return Collections.unmodifiableMap(needsParamTypes); - } - - public void setNeedsProxySupport(JClassType type) { - if (!isNeedsProxySupport(type)) { - needsProxySupport.add(type); - } - } - - private boolean isNeedsProxySupport(JClassType type) { - if (needsProxySupport.contains(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsProxySupport(type); - } - } - - public Set getNeedsProxySupport() { - return Collections.unmodifiableSet(needsProxySupport); - } - - public void setMethodAttribute(JClassType type, JMethod method, - MethodAttribute methodAttribute) { - if (!hasMethodAttribute(type, method, methodAttribute)) { - Map> typeData = methodAttributes - .get(type); - if (typeData == null) { - typeData = new TreeMap>( - jMethodComparator); - methodAttributes.put(type, typeData); - } - - Map> methods = methodAttributes - .get(type); - if (methods == null) { - methods = new TreeMap>( - jMethodComparator); - methodAttributes.put(type, methods); - } - - Set attributes = methods.get(method); - if (attributes == null) { - attributes = new TreeSet(); - methods.put(method, attributes); - } - - attributes.add(methodAttribute); - } - } - - private boolean hasMethodAttribute(JClassType type, JMethod method, - MethodAttribute methodAttribute) { - Map> typeData = methodAttributes - .get(type); - if (typeData != null && hasMapping(typeData, method, methodAttribute)) { - return true; - } else { - return previousBundle != null - && previousBundle.hasMethodAttribute(type, method, - methodAttribute); - } - } - - public Map>> getMethodAttributes() { - return Collections.unmodifiableMap(methodAttributes); - } - - public void setNeedsSerialize(JType type) { - if (!hasSerializeSupport(type)) { - needsSerializeSupport.add(type); - } - } - - private static Set> frameworkHandledTypes = new LinkedHashSet>(); - { - 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); - frameworkHandledTypes.add(Void.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; - } - - private boolean hasSerializeSupport(JType type) { - if (hasSerializeSupport.contains(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.hasSerializeSupport(type); - } - } - - public void setNeedsDelegateToWidget(Property property, JClassType type) { - if (!isNeedsDelegateToWidget(type)) { - TreeSet set = new TreeSet(); - set.add(property); - needsDelegateToWidget.put(type, set); - } else if (!needsDelegateToWidget.get(type).contains(property)) { - needsDelegateToWidget.get(type).add(property); - } - } - - private boolean isNeedsDelegateToWidget(JClassType type) { - if (needsDelegateToWidget.containsKey(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsDelegateToWidget(type); - } - } - - public Map> getNeedsDelegateToWidget() { - return Collections.unmodifiableMap(needsDelegateToWidget); - } - - public void setNeedsOnStateChangeHandler(JClassType type, JMethod method) { - if (!isNeedsOnStateChangeHandler(type, method)) { - addMapping(needsOnStateChange, type, method); - } - } - - private boolean isNeedsOnStateChangeHandler(JClassType type, JMethod method) { - if (hasMapping(needsOnStateChange, type, method)) { - return true; - } else { - return previousBundle != null - && previousBundle.isNeedsOnStateChangeHandler(type, method); - } - } - - public Map> getNeedsOnStateChangeHandler() { - return Collections.unmodifiableMap(needsOnStateChange); - } - - public static JMethod findInheritedMethod(JClassType type, - String methodName, JType... params) { - - JClassType currentType = type; - while (currentType != null) { - JMethod method = currentType.findMethod(methodName, params); - if (method != null) { - return method; - } - currentType = currentType.getSuperclass(); - } - - JClassType[] interfaces = type.getImplementedInterfaces(); - for (JClassType iface : interfaces) { - JMethod method = iface.findMethod(methodName, params); - if (method != null) { - return method; - } - } - - return null; - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java deleted file mode 100644 index ea3b097fa2..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -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.vaadin.shared.ui.Connect; - -public class ConnectorInitVisitor extends TypeVisitor { - - @Override - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - Connect connectAnnotation = type.getAnnotation(Connect.class); - if (connectAnnotation != null) { - logger.log(Type.INFO, type.getName() + " will be in the " - + bundle.getName().replaceAll("^_*", "") + " bundle"); - String identifier = connectAnnotation.value().getCanonicalName(); - - bundle.setIdentifier(type, identifier); - bundle.setNeedsGwtConstructor(type); - } - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java deleted file mode 100644 index bb3dd4f61d..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory; - -public class CustomSerializer implements GeneratedSerializer { - - private final JClassType serializerType; - - public CustomSerializer(JClassType serializerType) { - this.serializerType = serializerType; - } - - @Override - public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) - throws UnableToCompleteException { - w.print("return "); - w.print(GWT.class.getCanonicalName()); - w.print(".create("); - ConnectorBundleLoaderFactory.writeClassLiteral(w, serializerType); - w.println(");"); - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java deleted file mode 100644 index 9876baf946..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.JEnumConstant; -import com.google.gwt.core.ext.typeinfo.JEnumType; -import com.google.gwt.user.rebind.SourceWriter; - -import elemental.json.Json; - -public class EnumSerializer extends JsonSerializer { - - private final JEnumType enumType; - - public EnumSerializer(JEnumType type) { - super(type); - enumType = type; - } - - @Override - protected void printDeserializerBody(TreeLogger logger, SourceWriter w, - String type, String jsonValue, String connection) { - w.println("String enumIdentifier = " + jsonValue + ".asString();"); - for (JEnumConstant e : enumType.getEnumConstants()) { - w.println("if (\"" + e.getName() + "\".equals(enumIdentifier)) {"); - w.indent(); - w.println("return " + enumType.getQualifiedSourceName() + "." - + e.getName() + ";"); - w.outdent(); - w.println("}"); - } - w.println("return null;"); - } - - @Override - protected void printSerializerBody(TreeLogger logger, SourceWriter w, - String value, String applicationConnection) { - // return Json.create(castedValue.name()); - w.println("return " + Json.class.getName() + ".create(" + value - + ".name());"); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java deleted file mode 100644 index a31dafe05c..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JField; -import com.google.gwt.user.rebind.SourceWriter; - -public class FieldProperty extends Property { - - private final JField field; - - private FieldProperty(JClassType beanType, JField field) { - super(field.getName(), beanType, field.getType()); - this.field = field; - } - - @Override - public boolean hasAccessorMethods() { - return true; - } - - @Override - public void writeSetterBody(TreeLogger logger, SourceWriter w, - String beanVariable, String valueVariable) { - w.println("%s.@%s::%s = %s;", beanVariable, getBeanType() - .getQualifiedSourceName(), getName(), unboxValue(valueVariable)); - } - - @Override - public void writeGetterBody(TreeLogger logger, SourceWriter w, - String beanVariable) { - String value = String.format("%s.@%s::%s", beanVariable, getBeanType() - .getQualifiedSourceName(), getName()); - w.print("return "); - w.print(boxValue(value)); - w.println(";"); - } - - public static Collection findProperties(JClassType type) { - Collection properties = new ArrayList(); - - List fields = getPublicFields(type); - for (JField field : fields) { - properties.add(new FieldProperty(field.getEnclosingType(), field)); - } - - return properties; - } - - private static List getPublicFields(JClassType type) { - Set names = new HashSet(); - ArrayList fields = new ArrayList(); - for (JClassType subType : type.getFlattenedSupertypeHierarchy()) { - JField[] subFields = subType.getFields(); - for (JField field : subFields) { - if (field.isPublic() && !field.isStatic() - && names.add(field.getName())) { - fields.add(field); - } - } - } - return fields; - } - - @Override - public T getAnnotation(Class annotationClass) { - return field.getAnnotation(annotationClass); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java deleted file mode 100644 index 6afb172ea2..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.user.rebind.SourceWriter; - -public interface GeneratedSerializer { - public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) - throws UnableToCompleteException; -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java deleted file mode 100644 index a7a6c568da..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.user.rebind.SourceWriter; -import com.vaadin.client.ApplicationConnection; -import com.vaadin.client.communication.JSONSerializer; -import elemental.json.JsonValue; - -public abstract class JsonSerializer implements GeneratedSerializer { - - private final JType type; - - public JsonSerializer(JType type) { - this.type = type; - } - - @Override - public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) - throws UnableToCompleteException { - - w.print("return new "); - w.print(JSONSerializer.class.getCanonicalName()); - w.print("<"); - w.print(type.getQualifiedSourceName()); - w.println(">() {"); - w.indent(); - - writeSerializerBody(logger, w); - - w.outdent(); - w.println("};"); - } - - protected void writeSerializerBody(TreeLogger logger, SourceWriter w) { - String qualifiedSourceName = type.getQualifiedSourceName(); - w.println("public " + JsonValue.class.getName() + " serialize(" - + qualifiedSourceName + " value, " - + ApplicationConnection.class.getName() + " connection) {"); - w.indent(); - // MouseEventDetails castedValue = (MouseEventDetails) value; - w.println(qualifiedSourceName + " castedValue = (" - + qualifiedSourceName + ") value;"); - - printSerializerBody(logger, w, "castedValue", "connection"); - - // End of serializer method - w.outdent(); - w.println("}"); - - // Deserializer - // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection - // connection); - w.println("public " + qualifiedSourceName + " deserialize(Type type, " - + JsonValue.class.getName() + " jsonValue, " - + ApplicationConnection.class.getName() + " connection) {"); - w.indent(); - - printDeserializerBody(logger, w, "type", "jsonValue", "connection"); - - w.outdent(); - w.println("}"); - } - - protected abstract void printDeserializerBody(TreeLogger logger, - SourceWriter w, String type, String jsonValue, String connection); - - protected abstract void printSerializerBody(TreeLogger logger, - SourceWriter w, String value, String applicationConnection); - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java deleted file mode 100644 index 32aad92774..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.google.gwt.core.ext.TreeLogger; -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.user.rebind.SourceWriter; - -public class MethodProperty extends Property { - - private final JMethod setter; - - private final String getter; - - private MethodProperty(JClassType beanType, JMethod setter, String getter) { - super(getTransportFieldName(setter), beanType, setter - .getParameterTypes()[0]); - this.setter = setter; - this.getter = getter; - } - - @Override - public boolean hasAccessorMethods() { - return getter != null; - } - - public static Collection findProperties(JClassType type) { - Collection properties = new ArrayList(); - - Set getters = new HashSet(); - List setters = getSetters(type, getters); - for (JMethod setter : setters) { - String getter = findGetter(type, setter); - properties.add(new MethodProperty(setter.getEnclosingType(), - setter, getters.contains(getter) ? getter : null)); - } - - return properties; - } - - /** - * Returns a list of all setters found in the beanType or its parent class - * - * @param beanType - * The type to check - * @param getters - * Set that will be filled with names of getters. - * @return A list of setter methods from the class and its parents - */ - private static List getSetters(JClassType beanType, - Set getters) { - 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()) { - // Not getter/setter, skip to next method - continue; - } - String methodName = method.getName(); - if (methodName.startsWith("set") - && method.getParameterTypes().length == 1) { - setterMethods.add(method); - } else if (method.getParameterTypes().length == 0 - && methodName.startsWith("is") - || methodName.startsWith("get")) { - getters.add(methodName); - } - } - beanType = beanType.getSuperclass(); - } - - return setterMethods; - } - - @Override - public void writeGetterBody(TreeLogger logger, SourceWriter w, - String beanVariable) { - String value = String.format("%s.@%s::%s()()", beanVariable, - getBeanType().getQualifiedSourceName(), getter); - w.print("return "); - w.print(boxValue(value)); - w.println(";"); - } - - @Override - public void writeSetterBody(TreeLogger logger, SourceWriter w, - String beanVariable, String valueVariable) { - w.println("%s.@%s::%s(%s)(%s);", beanVariable, getBeanType() - .getQualifiedSourceName(), setter.getName(), setter - .getParameterTypes()[0].getJNISignature(), - unboxValue(valueVariable)); - - } - - private static 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; - } - } - - private static String getTransportFieldName(JMethod setter) { - String baseName = setter.getName().substring(3); - return Character.toLowerCase(baseName.charAt(0)) - + baseName.substring(1); - } - - @Override - public T getAnnotation(Class annotationClass) { - return setter.getAnnotation(annotationClass); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java deleted file mode 100644 index 1c0da9d9e8..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -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.vaadin.client.annotations.OnStateChange; -import com.vaadin.shared.ui.Connect; - -/** - * Visits Connector classes and check for methods with @OnStateChange - * annotations. - * - * @since 7.2 - * @author Vaadin Ltd - */ -public class OnStateChangeVisitor extends TypeVisitor { - - @Override - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - Connect connectAnnotation = type.getAnnotation(Connect.class); - if (connectAnnotation != null) { - // Find all the annotated methods in all the superclasses - JClassType connector = type; - while (connector != null) { - for (JMethod method : connector.getMethods()) { - if (method.getAnnotation(OnStateChange.class) != null) { - bundle.setNeedsInvoker(connector, method); - bundle.setNeedsOnStateChangeHandler(type, method); - } - } - - connector = connector.getSuperclass(); - } - } - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/Property.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/Property.java deleted file mode 100644 index 0c849bead5..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/Property.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import java.lang.annotation.Annotation; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JPrimitiveType; -import com.google.gwt.core.ext.typeinfo.JType; -import com.google.gwt.user.rebind.SourceWriter; - -public abstract class Property implements Comparable { - private final String name; - private final JClassType beanType; - private final JType propertyType; - - protected Property(String name, JClassType beanType, JType propertyType) { - this.name = name; - this.beanType = beanType; - this.propertyType = propertyType; - } - - public String getName() { - return name; - } - - public JType getPropertyType() { - return propertyType; - } - - public String getUnboxedPropertyTypeName() { - JType propertyType = getPropertyType(); - JPrimitiveType primitive = propertyType.isPrimitive(); - if (primitive != null) { - return primitive.getQualifiedBoxedSourceName(); - } else { - return propertyType.getQualifiedSourceName(); - } - } - - public String boxValue(String codeSnippet) { - JPrimitiveType primitive = propertyType.isPrimitive(); - if (primitive == null) { - return codeSnippet; - } else { - return String.format("@%s::valueOf(%s)(%s)", - primitive.getQualifiedBoxedSourceName(), - propertyType.getJNISignature(), codeSnippet); - } - } - - public String unboxValue(String codeSnippet) { - JPrimitiveType primitive = propertyType.isPrimitive(); - if (primitive == null) { - return codeSnippet; - } else { - return String.format("%s.@%s::%sValue()()", codeSnippet, - primitive.getQualifiedBoxedSourceName(), - primitive.getSimpleSourceName()); - } - } - - public JClassType getBeanType() { - return beanType; - } - - public abstract void writeSetterBody(TreeLogger logger, SourceWriter w, - String beanVariable, String valueVariable); - - public abstract void writeGetterBody(TreeLogger logger, SourceWriter w, - String beanVariable); - - public abstract boolean hasAccessorMethods(); - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else if (obj instanceof Property) { - Property other = (Property) obj; - return other.getClass() == getClass() - && other.getBeanType().equals(getBeanType()) - && other.getName().equals(getName()); - } else { - return false; - } - } - - @Override - public int hashCode() { - return getClass().hashCode() * 31 ^ 2 + getBeanType().hashCode() * 31 - + getName().hashCode(); - } - - @Override - public int compareTo(Property o) { - int comp = getName().compareTo(o.getName()); - if (comp == 0) { - comp = getBeanType().getQualifiedSourceName().compareTo( - o.getBeanType().getQualifiedSourceName()); - } - if (comp == 0) { - comp = getClass().getCanonicalName().compareTo( - o.getClass().getCanonicalName()); - } - return comp; - } - - public abstract T getAnnotation( - Class annotationClass); - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java deleted file mode 100644 index 2e54d00aab..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -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.vaadin.client.connectors.AbstractRendererConnector; - -/** - * Generates type data for renderer connectors. - *

    - *
  • Stores the return type of the overridden - * {@link AbstractRendererConnector#getRenderer() getRenderer} method to enable - * automatic creation of an instance of the proper renderer type. - *
  • Stores the presentation type of the connector to enable the - * {@link AbstractRendererConnector#decode(elemental.json.JsonValue) decode} - * method to work without having to implement a "getPresentationType" method. - *
- * - * @see WidgetInitVisitor - * - * @since 7.4 - * @author Vaadin Ltd - */ -public class RendererVisitor extends TypeVisitor { - - @Override - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - if (ConnectorBundle.isConnectedRendererConnector(type)) { - doRendererType(logger, type, bundle); - doPresentationType(logger, type, bundle); - } - } - - private static void doRendererType(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - // The class in which createRenderer is implemented - JClassType createRendererClass = ConnectorBundle.findInheritedMethod( - type, "createRenderer").getEnclosingType(); - - // Needs GWT constructor if createRenderer is not overridden - if (createRendererClass.getQualifiedSourceName().equals( - AbstractRendererConnector.class.getCanonicalName())) { - - JMethod getRenderer = ConnectorBundle.findInheritedMethod(type, - "getRenderer"); - if (getRenderer.getEnclosingType().getQualifiedSourceName() - .equals(AbstractRendererConnector.class.getCanonicalName())) { - logger.log(Type.ERROR, type.getQualifiedSourceName() - + " must override either createRenderer or getRenderer"); - throw new UnableToCompleteException(); - } - JClassType rendererType = getRenderer.getReturnType().isClass(); - - bundle.setNeedsGwtConstructor(rendererType); - - // Also needs renderer type to find the right GWT constructor - bundle.setNeedsReturnType(type, getRenderer); - - logger.log(Type.DEBUG, "Renderer type of " + type + " is " - + rendererType); - } - } - - private void doPresentationType(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - JType presentationType = getPresentationType(type, logger); - bundle.setPresentationType(type, presentationType); - - bundle.setNeedsSerialize(presentationType); - - logger.log(Type.DEBUG, "Presentation type of " + type + " is " - + presentationType); - } - - private static JType getPresentationType(JClassType type, TreeLogger logger) - throws UnableToCompleteException { - JClassType originalType = type; - while (type != null) { - if (type.getQualifiedBinaryName().equals( - AbstractRendererConnector.class.getName())) { - JParameterizedType parameterized = type.isParameterized(); - if (parameterized == null) { - logger.log( - Type.ERROR, - type.getQualifiedSourceName() - + " must define the generic parameter of the inherited " - + AbstractRendererConnector.class - .getSimpleName()); - throw new UnableToCompleteException(); - } - return parameterized.getTypeArgs()[0]; - } - type = type.getSuperclass(); - } - throw new IllegalArgumentException("The type " - + originalType.getQualifiedSourceName() + " does not extend " - + AbstractRendererConnector.class.getName()); - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java deleted file mode 100644 index 86ece28041..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server.widgetsetutils.metadata; - -import java.util.Set; - -import com.google.gwt.core.ext.TreeLogger; -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.vaadin.client.metadata.TypeDataStore.MethodAttribute; -import com.vaadin.shared.annotations.NoLoadingIndicator; -import com.vaadin.shared.annotations.Delayed; - -public class ServerRpcVisitor extends TypeVisitor { - @Override - public void visitServerRpc(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - ClientRpcVisitor.checkGenericType(logger, type); - bundle.setNeedsProxySupport(type); - - Set superTypes = type - .getFlattenedSupertypeHierarchy(); - for (JClassType subType : superTypes) { - if (subType.isInterface() != null) { - JMethod[] methods = subType.getMethods(); - for (JMethod method : methods) { - ClientRpcVisitor.checkReturnType(logger, method); - - Delayed delayed = method.getAnnotation(Delayed.class); - if (delayed != null) { - bundle.setMethodAttribute(type, method, - MethodAttribute.DELAYED); - if (delayed.lastOnly()) { - bundle.setMethodAttribute(type, method, - MethodAttribute.LAST_ONLY); - } - } - - if (method.getAnnotation(NoLoadingIndicator.class) != null) { - bundle.setMethodAttribute(type, method, - MethodAttribute.NO_LOADING_INDICATOR); - } - - bundle.setNeedsParamTypes(type, method); - - JType[] parameterTypes = method.getParameterTypes(); - for (JType paramType : parameterTypes) { - bundle.setNeedsSerialize(paramType); - } - } - } - } - } -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java deleted file mode 100644 index 046c5c4611..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.JMethod; -import com.google.gwt.core.ext.typeinfo.JType; - -public class StateInitVisitor extends TypeVisitor { - @Override - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) { - JMethod getState = ConnectorBundle - .findInheritedMethod(type, "getState"); - bundle.setNeedsReturnType(type, getState); - - bundle.setNeedsSerialize(getState.getReturnType()); - - JType stateType = getState.getReturnType(); - bundle.setNeedsGwtConstructor(stateType.isClass()); - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java deleted file mode 100644 index 028e4cc44d..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.typeinfo.JClassType; -import com.google.gwt.core.ext.typeinfo.NotFoundException; -import com.google.gwt.core.ext.typeinfo.TypeOracle; - -public abstract class TypeVisitor { - public void init(TypeOracle oracle) throws NotFoundException { - // Default does nothing - } - - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - // Default does nothing - } - - public void visitClientRpc(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - // Default does nothing - } - - public void visitServerRpc(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - // Default does nothing - } - -} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java deleted file mode 100644 index 9a9cac18ba..0000000000 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server.widgetsetutils.metadata; - -import java.util.Collection; - -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.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.shared.annotations.DelegateToWidget; - -public class WidgetInitVisitor extends TypeVisitor { - - @Override - public void visitConnector(TreeLogger logger, JClassType type, - ConnectorBundle bundle) throws UnableToCompleteException { - if (ConnectorBundle.isConnectedComponentConnector(type)) { - // The class in which createWidget is implemented - JClassType createWidgetClass = ConnectorBundle.findInheritedMethod( - type, "createWidget").getEnclosingType(); - - JMethod getWidget = ConnectorBundle.findInheritedMethod(type, - "getWidget"); - JClassType widgetType = getWidget.getReturnType().isClass(); - - // Needs GWT constructor if createWidget is not overridden - if (createWidgetClass.getQualifiedSourceName().equals( - AbstractComponentConnector.class.getCanonicalName())) { - if (getWidget - .getEnclosingType() - .getQualifiedSourceName() - .equals(AbstractComponentConnector.class - .getCanonicalName())) { - logger.log(Type.ERROR, type.getQualifiedSourceName() - + " must override either createWidget or getWidget"); - throw new UnableToCompleteException(); - } - - bundle.setNeedsGwtConstructor(widgetType); - - // Also needs widget type to find the right GWT constructor - bundle.setNeedsReturnType(type, getWidget); - } - - // Check state properties for @DelegateToWidget - JMethod getState = ConnectorBundle.findInheritedMethod(type, - "getState"); - JClassType stateType = getState.getReturnType().isClass(); - - Collection properties = bundle.getProperties(stateType); - for (Property property : properties) { - DelegateToWidget delegateToWidget = property - .getAnnotation(DelegateToWidget.class); - if (delegateToWidget != null) { - // Generate meta data required for @DelegateToWidget - bundle.setNeedsDelegateToWidget(property, stateType); - - // Find the delegate target method - String methodName = DelegateToWidget.Helper - .getDelegateTarget(property.getName(), - delegateToWidget.value()); - JMethod delegatedSetter = ConnectorBundle - .findInheritedMethod(widgetType, methodName, - property.getPropertyType()); - if (delegatedSetter == null) { - logger.log( - Type.ERROR, - widgetType.getName() - + "." - + methodName - + "(" - + property.getPropertyType() - .getSimpleSourceName() - + ") required by @DelegateToWidget for " - + stateType.getName() + "." - + property.getName() - + " can not be found."); - throw new UnableToCompleteException(); - } - bundle.setNeedsInvoker(widgetType, delegatedSetter); - - // GWT code needs widget type to find the target method - bundle.setNeedsReturnType(type, getWidget); - } - } - - } - } -} diff --git a/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java b/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java deleted file mode 100644 index aab7231258..0000000000 --- a/client-compiler/src/com/vaadin/tools/CvalAddonsChecker.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.tools; - -import static com.vaadin.tools.CvalChecker.LINE; -import static com.vaadin.tools.CvalChecker.computeMajorVersion; -import static com.vaadin.tools.CvalChecker.getErrorMessage; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; -import com.vaadin.tools.CvalChecker.CvalInfo; -import com.vaadin.tools.CvalChecker.CvalServer; -import com.vaadin.tools.CvalChecker.InvalidCvalException; -import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; - -/** - * This class is able to visit all MANIFEST.MF files present in the classpath, - * filter by name, and check if the user has a valid license. - * - * Manifest files should have a few attributes indicating the license type of - * the addon: - *
    - *
  • Implementation-Version: 4.x.x - *
  • AdVaaName: addon_name - *
  • AdVaaLicen: cval, agpl, empty - *
  • AdVaaPkg: package of the widgets in this addon - *
- * - * The class also have a method to check just one product. - * - * @since 7.3 - */ -public final class CvalAddonsChecker { - - // Manifest attributes - public static final String VAADIN_ADDON_LICENSE = "AdVaaLicen"; - public static final String VAADIN_ADDON_NAME = "AdVaaName"; - public static final String VAADIN_ADDON_WIDGETSET = "Vaadin-Widgetsets"; - public static final String VAADIN_ADDON_VERSION = "Implementation-Version"; - public static final String VAADIN_ADDON_TITLE = "Implementation-Title"; - - // License types - public static final String VAADIN_AGPL = "agpl"; - public static final String VAADIN_CVAL = "cval"; - - private CvalChecker cvalChecker = new CvalChecker(); - private String filterPattern; - - /** - * The constructor. - */ - public CvalAddonsChecker() { - setLicenseProvider(new CvalServer()); - setFilter(".*vaadin.*"); - } - - /** - * Visit all MANIFEST.MF files in the classpath validating licenses. - * - * Return a list of Cval licensed products in order to have enough info to - * generate nag messages in the UI. - */ - public List run() throws InvalidCvalException { - List ret = new ArrayList(); - try { - // Visit all MANIFEST in our classpath - Enumeration manifests = Thread.currentThread() - .getContextClassLoader() - .getResources(JarFile.MANIFEST_NAME); - while (manifests.hasMoreElements()) { - try { - URL url = manifests.nextElement(); - // Discard manifests whose name does not match the filter - // pattern - if (!url.getPath().matches(filterPattern)) { - continue; - } - InputStream is = url.openStream(); - // Should never happen, but we don't want a NPE here - if (is == null) { - continue; - } - // Read manifest attributes - Manifest manifest = new Manifest(is); - Attributes attribs = manifest.getMainAttributes(); - String license = attribs.getValue(VAADIN_ADDON_LICENSE); - String name = attribs.getValue(VAADIN_ADDON_NAME); - String vers = attribs.getValue(VAADIN_ADDON_VERSION) == null ? "" - : attribs.getValue(VAADIN_ADDON_VERSION); - String title = attribs.getValue(VAADIN_ADDON_TITLE) == null ? name - : attribs.getValue(VAADIN_ADDON_TITLE); - - String widgetsets = attribs - .getValue(VAADIN_ADDON_WIDGETSET) == null ? name - : attribs.getValue(VAADIN_ADDON_WIDGETSET); - - if (name == null || license == null) { - continue; - } - if (VAADIN_AGPL.equals(license)) { - // For agpl version we print an info message - printAgplLicense(title, vers); - } else if (VAADIN_CVAL.equals(license)) { - // We only check cval licensed products - CvalInfo info; - try { - info = cvalChecker.validateProduct(name, vers, - title); - printValidLicense(info, title, vers); - } catch (UnreachableCvalServerException e) { - info = CvalChecker.parseJson("{'product':{'name':'" - + name + "'}}"); - printServerUnreachable(title, vers); - } - for (String w : widgetsets.split("[, ]+")) { - ret.add(new CValUiInfo(title, String - .valueOf(computeMajorVersion(vers)), w, - info.getType())); - } - } - } catch (IOException ignored) { - } - } - } catch (IOException ignored) { - } - return ret; - } - - /** - * Set the filter regexp of .jar names which we have to consider. - * - * default is '.*touchkit.*' - */ - public CvalAddonsChecker setFilter(String regexp) { - filterPattern = regexp; - return this; - } - - /* - * Change the license provider, only used in tests. - */ - protected CvalAddonsChecker setLicenseProvider(CvalServer p) { - cvalChecker.setLicenseProvider(p); - return this; - } - - private void printAgplLicense(String name, String version) { - System.out.println(LINE + "\n" - + getErrorMessage("agpl", name, computeMajorVersion(version)) - + "\n" + LINE); - } - - private void printServerUnreachable(String name, String version) { - System.out.println(LINE - + "\n" - + getErrorMessage("unreachable", name, - computeMajorVersion(version)) + "\n" + LINE); - } - - private void printValidLicense(CvalInfo info, String title, String version) { - String msg = info.getMessage(); - if (msg == null) { - String key = "evaluation".equals(info.getType()) ? "evaluation" - : "valid"; - msg = getErrorMessage(key, title, computeMajorVersion(version), - info.getLicensee()); - } - System.out.println("\n" + LINE + "\n" + msg + "\n" + LINE + "\n"); - } -} diff --git a/client-compiler/src/com/vaadin/tools/CvalChecker.java b/client-compiler/src/com/vaadin/tools/CvalChecker.java deleted file mode 100644 index 9217781695..0000000000 --- a/client-compiler/src/com/vaadin/tools/CvalChecker.java +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.tools; - -import static java.lang.Integer.parseInt; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.ResourceBundle; -import java.util.prefs.Preferences; - -import org.apache.commons.io.IOUtils; - -import elemental.json.JsonException; -import elemental.json.JsonNull; -import elemental.json.JsonObject; -import elemental.json.impl.JsonUtil; - -/** - * This class is able to validate the vaadin CVAL license. - * - * It reads the developer license file and asks the server to validate the - * licenseKey. If the license is invalid it throws an exception with the - * information about the problem and the server response. - * - * @since 7.3 - */ -public final class CvalChecker { - - /* - * Class used for binding the JSON gotten from server. - * - * It is not in a separate f le, so as it is easier to copy into any product - * which does not depend on vaadin core. - * - * We are using elemental.json in order not to use additional dependency - * like auto-beans, gson, etc. - */ - public static class CvalInfo { - - public static class Product { - private JsonObject o; - - public Product(JsonObject o) { - this.o = o; - } - - public String getName() { - return get(o, "name", String.class); - } - - public Integer getVersion() { - return get(o, "version", Integer.class); - } - } - - @SuppressWarnings("unchecked") - private static T get(JsonObject o, String k, Class clz) { - Object ret = null; - try { - if (o == null || o.get(k) == null - || o.get(k) instanceof JsonNull) { - return null; - } - if (clz == String.class) { - ret = o.getString(k); - } else if (clz == JsonObject.class) { - ret = o.getObject(k); - } else if (clz == Integer.class) { - ret = Integer.valueOf((int) o.getNumber(k)); - } else if (clz == Date.class) { - ret = new Date((long) o.getNumber(k)); - } else if (clz == Boolean.class) { - ret = o.getBoolean(k); - } - } catch (JsonException e) { - } - return (T) ret; - } - - private JsonObject o; - - private Product product; - - public CvalInfo(JsonObject o) { - this.o = o; - product = new Product(get(o, "product", JsonObject.class)); - } - - public Boolean getExpired() { - return get(o, "expired", Boolean.class); - } - - public Date getExpiredEpoch() { - return get(o, "expiredEpoch", Date.class); - } - - public String getLicensee() { - return get(o, "licensee", String.class); - } - - public String getLicenseKey() { - return get(o, "licenseKey", String.class); - } - - public String getMessage() { - return get(o, "message", String.class); - } - - public Product getProduct() { - return product; - } - - public String getType() { - return get(o, "type", String.class); - } - - public void setExpiredEpoch(Date expiredEpoch) { - o.put("expiredEpoch", expiredEpoch.getTime()); - } - - public void setMessage(String msg) { - o.put("message", msg); - } - - @Override - public String toString() { - return o.toString(); - } - - public boolean isLicenseExpired() { - return (getExpired() != null && getExpired()) - || (getExpiredEpoch() != null && getExpiredEpoch().before( - new Date())); - } - - public boolean isValidVersion(int majorVersion) { - return getProduct().getVersion() == null - || getProduct().getVersion() >= majorVersion; - - } - - private boolean isValidInfo(String name, String key) { - return getProduct() != null && getProduct().getName() != null - && getLicenseKey() != null - && getProduct().getName().equals(name) - && getLicenseKey().equals(key); - } - } - - /* - * The class with the method for getting json from server side. It is here - * and protected just for replacing it in tests. - */ - public static class CvalServer { - protected String licenseUrl = LICENSE_URL_PROD; - - String askServer(String productName, String productKey, int timeoutMs) - throws IOException { - String url = licenseUrl + productKey; - URLConnection con; - try { - // Send some additional info in the User-Agent string. - String ua = "Cval " + productName + " " + productKey + " " - + getFirstLaunch(); - for (String prop : Arrays.asList("java.vendor.url", - "java.version", "os.name", "os.version", "os.arch")) { - ua += " " + System.getProperty(prop, "-").replace(" ", "_"); - } - con = new URL(url).openConnection(); - con.setRequestProperty("User-Agent", ua); - con.setConnectTimeout(timeoutMs); - con.setReadTimeout(timeoutMs); - String r = IOUtils.toString(con.getInputStream()); - return r; - } catch (MalformedURLException e) { - e.printStackTrace(); - return null; - } - } - - /* - * Get the GWT firstLaunch timestamp. - */ - String getFirstLaunch() { - try { - Class clz = Class - .forName("com.google.gwt.dev.shell.CheckForUpdates"); - return Preferences.userNodeForPackage(clz).get("firstLaunch", - "-"); - } catch (ClassNotFoundException e) { - return "-"; - } - } - } - - /** - * Exception thrown when the user does not have a valid cval license. - */ - public static class InvalidCvalException extends Exception { - private static final long serialVersionUID = 1L; - public final CvalInfo info; - public final String name; - public final String key; - public final String version; - public final String title; - - public InvalidCvalException(String name, String version, String title, - String key, CvalInfo info) { - super(composeMessage(title, version, key, info)); - this.info = info; - this.name = name; - this.key = key; - this.version = version; - this.title = title; - } - - static String composeMessage(String title, String version, String key, - CvalInfo info) { - String msg = ""; - int majorVers = computeMajorVersion(version); - - if (info != null && !info.isValidVersion(majorVers)) { - msg = getErrorMessage("invalid", title, majorVers); - } else if (info != null && info.getMessage() != null) { - msg = info.getMessage().replace("\\n", "\n"); - } else if (info != null && info.isLicenseExpired()) { - String type = "evaluation".equals(info.getType()) ? "Evaluation license" - : "License"; - msg = getErrorMessage("expired", title, majorVers, type); - } else if (key == null) { - msg = getErrorMessage("none", title, majorVers); - } else { - msg = getErrorMessage("invalid", title, majorVers); - } - return msg; - } - } - - /** - * Exception thrown when the license server is unreachable - */ - public static class UnreachableCvalServerException extends Exception { - private static final long serialVersionUID = 1L; - public final String name; - - public UnreachableCvalServerException(String name, Exception e) { - super(e); - this.name = name; - } - } - - public static final String LINE = "----------------------------------------------------------------------------------------------------------------------"; - - static final int GRACE_DAYS_MSECS = 2 * 24 * 60 * 60 * 1000; - - private static final String LICENSE_URL_PROD = "https://tools.vaadin.com/vaadin-license-server/licenses/"; - - /* - * used in tests - */ - static void cacheLicenseInfo(CvalInfo info) { - if (info != null) { - Preferences p = Preferences.userNodeForPackage(CvalInfo.class); - if (info.toString().length() > Preferences.MAX_VALUE_LENGTH) { - // This should never happen since MAX_VALUE_LENGTH is big - // enough. - // But server could eventually send a very big message, so we - // discard it in cache and would use hard-coded messages. - info.setMessage(null); - } - p.put(info.getProduct().getName(), info.toString()); - } - } - - /* - * used in tests - */ - static void deleteCache(String productName) { - Preferences p = Preferences.userNodeForPackage(CvalInfo.class); - p.remove(productName); - } - - /** - * Given a product name returns the name of the file with the license key. - * - * Traditionally we have delivered license keys with a name like - * 'vaadin.touchkit.developer.license' but our database product name is - * 'vaadin-touchkit' so we have to replace '-' by '.' to maintain - * compatibility. - */ - static final String computeLicenseName(String productName) { - return productName.replace("-", ".") + ".developer.license"; - } - - static final int computeMajorVersion(String productVersion) { - return productVersion == null || productVersion.isEmpty() ? 0 - : parseInt(productVersion.replaceFirst("[^\\d]+.*$", "")); - } - - /* - * used in tests - */ - static CvalInfo parseJson(String json) { - if (json == null) { - return null; - } - try { - JsonObject o = JsonUtil.parse(json); - return new CvalInfo(o); - } catch (JsonException e) { - return null; - } - } - - private CvalServer provider; - - /** - * The constructor. - */ - public CvalChecker() { - setLicenseProvider(new CvalServer()); - } - - /** - * Validate whether there is a valid license key for a product. - * - * @param productName - * for example vaadin-touchkit - * @param productVersion - * for instance 4.0.1 - * @return CvalInfo Server response or cache response if server is offline - * @throws InvalidCvalException - * when there is no a valid license for the product - * @throws UnreachableCvalServerException - * when we have license key but server is unreachable - */ - public CvalInfo validateProduct(String productName, String productVersion, - String productTitle) throws InvalidCvalException, - UnreachableCvalServerException { - String key = getDeveloperLicenseKey(productName, productVersion, - productTitle); - - CvalInfo info = null; - if (key != null && !key.isEmpty()) { - info = getCachedLicenseInfo(productName); - if (info != null && !info.isValidInfo(productName, key)) { - deleteCache(productName); - info = null; - } - info = askLicenseServer(productName, key, productVersion, info); - if (info != null && info.isValidInfo(productName, key) - && info.isValidVersion(computeMajorVersion(productVersion)) - && !info.isLicenseExpired()) { - return info; - } - } - - throw new InvalidCvalException(productName, productVersion, - productTitle, key, info); - } - - /* - * Change the license provider, only used in tests. - */ - final CvalChecker setLicenseProvider(CvalServer p) { - provider = p; - return this; - } - - private CvalInfo askLicenseServer(String productName, String productKey, - String productVersion, CvalInfo info) - throws UnreachableCvalServerException { - - int majorVersion = computeMajorVersion(productVersion); - - // If we have a valid license info here, it means that we got it from - // cache. - // We add a grace time when so as if the server is unreachable - // we allow the user to use the product. - if (info != null && info.getExpiredEpoch() != null - && !"evaluation".equals(info.getType())) { - long ts = info.getExpiredEpoch().getTime() + GRACE_DAYS_MSECS; - info.setExpiredEpoch(new Date(ts)); - } - - boolean validCache = info != null - && info.isValidInfo(productName, productKey) - && info.isValidVersion(majorVersion) - && !info.isLicenseExpired(); - - // if we have a validCache we set the timeout smaller - int timeout = validCache ? 2000 : 10000; - - try { - CvalInfo srvinfo = parseJson(provider.askServer(productName + "-" - + productVersion, productKey, timeout)); - if (srvinfo != null && srvinfo.isValidInfo(productName, productKey) - && srvinfo.isValidVersion(majorVersion)) { - // We always cache the info if it is valid although it is - // expired - cacheLicenseInfo(srvinfo); - info = srvinfo; - } - } catch (FileNotFoundException e) { - // 404 - return null; - } catch (Exception e) { - if (info == null) { - throw new UnreachableCvalServerException(productName, e); - } - } - return info; - } - - private CvalInfo getCachedLicenseInfo(String productName) { - Preferences p = Preferences.userNodeForPackage(CvalInfo.class); - String json = p.get(productName, ""); - if (!json.isEmpty()) { - CvalInfo info = parseJson(json); - if (info != null) { - return info; - } - } - return null; - } - - private String getDeveloperLicenseKey(String productName, - String productVersion, String productTitle) - throws InvalidCvalException { - String licenseName = computeLicenseName(productName); - - String key = System.getProperty(licenseName); - if (key != null && !key.isEmpty()) { - return key; - } - - try { - String dotLicenseName = "." + licenseName; - String userHome = System.getProperty("user.home"); - for (URL url : new URL[] { - new File(userHome, dotLicenseName).toURI().toURL(), - new File(userHome, licenseName).toURI().toURL(), - URL.class.getResource("/" + dotLicenseName), - URL.class.getResource("/" + licenseName) }) { - if (url != null) { - try { - key = readKeyFromFile(url, - computeMajorVersion(productVersion)); - if (key != null && !(key = key.trim()).isEmpty()) { - return key; - } - } catch (IOException ignored) { - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - throw new InvalidCvalException(productName, productVersion, - productTitle, null, null); - } - - String readKeyFromFile(URL url, int majorVersion) throws IOException { - String majorVersionStr = String.valueOf(majorVersion); - List lines = IOUtils.readLines(url.openStream()); - String defaultKey = null; - for (String line : lines) { - String[] parts = line.split("\\s*=\\s*"); - if (parts.length < 2) { - defaultKey = parts[0].trim(); - } - if (parts[0].equals(majorVersionStr)) { - return parts[1].trim(); - } - } - return defaultKey; - } - - static String getErrorMessage(String key, Object... pars) { - Locale loc = Locale.getDefault(); - ResourceBundle res = ResourceBundle.getBundle( - CvalChecker.class.getName(), loc); - String msg = res.getString(key); - return new MessageFormat(msg, loc).format(pars); - } -} diff --git a/client-compiler/src/com/vaadin/tools/CvalChecker.properties b/client-compiler/src/com/vaadin/tools/CvalChecker.properties deleted file mode 100644 index 3f4fd52cb7..0000000000 --- a/client-compiler/src/com/vaadin/tools/CvalChecker.properties +++ /dev/null @@ -1,13 +0,0 @@ -expired={2} for {0} {1} has expired. Get a valid license at vaadin.com/pro - -none=License for {0} {1} not found. Go to vaadin.com/pro for more details. - -invalid=License for {0} {1} is not valid. Get a valid license from vaadin.com/pro - -unreachable=License for {0} {1} has not been validated. Check your network connection. - -evaluation= > Using an evaluation license for {0} {1}. - -valid= > Using a valid license for {0} {1}. - -agpl=Using AGPL version of {0} {1}. Commercial licensing options available at vaadin.com/pro \ No newline at end of file diff --git a/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java b/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java deleted file mode 100755 index 7c06e9d7af..0000000000 --- a/client-compiler/src/com/vaadin/tools/WidgetsetCompiler.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.tools; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.server.widgetsetutils.WidgetSetBuilder; - -/** - * A wrapper for the GWT compiler that runs the compiler in a new thread after - * updating the widgetset file. - * - * This class originally existed to allow circumventing a J2SE 5.0 bug (6316197) - * that prevents setting the stack size for the main thread. - * - * This class takes the same command line arguments as the - * com.google.gwt.dev.Compiler class. - * - * A typical invocation would use e.g. the following arguments - * - * "-war WebContent/VAADIN/widgetsets com.vaadin.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 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"); - com.google.gwt.dev.Compiler.main(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-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java b/client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java new file mode 100644 index 0000000000..dda6733384 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java @@ -0,0 +1,233 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.sass.linker; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.w3c.css.sac.CSSException; + +import com.google.gwt.core.ext.LinkerContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.linker.AbstractLinker; +import com.google.gwt.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.EmittedArtifact; +import com.google.gwt.core.ext.linker.LinkerOrder; +import com.google.gwt.core.ext.linker.LinkerOrder.Order; +import com.google.gwt.core.ext.linker.Shardable; +import com.vaadin.sass.internal.ScssStylesheet; + +/** + * Pre-linker that checks for the existence of SASS files in public folders, + * compiles them to CSS files with the SassCompiler from Vaadin and adds the CSS + * back into the artifact. + * + */ +@LinkerOrder(Order.PRE) +@Shardable +public class SassLinker extends AbstractLinker { + + @Override + public String getDescription() { + return "Compiling SCSS files in public folders to standard CSS"; + } + + @Override + public ArtifactSet link(TreeLogger logger, LinkerContext context, + ArtifactSet artifacts, boolean onePermutation) + throws UnableToCompleteException { + + if (!onePermutation) { + // The artifact to return + ArtifactSet toReturn = new ArtifactSet(artifacts); + + // The temporary scss files provided from the artefacts + List scssFiles = new ArrayList(); + + // The public files are provided as inputstream, but the compiler + // needs real files, as they can contain references to other + // files. They will be stored here, with their relative paths intact + String tempFolderName = new Date().getTime() + File.separator; + File tempFolder = createTempDir(tempFolderName); + + // Can't search here specifically for public resources, as the type + // is different during compilation. This means we have to loop + // through all the artifacts + for (EmittedArtifact resource : artifacts + .find(EmittedArtifact.class)) { + + // Create the temporary files. + String partialPath = resource.getPartialPath(); + if (partialPath.endsWith(".scss")) { + // In my opinion, the SCSS file does not need to be + // output to the web content folder, as they can't + // be used there + toReturn.remove(resource); + + String fileName = partialPath; + File path = tempFolder; + + int separatorIndex = fileName.lastIndexOf(File.separator); + if (-1 != separatorIndex) { + fileName = fileName.substring(separatorIndex + 1); + + String filePath = partialPath.substring(0, + separatorIndex); + path = createTempDir(tempFolderName + filePath); + } + + File tempfile = new File(path, fileName); + try { + boolean fileCreated = tempfile.createNewFile(); + if (fileCreated) { + + // write the received inputstream to the temp file + writeFromInputStream(resource.getContents(logger), + tempfile); + + // Store the file info for the compilation + scssFiles.add(new FileInfo(tempfile, partialPath)); + } else { + logger.log(TreeLogger.WARN, "Duplicate file " + + tempfile.getPath()); + } + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Could not write temporary file " + fileName, e); + } + } + } + + // Compile the files and store them in the artifact + logger.log(TreeLogger.INFO, "Processing " + scssFiles.size() + + " Sass file(s)"); + for (FileInfo fileInfo : scssFiles) { + logger.log(TreeLogger.INFO, " " + fileInfo.originalScssPath + + " -> " + fileInfo.getOriginalCssPath()); + + try { + ScssStylesheet scss = ScssStylesheet.get(fileInfo + .getAbsolutePath()); + if (!fileInfo.isMixin()) { + scss.compile(); + InputStream is = new ByteArrayInputStream(scss + .printState().getBytes()); + + toReturn.add(this.emitInputStream(logger, is, + fileInfo.getOriginalCssPath())); + } + + fileInfo.getFile().delete(); + } catch (CSSException e) { + logger.log(TreeLogger.ERROR, "SCSS compilation failed for " + + fileInfo.getOriginalCssPath(), e); + } catch (IOException e) { + logger.log( + TreeLogger.ERROR, + "Could not write CSS file for " + + fileInfo.getOriginalCssPath(), e); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, "SCSS compilation failed for " + + fileInfo.getOriginalCssPath(), e); + } + } + + return toReturn; + } + + return artifacts; + } + + /** + * Writes the contents of an InputStream out to a file. + * + * @param contents + * @param tempfile + * @throws IOException + */ + private void writeFromInputStream(InputStream contents, File tempfile) + throws IOException { + // write the inputStream to a FileOutputStream + OutputStream out = new FileOutputStream(tempfile); + + int read = 0; + byte[] bytes = new byte[1024]; + + while ((read = contents.read(bytes)) != -1) { + out.write(bytes, 0, read); + } + + contents.close(); + out.flush(); + out.close(); + } + + /** + * Create folder in temporary space on disk. + * + * @param partialPath + * @return + */ + private File createTempDir(String partialPath) { + String baseTempPath = System.getProperty("java.io.tmpdir"); + + File tempDir = new File(baseTempPath + File.separator + partialPath); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + + tempDir.deleteOnExit(); + return tempDir; + } + + /** + * Temporal storage for file info from Artifact. + */ + private class FileInfo { + private String originalScssPath; + private File file; + + public FileInfo(File file, String originalScssPath) { + this.file = file; + this.originalScssPath = originalScssPath; + } + + public boolean isMixin() { + return file.getName().startsWith("_"); + } + + public String getAbsolutePath() { + return file.getAbsolutePath(); + } + + public String getOriginalCssPath() { + return originalScssPath.substring(0, originalScssPath.length() - 5) + + ".css"; + } + + public File getFile() { + return file; + } + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java new file mode 100644 index 0000000000..2d08329e9a --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/AcceptCriteriaFactoryGenerator.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils; + +import java.io.PrintWriter; +import java.util.Arrays; +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.client.ui.dd.VAcceptCriterion; +import com.vaadin.client.ui.dd.VAcceptCriterionFactory; +import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; +import com.vaadin.shared.ui.dd.AcceptCriterion; + +/** + * 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.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()); + JClassType[] subtypes = criteriaType.getSubtypes(); + Arrays.sort(subtypes, ConnectorBundle.jClassComparator); + for (JClassType clientClass : subtypes) { + 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/main/java/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java new file mode 100644 index 0000000000..2b8ccc87d0 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java @@ -0,0 +1,1306 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.RunAsyncCallback; +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.JPrimitiveType; +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.client.JsArrayObject; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.communication.JsonDecoder; +import com.vaadin.client.metadata.ConnectorBundleLoader; +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; +import com.vaadin.client.metadata.InvokationHandler; +import com.vaadin.client.metadata.OnStateChangeMethod; +import com.vaadin.client.metadata.ProxyHandler; +import com.vaadin.client.metadata.TypeData; +import com.vaadin.client.metadata.TypeDataStore; +import com.vaadin.client.metadata.TypeDataStore.MethodAttribute; +import com.vaadin.client.ui.UnknownComponentConnector; +import com.vaadin.server.widgetsetutils.metadata.ClientRpcVisitor; +import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle; +import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor; +import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer; +import com.vaadin.server.widgetsetutils.metadata.OnStateChangeVisitor; +import com.vaadin.server.widgetsetutils.metadata.Property; +import com.vaadin.server.widgetsetutils.metadata.RendererVisitor; +import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor; +import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor; +import com.vaadin.server.widgetsetutils.metadata.TypeVisitor; +import com.vaadin.server.widgetsetutils.metadata.WidgetInitVisitor; +import com.vaadin.shared.annotations.DelegateToWidget; +import com.vaadin.shared.annotations.NoLayout; +import com.vaadin.shared.communication.ClientRpc; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.tools.CvalAddonsChecker; +import com.vaadin.tools.CvalChecker; +import com.vaadin.tools.CvalChecker.InvalidCvalException; + +public class ConnectorBundleLoaderFactory extends Generator { + /** + * Special SourceWriter that approximates the number of written bytes to + * support splitting long methods into shorter chunks to avoid hitting the + * 65535 byte limit. + */ + private class SplittingSourceWriter implements SourceWriter { + private final SourceWriter target; + private final String baseName; + private final int splitSize; + private final List methodNames; + + // Seems to be undercounted by about 15% + private int approximateChars = 0; + private int wrapCount = 0; + + public SplittingSourceWriter(SourceWriter target, String baseName, + int splitSize) { + this.target = target; + this.baseName = baseName; + this.splitSize = splitSize; + methodNames = new ArrayList(); + methodNames.add(baseName); + } + + @Override + public void beginJavaDocComment() { + target.beginJavaDocComment(); + addChars(10); + } + + private void addChars(int i) { + approximateChars += i; + } + + private void addChars(String s) { + addChars(s.length()); + } + + private void addChars(String s, Object[] args) { + addChars(String.format(s, args)); + } + + @Override + public void commit(TreeLogger logger) { + target.commit(logger); + } + + @Override + public void endJavaDocComment() { + target.endJavaDocComment(); + addChars(10); + } + + @Override + public void indent() { + target.indent(); + addChars(10); + } + + @Override + public void indentln(String s) { + target.indentln(s); + addChars(s); + } + + @Override + public void indentln(String s, Object... args) { + target.indentln(s, args); + addChars(s, args); + } + + @Override + public void outdent() { + target.outdent(); + } + + @Override + public void print(String s) { + target.print(s); + addChars(s); + } + + @Override + public void print(String s, Object... args) { + target.print(s, args); + addChars(s, args); + } + + @Override + public void println() { + target.println(); + addChars(5); + } + + @Override + public void println(String s) { + target.println(s); + addChars(s); + } + + @Override + public void println(String s, Object... args) { + target.println(s, args); + addChars(s, args); + } + + public void splitIfNeeded() { + splitIfNeeded(false, null); + } + + public void splitIfNeeded(boolean isNative, String params) { + if (approximateChars > splitSize) { + String newMethod = baseName + wrapCount++; + String args = params == null ? "" : params; + if (isNative) { + outdent(); + println("}-*/;"); + // To support fields of type long (#13692) + println("@com.google.gwt.core.client.UnsafeNativeLong"); + println("private native void %s(%s) /*-{", newMethod, args); + } else { + println("%s();", newMethod); + outdent(); + println("}"); + println("private void %s(%s) {", newMethod, args); + } + methodNames.add(newMethod); + indent(); + + approximateChars = 0; + } + } + + public List getMethodNames() { + return Collections.unmodifiableList(methodNames); + } + + } + + private CvalAddonsChecker cvalChecker = new CvalAddonsChecker(); + + @Override + public String generate(TreeLogger logger, GeneratorContext context, + String typeName) throws UnableToCompleteException { + TypeOracle typeOracle = context.getTypeOracle(); + + try { + JClassType classType = typeOracle.getType(typeName); + String packageName = classType.getPackage().getName(); + String className = classType.getSimpleSourceName() + "Impl"; + + generateClass(logger, context, packageName, className, typeName); + + return packageName + "." + className; + } catch (UnableToCompleteException e) { + // Just rethrow + throw e; + } catch (Exception e) { + logger.log(Type.ERROR, getClass() + " failed", e); + throw new UnableToCompleteException(); + } + } + + private void generateClass(TreeLogger logger, GeneratorContext context, + String packageName, String className, String requestedType) + throws Exception { + PrintWriter printWriter = context.tryCreate(logger, packageName, + className); + if (printWriter == null) { + return; + } + + List cvalInfos = null; + try { + if (cvalChecker != null) { + cvalInfos = cvalChecker.run(); + // Don't run twice + cvalChecker = null; + } + } catch (InvalidCvalException e) { + System.err.println("\n\n\n\n" + CvalChecker.LINE); + for (String line : e.getMessage().split("\n")) { + System.err.println(line); + } + System.err.println(CvalChecker.LINE + "\n\n\n\n"); + System.exit(1); + throw new UnableToCompleteException(); + } + + List bundles = buildBundles(logger, + context.getTypeOracle()); + + ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( + packageName, className); + composer.setSuperclass(requestedType); + + SourceWriter w = composer.createSourceWriter(context, printWriter); + + w.println("public void init() {"); + w.indent(); + + for (ConnectorBundle bundle : bundles) { + detectBadProperties(bundle, logger); + + String name = bundle.getName(); + boolean isEager = name + .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME); + + w.print("addAsyncBlockLoader(new AsyncBundleLoader(\""); + w.print(escape(name)); + w.print("\", "); + + w.print("new String[] {"); + for (Entry> entry : bundle.getIdentifiers() + .entrySet()) { + Set identifiers = entry.getValue(); + for (String id : identifiers) { + w.print("\""); + w.print(escape(id)); + w.print("\","); + } + } + w.println("}) {"); + w.indent(); + + w.print("protected void load(final "); + w.print(TypeDataStore.class.getName()); + w.println(" store) {"); + w.indent(); + + if (!isEager) { + w.print(GWT.class.getName()); + w.print(".runAsync("); + } + + w.println("new %s() {", RunAsyncCallback.class.getName()); + w.indent(); + + w.println("public void onSuccess() {"); + w.indent(); + + w.println("load();"); + w.println("%s.get().setLoaded(getName());", + ConnectorBundleLoader.class.getName()); + + // Close onSuccess method + w.outdent(); + w.println("}"); + + w.println("private void load() {"); + w.indent(); + + String loadNativeJsBundle = "loadJsBundle"; + printBundleData(logger, w, bundle, loadNativeJsBundle); + + // Close load method + w.outdent(); + w.println("}"); + + // Separate method for loading native JS stuff (e.g. callbacks) + String loadNativeJsMethodName = "loadNativeJs"; + // To support fields of type long (#13692) + w.println("@com.google.gwt.core.client.UnsafeNativeLong"); + w.println("private native void %s(%s store) /*-{", + loadNativeJsMethodName, TypeDataStore.class.getName()); + w.indent(); + List jsMethodNames = printJsBundleData(logger, w, bundle, + loadNativeJsMethodName); + + w.outdent(); + w.println("}-*/;"); + + // Call all generated native method inside one Java method to avoid + // refercences inside native methods to each other + w.println("private void %s(%s store) {", loadNativeJsBundle, + TypeDataStore.class.getName()); + w.indent(); + printLoadJsBundleData(w, loadNativeJsBundle, jsMethodNames); + w.outdent(); + w.println("}"); + + // onFailure method declaration starts + w.println("public void onFailure(Throwable reason) {"); + w.indent(); + + w.println("%s.get().setLoadFailure(getName(), reason);", + ConnectorBundleLoader.class.getName()); + + w.outdent(); + w.println("}"); + + // Close new RunAsyncCallback() {} + w.outdent(); + w.print("}"); + + if (isEager) { + w.println(".onSuccess();"); + } else { + w.println(");"); + } + + // Close load method + w.outdent(); + w.println("}"); + + // Close add(new ... + w.outdent(); + w.println("});"); + } + + if (cvalInfos != null && !cvalInfos.isEmpty()) { + w.println("{"); + for (CValUiInfo c : cvalInfos) { + if ("evaluation".equals(c.type)) { + w.println("cvals.add(new CValUiInfo(\"" + c.product + + "\", \"" + c.version + "\", \"" + c.widgetset + + "\", null));"); + } + } + w.println("}"); + } + + w.outdent(); + w.println("}"); + + w.commit(logger); + } + + private void printLoadJsBundleData(SourceWriter w, String methodName, + List methods) { + SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, + 30000); + + for (String method : methods) { + writer.println("%s(store);", method); + writer.splitIfNeeded(); + } + } + + private void detectBadProperties(ConnectorBundle bundle, TreeLogger logger) + throws UnableToCompleteException { + Map> definedProperties = new HashMap>(); + + for (Property property : bundle.getNeedsProperty()) { + JClassType beanType = property.getBeanType(); + Set usedPropertyNames = definedProperties.get(beanType); + if (usedPropertyNames == null) { + usedPropertyNames = new HashSet(); + definedProperties.put(beanType, usedPropertyNames); + } + + String name = property.getName(); + if (!usedPropertyNames.add(name)) { + logger.log(Type.ERROR, beanType.getQualifiedSourceName() + + " has multiple properties with the name " + name + + ". This can happen if there are multiple " + + "setters with identical names ignoring case."); + throw new UnableToCompleteException(); + } + if (!property.hasAccessorMethods()) { + logger.log(Type.ERROR, beanType.getQualifiedSourceName() + + " has the property '" + name + + "' without getter defined."); + throw new UnableToCompleteException(); + } + } + } + + private List printJsBundleData(TreeLogger logger, SourceWriter w, + ConnectorBundle bundle, String methodName) { + SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName, + 30000); + Set needsProperty = bundle.getNeedsProperty(); + for (Property property : needsProperty) { + writer.println("var data = {"); + writer.indent(); + + if (property.getAnnotation(NoLayout.class) != null) { + writer.println("noLayout: 1, "); + } + + writer.println("setter: function(bean, value) {"); + writer.indent(); + property.writeSetterBody(logger, writer, "bean", "value"); + writer.outdent(); + writer.println("},"); + + writer.println("getter: function(bean) {"); + writer.indent(); + property.writeGetterBody(logger, writer, "bean"); + writer.outdent(); + writer.println("}"); + + writer.outdent(); + writer.println("};"); + + // Method declaration + writer.print( + "store.@%s::setPropertyData(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)", + TypeDataStore.class.getName()); + writer.println("(@%s::class, '%s', data);", property.getBeanType() + .getQualifiedSourceName(), property.getName()); + writer.println(); + writer.splitIfNeeded(true, + String.format("%s store", TypeDataStore.class.getName())); + } + return writer.getMethodNames(); + } + + private void printBundleData(TreeLogger logger, SourceWriter sourceWriter, + ConnectorBundle bundle, String loadNativeJsMethodName) + throws UnableToCompleteException { + // Split into new load method when reaching approximately 30000 bytes + SplittingSourceWriter w = new SplittingSourceWriter(sourceWriter, + "load", 30000); + + writeSuperClasses(w, bundle); + writeIdentifiers(w, bundle); + writeGwtConstructors(w, bundle); + writeReturnTypes(w, bundle); + writeInvokers(logger, w, bundle); + writeParamTypes(w, bundle); + writeProxys(w, bundle); + writeMethodAttributes(logger, w, bundle); + + w.println("%s(store);", loadNativeJsMethodName); + + // Must use Java code to generate Type data (because of Type[]), doing + // this after the JS property data has been initialized + writePropertyTypes(logger, w, bundle); + writeSerializers(logger, w, bundle); + writePresentationTypes(w, bundle); + writeDelegateToWidget(logger, w, bundle); + writeOnStateChangeHandlers(logger, w, bundle); + } + + private void writeOnStateChangeHandlers(TreeLogger logger, + SplittingSourceWriter w, ConnectorBundle bundle) + throws UnableToCompleteException { + Map> needsOnStateChangeHandler = bundle + .getNeedsOnStateChangeHandler(); + for (Entry> entry : needsOnStateChangeHandler + .entrySet()) { + JClassType connector = entry.getKey(); + + TreeLogger typeLogger = logger.branch( + Type.DEBUG, + "Generating @OnStateChange support for " + + connector.getName()); + + // Build map to speed up error checking + HashMap stateProperties = new HashMap(); + JClassType stateType = ConnectorBundle + .findInheritedMethod(connector, "getState").getReturnType() + .isClassOrInterface(); + for (Property property : bundle.getProperties(stateType)) { + stateProperties.put(property.getName(), property); + } + + for (JMethod method : entry.getValue()) { + TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, + "Processing method " + method.getName()); + + if (method.isPublic() || method.isProtected()) { + methodLogger + .log(Type.ERROR, + "@OnStateChange is only supported for methods with private or default visibility."); + throw new UnableToCompleteException(); + } + + OnStateChange onStateChange = method + .getAnnotation(OnStateChange.class); + + String[] properties = onStateChange.value(); + + if (properties.length == 0) { + methodLogger.log(Type.ERROR, + "There are no properties to listen to"); + throw new UnableToCompleteException(); + } + + // Verify that all properties do exist + for (String propertyName : properties) { + if (!stateProperties.containsKey(propertyName)) { + methodLogger.log(Type.ERROR, + "State class has no property named " + + propertyName); + throw new UnableToCompleteException(); + } + } + + if (method.getParameters().length != 0) { + methodLogger.log(Type.ERROR, + "Method should accept zero parameters"); + throw new UnableToCompleteException(); + } + + // new OnStateChangeMethod(Class declaringClass, String + // methodName, String[], properties) + w.print("store.addOnStateChangeMethod(%s, new %s(", + getClassLiteralString(connector), + OnStateChangeMethod.class.getName()); + if (!connector.equals(method.getEnclosingType())) { + w.print("%s, ", + getClassLiteralString(method.getEnclosingType())); + } + w.print("\"%s\", ", method.getName()); + + w.print("new String[] {"); + for (String propertyName : properties) { + w.print("\"%s\", ", propertyName); + } + w.print("}"); + + w.println("));"); + + w.splitIfNeeded(); + } + } + } + + private void writeSuperClasses(SplittingSourceWriter w, + ConnectorBundle bundle) { + List needsSuperclass = new ArrayList( + bundle.getNeedsSuperclass()); + // Emit in hierarchy order to ensure superclass is defined when + // referenced + Collections.sort(needsSuperclass, new Comparator() { + + @Override + public int compare(JClassType type1, JClassType type2) { + int depthDiff = getDepth(type1) - getDepth(type2); + if (depthDiff != 0) { + return depthDiff; + } else { + // Just something to get a stable compare + return type1.getName().compareTo(type2.getName()); + } + } + + private int getDepth(JClassType type) { + int depth = 0; + while (type != null) { + depth++; + type = type.getSuperclass(); + } + return depth; + } + }); + + for (JClassType jClassType : needsSuperclass) { + JClassType superclass = jClassType.getSuperclass(); + while (superclass != null && !superclass.isPublic()) { + superclass = superclass.getSuperclass(); + } + String classLiteralString; + if (superclass == null) { + classLiteralString = "null"; + } else { + classLiteralString = getClassLiteralString(superclass); + } + w.println("store.setSuperClass(%s, %s);", + getClassLiteralString(jClassType), classLiteralString); + } + } + + private void writeDelegateToWidget(TreeLogger logger, + SplittingSourceWriter w, ConnectorBundle bundle) { + Map> needsDelegateToWidget = bundle + .getNeedsDelegateToWidget(); + for (Entry> entry : needsDelegateToWidget + .entrySet()) { + JClassType beanType = entry.getKey(); + for (Property property : entry.getValue()) { + w.println( + "store.setDelegateToWidget(%s, \"%s\", \"%s\");", + getClassLiteralString(beanType),// property.getBeanType()), + property.getName(), + property.getAnnotation(DelegateToWidget.class).value()); + } + w.splitIfNeeded(); + } + } + + private void writeSerializers(TreeLogger logger, SplittingSourceWriter w, + ConnectorBundle bundle) throws UnableToCompleteException { + Map serializers = bundle.getSerializers(); + for (Entry entry : serializers.entrySet()) { + JType type = entry.getKey(); + GeneratedSerializer serializer = entry.getValue(); + + w.print("store.setSerializerFactory("); + writeClassLiteral(w, type); + w.print(", "); + w.println("new Invoker() {"); + w.indent(); + + w.println("public Object invoke(Object target, Object[] params) {"); + w.indent(); + + serializer.writeSerializerInstantiator(logger, w); + + w.outdent(); + w.println("}"); + + w.outdent(); + w.print("}"); + w.println(");"); + + w.splitIfNeeded(); + } + } + + private void writePresentationTypes(SplittingSourceWriter w, + ConnectorBundle bundle) { + Map presentationTypes = bundle + .getPresentationTypes(); + for (Entry entry : presentationTypes.entrySet()) { + + w.print("store.setPresentationType("); + writeClassLiteral(w, entry.getKey()); + w.print(", "); + writeClassLiteral(w, entry.getValue()); + w.println(");"); + w.splitIfNeeded(); + } + } + + private void writePropertyTypes(TreeLogger logger, SplittingSourceWriter w, + ConnectorBundle bundle) { + Set properties = bundle.getNeedsProperty(); + for (Property property : properties) { + w.print("store.setPropertyType("); + writeClassLiteral(w, property.getBeanType()); + w.print(", \""); + w.print(escape(property.getName())); + w.print("\", "); + writeTypeCreator(w, property.getPropertyType()); + w.println(");"); + + w.splitIfNeeded(); + } + } + + private void writeMethodAttributes(TreeLogger logger, + SplittingSourceWriter w, ConnectorBundle bundle) { + for (Entry>> typeEntry : bundle + .getMethodAttributes().entrySet()) { + JClassType type = typeEntry.getKey(); + for (Entry> methodEntry : typeEntry + .getValue().entrySet()) { + JMethod method = methodEntry.getKey(); + Set attributes = methodEntry.getValue(); + for (MethodAttribute attribute : attributes) { + w.println("store.setMethodAttribute(%s, \"%s\", %s.%s);", + getClassLiteralString(type), method.getName(), + MethodAttribute.class.getCanonicalName(), + attribute.name()); + w.splitIfNeeded(); + } + } + } + } + + private void writeProxys(SplittingSourceWriter w, ConnectorBundle bundle) { + Set needsProxySupport = bundle.getNeedsProxySupport(); + for (JClassType type : needsProxySupport) { + w.print("store.setProxyHandler("); + writeClassLiteral(w, type); + w.print(", new "); + w.print(ProxyHandler.class.getCanonicalName()); + w.println("() {"); + w.indent(); + + w.println("public Object createProxy(final " + + InvokationHandler.class.getName() + " handler) {"); + w.indent(); + + w.print("return new "); + w.print(type.getQualifiedSourceName()); + w.println("() {"); + w.indent(); + + JMethod[] methods = type.getOverridableMethods(); + for (JMethod method : methods) { + if (method.isAbstract()) { + w.print("public "); + w.print(method.getReturnType().getQualifiedSourceName()); + w.print(" "); + w.print(method.getName()); + w.print("("); + + JType[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) { + if (i != 0) { + w.print(", "); + } + w.print(types[i].getQualifiedSourceName()); + w.print(" p"); + w.print(Integer.toString(i)); + } + + w.println(") {"); + w.indent(); + + if (!method.getReturnType().getQualifiedSourceName() + .equals("void")) { + w.print("return "); + } + + w.print("handler.invoke(this, "); + w.print(TypeData.class.getCanonicalName()); + w.print(".getType("); + writeClassLiteral(w, type); + w.print(").getMethod(\""); + w.print(escape(method.getName())); + w.print("\"), new Object [] {"); + for (int i = 0; i < types.length; i++) { + w.print("p" + i + ", "); + } + w.println("});"); + + w.outdent(); + w.println("}"); + } + } + + w.outdent(); + w.println("};"); + + w.outdent(); + w.println("}"); + + w.outdent(); + w.println("});"); + + w.splitIfNeeded(); + } + } + + private void writeParamTypes(SplittingSourceWriter w, ConnectorBundle bundle) { + Map> needsParamTypes = bundle + .getNeedsParamTypes(); + for (Entry> entry : needsParamTypes.entrySet()) { + JClassType type = entry.getKey(); + + Set methods = entry.getValue(); + for (JMethod method : methods) { + w.print("store.setParamTypes("); + writeClassLiteral(w, type); + w.print(", \""); + w.print(escape(method.getName())); + w.print("\", new Type[] {"); + + for (JType parameter : method.getParameterTypes()) { + ConnectorBundleLoaderFactory.writeTypeCreator(w, parameter); + w.print(", "); + } + + w.println("});"); + + w.splitIfNeeded(); + } + } + } + + private void writeInvokers(TreeLogger logger, SplittingSourceWriter w, + ConnectorBundle bundle) throws UnableToCompleteException { + Map> needsInvoker = bundle.getNeedsInvoker(); + for (Entry> entry : needsInvoker.entrySet()) { + JClassType type = entry.getKey(); + + TreeLogger typeLogger = logger.branch(Type.DEBUG, + "Creating invokers for " + type); + + Set methods = entry.getValue(); + for (JMethod method : methods) { + w.print("store.setInvoker("); + writeClassLiteral(w, type); + w.print(", \""); + w.print(escape(method.getName())); + w.print("\","); + + if (method.isPublic()) { + typeLogger.log(Type.DEBUG, "Invoking " + method.getName() + + " using java"); + + writeJavaInvoker(w, type, method); + } else { + TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, + "Invoking " + method.getName() + " using jsni"); + // Must use JSNI to access non-public methods + writeJsniInvoker(methodLogger, w, type, method); + } + + w.println(");"); + + w.splitIfNeeded(); + } + } + } + + private void writeJsniInvoker(TreeLogger logger, SplittingSourceWriter w, + JClassType type, JMethod method) throws UnableToCompleteException { + w.println("new JsniInvoker() {"); + w.indent(); + + w.println( + "protected native Object jsniInvoke(Object target, %s params) /*-{ ", + JsArrayObject.class.getName()); + w.indent(); + + JType returnType = method.getReturnType(); + boolean hasReturnType = !"void".equals(returnType + .getQualifiedSourceName()); + + // Note that void is also a primitive type + boolean hasPrimitiveReturnType = hasReturnType + && returnType.isPrimitive() != null; + + if (hasReturnType) { + w.print("return "); + + if (hasPrimitiveReturnType) { + // Integer.valueOf(expression); + w.print("@%s::valueOf(%s)(", returnType.isPrimitive() + .getQualifiedBoxedSourceName(), returnType + .getJNISignature()); + + // Implementation tested briefly, but I don't dare leave it + // enabled since we are not using it in the framework and we + // have not tests for it. + logger.log(Type.ERROR, + "JSNI invocation is not yet supported for methods with " + + "primitive return types. Change your method " + + "to public to be able to use conventional" + + " Java invoking instead."); + throw new UnableToCompleteException(); + } + } + + JType[] parameterTypes = method.getParameterTypes(); + + w.print("target.@%s::" + method.getName() + "(*)(", method + .getEnclosingType().getQualifiedSourceName()); + for (int i = 0; i < parameterTypes.length; i++) { + if (i != 0) { + w.print(", "); + } + + w.print("params[" + i + "]"); + + JPrimitiveType primitive = parameterTypes[i].isPrimitive(); + if (primitive != null) { + // param.intValue(); + w.print(".@%s::%sValue()()", + primitive.getQualifiedBoxedSourceName(), + primitive.getQualifiedSourceName()); + } + } + + if (hasPrimitiveReturnType) { + assert hasReturnType; + w.print(")"); + } + + w.println(");"); + + if (!hasReturnType) { + w.println("return null;"); + } + + w.outdent(); + w.println("}-*/;"); + + w.outdent(); + w.print("}"); + } + + private void writeJavaInvoker(SplittingSourceWriter w, JClassType type, + JMethod method) { + w.println("new Invoker() {"); + w.indent(); + + w.println("public Object invoke(Object target, Object[] params) {"); + w.indent(); + + JType returnType = method.getReturnType(); + boolean hasReturnType = !"void".equals(returnType + .getQualifiedSourceName()); + if (hasReturnType) { + w.print("return "); + } + + JType[] parameterTypes = method.getParameterTypes(); + + w.print("((" + type.getQualifiedSourceName() + ") target)." + + method.getName() + "("); + for (int i = 0; i < parameterTypes.length; i++) { + JType parameterType = parameterTypes[i]; + if (i != 0) { + w.print(", "); + } + String parameterTypeName = getBoxedTypeName(parameterType); + + if (parameterTypeName.startsWith("elemental.json.Json")) { + // Need to pass through native method to allow casting Object to + // JSO if the value is a string + w.print("%s.<%s>obj2jso(params[%d])", + JsonDecoder.class.getCanonicalName(), + parameterTypeName, i); + } else { + w.print("(" + parameterTypeName + ") params[" + i + "]"); + } + } + w.println(");"); + + if (!hasReturnType) { + w.println("return null;"); + } + + w.outdent(); + w.println("}"); + + w.outdent(); + w.print("}"); + } + + private void writeReturnTypes(SplittingSourceWriter w, + ConnectorBundle bundle) { + Map> methodReturnTypes = bundle + .getMethodReturnTypes(); + for (Entry> entry : methodReturnTypes + .entrySet()) { + JClassType type = entry.getKey(); + + Set methods = entry.getValue(); + for (JMethod method : methods) { + // setReturnType(Class type, String methodName, Type + // returnType) + w.print("store.setReturnType("); + writeClassLiteral(w, type); + w.print(", \""); + w.print(escape(method.getName())); + w.print("\", "); + writeTypeCreator(w, method.getReturnType()); + w.println(");"); + + w.splitIfNeeded(); + } + } + } + + private void writeGwtConstructors(SplittingSourceWriter w, + ConnectorBundle bundle) { + Set constructors = bundle.getGwtConstructors(); + for (JClassType type : constructors) { + w.print("store.setConstructor("); + writeClassLiteral(w, type); + w.println(", new Invoker() {"); + w.indent(); + + w.println("public Object invoke(Object target, Object[] params) {"); + w.indent(); + + w.print("return "); + w.print(GWT.class.getName()); + w.print(".create("); + writeClassLiteral(w, type); + w.println(");"); + + w.outdent(); + w.println("}"); + + w.outdent(); + w.println("});"); + + w.splitIfNeeded(); + } + } + + public static void writeClassLiteral(SourceWriter w, JType type) { + w.print(getClassLiteralString(type)); + } + + public static String getClassLiteralString(JType type) { + return type.getQualifiedSourceName() + ".class"; + } + + private void writeIdentifiers(SplittingSourceWriter w, + ConnectorBundle bundle) { + Map> identifiers = bundle.getIdentifiers(); + for (Entry> entry : identifiers.entrySet()) { + Set ids = entry.getValue(); + JClassType type = entry.getKey(); + for (String id : ids) { + w.print("store.setClass(\""); + w.print(escape(id)); + w.print("\", "); + writeClassLiteral(w, type); + w.println(");"); + + w.splitIfNeeded(); + } + } + } + + private List buildBundles(TreeLogger logger, + TypeOracle typeOracle) throws NotFoundException, + UnableToCompleteException { + + Map> connectorsByLoadStyle = new HashMap>(); + for (LoadStyle loadStyle : LoadStyle.values()) { + connectorsByLoadStyle.put(loadStyle, new ArrayList()); + } + + // Find all types with a valid mapping + Collection selectedTypes = getConnectorsForWidgetset( + logger, typeOracle); + + // Group by load style + for (JClassType connectorSubtype : selectedTypes) { + LoadStyle loadStyle = getLoadStyle(connectorSubtype); + if (loadStyle != null) { + connectorsByLoadStyle.get(loadStyle).add(connectorSubtype); + } + } + + List bundles = new ArrayList(); + + Collection visitors = getVisitors(typeOracle); + + ConnectorBundle eagerBundle = new ConnectorBundle( + ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle); + TreeLogger eagerLogger = logger.branch(Type.TRACE, + "Populating eager bundle"); + + // Eager connectors and all RPC interfaces are loaded by default + eagerBundle.processTypes(eagerLogger, + connectorsByLoadStyle.get(LoadStyle.EAGER)); + eagerBundle.processType(eagerLogger, typeOracle + .findType(UnknownComponentConnector.class.getCanonicalName())); + eagerBundle.processSubTypes(eagerLogger, + typeOracle.getType(ClientRpc.class.getName())); + eagerBundle.processSubTypes(eagerLogger, + typeOracle.getType(ServerRpc.class.getName())); + + bundles.add(eagerBundle); + + ConnectorBundle deferredBundle = new ConnectorBundle( + ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle); + TreeLogger deferredLogger = logger.branch(Type.TRACE, + "Populating deferred bundle"); + deferredBundle.processTypes(deferredLogger, + connectorsByLoadStyle.get(LoadStyle.DEFERRED)); + + bundles.add(deferredBundle); + + Collection lazy = connectorsByLoadStyle.get(LoadStyle.LAZY); + for (JClassType type : lazy) { + ConnectorBundle bundle = new ConnectorBundle(type.getName(), + eagerBundle); + TreeLogger subLogger = logger.branch(Type.TRACE, "Populating " + + type.getName() + " bundle"); + bundle.processType(subLogger, type); + + bundles.add(bundle); + } + + return bundles; + } + + /** + * Returns the connector types that should be included in the widgetset. + * This method can be overridden to create a widgetset only containing + * selected connectors. + *

+ * The default implementation finds all type implementing + * {@link ServerConnector} that have a @{@link Connect} annotation. It also + * checks that multiple connectors aren't connected to the same server-side + * class. + * + * @param logger + * the logger to which information can be logged + * @param typeOracle + * the type oracle that can be used for finding types + * @return a collection of all the connector types that should be included + * in the widgetset + * @throws UnableToCompleteException + * if the operation fails + */ + protected Collection getConnectorsForWidgetset( + TreeLogger logger, TypeOracle typeOracle) + throws UnableToCompleteException { + JClassType serverConnectorType; + try { + serverConnectorType = typeOracle.getType(ServerConnector.class + .getName()); + } catch (NotFoundException e) { + logger.log(Type.ERROR, + "Can't find " + ServerConnector.class.getName()); + throw new UnableToCompleteException(); + } + + JClassType[] types = serverConnectorType.getSubtypes(); + + Map mappings = new TreeMap(); + + // Keep track of what has happened to avoid logging intermediate state + Map> replaced = new TreeMap>( + ConnectorBundle.jClassComparator); + + for (JClassType type : types) { + Connect connectAnnotation = type.getAnnotation(Connect.class); + if (connectAnnotation == null) { + continue; + } + + String identifier = connectAnnotation.value().getCanonicalName(); + + JClassType previousMapping = mappings.put(identifier, type); + if (previousMapping != null) { + // There are multiple mappings, pick the subclass + JClassType subclass; + JClassType superclass; + if (previousMapping.isAssignableFrom(type)) { + subclass = type; + superclass = previousMapping; + } else if (type.isAssignableFrom(previousMapping)) { + subclass = previousMapping; + superclass = type; + } else { + // Neither inherits from the other - this is a conflict + logger.log( + Type.ERROR, + "Conflicting @Connect mappings detected for " + + identifier + + ": " + + type.getQualifiedSourceName() + + " and " + + previousMapping.getQualifiedSourceName() + + ". There can only be multiple @Connect mappings for the same server-side type if one is the subclass of the other."); + throw new UnableToCompleteException(); + } + + mappings.put(identifier, subclass); + + // Inherit any previous replacements + List previousReplacements = replaced + .remove(superclass); + if (previousReplacements == null) { + previousReplacements = new ArrayList(); + } + + previousReplacements.add(superclass); + replaced.put(subclass, previousReplacements); + } + } + + // Log the final set of replacements + for (Entry> entry : replaced.entrySet()) { + String msg = entry.getKey().getQualifiedSourceName() + " replaces "; + + List list = entry.getValue(); + for (int i = 0; i < list.size(); i++) { + if (i != 0) { + msg += ", "; + } + msg += list.get(i).getQualifiedSourceName(); + } + + logger.log(Type.INFO, msg); + } + + // Return the types of the final mapping + return mappings.values(); + } + + private Collection getVisitors(TypeOracle oracle) + throws NotFoundException { + List visitors = Arrays. asList( + new ConnectorInitVisitor(), new StateInitVisitor(), + new WidgetInitVisitor(), new RendererVisitor(), + new ClientRpcVisitor(), new ServerRpcVisitor(), + new OnStateChangeVisitor()); + for (TypeVisitor typeVisitor : visitors) { + typeVisitor.init(oracle); + } + return visitors; + } + + protected LoadStyle getLoadStyle(JClassType connectorType) { + Connect annotation = connectorType.getAnnotation(Connect.class); + return annotation.loadStyle(); + } + + public static String getBoxedTypeName(JType type) { + if (type.isPrimitive() != null) { + // Used boxed types for primitives + return type.isPrimitive().getQualifiedBoxedSourceName(); + } else { + return type.getErasedType().getQualifiedSourceName(); + } + } + + public static void writeTypeCreator(SourceWriter sourceWriter, JType type) { + String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type); + JParameterizedType parameterized = type.isParameterized(); + if (parameterized != null) { + sourceWriter.print("new Type(\"" + typeName + "\", "); + sourceWriter.print("new Type[] {"); + JClassType[] typeArgs = parameterized.getTypeArgs(); + for (JClassType jClassType : typeArgs) { + writeTypeCreator(sourceWriter, jClassType); + sourceWriter.print(", "); + } + sourceWriter.print("}"); + } else { + sourceWriter.print("new Type(" + typeName + ".class"); + } + sourceWriter.print(")"); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java new file mode 100644 index 0000000000..0049ae9b50 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ArraySerializer.java @@ -0,0 +1,95 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JArrayType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.client.communication.JsonDecoder; +import com.vaadin.client.communication.JsonEncoder; +import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory; + +import elemental.json.Json; +import elemental.json.JsonArray; + +public class ArraySerializer extends JsonSerializer { + + private final JArrayType arrayType; + + public ArraySerializer(JArrayType arrayType) { + super(arrayType); + this.arrayType = arrayType; + } + + @Override + protected void printDeserializerBody(TreeLogger logger, SourceWriter w, + String type, String jsonValue, String connection) { + JType leafType = arrayType.getLeafType(); + int rank = arrayType.getRank(); + + w.println(JsonArray.class.getName() + " jsonArray = (" + + JsonArray.class.getName() + ")" + jsonValue + ";"); + + // Type value = new Type[jsonArray.size()][][]; + w.print(arrayType.getQualifiedSourceName() + " value = new " + + leafType.getQualifiedSourceName() + "[jsonArray.length()]"); + for (int i = 1; i < rank; i++) { + w.print("[]"); + } + w.println(";"); + + w.println("for(int i = 0 ; i < value.length; i++) {"); + w.indent(); + + JType componentType = arrayType.getComponentType(); + + w.print("value[i] = (" + + ConnectorBundleLoaderFactory.getBoxedTypeName(componentType) + + ") " + JsonDecoder.class.getName() + ".decodeValue("); + ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType); + w.print(", jsonArray.get(i), null, " + connection + ")"); + + w.println(";"); + + w.outdent(); + w.println("}"); + + w.println("return value;"); + } + + @Override + protected void printSerializerBody(TreeLogger logger, SourceWriter w, + String value, String applicationConnection) { + JType componentType = arrayType.getComponentType(); + + w.println(JsonArray.class.getName() + " values = " + + Json.class.getName() + ".createArray();"); + // JPrimitiveType primitive = componentType.isPrimitive(); + w.println("for (int i = 0; i < " + value + ".length; i++) {"); + w.indent(); + w.print("values.set(i, "); + w.print(JsonEncoder.class.getName() + ".encode(" + value + "[i],"); + ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType); + w.print(", " + applicationConnection + ")"); + w.println(");"); + w.outdent(); + w.println("}"); + w.println("return values;"); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java new file mode 100644 index 0000000000..992a012005 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ClientRpcVisitor.java @@ -0,0 +1,84 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import java.util.Set; + +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.vaadin.client.metadata.TypeDataStore.MethodAttribute; +import com.vaadin.shared.annotations.NoLayout; + +public class ClientRpcVisitor extends TypeVisitor { + @Override + public void visitClientRpc(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + checkGenericType(logger, type); + Set hierarchy = type + .getFlattenedSupertypeHierarchy(); + for (JClassType subType : hierarchy) { + JMethod[] methods = subType.getMethods(); + for (JMethod method : methods) { + checkReturnType(logger, method); + + bundle.setNeedsInvoker(type, method); + bundle.setNeedsParamTypes(type, method); + if (method.getAnnotation(NoLayout.class) != null) { + bundle.setMethodAttribute(type, method, + MethodAttribute.NO_LAYOUT); + } + + JType[] parameterTypes = method.getParameterTypes(); + for (JType paramType : parameterTypes) { + bundle.setNeedsSerialize(paramType); + } + } + } + } + + public static void checkGenericType(TreeLogger logger, JClassType type) + throws UnableToCompleteException { + if (type.isGenericType() != null) { + logger.log(Type.ERROR, + "Type " + type.getParameterizedQualifiedSourceName() + + "is parameterizied generic. RPC proxy " + + "for parameterizied types is not supported."); + throw new UnableToCompleteException(); + } + } + + public static void checkReturnType(TreeLogger logger, JMethod method) + throws UnableToCompleteException { + if (!method.getReturnType().getQualifiedSourceName().equals("void")) { + logger.log( + Type.ERROR, + "The method " + + method.getEnclosingType() + .getQualifiedSourceName() + + "." + + method.getName() + + " returns " + + method.getReturnType().getQualifiedSourceName() + + " but only void is supported for methods in RPC interfaces."); + throw new UnableToCompleteException(); + } + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java new file mode 100644 index 0000000000..b4531eb08e --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java @@ -0,0 +1,737 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +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.JEnumType; +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.vaadin.client.ApplicationConnection; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.communication.JSONSerializer; +import com.vaadin.client.connectors.AbstractRendererConnector; +import com.vaadin.client.metadata.TypeDataStore.MethodAttribute; +import com.vaadin.client.ui.UnknownComponentConnector; +import com.vaadin.shared.communication.ClientRpc; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.shared.ui.Connect; + +import elemental.json.JsonValue; + +public class ConnectorBundle { + private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; + + public static final Comparator jClassComparator = new Comparator() { + @Override + public int compare(JClassType o1, JClassType o2) { + return o1.getQualifiedSourceName().compareTo( + o2.getQualifiedSourceName()); + } + }; + + public static final Comparator jMethodComparator = new Comparator() { + @Override + public int compare(JMethod o1, JMethod o2) { + return o1.getReadableDeclaration().compareTo( + o2.getReadableDeclaration()); + } + }; + + private final String name; + private final ConnectorBundle previousBundle; + private final Collection visitors; + private final Map customSerializers; + + private final Set hasSerializeSupport = new HashSet(); + private final Set needsSerializeSupport = new HashSet(); + + private final Map serializers = new TreeMap( + new Comparator() { + @Override + public int compare(JType o1, JType o2) { + return o1.toString().compareTo(o2.toString()); + } + }); + + private final Map>> methodAttributes = new TreeMap>>( + jClassComparator); + private final Set needsSuperClass = new TreeSet( + jClassComparator); + private final Set needsGwtConstructor = new TreeSet( + jClassComparator); + private final Set visitedTypes = new HashSet(); + + private final Set needsProxySupport = new TreeSet( + jClassComparator); + + private final Map presentationTypes = new TreeMap( + jClassComparator); + private final Map> identifiers = new TreeMap>( + jClassComparator); + private final Map> needsReturnType = new TreeMap>( + jClassComparator); + private final Map> needsInvoker = new TreeMap>( + jClassComparator); + private final Map> needsParamTypes = new TreeMap>( + jClassComparator); + private final Map> needsOnStateChange = new TreeMap>( + jClassComparator); + + private final Set needsProperty = new TreeSet(); + private final Map> needsDelegateToWidget = new TreeMap>( + jClassComparator); + + private ConnectorBundle(String name, ConnectorBundle previousBundle, + Collection visitors, + Map customSerializers) { + this.name = name; + this.previousBundle = previousBundle; + this.visitors = visitors; + this.customSerializers = customSerializers; + } + + public ConnectorBundle(String name, ConnectorBundle previousBundle) { + this(name, previousBundle, previousBundle.visitors, + previousBundle.customSerializers); + } + + public ConnectorBundle(String name, Collection visitors, + TypeOracle oracle) throws NotFoundException { + this(name, null, visitors, findCustomSerializers(oracle)); + } + + private static Map findCustomSerializers( + TypeOracle oracle) throws NotFoundException { + Map serializers = new HashMap(); + + JClassType serializerInterface = oracle.findType(JSONSerializer.class + .getName()); + JType[] deserializeParamTypes = new JType[] { + oracle.findType(com.vaadin.client.metadata.Type.class.getName()), + oracle.findType(JsonValue.class.getName()), + oracle.findType(ApplicationConnection.class.getName()) }; + String deserializeMethodName = "deserialize"; + // Just test that the method exists + serializerInterface.getMethod(deserializeMethodName, + deserializeParamTypes); + + for (JClassType serializer : serializerInterface.getSubtypes()) { + JMethod deserializeMethod = serializer.findMethod( + deserializeMethodName, deserializeParamTypes); + if (deserializeMethod == null) { + continue; + } + JType returnType = deserializeMethod.getReturnType(); + + serializers.put(returnType, serializer); + } + return serializers; + } + + public void setNeedsGwtConstructor(JClassType type) { + if (!needsGwtConstructor(type)) { + needsGwtConstructor.add(type); + } + } + + private boolean needsGwtConstructor(JClassType type) { + if (needsGwtConstructor.contains(type)) { + return true; + } else { + return previousBundle != null + && previousBundle.needsGwtConstructor(type); + } + } + + public void setIdentifier(JClassType type, String identifier) { + if (!hasIdentifier(type, identifier)) { + addMapping(identifiers, type, identifier); + } + } + + private boolean hasIdentifier(JClassType type, String identifier) { + if (hasMapping(identifiers, type, identifier)) { + return true; + } else { + return previousBundle != null + && previousBundle.hasIdentifier(type, identifier); + } + } + + public ConnectorBundle getPreviousBundle() { + return previousBundle; + } + + public String getName() { + return name; + } + + public Map> getIdentifiers() { + return Collections.unmodifiableMap(identifiers); + } + + public Set getGwtConstructors() { + return Collections.unmodifiableSet(needsGwtConstructor); + } + + public void processTypes(TreeLogger logger, Collection types) + throws UnableToCompleteException { + for (JClassType type : types) { + processType(logger, type); + } + } + + public void processType(TreeLogger logger, JClassType type) + throws UnableToCompleteException { + if (!isTypeVisited(type)) { + for (TypeVisitor typeVisitor : visitors) { + invokeVisitor(logger, type, typeVisitor); + } + visitedTypes.add(type); + purgeSerializeSupportQueue(logger); + } + } + + private boolean isTypeVisited(JClassType type) { + if (visitedTypes.contains(type)) { + return true; + } else { + return previousBundle != null && previousBundle.isTypeVisited(type); + } + } + + private void purgeSerializeSupportQueue(TreeLogger logger) + throws UnableToCompleteException { + while (!needsSerializeSupport.isEmpty()) { + Iterator iterator = needsSerializeSupport.iterator(); + JType type = iterator.next(); + iterator.remove(); + + if (hasSerializeSupport(type)) { + continue; + } + + addSerializeSupport(logger, type); + } + } + + private void addSerializeSupport(TreeLogger logger, JType type) + throws UnableToCompleteException { + hasSerializeSupport.add(type); + + JParameterizedType parametrized = type.isParameterized(); + if (parametrized != null) { + for (JClassType parameterType : parametrized.getTypeArgs()) { + setNeedsSerialize(parameterType); + } + } + + if (serializationHandledByFramework(type)) { + return; + } + + JClassType customSerializer = customSerializers.get(type); + JClassType typeAsClass = type.isClass(); + JEnumType enumType = type.isEnum(); + JArrayType arrayType = type.isArray(); + + if (customSerializer != null) { + logger.log(Type.INFO, "Will serialize " + type + " using " + + customSerializer.getName()); + setSerializer(type, new CustomSerializer(customSerializer)); + } else if (arrayType != null) { + logger.log(Type.INFO, "Will serialize " + type + " as an array"); + setSerializer(type, new ArraySerializer(arrayType)); + setNeedsSerialize(arrayType.getComponentType()); + } else if (enumType != null) { + logger.log(Type.INFO, "Will serialize " + type + " as an enum"); + setSerializer(type, new EnumSerializer(enumType)); + } else if (typeAsClass != null) { + // Bean + checkSerializable(logger, typeAsClass); + + logger.log(Type.INFO, "Will serialize " + type + " as a bean"); + + JClassType needsSuperClass = typeAsClass; + while (needsSuperClass != null) { + if (needsSuperClass.isPublic()) { + setNeedsSuperclass(needsSuperClass); + } + needsSuperClass = needsSuperClass.getSuperclass(); + } + + setNeedsGwtConstructor(typeAsClass); + + for (Property property : getProperties(typeAsClass)) { + setNeedsProperty(property); + + JType propertyType = property.getPropertyType(); + setNeedsSerialize(propertyType); + } + } + } + + private void checkSerializable(TreeLogger logger, JClassType type) + throws UnableToCompleteException { + JClassType javaSerializable = type.getOracle().findType( + Serializable.class.getName()); + 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 void setSerializer(JType type, GeneratedSerializer serializer) { + if (!hasSerializer(type)) { + serializers.put(type, serializer); + } + } + + private boolean hasSerializer(JType type) { + if (serializers.containsKey(type)) { + return true; + } else { + return previousBundle != null && previousBundle.hasSerializer(type); + } + } + + public Map getSerializers() { + return Collections.unmodifiableMap(serializers); + } + + public void setPresentationType(JClassType type, JType presentationType) { + if (!hasPresentationType(type)) { + presentationTypes.put(type, presentationType); + } + } + + private boolean hasPresentationType(JClassType type) { + if (presentationTypes.containsKey(type)) { + return true; + } else { + return previousBundle != null + && previousBundle.hasPresentationType(type); + } + } + + public Map getPresentationTypes() { + return Collections.unmodifiableMap(presentationTypes); + } + + private void setNeedsSuperclass(JClassType typeAsClass) { + if (!isNeedsSuperClass(typeAsClass)) { + needsSuperClass.add(typeAsClass); + } + } + + private boolean isNeedsSuperClass(JClassType typeAsClass) { + if (needsSuperClass.contains(typeAsClass)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsSuperClass(typeAsClass); + } + } + + public Set getNeedsSuperclass() { + return Collections.unmodifiableSet(needsSuperClass); + } + + private void setNeedsProperty(Property property) { + if (!isNeedsProperty(property)) { + needsProperty.add(property); + } + } + + private boolean isNeedsProperty(Property property) { + if (needsProperty.contains(property)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsProperty(property); + } + } + + public Set getNeedsProperty() { + return Collections.unmodifiableSet(needsProperty); + } + + public Collection getProperties(JClassType type) { + Set properties = new TreeSet(); + + properties.addAll(MethodProperty.findProperties(type)); + properties.addAll(FieldProperty.findProperties(type)); + + return properties; + } + + private void invokeVisitor(TreeLogger logger, JClassType type, + TypeVisitor typeVisitor) throws UnableToCompleteException { + TreeLogger subLogger = logger.branch(Type.TRACE, + "Visiting " + type.getName() + " with " + + typeVisitor.getClass().getSimpleName()); + if (isConnectedConnector(type)) { + typeVisitor.visitConnector(subLogger, type, this); + } + if (isClientRpc(type)) { + typeVisitor.visitClientRpc(subLogger, type, this); + } + if (isServerRpc(type)) { + typeVisitor.visitServerRpc(subLogger, type, this); + } + } + + public void processSubTypes(TreeLogger logger, JClassType type) + throws UnableToCompleteException { + processTypes(logger, Arrays.asList(type.getSubtypes())); + } + + public void setNeedsReturnType(JClassType type, JMethod method) { + if (!isNeedsReturnType(type, method)) { + addMapping(needsReturnType, type, method); + } + } + + private boolean isNeedsReturnType(JClassType type, JMethod method) { + if (hasMapping(needsReturnType, type, method)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsReturnType(type, method); + } + } + + public Map> getMethodReturnTypes() { + return Collections.unmodifiableMap(needsReturnType); + } + + private static boolean isClientRpc(JClassType type) { + return isInterfaceType(type, ClientRpc.class); + } + + private static boolean isServerRpc(JClassType type) { + return isInterfaceType(type, ServerRpc.class); + } + + public static boolean isConnectedConnector(JClassType type) { + return isConnected(type) && isType(type, ServerConnector.class); + } + + private static boolean isConnected(JClassType type) { + return type.isAnnotationPresent(Connect.class) + || type.getQualifiedSourceName().equals( + UnknownComponentConnector.class.getCanonicalName()); + } + + public static boolean isConnectedComponentConnector(JClassType type) { + return isConnected(type) && isType(type, ComponentConnector.class); + } + + public static boolean isConnectedRendererConnector(JClassType type) { + return isConnected(type) + && isType(type, AbstractRendererConnector.class); + } + + private static boolean isInterfaceType(JClassType type, Class class1) { + return type.isInterface() != null && isType(type, class1); + } + + private static boolean isType(JClassType type, Class class1) { + try { + return type.getOracle().getType(class1.getName()) + .isAssignableFrom(type); + } catch (NotFoundException e) { + throw new RuntimeException("Could not find " + class1.getName(), e); + } + } + + public void setNeedsInvoker(JClassType type, JMethod method) { + if (!isNeedsInvoker(type, method)) { + addMapping(needsInvoker, type, method); + } + } + + private void addMapping(Map> map, K key, String value) { + Set set = map.get(key); + if (set == null) { + set = new TreeSet(); + map.put(key, set); + } + set.add(value); + } + + private void addMapping(Map> map, K key, JMethod value) { + Set set = map.get(key); + if (set == null) { + set = new TreeSet(jMethodComparator); + map.put(key, set); + } + set.add(value); + } + + private boolean hasMapping(Map> map, K key, V value) { + return map.containsKey(key) && map.get(key).contains(value); + } + + private boolean isNeedsInvoker(JClassType type, JMethod method) { + if (hasMapping(needsInvoker, type, method)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsInvoker(type, method); + } + } + + public Map> getNeedsInvoker() { + return Collections.unmodifiableMap(needsInvoker); + } + + public void setNeedsParamTypes(JClassType type, JMethod method) { + if (!isNeedsParamTypes(type, method)) { + addMapping(needsParamTypes, type, method); + } + } + + private boolean isNeedsParamTypes(JClassType type, JMethod method) { + if (hasMapping(needsParamTypes, type, method)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsParamTypes(type, method); + } + } + + public Map> getNeedsParamTypes() { + return Collections.unmodifiableMap(needsParamTypes); + } + + public void setNeedsProxySupport(JClassType type) { + if (!isNeedsProxySupport(type)) { + needsProxySupport.add(type); + } + } + + private boolean isNeedsProxySupport(JClassType type) { + if (needsProxySupport.contains(type)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsProxySupport(type); + } + } + + public Set getNeedsProxySupport() { + return Collections.unmodifiableSet(needsProxySupport); + } + + public void setMethodAttribute(JClassType type, JMethod method, + MethodAttribute methodAttribute) { + if (!hasMethodAttribute(type, method, methodAttribute)) { + Map> typeData = methodAttributes + .get(type); + if (typeData == null) { + typeData = new TreeMap>( + jMethodComparator); + methodAttributes.put(type, typeData); + } + + Map> methods = methodAttributes + .get(type); + if (methods == null) { + methods = new TreeMap>( + jMethodComparator); + methodAttributes.put(type, methods); + } + + Set attributes = methods.get(method); + if (attributes == null) { + attributes = new TreeSet(); + methods.put(method, attributes); + } + + attributes.add(methodAttribute); + } + } + + private boolean hasMethodAttribute(JClassType type, JMethod method, + MethodAttribute methodAttribute) { + Map> typeData = methodAttributes + .get(type); + if (typeData != null && hasMapping(typeData, method, methodAttribute)) { + return true; + } else { + return previousBundle != null + && previousBundle.hasMethodAttribute(type, method, + methodAttribute); + } + } + + public Map>> getMethodAttributes() { + return Collections.unmodifiableMap(methodAttributes); + } + + public void setNeedsSerialize(JType type) { + if (!hasSerializeSupport(type)) { + needsSerializeSupport.add(type); + } + } + + private static Set> frameworkHandledTypes = new LinkedHashSet>(); + { + 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); + frameworkHandledTypes.add(Void.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; + } + + private boolean hasSerializeSupport(JType type) { + if (hasSerializeSupport.contains(type)) { + return true; + } else { + return previousBundle != null + && previousBundle.hasSerializeSupport(type); + } + } + + public void setNeedsDelegateToWidget(Property property, JClassType type) { + if (!isNeedsDelegateToWidget(type)) { + TreeSet set = new TreeSet(); + set.add(property); + needsDelegateToWidget.put(type, set); + } else if (!needsDelegateToWidget.get(type).contains(property)) { + needsDelegateToWidget.get(type).add(property); + } + } + + private boolean isNeedsDelegateToWidget(JClassType type) { + if (needsDelegateToWidget.containsKey(type)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsDelegateToWidget(type); + } + } + + public Map> getNeedsDelegateToWidget() { + return Collections.unmodifiableMap(needsDelegateToWidget); + } + + public void setNeedsOnStateChangeHandler(JClassType type, JMethod method) { + if (!isNeedsOnStateChangeHandler(type, method)) { + addMapping(needsOnStateChange, type, method); + } + } + + private boolean isNeedsOnStateChangeHandler(JClassType type, JMethod method) { + if (hasMapping(needsOnStateChange, type, method)) { + return true; + } else { + return previousBundle != null + && previousBundle.isNeedsOnStateChangeHandler(type, method); + } + } + + public Map> getNeedsOnStateChangeHandler() { + return Collections.unmodifiableMap(needsOnStateChange); + } + + public static JMethod findInheritedMethod(JClassType type, + String methodName, JType... params) { + + JClassType currentType = type; + while (currentType != null) { + JMethod method = currentType.findMethod(methodName, params); + if (method != null) { + return method; + } + currentType = currentType.getSuperclass(); + } + + JClassType[] interfaces = type.getImplementedInterfaces(); + for (JClassType iface : interfaces) { + JMethod method = iface.findMethod(methodName, params); + if (method != null) { + return method; + } + } + + return null; + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java new file mode 100644 index 0000000000..ea3b097fa2 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ConnectorInitVisitor.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +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.vaadin.shared.ui.Connect; + +public class ConnectorInitVisitor extends TypeVisitor { + + @Override + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + Connect connectAnnotation = type.getAnnotation(Connect.class); + if (connectAnnotation != null) { + logger.log(Type.INFO, type.getName() + " will be in the " + + bundle.getName().replaceAll("^_*", "") + " bundle"); + String identifier = connectAnnotation.value().getCanonicalName(); + + bundle.setIdentifier(type, identifier); + bundle.setNeedsGwtConstructor(type); + } + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java new file mode 100644 index 0000000000..bb3dd4f61d --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/CustomSerializer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory; + +public class CustomSerializer implements GeneratedSerializer { + + private final JClassType serializerType; + + public CustomSerializer(JClassType serializerType) { + this.serializerType = serializerType; + } + + @Override + public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) + throws UnableToCompleteException { + w.print("return "); + w.print(GWT.class.getCanonicalName()); + w.print(".create("); + ConnectorBundleLoaderFactory.writeClassLiteral(w, serializerType); + w.println(");"); + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java new file mode 100644 index 0000000000..9876baf946 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/EnumSerializer.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JEnumConstant; +import com.google.gwt.core.ext.typeinfo.JEnumType; +import com.google.gwt.user.rebind.SourceWriter; + +import elemental.json.Json; + +public class EnumSerializer extends JsonSerializer { + + private final JEnumType enumType; + + public EnumSerializer(JEnumType type) { + super(type); + enumType = type; + } + + @Override + protected void printDeserializerBody(TreeLogger logger, SourceWriter w, + String type, String jsonValue, String connection) { + w.println("String enumIdentifier = " + jsonValue + ".asString();"); + for (JEnumConstant e : enumType.getEnumConstants()) { + w.println("if (\"" + e.getName() + "\".equals(enumIdentifier)) {"); + w.indent(); + w.println("return " + enumType.getQualifiedSourceName() + "." + + e.getName() + ";"); + w.outdent(); + w.println("}"); + } + w.println("return null;"); + } + + @Override + protected void printSerializerBody(TreeLogger logger, SourceWriter w, + String value, String applicationConnection) { + // return Json.create(castedValue.name()); + w.println("return " + Json.class.getName() + ".create(" + value + + ".name());"); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java new file mode 100644 index 0000000000..a31dafe05c --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/FieldProperty.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JField; +import com.google.gwt.user.rebind.SourceWriter; + +public class FieldProperty extends Property { + + private final JField field; + + private FieldProperty(JClassType beanType, JField field) { + super(field.getName(), beanType, field.getType()); + this.field = field; + } + + @Override + public boolean hasAccessorMethods() { + return true; + } + + @Override + public void writeSetterBody(TreeLogger logger, SourceWriter w, + String beanVariable, String valueVariable) { + w.println("%s.@%s::%s = %s;", beanVariable, getBeanType() + .getQualifiedSourceName(), getName(), unboxValue(valueVariable)); + } + + @Override + public void writeGetterBody(TreeLogger logger, SourceWriter w, + String beanVariable) { + String value = String.format("%s.@%s::%s", beanVariable, getBeanType() + .getQualifiedSourceName(), getName()); + w.print("return "); + w.print(boxValue(value)); + w.println(";"); + } + + public static Collection findProperties(JClassType type) { + Collection properties = new ArrayList(); + + List fields = getPublicFields(type); + for (JField field : fields) { + properties.add(new FieldProperty(field.getEnclosingType(), field)); + } + + return properties; + } + + private static List getPublicFields(JClassType type) { + Set names = new HashSet(); + ArrayList fields = new ArrayList(); + for (JClassType subType : type.getFlattenedSupertypeHierarchy()) { + JField[] subFields = subType.getFields(); + for (JField field : subFields) { + if (field.isPublic() && !field.isStatic() + && names.add(field.getName())) { + fields.add(field); + } + } + } + return fields; + } + + @Override + public T getAnnotation(Class annotationClass) { + return field.getAnnotation(annotationClass); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java new file mode 100644 index 0000000000..6afb172ea2 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/GeneratedSerializer.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.user.rebind.SourceWriter; + +public interface GeneratedSerializer { + public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) + throws UnableToCompleteException; +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java new file mode 100644 index 0000000000..a7a6c568da --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/JsonSerializer.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.communication.JSONSerializer; +import elemental.json.JsonValue; + +public abstract class JsonSerializer implements GeneratedSerializer { + + private final JType type; + + public JsonSerializer(JType type) { + this.type = type; + } + + @Override + public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w) + throws UnableToCompleteException { + + w.print("return new "); + w.print(JSONSerializer.class.getCanonicalName()); + w.print("<"); + w.print(type.getQualifiedSourceName()); + w.println(">() {"); + w.indent(); + + writeSerializerBody(logger, w); + + w.outdent(); + w.println("};"); + } + + protected void writeSerializerBody(TreeLogger logger, SourceWriter w) { + String qualifiedSourceName = type.getQualifiedSourceName(); + w.println("public " + JsonValue.class.getName() + " serialize(" + + qualifiedSourceName + " value, " + + ApplicationConnection.class.getName() + " connection) {"); + w.indent(); + // MouseEventDetails castedValue = (MouseEventDetails) value; + w.println(qualifiedSourceName + " castedValue = (" + + qualifiedSourceName + ") value;"); + + printSerializerBody(logger, w, "castedValue", "connection"); + + // End of serializer method + w.outdent(); + w.println("}"); + + // Deserializer + // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection + // connection); + w.println("public " + qualifiedSourceName + " deserialize(Type type, " + + JsonValue.class.getName() + " jsonValue, " + + ApplicationConnection.class.getName() + " connection) {"); + w.indent(); + + printDeserializerBody(logger, w, "type", "jsonValue", "connection"); + + w.outdent(); + w.println("}"); + } + + protected abstract void printDeserializerBody(TreeLogger logger, + SourceWriter w, String type, String jsonValue, String connection); + + protected abstract void printSerializerBody(TreeLogger logger, + SourceWriter w, String value, String applicationConnection); + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java new file mode 100644 index 0000000000..32aad92774 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/MethodProperty.java @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.core.ext.TreeLogger; +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.user.rebind.SourceWriter; + +public class MethodProperty extends Property { + + private final JMethod setter; + + private final String getter; + + private MethodProperty(JClassType beanType, JMethod setter, String getter) { + super(getTransportFieldName(setter), beanType, setter + .getParameterTypes()[0]); + this.setter = setter; + this.getter = getter; + } + + @Override + public boolean hasAccessorMethods() { + return getter != null; + } + + public static Collection findProperties(JClassType type) { + Collection properties = new ArrayList(); + + Set getters = new HashSet(); + List setters = getSetters(type, getters); + for (JMethod setter : setters) { + String getter = findGetter(type, setter); + properties.add(new MethodProperty(setter.getEnclosingType(), + setter, getters.contains(getter) ? getter : null)); + } + + return properties; + } + + /** + * Returns a list of all setters found in the beanType or its parent class + * + * @param beanType + * The type to check + * @param getters + * Set that will be filled with names of getters. + * @return A list of setter methods from the class and its parents + */ + private static List getSetters(JClassType beanType, + Set getters) { + 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()) { + // Not getter/setter, skip to next method + continue; + } + String methodName = method.getName(); + if (methodName.startsWith("set") + && method.getParameterTypes().length == 1) { + setterMethods.add(method); + } else if (method.getParameterTypes().length == 0 + && methodName.startsWith("is") + || methodName.startsWith("get")) { + getters.add(methodName); + } + } + beanType = beanType.getSuperclass(); + } + + return setterMethods; + } + + @Override + public void writeGetterBody(TreeLogger logger, SourceWriter w, + String beanVariable) { + String value = String.format("%s.@%s::%s()()", beanVariable, + getBeanType().getQualifiedSourceName(), getter); + w.print("return "); + w.print(boxValue(value)); + w.println(";"); + } + + @Override + public void writeSetterBody(TreeLogger logger, SourceWriter w, + String beanVariable, String valueVariable) { + w.println("%s.@%s::%s(%s)(%s);", beanVariable, getBeanType() + .getQualifiedSourceName(), setter.getName(), setter + .getParameterTypes()[0].getJNISignature(), + unboxValue(valueVariable)); + + } + + private static 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; + } + } + + private static String getTransportFieldName(JMethod setter) { + String baseName = setter.getName().substring(3); + return Character.toLowerCase(baseName.charAt(0)) + + baseName.substring(1); + } + + @Override + public T getAnnotation(Class annotationClass) { + return setter.getAnnotation(annotationClass); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java new file mode 100644 index 0000000000..1c0da9d9e8 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/OnStateChangeVisitor.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +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.vaadin.client.annotations.OnStateChange; +import com.vaadin.shared.ui.Connect; + +/** + * Visits Connector classes and check for methods with @OnStateChange + * annotations. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class OnStateChangeVisitor extends TypeVisitor { + + @Override + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + Connect connectAnnotation = type.getAnnotation(Connect.class); + if (connectAnnotation != null) { + // Find all the annotated methods in all the superclasses + JClassType connector = type; + while (connector != null) { + for (JMethod method : connector.getMethods()) { + if (method.getAnnotation(OnStateChange.class) != null) { + bundle.setNeedsInvoker(connector, method); + bundle.setNeedsOnStateChangeHandler(type, method); + } + } + + connector = connector.getSuperclass(); + } + } + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/Property.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/Property.java new file mode 100644 index 0000000000..0c849bead5 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/Property.java @@ -0,0 +1,127 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import java.lang.annotation.Annotation; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.user.rebind.SourceWriter; + +public abstract class Property implements Comparable { + private final String name; + private final JClassType beanType; + private final JType propertyType; + + protected Property(String name, JClassType beanType, JType propertyType) { + this.name = name; + this.beanType = beanType; + this.propertyType = propertyType; + } + + public String getName() { + return name; + } + + public JType getPropertyType() { + return propertyType; + } + + public String getUnboxedPropertyTypeName() { + JType propertyType = getPropertyType(); + JPrimitiveType primitive = propertyType.isPrimitive(); + if (primitive != null) { + return primitive.getQualifiedBoxedSourceName(); + } else { + return propertyType.getQualifiedSourceName(); + } + } + + public String boxValue(String codeSnippet) { + JPrimitiveType primitive = propertyType.isPrimitive(); + if (primitive == null) { + return codeSnippet; + } else { + return String.format("@%s::valueOf(%s)(%s)", + primitive.getQualifiedBoxedSourceName(), + propertyType.getJNISignature(), codeSnippet); + } + } + + public String unboxValue(String codeSnippet) { + JPrimitiveType primitive = propertyType.isPrimitive(); + if (primitive == null) { + return codeSnippet; + } else { + return String.format("%s.@%s::%sValue()()", codeSnippet, + primitive.getQualifiedBoxedSourceName(), + primitive.getSimpleSourceName()); + } + } + + public JClassType getBeanType() { + return beanType; + } + + public abstract void writeSetterBody(TreeLogger logger, SourceWriter w, + String beanVariable, String valueVariable); + + public abstract void writeGetterBody(TreeLogger logger, SourceWriter w, + String beanVariable); + + public abstract boolean hasAccessorMethods(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof Property) { + Property other = (Property) obj; + return other.getClass() == getClass() + && other.getBeanType().equals(getBeanType()) + && other.getName().equals(getName()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return getClass().hashCode() * 31 ^ 2 + getBeanType().hashCode() * 31 + + getName().hashCode(); + } + + @Override + public int compareTo(Property o) { + int comp = getName().compareTo(o.getName()); + if (comp == 0) { + comp = getBeanType().getQualifiedSourceName().compareTo( + o.getBeanType().getQualifiedSourceName()); + } + if (comp == 0) { + comp = getClass().getCanonicalName().compareTo( + o.getClass().getCanonicalName()); + } + return comp; + } + + public abstract T getAnnotation( + Class annotationClass); + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java new file mode 100644 index 0000000000..2e54d00aab --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/RendererVisitor.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +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.vaadin.client.connectors.AbstractRendererConnector; + +/** + * Generates type data for renderer connectors. + *

    + *
  • Stores the return type of the overridden + * {@link AbstractRendererConnector#getRenderer() getRenderer} method to enable + * automatic creation of an instance of the proper renderer type. + *
  • Stores the presentation type of the connector to enable the + * {@link AbstractRendererConnector#decode(elemental.json.JsonValue) decode} + * method to work without having to implement a "getPresentationType" method. + *
+ * + * @see WidgetInitVisitor + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class RendererVisitor extends TypeVisitor { + + @Override + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + if (ConnectorBundle.isConnectedRendererConnector(type)) { + doRendererType(logger, type, bundle); + doPresentationType(logger, type, bundle); + } + } + + private static void doRendererType(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + // The class in which createRenderer is implemented + JClassType createRendererClass = ConnectorBundle.findInheritedMethod( + type, "createRenderer").getEnclosingType(); + + // Needs GWT constructor if createRenderer is not overridden + if (createRendererClass.getQualifiedSourceName().equals( + AbstractRendererConnector.class.getCanonicalName())) { + + JMethod getRenderer = ConnectorBundle.findInheritedMethod(type, + "getRenderer"); + if (getRenderer.getEnclosingType().getQualifiedSourceName() + .equals(AbstractRendererConnector.class.getCanonicalName())) { + logger.log(Type.ERROR, type.getQualifiedSourceName() + + " must override either createRenderer or getRenderer"); + throw new UnableToCompleteException(); + } + JClassType rendererType = getRenderer.getReturnType().isClass(); + + bundle.setNeedsGwtConstructor(rendererType); + + // Also needs renderer type to find the right GWT constructor + bundle.setNeedsReturnType(type, getRenderer); + + logger.log(Type.DEBUG, "Renderer type of " + type + " is " + + rendererType); + } + } + + private void doPresentationType(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + JType presentationType = getPresentationType(type, logger); + bundle.setPresentationType(type, presentationType); + + bundle.setNeedsSerialize(presentationType); + + logger.log(Type.DEBUG, "Presentation type of " + type + " is " + + presentationType); + } + + private static JType getPresentationType(JClassType type, TreeLogger logger) + throws UnableToCompleteException { + JClassType originalType = type; + while (type != null) { + if (type.getQualifiedBinaryName().equals( + AbstractRendererConnector.class.getName())) { + JParameterizedType parameterized = type.isParameterized(); + if (parameterized == null) { + logger.log( + Type.ERROR, + type.getQualifiedSourceName() + + " must define the generic parameter of the inherited " + + AbstractRendererConnector.class + .getSimpleName()); + throw new UnableToCompleteException(); + } + return parameterized.getTypeArgs()[0]; + } + type = type.getSuperclass(); + } + throw new IllegalArgumentException("The type " + + originalType.getQualifiedSourceName() + " does not extend " + + AbstractRendererConnector.class.getName()); + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java new file mode 100644 index 0000000000..86ece28041 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/ServerRpcVisitor.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.widgetsetutils.metadata; + +import java.util.Set; + +import com.google.gwt.core.ext.TreeLogger; +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.vaadin.client.metadata.TypeDataStore.MethodAttribute; +import com.vaadin.shared.annotations.NoLoadingIndicator; +import com.vaadin.shared.annotations.Delayed; + +public class ServerRpcVisitor extends TypeVisitor { + @Override + public void visitServerRpc(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + ClientRpcVisitor.checkGenericType(logger, type); + bundle.setNeedsProxySupport(type); + + Set superTypes = type + .getFlattenedSupertypeHierarchy(); + for (JClassType subType : superTypes) { + if (subType.isInterface() != null) { + JMethod[] methods = subType.getMethods(); + for (JMethod method : methods) { + ClientRpcVisitor.checkReturnType(logger, method); + + Delayed delayed = method.getAnnotation(Delayed.class); + if (delayed != null) { + bundle.setMethodAttribute(type, method, + MethodAttribute.DELAYED); + if (delayed.lastOnly()) { + bundle.setMethodAttribute(type, method, + MethodAttribute.LAST_ONLY); + } + } + + if (method.getAnnotation(NoLoadingIndicator.class) != null) { + bundle.setMethodAttribute(type, method, + MethodAttribute.NO_LOADING_INDICATOR); + } + + bundle.setNeedsParamTypes(type, method); + + JType[] parameterTypes = method.getParameterTypes(); + for (JType paramType : parameterTypes) { + bundle.setNeedsSerialize(paramType); + } + } + } + } + } +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java new file mode 100644 index 0000000000..046c5c4611 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/StateInitVisitor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JType; + +public class StateInitVisitor extends TypeVisitor { + @Override + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) { + JMethod getState = ConnectorBundle + .findInheritedMethod(type, "getState"); + bundle.setNeedsReturnType(type, getState); + + bundle.setNeedsSerialize(getState.getReturnType()); + + JType stateType = getState.getReturnType(); + bundle.setNeedsGwtConstructor(stateType.isClass()); + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java new file mode 100644 index 0000000000..028e4cc44d --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/TypeVisitor.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.NotFoundException; +import com.google.gwt.core.ext.typeinfo.TypeOracle; + +public abstract class TypeVisitor { + public void init(TypeOracle oracle) throws NotFoundException { + // Default does nothing + } + + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + // Default does nothing + } + + public void visitClientRpc(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + // Default does nothing + } + + public void visitServerRpc(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + // Default does nothing + } + +} diff --git a/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java new file mode 100644 index 0000000000..9a9cac18ba --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.widgetsetutils.metadata; + +import java.util.Collection; + +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.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.shared.annotations.DelegateToWidget; + +public class WidgetInitVisitor extends TypeVisitor { + + @Override + public void visitConnector(TreeLogger logger, JClassType type, + ConnectorBundle bundle) throws UnableToCompleteException { + if (ConnectorBundle.isConnectedComponentConnector(type)) { + // The class in which createWidget is implemented + JClassType createWidgetClass = ConnectorBundle.findInheritedMethod( + type, "createWidget").getEnclosingType(); + + JMethod getWidget = ConnectorBundle.findInheritedMethod(type, + "getWidget"); + JClassType widgetType = getWidget.getReturnType().isClass(); + + // Needs GWT constructor if createWidget is not overridden + if (createWidgetClass.getQualifiedSourceName().equals( + AbstractComponentConnector.class.getCanonicalName())) { + if (getWidget + .getEnclosingType() + .getQualifiedSourceName() + .equals(AbstractComponentConnector.class + .getCanonicalName())) { + logger.log(Type.ERROR, type.getQualifiedSourceName() + + " must override either createWidget or getWidget"); + throw new UnableToCompleteException(); + } + + bundle.setNeedsGwtConstructor(widgetType); + + // Also needs widget type to find the right GWT constructor + bundle.setNeedsReturnType(type, getWidget); + } + + // Check state properties for @DelegateToWidget + JMethod getState = ConnectorBundle.findInheritedMethod(type, + "getState"); + JClassType stateType = getState.getReturnType().isClass(); + + Collection properties = bundle.getProperties(stateType); + for (Property property : properties) { + DelegateToWidget delegateToWidget = property + .getAnnotation(DelegateToWidget.class); + if (delegateToWidget != null) { + // Generate meta data required for @DelegateToWidget + bundle.setNeedsDelegateToWidget(property, stateType); + + // Find the delegate target method + String methodName = DelegateToWidget.Helper + .getDelegateTarget(property.getName(), + delegateToWidget.value()); + JMethod delegatedSetter = ConnectorBundle + .findInheritedMethod(widgetType, methodName, + property.getPropertyType()); + if (delegatedSetter == null) { + logger.log( + Type.ERROR, + widgetType.getName() + + "." + + methodName + + "(" + + property.getPropertyType() + .getSimpleSourceName() + + ") required by @DelegateToWidget for " + + stateType.getName() + "." + + property.getName() + + " can not be found."); + throw new UnableToCompleteException(); + } + bundle.setNeedsInvoker(widgetType, delegatedSetter); + + // GWT code needs widget type to find the target method + bundle.setNeedsReturnType(type, getWidget); + } + } + + } + } +} diff --git a/client-compiler/src/main/java/com/vaadin/tools/CvalAddonsChecker.java b/client-compiler/src/main/java/com/vaadin/tools/CvalAddonsChecker.java new file mode 100644 index 0000000000..aab7231258 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/tools/CvalAddonsChecker.java @@ -0,0 +1,193 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalChecker.LINE; +import static com.vaadin.tools.CvalChecker.computeMajorVersion; +import static com.vaadin.tools.CvalChecker.getErrorMessage; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; +import com.vaadin.tools.CvalChecker.CvalInfo; +import com.vaadin.tools.CvalChecker.CvalServer; +import com.vaadin.tools.CvalChecker.InvalidCvalException; +import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; + +/** + * This class is able to visit all MANIFEST.MF files present in the classpath, + * filter by name, and check if the user has a valid license. + * + * Manifest files should have a few attributes indicating the license type of + * the addon: + *
    + *
  • Implementation-Version: 4.x.x + *
  • AdVaaName: addon_name + *
  • AdVaaLicen: cval, agpl, empty + *
  • AdVaaPkg: package of the widgets in this addon + *
+ * + * The class also have a method to check just one product. + * + * @since 7.3 + */ +public final class CvalAddonsChecker { + + // Manifest attributes + public static final String VAADIN_ADDON_LICENSE = "AdVaaLicen"; + public static final String VAADIN_ADDON_NAME = "AdVaaName"; + public static final String VAADIN_ADDON_WIDGETSET = "Vaadin-Widgetsets"; + public static final String VAADIN_ADDON_VERSION = "Implementation-Version"; + public static final String VAADIN_ADDON_TITLE = "Implementation-Title"; + + // License types + public static final String VAADIN_AGPL = "agpl"; + public static final String VAADIN_CVAL = "cval"; + + private CvalChecker cvalChecker = new CvalChecker(); + private String filterPattern; + + /** + * The constructor. + */ + public CvalAddonsChecker() { + setLicenseProvider(new CvalServer()); + setFilter(".*vaadin.*"); + } + + /** + * Visit all MANIFEST.MF files in the classpath validating licenses. + * + * Return a list of Cval licensed products in order to have enough info to + * generate nag messages in the UI. + */ + public List run() throws InvalidCvalException { + List ret = new ArrayList(); + try { + // Visit all MANIFEST in our classpath + Enumeration manifests = Thread.currentThread() + .getContextClassLoader() + .getResources(JarFile.MANIFEST_NAME); + while (manifests.hasMoreElements()) { + try { + URL url = manifests.nextElement(); + // Discard manifests whose name does not match the filter + // pattern + if (!url.getPath().matches(filterPattern)) { + continue; + } + InputStream is = url.openStream(); + // Should never happen, but we don't want a NPE here + if (is == null) { + continue; + } + // Read manifest attributes + Manifest manifest = new Manifest(is); + Attributes attribs = manifest.getMainAttributes(); + String license = attribs.getValue(VAADIN_ADDON_LICENSE); + String name = attribs.getValue(VAADIN_ADDON_NAME); + String vers = attribs.getValue(VAADIN_ADDON_VERSION) == null ? "" + : attribs.getValue(VAADIN_ADDON_VERSION); + String title = attribs.getValue(VAADIN_ADDON_TITLE) == null ? name + : attribs.getValue(VAADIN_ADDON_TITLE); + + String widgetsets = attribs + .getValue(VAADIN_ADDON_WIDGETSET) == null ? name + : attribs.getValue(VAADIN_ADDON_WIDGETSET); + + if (name == null || license == null) { + continue; + } + if (VAADIN_AGPL.equals(license)) { + // For agpl version we print an info message + printAgplLicense(title, vers); + } else if (VAADIN_CVAL.equals(license)) { + // We only check cval licensed products + CvalInfo info; + try { + info = cvalChecker.validateProduct(name, vers, + title); + printValidLicense(info, title, vers); + } catch (UnreachableCvalServerException e) { + info = CvalChecker.parseJson("{'product':{'name':'" + + name + "'}}"); + printServerUnreachable(title, vers); + } + for (String w : widgetsets.split("[, ]+")) { + ret.add(new CValUiInfo(title, String + .valueOf(computeMajorVersion(vers)), w, + info.getType())); + } + } + } catch (IOException ignored) { + } + } + } catch (IOException ignored) { + } + return ret; + } + + /** + * Set the filter regexp of .jar names which we have to consider. + * + * default is '.*touchkit.*' + */ + public CvalAddonsChecker setFilter(String regexp) { + filterPattern = regexp; + return this; + } + + /* + * Change the license provider, only used in tests. + */ + protected CvalAddonsChecker setLicenseProvider(CvalServer p) { + cvalChecker.setLicenseProvider(p); + return this; + } + + private void printAgplLicense(String name, String version) { + System.out.println(LINE + "\n" + + getErrorMessage("agpl", name, computeMajorVersion(version)) + + "\n" + LINE); + } + + private void printServerUnreachable(String name, String version) { + System.out.println(LINE + + "\n" + + getErrorMessage("unreachable", name, + computeMajorVersion(version)) + "\n" + LINE); + } + + private void printValidLicense(CvalInfo info, String title, String version) { + String msg = info.getMessage(); + if (msg == null) { + String key = "evaluation".equals(info.getType()) ? "evaluation" + : "valid"; + msg = getErrorMessage(key, title, computeMajorVersion(version), + info.getLicensee()); + } + System.out.println("\n" + LINE + "\n" + msg + "\n" + LINE + "\n"); + } +} diff --git a/client-compiler/src/main/java/com/vaadin/tools/CvalChecker.java b/client-compiler/src/main/java/com/vaadin/tools/CvalChecker.java new file mode 100644 index 0000000000..9217781695 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/tools/CvalChecker.java @@ -0,0 +1,510 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static java.lang.Integer.parseInt; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.prefs.Preferences; + +import org.apache.commons.io.IOUtils; + +import elemental.json.JsonException; +import elemental.json.JsonNull; +import elemental.json.JsonObject; +import elemental.json.impl.JsonUtil; + +/** + * This class is able to validate the vaadin CVAL license. + * + * It reads the developer license file and asks the server to validate the + * licenseKey. If the license is invalid it throws an exception with the + * information about the problem and the server response. + * + * @since 7.3 + */ +public final class CvalChecker { + + /* + * Class used for binding the JSON gotten from server. + * + * It is not in a separate f le, so as it is easier to copy into any product + * which does not depend on vaadin core. + * + * We are using elemental.json in order not to use additional dependency + * like auto-beans, gson, etc. + */ + public static class CvalInfo { + + public static class Product { + private JsonObject o; + + public Product(JsonObject o) { + this.o = o; + } + + public String getName() { + return get(o, "name", String.class); + } + + public Integer getVersion() { + return get(o, "version", Integer.class); + } + } + + @SuppressWarnings("unchecked") + private static T get(JsonObject o, String k, Class clz) { + Object ret = null; + try { + if (o == null || o.get(k) == null + || o.get(k) instanceof JsonNull) { + return null; + } + if (clz == String.class) { + ret = o.getString(k); + } else if (clz == JsonObject.class) { + ret = o.getObject(k); + } else if (clz == Integer.class) { + ret = Integer.valueOf((int) o.getNumber(k)); + } else if (clz == Date.class) { + ret = new Date((long) o.getNumber(k)); + } else if (clz == Boolean.class) { + ret = o.getBoolean(k); + } + } catch (JsonException e) { + } + return (T) ret; + } + + private JsonObject o; + + private Product product; + + public CvalInfo(JsonObject o) { + this.o = o; + product = new Product(get(o, "product", JsonObject.class)); + } + + public Boolean getExpired() { + return get(o, "expired", Boolean.class); + } + + public Date getExpiredEpoch() { + return get(o, "expiredEpoch", Date.class); + } + + public String getLicensee() { + return get(o, "licensee", String.class); + } + + public String getLicenseKey() { + return get(o, "licenseKey", String.class); + } + + public String getMessage() { + return get(o, "message", String.class); + } + + public Product getProduct() { + return product; + } + + public String getType() { + return get(o, "type", String.class); + } + + public void setExpiredEpoch(Date expiredEpoch) { + o.put("expiredEpoch", expiredEpoch.getTime()); + } + + public void setMessage(String msg) { + o.put("message", msg); + } + + @Override + public String toString() { + return o.toString(); + } + + public boolean isLicenseExpired() { + return (getExpired() != null && getExpired()) + || (getExpiredEpoch() != null && getExpiredEpoch().before( + new Date())); + } + + public boolean isValidVersion(int majorVersion) { + return getProduct().getVersion() == null + || getProduct().getVersion() >= majorVersion; + + } + + private boolean isValidInfo(String name, String key) { + return getProduct() != null && getProduct().getName() != null + && getLicenseKey() != null + && getProduct().getName().equals(name) + && getLicenseKey().equals(key); + } + } + + /* + * The class with the method for getting json from server side. It is here + * and protected just for replacing it in tests. + */ + public static class CvalServer { + protected String licenseUrl = LICENSE_URL_PROD; + + String askServer(String productName, String productKey, int timeoutMs) + throws IOException { + String url = licenseUrl + productKey; + URLConnection con; + try { + // Send some additional info in the User-Agent string. + String ua = "Cval " + productName + " " + productKey + " " + + getFirstLaunch(); + for (String prop : Arrays.asList("java.vendor.url", + "java.version", "os.name", "os.version", "os.arch")) { + ua += " " + System.getProperty(prop, "-").replace(" ", "_"); + } + con = new URL(url).openConnection(); + con.setRequestProperty("User-Agent", ua); + con.setConnectTimeout(timeoutMs); + con.setReadTimeout(timeoutMs); + String r = IOUtils.toString(con.getInputStream()); + return r; + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + /* + * Get the GWT firstLaunch timestamp. + */ + String getFirstLaunch() { + try { + Class clz = Class + .forName("com.google.gwt.dev.shell.CheckForUpdates"); + return Preferences.userNodeForPackage(clz).get("firstLaunch", + "-"); + } catch (ClassNotFoundException e) { + return "-"; + } + } + } + + /** + * Exception thrown when the user does not have a valid cval license. + */ + public static class InvalidCvalException extends Exception { + private static final long serialVersionUID = 1L; + public final CvalInfo info; + public final String name; + public final String key; + public final String version; + public final String title; + + public InvalidCvalException(String name, String version, String title, + String key, CvalInfo info) { + super(composeMessage(title, version, key, info)); + this.info = info; + this.name = name; + this.key = key; + this.version = version; + this.title = title; + } + + static String composeMessage(String title, String version, String key, + CvalInfo info) { + String msg = ""; + int majorVers = computeMajorVersion(version); + + if (info != null && !info.isValidVersion(majorVers)) { + msg = getErrorMessage("invalid", title, majorVers); + } else if (info != null && info.getMessage() != null) { + msg = info.getMessage().replace("\\n", "\n"); + } else if (info != null && info.isLicenseExpired()) { + String type = "evaluation".equals(info.getType()) ? "Evaluation license" + : "License"; + msg = getErrorMessage("expired", title, majorVers, type); + } else if (key == null) { + msg = getErrorMessage("none", title, majorVers); + } else { + msg = getErrorMessage("invalid", title, majorVers); + } + return msg; + } + } + + /** + * Exception thrown when the license server is unreachable + */ + public static class UnreachableCvalServerException extends Exception { + private static final long serialVersionUID = 1L; + public final String name; + + public UnreachableCvalServerException(String name, Exception e) { + super(e); + this.name = name; + } + } + + public static final String LINE = "----------------------------------------------------------------------------------------------------------------------"; + + static final int GRACE_DAYS_MSECS = 2 * 24 * 60 * 60 * 1000; + + private static final String LICENSE_URL_PROD = "https://tools.vaadin.com/vaadin-license-server/licenses/"; + + /* + * used in tests + */ + static void cacheLicenseInfo(CvalInfo info) { + if (info != null) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + if (info.toString().length() > Preferences.MAX_VALUE_LENGTH) { + // This should never happen since MAX_VALUE_LENGTH is big + // enough. + // But server could eventually send a very big message, so we + // discard it in cache and would use hard-coded messages. + info.setMessage(null); + } + p.put(info.getProduct().getName(), info.toString()); + } + } + + /* + * used in tests + */ + static void deleteCache(String productName) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + p.remove(productName); + } + + /** + * Given a product name returns the name of the file with the license key. + * + * Traditionally we have delivered license keys with a name like + * 'vaadin.touchkit.developer.license' but our database product name is + * 'vaadin-touchkit' so we have to replace '-' by '.' to maintain + * compatibility. + */ + static final String computeLicenseName(String productName) { + return productName.replace("-", ".") + ".developer.license"; + } + + static final int computeMajorVersion(String productVersion) { + return productVersion == null || productVersion.isEmpty() ? 0 + : parseInt(productVersion.replaceFirst("[^\\d]+.*$", "")); + } + + /* + * used in tests + */ + static CvalInfo parseJson(String json) { + if (json == null) { + return null; + } + try { + JsonObject o = JsonUtil.parse(json); + return new CvalInfo(o); + } catch (JsonException e) { + return null; + } + } + + private CvalServer provider; + + /** + * The constructor. + */ + public CvalChecker() { + setLicenseProvider(new CvalServer()); + } + + /** + * Validate whether there is a valid license key for a product. + * + * @param productName + * for example vaadin-touchkit + * @param productVersion + * for instance 4.0.1 + * @return CvalInfo Server response or cache response if server is offline + * @throws InvalidCvalException + * when there is no a valid license for the product + * @throws UnreachableCvalServerException + * when we have license key but server is unreachable + */ + public CvalInfo validateProduct(String productName, String productVersion, + String productTitle) throws InvalidCvalException, + UnreachableCvalServerException { + String key = getDeveloperLicenseKey(productName, productVersion, + productTitle); + + CvalInfo info = null; + if (key != null && !key.isEmpty()) { + info = getCachedLicenseInfo(productName); + if (info != null && !info.isValidInfo(productName, key)) { + deleteCache(productName); + info = null; + } + info = askLicenseServer(productName, key, productVersion, info); + if (info != null && info.isValidInfo(productName, key) + && info.isValidVersion(computeMajorVersion(productVersion)) + && !info.isLicenseExpired()) { + return info; + } + } + + throw new InvalidCvalException(productName, productVersion, + productTitle, key, info); + } + + /* + * Change the license provider, only used in tests. + */ + final CvalChecker setLicenseProvider(CvalServer p) { + provider = p; + return this; + } + + private CvalInfo askLicenseServer(String productName, String productKey, + String productVersion, CvalInfo info) + throws UnreachableCvalServerException { + + int majorVersion = computeMajorVersion(productVersion); + + // If we have a valid license info here, it means that we got it from + // cache. + // We add a grace time when so as if the server is unreachable + // we allow the user to use the product. + if (info != null && info.getExpiredEpoch() != null + && !"evaluation".equals(info.getType())) { + long ts = info.getExpiredEpoch().getTime() + GRACE_DAYS_MSECS; + info.setExpiredEpoch(new Date(ts)); + } + + boolean validCache = info != null + && info.isValidInfo(productName, productKey) + && info.isValidVersion(majorVersion) + && !info.isLicenseExpired(); + + // if we have a validCache we set the timeout smaller + int timeout = validCache ? 2000 : 10000; + + try { + CvalInfo srvinfo = parseJson(provider.askServer(productName + "-" + + productVersion, productKey, timeout)); + if (srvinfo != null && srvinfo.isValidInfo(productName, productKey) + && srvinfo.isValidVersion(majorVersion)) { + // We always cache the info if it is valid although it is + // expired + cacheLicenseInfo(srvinfo); + info = srvinfo; + } + } catch (FileNotFoundException e) { + // 404 + return null; + } catch (Exception e) { + if (info == null) { + throw new UnreachableCvalServerException(productName, e); + } + } + return info; + } + + private CvalInfo getCachedLicenseInfo(String productName) { + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + String json = p.get(productName, ""); + if (!json.isEmpty()) { + CvalInfo info = parseJson(json); + if (info != null) { + return info; + } + } + return null; + } + + private String getDeveloperLicenseKey(String productName, + String productVersion, String productTitle) + throws InvalidCvalException { + String licenseName = computeLicenseName(productName); + + String key = System.getProperty(licenseName); + if (key != null && !key.isEmpty()) { + return key; + } + + try { + String dotLicenseName = "." + licenseName; + String userHome = System.getProperty("user.home"); + for (URL url : new URL[] { + new File(userHome, dotLicenseName).toURI().toURL(), + new File(userHome, licenseName).toURI().toURL(), + URL.class.getResource("/" + dotLicenseName), + URL.class.getResource("/" + licenseName) }) { + if (url != null) { + try { + key = readKeyFromFile(url, + computeMajorVersion(productVersion)); + if (key != null && !(key = key.trim()).isEmpty()) { + return key; + } + } catch (IOException ignored) { + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + throw new InvalidCvalException(productName, productVersion, + productTitle, null, null); + } + + String readKeyFromFile(URL url, int majorVersion) throws IOException { + String majorVersionStr = String.valueOf(majorVersion); + List lines = IOUtils.readLines(url.openStream()); + String defaultKey = null; + for (String line : lines) { + String[] parts = line.split("\\s*=\\s*"); + if (parts.length < 2) { + defaultKey = parts[0].trim(); + } + if (parts[0].equals(majorVersionStr)) { + return parts[1].trim(); + } + } + return defaultKey; + } + + static String getErrorMessage(String key, Object... pars) { + Locale loc = Locale.getDefault(); + ResourceBundle res = ResourceBundle.getBundle( + CvalChecker.class.getName(), loc); + String msg = res.getString(key); + return new MessageFormat(msg, loc).format(pars); + } +} diff --git a/client-compiler/src/main/java/com/vaadin/tools/WidgetsetCompiler.java b/client-compiler/src/main/java/com/vaadin/tools/WidgetsetCompiler.java new file mode 100755 index 0000000000..7c06e9d7af --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/tools/WidgetsetCompiler.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tools; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.server.widgetsetutils.WidgetSetBuilder; + +/** + * A wrapper for the GWT compiler that runs the compiler in a new thread after + * updating the widgetset file. + * + * This class originally existed to allow circumventing a J2SE 5.0 bug (6316197) + * that prevents setting the stack size for the main thread. + * + * This class takes the same command line arguments as the + * com.google.gwt.dev.Compiler class. + * + * A typical invocation would use e.g. the following arguments + * + * "-war WebContent/VAADIN/widgetsets com.vaadin.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 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"); + com.google.gwt.dev.Compiler.main(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-compiler/src/main/resources/com/google/gwt/dev/About.properties b/client-compiler/src/main/resources/com/google/gwt/dev/About.properties new file mode 100644 index 0000000000..364c323136 --- /dev/null +++ b/client-compiler/src/main/resources/com/google/gwt/dev/About.properties @@ -0,0 +1,2 @@ +gwt.version=${project.version} +gwt.svnrev= diff --git a/client-compiler/src/main/resources/com/vaadin/tools/CvalChecker.properties b/client-compiler/src/main/resources/com/vaadin/tools/CvalChecker.properties new file mode 100644 index 0000000000..3f4fd52cb7 --- /dev/null +++ b/client-compiler/src/main/resources/com/vaadin/tools/CvalChecker.properties @@ -0,0 +1,13 @@ +expired={2} for {0} {1} has expired. Get a valid license at vaadin.com/pro + +none=License for {0} {1} not found. Go to vaadin.com/pro for more details. + +invalid=License for {0} {1} is not valid. Get a valid license from vaadin.com/pro + +unreachable=License for {0} {1} has not been validated. Check your network connection. + +evaluation= > Using an evaluation license for {0} {1}. + +valid= > Using a valid license for {0} {1}. + +agpl=Using AGPL version of {0} {1}. Commercial licensing options available at vaadin.com/pro \ No newline at end of file diff --git a/client-compiler/src/test/java/com/vaadin/tools/CvalAddonsCheckerTest.java b/client-compiler/src/test/java/com/vaadin/tools/CvalAddonsCheckerTest.java new file mode 100644 index 0000000000..c6200bc7e0 --- /dev/null +++ b/client-compiler/src/test/java/com/vaadin/tools/CvalAddonsCheckerTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; +import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; +import static com.vaadin.tools.CvalChecker.computeLicenseName; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; +import static com.vaadin.tools.CvalCheckerTest.cacheExists; +import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; +import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; +import static com.vaadin.tools.CvalCheckerTest.productNameApache; +import static com.vaadin.tools.CvalCheckerTest.productNameCval; +import static com.vaadin.tools.CvalCheckerTest.readSystemOut; +import static com.vaadin.tools.CvalCheckerTest.saveCache; +import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; +import com.vaadin.tools.CvalChecker.InvalidCvalException; + +/** + * The CvalAddonsChecker test. + */ +public class CvalAddonsCheckerTest { + + CvalAddonsChecker addonChecker; + private String licenseName; + + @Before + public void setup() { + addonChecker = new CvalAddonsChecker().setLicenseProvider( + validLicenseProvider).setFilter(".*test.*"); + licenseName = computeLicenseName(productNameCval); + + deleteCache(productNameCval); + System.getProperties().remove(licenseName); + + // Set up a new URLClassLoader for the thread + Thread thread = Thread.currentThread(); + thread.setContextClassLoader(new URLClassLoader(new URL[0], null)); + } + + @Test + public void testRunChecker() throws Exception { + // Create a product .jar with a cval license non required and add to our + // classpath + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + // Remove other products in case other tests added them previously + addLicensedJarToClasspath(productNameAgpl, null); + addLicensedJarToClasspath(productNameApache, null); + + // No license + // -> Break compilation + System.getProperties().remove(licenseName); + addonChecker.setLicenseProvider(validLicenseProvider); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + } + Assert.assertFalse(cacheExists(productNameCval)); + + // We have a license that has never been validated from the server and + // we are offline + // -> Show a message on compile time (“Your license for TouchKit 4 has + // not been validated.") + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(readSystemOut().contains("has not been validated")); + Assert.assertFalse(cacheExists(productNameCval)); + + // Valid license has previously been validated from the server and we + // are offline + // -> Use the cached server response + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(validLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(cacheExists(productNameCval)); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + addonChecker.run(); + + // Expired license and we are offline + // -> If it has expired less than 14 days ago, just work with no nag + // messages + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), + "normal"); + captureSystemOut(); + addonChecker.run(); + + // Expired license and we are offline + // -> After 14 days, interpret it as expired license + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS * 2), + "normal"); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + } + + // Invalid evaluation license + // -> Fail compilation with a message + // "Your evaluation license for TouchKit 4 is not valid" + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), + "evaluation"); + try { + addonChecker.run(); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.assertTrue(expected.getMessage().contains("expired")); + } + + // Valid evaluation license + // -> The choice on whether to show the message is generated in + // widgetset + // compilation phase. No license checks are done in application runtime. + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(unreachableLicenseProvider); + setCacheFileTs(System.currentTimeMillis() + GRACE_DAYS_MSECS, + "evaluation"); + List uiInfo = addonChecker.run(); + Assert.assertEquals(1, uiInfo.size()); + Assert.assertEquals("Test " + productNameCval, uiInfo.get(0).product); + Assert.assertEquals("evaluation", uiInfo.get(0).type); + + // Valid real license + // -> Work as expected + // -> Show info message “Using TouchKit 4 license + // 312-312321-321312-3-12-312-312 + // licensed to (1 developer license)" + System.setProperty(licenseName, VALID_KEY); + addonChecker.setLicenseProvider(validLicenseProvider); + captureSystemOut(); + addonChecker.run(); + Assert.assertTrue(readSystemOut().contains("valid")); + } + + @Test + public void validateMultipleLicenses() throws Exception { + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); + addLicensedJarToClasspath(productNameApache, "apache"); + + // We have a valid license for all products + System.setProperty(licenseName, VALID_KEY); + captureSystemOut(); + addonChecker.run(); + String out = readSystemOut(); + Assert.assertTrue(out.contains("valid")); + Assert.assertTrue(out.contains("AGPL")); + Assert.assertTrue(cacheExists(productNameCval)); + } + + private void setCacheFileTs(long expireTs, String type) { + saveCache(productNameCval, null, false, expireTs, type); + } + +} diff --git a/client-compiler/src/test/java/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java b/client-compiler/src/test/java/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java new file mode 100644 index 0000000000..01cfe2087e --- /dev/null +++ b/client-compiler/src/test/java/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java @@ -0,0 +1,273 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; +import static com.vaadin.tools.CvalChecker.computeLicenseName; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalCheckerTest.INVALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; +import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; +import static com.vaadin.tools.CvalCheckerTest.cachedPreferences; +import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; +import static com.vaadin.tools.CvalCheckerTest.expiredLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; +import static com.vaadin.tools.CvalCheckerTest.productNameCval; +import static com.vaadin.tools.CvalCheckerTest.readSystemOut; +import static com.vaadin.tools.CvalCheckerTest.restoreSystemOut; +import static com.vaadin.tools.CvalCheckerTest.saveCache; +import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validEvaluationLicenseProvider; +import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.tools.CvalChecker.CvalServer; + +/** + * Tests for Use Cases + */ +public class CvalAddonstCheckerUseCasesTest { + + enum License { + NONE, EVAL, INVALID, REAL, EVAL_EXPIRED, REAL_EXPIRED + }; + + enum Version { + AGPL, CVAL + }; + + enum Validated { + YES, NO, OLD_KEY + }; + + enum Network { + ON, OFF + }; + + enum Compile { + YES, NO + }; + + enum Cached { + YES, NO + }; + + enum Message { + AGPL("AGPL"), VALID(">.* valid"), INVALID("not valid"), NO_LICENSE( + "not found"), NO_VALIDATED("has not been validated"), EXPIRED( + "has expired"), EVALUATION("evaluation"); + + String msg; + + Message(String s) { + msg = s; + } + } + + @Before + public void setUp() { + // Set up a new URLClassLoader for the thread + Thread thread = Thread.currentThread(); + thread.setContextClassLoader(new URLClassLoader(new URL[0], null)); + } + + /* TODO: Use more descriptive test names */ + + @Test + public void testUseCase1() throws Exception { + useCase(1, License.NONE, Version.AGPL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.AGPL); + } + + @Test + public void testUseCase2() throws Exception { + useCase(2, License.NONE, Version.CVAL, Validated.NO, Network.ON, + Compile.NO, Cached.NO, Message.NO_LICENSE); + } + + @Test + public void testUseCase3() throws Exception { + useCase(3, License.NONE, Version.CVAL, Validated.NO, Network.OFF, + Compile.NO, Cached.NO, Message.NO_LICENSE); + } + + @Test + public void testUseCase4() throws Exception { + useCase(4, License.EVAL, Version.CVAL, Validated.NO, Network.ON, + Compile.YES, Cached.YES, Message.EVALUATION); + } + + @Test + public void testUseCase5() throws Exception { + useCase(5, License.INVALID, Version.CVAL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.NO_VALIDATED); + } + + @Test + public void testUseCase6() throws Exception { + useCase(6, License.INVALID, Version.CVAL, Validated.NO, Network.ON, + Compile.NO, Cached.NO, Message.INVALID); + } + + @Test + public void testUseCase7() throws Exception { + useCase(7, License.REAL, Version.CVAL, Validated.NO, Network.ON, + Compile.YES, Cached.YES, Message.VALID); + } + + @Test + public void testUseCase8() throws Exception { + useCase(8, License.REAL, Version.CVAL, Validated.NO, Network.OFF, + Compile.YES, Cached.NO, Message.NO_VALIDATED); + } + + @Test + public void testUseCase9() throws Exception { + useCase(9, License.REAL, Version.CVAL, Validated.YES, Network.OFF, + Compile.YES, Cached.YES, Message.VALID); + } + + @Test + public void testUseCase10() throws Exception { + useCase(10, License.EVAL_EXPIRED, Version.CVAL, Validated.NO, + Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); + } + + @Test + public void testUseCase11() throws Exception { + useCase(11, License.EVAL_EXPIRED, Version.CVAL, Validated.YES, + Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); + } + + @Test + public void testUseCase12() throws Exception { + useCase(12, License.REAL_EXPIRED, Version.CVAL, Validated.YES, + Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); + } + + @Test + public void testUseCase13() throws Exception { + useCase(13, License.REAL_EXPIRED, Version.CVAL, Validated.NO, + Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); + } + + @Test + public void testUseCase14() throws Exception { + useCase(14, License.INVALID, Version.CVAL, Validated.OLD_KEY, + Network.OFF, Compile.YES, Cached.NO, Message.NO_VALIDATED); + } + + @Test + public void testMultipleLicenseUseCase15() throws Exception { + addLicensedJarToClasspath("test.foo", VAADIN_CVAL); + System.setProperty(computeLicenseName("test.foo"), VALID_KEY); + useCase(15, License.REAL, Version.CVAL, Validated.YES, Network.OFF, + Compile.YES, Cached.YES, Message.NO_VALIDATED); + } + + @Test + public void testMultipleLicenseUseCase16() throws Exception { + addLicensedJarToClasspath("test.foo", VAADIN_CVAL); + System.setProperty(computeLicenseName("test.foo"), VALID_KEY); + useCase(16, License.REAL, Version.CVAL, Validated.YES, Network.ON, + Compile.NO, Cached.YES, Message.INVALID); + } + + private void useCase(int number, License lic, Version ver, Validated val, + Network net, Compile res, Cached cached, Message msg) + throws Exception { + + if (ver == Version.AGPL) { + addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); + addLicensedJarToClasspath(productNameCval, null); + } else { + addLicensedJarToClasspath(productNameAgpl, null); + addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); + } + + String licenseName = computeLicenseName(productNameCval); + + if (lic == License.NONE) { + System.getProperties().remove(licenseName); + } else if (lic == License.INVALID) { + System.setProperty(licenseName, INVALID_KEY); + } else { + System.setProperty(licenseName, VALID_KEY); + } + + if (val == Validated.NO) { + deleteCache(productNameCval); + } else { + String type = lic == License.EVAL || lic == License.EVAL_EXPIRED ? "evaluation" + : null; + Boolean expired = lic == License.EVAL_EXPIRED + || lic == License.REAL_EXPIRED ? true : null; + String key = val == Validated.OLD_KEY ? "oldkey" : null; + saveCache(productNameCval, key, expired, null, type); + } + + CvalServer licenseProvider = validLicenseProvider; + if (net == Network.OFF) { + licenseProvider = unreachableLicenseProvider; + } else if (lic == License.EVAL_EXPIRED || lic == License.REAL_EXPIRED) { + licenseProvider = expiredLicenseProvider; + } else if (lic == License.EVAL) { + licenseProvider = validEvaluationLicenseProvider; + } + + CvalAddonsChecker addonChecker = new CvalAddonsChecker(); + addonChecker.setLicenseProvider(licenseProvider).setFilter(".*test.*"); + + captureSystemOut(); + + String testNumber = "Test #" + number + " "; + String message; + try { + addonChecker.run(); + message = readSystemOut(); + if (res == Compile.NO) { + Assert.fail(testNumber + "Exception not thrown:" + message); + } + } catch (Exception e) { + restoreSystemOut(); + message = e.getMessage(); + if (res == Compile.YES) { + Assert.fail(testNumber + "Unexpected Exception: " + + e.getMessage()); + } + } + + // System.err.println("\n> " + testNumber + " " + lic + " " + ver + " " + // + val + " " + net + " " + res + " " + cached + "\n" + message); + + Assert.assertTrue(testNumber + "Fail:\n" + message + + "\nDoes not match:" + msg.msg, + message.matches("(?s).*" + msg.msg + ".*")); + + String c = cachedPreferences(productNameCval); + Assert.assertTrue(testNumber + "Fail: cacheExists != " + + (cached == Cached.YES) + "\n " + c, + (c != null) == (cached == Cached.YES)); + } +} diff --git a/client-compiler/src/test/java/com/vaadin/tools/CvalCheckerTest.java b/client-compiler/src/test/java/com/vaadin/tools/CvalCheckerTest.java new file mode 100644 index 0000000000..6e1a8e57ed --- /dev/null +++ b/client-compiler/src/test/java/com/vaadin/tools/CvalCheckerTest.java @@ -0,0 +1,495 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tools; + +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_LICENSE; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_NAME; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_TITLE; +import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_VERSION; +import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; +import static com.vaadin.tools.CvalChecker.cacheLicenseInfo; +import static com.vaadin.tools.CvalChecker.deleteCache; +import static com.vaadin.tools.CvalChecker.parseJson; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.prefs.Preferences; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.tools.CvalChecker.CvalInfo; +import com.vaadin.tools.CvalChecker.CvalServer; +import com.vaadin.tools.CvalChecker.InvalidCvalException; +import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; + +/** + * The CvalChecker test. + */ +public class CvalCheckerTest { + + static final String productNameCval = "test.cval"; + static final String productTitleCval = "Vaadin Test"; + static final String productNameAgpl = "test.agpl"; + static final String productTitleAgpl = "Vaadin Test"; + static final String productNameApache = "test.apache"; + static final String VALID_KEY = "valid"; + static final String INVALID_KEY = "invalid"; + + static final String responseJson = "{'licenseKey':'" + VALID_KEY + "'," + + "'licensee':'Test User','type':'normal'," + + "'expiredEpoch':1893511225000," + "'product':{'name':'" + + productNameCval + "', 'version': 2}}"; + + static final String responseJsonWithNullVersion = "{'licenseKey':'" + + VALID_KEY + "'," + "'licensee':'Test User','type':'normal'," + + "'expiredEpoch':1893511225000," + "'product':{'name':'" + + productNameCval + "', 'version': null}}"; + + private static ByteArrayOutputStream outContent; + + // A provider returning a valid license if productKey is valid or null if + // invalid + static final CvalServer validLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return VALID_KEY.equals(productKey) ? responseJson : null; + } + }; + // A provider returning a valid evaluation license + static final CvalServer validEvaluationLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replace("normal", "evaluation"); + } + }; + // A provider returning an expired license with a server message + static final CvalServer expiredLicenseProviderWithMessage = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson + .replace("'expired", + "'message':'Custom\\\\nServer\\\\nMessage','expired':true,'expired"); + } + }; + // A provider returning an expired license with a server message + static final CvalServer expiredLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replace("'expired", "'expired':true,'expired"); + } + }; + // A provider returning an expired epoch license + static final CvalServer expiredEpochLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + long ts = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; + return responseJson.replace("1893511225000", "" + ts); + } + }; + // A provider returning an unlimited license + static final CvalServer unlimitedLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) { + return responseJson.replaceFirst("1893511225000", "null"); + } + }; + // An unreachable provider + static final CvalServer unreachableLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) + throws IOException { + // Normally there is no route for this ip in public routers, so we + // should get a timeout. + licenseUrl = "http://localhost:9999/"; + return super.askServer(productName, productKey, 1000); + } + }; + // A provider with 'null' in the version field + static final CvalServer nullVersionLicenseProvider = new CvalServer() { + @Override + String askServer(String productName, String productKey, int timeout) + throws IOException { + return responseJsonWithNullVersion; + } + }; + + private CvalChecker licenseChecker; + private String licenseName; + + @Before + public void setup() { + licenseChecker = new CvalChecker() + .setLicenseProvider(validLicenseProvider); + licenseName = CvalChecker.computeLicenseName(productNameCval); + System.getProperties().remove(licenseName); + deleteCache(productNameCval); + } + + @Test + public void testValidateProduct() throws Exception { + deleteCache(productNameCval); + + // If the license key in our environment is null, throw an exception + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // If the license key is empty, throw an exception + System.setProperty(licenseName, ""); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // If license key is invalid, throw an exception + System.setProperty(licenseName, "invalid"); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail if version is bigger + System.setProperty(licenseName, VALID_KEY); + try { + licenseChecker.validateProduct(productNameCval, "3.0", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + assertTrue(expected.getMessage().contains("is not valid")); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Success if license key and version are valid + System.setProperty(licenseName, VALID_KEY); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Success if license and cache file are valid, although the license + // server is offline + licenseChecker.setLicenseProvider(unreachableLicenseProvider); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Fail if license key changes although cache file were validated + // previously and it is ok, we are offline + try { + System.setProperty(licenseName, INVALID_KEY); + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.fail(); + } catch (UnreachableCvalServerException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail with unreachable exception if license has never verified and + // server is offline + try { + System.setProperty(licenseName, VALID_KEY); + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + Assert.fail(); + } catch (UnreachableCvalServerException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertFalse(cacheExists(productNameCval)); + + // Fail when expired flag comes in the server response, although the + // expired is valid. + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(expiredLicenseProviderWithMessage); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + // Check that we use server customized message if it comes + Assert.assertTrue(expected.getMessage().contains("Custom")); + } + Assert.assertTrue(cacheExists(productNameCval)); + + // Check an unlimited license + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(unlimitedLicenseProvider); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + + // Fail if expired flag does not come, but expired epoch is in the past + System.setProperty(licenseName, VALID_KEY); + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(expiredEpochLicenseProvider); + try { + licenseChecker.validateProduct(productNameCval, "2.1", + productTitleCval); + Assert.fail(); + } catch (InvalidCvalException expected) { + assertEquals(productNameCval, expected.name); + } + Assert.assertTrue(cacheExists(productNameCval)); + + deleteCache(productNameCval); + licenseChecker.setLicenseProvider(nullVersionLicenseProvider); + licenseChecker + .validateProduct(productNameCval, "2.1", productTitleCval); + Assert.assertTrue(cacheExists(productNameCval)); + } + + /* + * Creates a new .jar file with a MANIFEST.MF with all vaadin license info + * attributes set, and add the .jar to the classpath + */ + static void addLicensedJarToClasspath(String productName, String licenseType) + throws Exception { + // Create a manifest with Vaadin CVAL license + Manifest testManifest = new Manifest(); + testManifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_LICENSE, + licenseType); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_NAME, + productName); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_TITLE, + "Test " + productName); + testManifest.getMainAttributes().putValue(VAADIN_ADDON_VERSION, "2"); + + // Create a temporary Jar + File testJarFile = File.createTempFile("vaadin." + productName, ".jar"); + testJarFile.deleteOnExit(); + JarOutputStream target = new JarOutputStream(new FileOutputStream( + testJarFile), testManifest); + target.close(); + + // Add the new jar to our classpath (use reflection) + URL url = testJarFile.toURI().toURL(); + final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", + new Class[] { URL.class }); + addURL.setAccessible(true); + final URLClassLoader urlClassLoader = (URLClassLoader) Thread + .currentThread().getContextClassLoader(); + addURL.invoke(urlClassLoader, new Object[] { url }); + } + + static boolean cacheExists(String productName) { + return cachedPreferences(productName) != null; + } + + static String cachedPreferences(String productName) { + // ~/Library/Preferences/com.apple.java.util.prefs.plist + // .java/.userPrefs/com/google/gwt/dev/shell/prefs.xml + // HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs + Preferences p = Preferences.userNodeForPackage(CvalInfo.class); + return p.get(productName, null); + } + + static void saveCache(String productName, String key, Boolean expired, + Long expireTs, String type) { + String json = responseJson.replace(productNameCval, productName); + if (expired != null && expired) { + expireTs = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; + } + if (expireTs != null) { + json = json.replace("1893511225000", "" + expireTs); + } + if (key != null) { + json = json.replace(VALID_KEY, key); + } + if (type != null) { + json = json.replace("normal", type); + + } + cacheLicenseInfo(parseJson(json)); + } + + static void captureSystemOut() { + outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + } + + static String readSystemOut() { + restoreSystemOut(); + return outContent.toString(); + } + + static void restoreSystemOut() { + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } + + @Test(expected = FileNotFoundException.class) + public void testReadKeyFromFile_NonexistingLicenseFile() throws Exception { + licenseChecker.readKeyFromFile(new URL("file:///foobar.baz"), 4); + } + + @Test + public void testReadKeyFromFile_LicenseFileEmpty() throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + + assertNull(licenseChecker.readKeyFromFile(tmpLicenseFile.toURI() + .toURL(), 4)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_LicenseFileHasSingleUnidentifiedKey() + throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("this-is-a-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_LicenseFileHasSingleIdentifiedKey() + throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("4=this-is-a-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_LicenseFileHasMultipleKeys() + throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("4=this-is-a-license"); + out.println("5=this-is-another-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 5)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_LicenseFileHasMultipleKeysWithWhitespace() + throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("4 = this-is-a-license"); + out.println("5 = this-is-another-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 5)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_RequestedVersionMissing() throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("4 = this-is-a-license"); + out.println("5 = this-is-another-license"); + out.close(); + + assertNull(licenseChecker.readKeyFromFile(tmpLicenseFile.toURI() + .toURL(), 3)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_FallbackToDefaultKey() throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("this-is-a-license"); + out.println("5 = this-is-another-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 3)); + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 5)); + + tmpLicenseFile.delete(); + } + + @Test + public void testReadKeyFromFile_FallbackToDefaultKeyReversed() + throws Exception { + File tmpLicenseFile = File.createTempFile("license", "lic"); + PrintWriter out = new PrintWriter(tmpLicenseFile); + out.println("5 = this-is-another-license"); + out.println("this-is-a-license"); + out.close(); + + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 3)); + assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 4)); + assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( + tmpLicenseFile.toURI().toURL(), 5)); + + tmpLicenseFile.delete(); + } +} diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java deleted file mode 100644 index 1fb9413ee4..0000000000 --- a/client-compiler/tests/src/com/vaadin/tools/CvalAddonsCheckerTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.tools; - -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; -import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; -import static com.vaadin.tools.CvalChecker.computeLicenseName; -import static com.vaadin.tools.CvalChecker.deleteCache; -import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; -import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; -import static com.vaadin.tools.CvalCheckerTest.cacheExists; -import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; -import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; -import static com.vaadin.tools.CvalCheckerTest.productNameApache; -import static com.vaadin.tools.CvalCheckerTest.productNameCval; -import static com.vaadin.tools.CvalCheckerTest.readSystemOut; -import static com.vaadin.tools.CvalCheckerTest.saveCache; -import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; -import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo; -import com.vaadin.tools.CvalChecker.InvalidCvalException; - -/** - * The CvalAddonsChecker test. - */ -public class CvalAddonsCheckerTest { - - CvalAddonsChecker addonChecker; - private String licenseName; - - @Before - public void setup() { - addonChecker = new CvalAddonsChecker().setLicenseProvider( - validLicenseProvider).setFilter(".*test.*"); - licenseName = computeLicenseName(productNameCval); - - deleteCache(productNameCval); - System.getProperties().remove(licenseName); - } - - @Test - public void testRunChecker() throws Exception { - // Create a product .jar with a cval license non required and add to our - // classpath - addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); - // Remove other products in case other tests added them previously - addLicensedJarToClasspath(productNameAgpl, null); - addLicensedJarToClasspath(productNameApache, null); - - // No license - // -> Break compilation - System.getProperties().remove(licenseName); - addonChecker.setLicenseProvider(validLicenseProvider); - try { - addonChecker.run(); - Assert.fail(); - } catch (InvalidCvalException expected) { - } - Assert.assertFalse(cacheExists(productNameCval)); - - // We have a license that has never been validated from the server and - // we are offline - // -> Show a message on compile time (“Your license for TouchKit 4 has - // not been validated.”) - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(unreachableLicenseProvider); - captureSystemOut(); - addonChecker.run(); - Assert.assertTrue(readSystemOut().contains("has not been validated")); - Assert.assertFalse(cacheExists(productNameCval)); - - // Valid license has previously been validated from the server and we - // are offline - // -> Use the cached server response - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(validLicenseProvider); - captureSystemOut(); - addonChecker.run(); - Assert.assertTrue(cacheExists(productNameCval)); - addonChecker.setLicenseProvider(unreachableLicenseProvider); - addonChecker.run(); - - // Expired license and we are offline - // -> If it has expired less than 14 days ago, just work with no nag - // messages - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(unreachableLicenseProvider); - setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), - "normal"); - captureSystemOut(); - addonChecker.run(); - - // Expired license and we are offline - // -> After 14 days, interpret it as expired license - setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS * 2), - "normal"); - try { - addonChecker.run(); - Assert.fail(); - } catch (InvalidCvalException expected) { - } - - // Invalid evaluation license - // -> Fail compilation with a message - // "Your evaluation license for TouchKit 4 is not valid" - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(unreachableLicenseProvider); - setCacheFileTs(System.currentTimeMillis() - (GRACE_DAYS_MSECS / 2), - "evaluation"); - try { - addonChecker.run(); - Assert.fail(); - } catch (InvalidCvalException expected) { - Assert.assertTrue(expected.getMessage().contains("expired")); - } - - // Valid evaluation license - // -> The choice on whether to show the message is generated in - // widgetset - // compilation phase. No license checks are done in application runtime. - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(unreachableLicenseProvider); - setCacheFileTs(System.currentTimeMillis() + GRACE_DAYS_MSECS, - "evaluation"); - List uiInfo = addonChecker.run(); - Assert.assertEquals(1, uiInfo.size()); - Assert.assertEquals("Test " + productNameCval, uiInfo.get(0).product); - Assert.assertEquals("evaluation", uiInfo.get(0).type); - - // Valid real license - // -> Work as expected - // -> Show info message “Using TouchKit 4 license - // 312-312321-321312-3-12-312-312 - // licensed to (1 developer license)” - System.setProperty(licenseName, VALID_KEY); - addonChecker.setLicenseProvider(validLicenseProvider); - captureSystemOut(); - addonChecker.run(); - Assert.assertTrue(readSystemOut().contains("valid")); - } - - @Test - public void validateMultipleLicenses() throws Exception { - addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); - addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); - addLicensedJarToClasspath(productNameApache, "apache"); - - // We have a valid license for all products - System.setProperty(licenseName, VALID_KEY); - captureSystemOut(); - addonChecker.run(); - String out = readSystemOut(); - Assert.assertTrue(out.contains("valid")); - Assert.assertTrue(out.contains("AGPL")); - Assert.assertTrue(cacheExists(productNameCval)); - } - - private void setCacheFileTs(long expireTs, String type) { - saveCache(productNameCval, null, false, expireTs, type); - } - -} diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java deleted file mode 100644 index 89c8fc1f81..0000000000 --- a/client-compiler/tests/src/com/vaadin/tools/CvalAddonstCheckerUseCasesTest.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.tools; - -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_AGPL; -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_CVAL; -import static com.vaadin.tools.CvalChecker.computeLicenseName; -import static com.vaadin.tools.CvalChecker.deleteCache; -import static com.vaadin.tools.CvalCheckerTest.INVALID_KEY; -import static com.vaadin.tools.CvalCheckerTest.VALID_KEY; -import static com.vaadin.tools.CvalCheckerTest.addLicensedJarToClasspath; -import static com.vaadin.tools.CvalCheckerTest.cachedPreferences; -import static com.vaadin.tools.CvalCheckerTest.captureSystemOut; -import static com.vaadin.tools.CvalCheckerTest.expiredLicenseProvider; -import static com.vaadin.tools.CvalCheckerTest.productNameAgpl; -import static com.vaadin.tools.CvalCheckerTest.productNameCval; -import static com.vaadin.tools.CvalCheckerTest.readSystemOut; -import static com.vaadin.tools.CvalCheckerTest.restoreSystemOut; -import static com.vaadin.tools.CvalCheckerTest.saveCache; -import static com.vaadin.tools.CvalCheckerTest.unreachableLicenseProvider; -import static com.vaadin.tools.CvalCheckerTest.validEvaluationLicenseProvider; -import static com.vaadin.tools.CvalCheckerTest.validLicenseProvider; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.tools.CvalChecker.CvalServer; - -/** - * Tests for Use Cases - */ -public class CvalAddonstCheckerUseCasesTest { - - enum License { - NONE, EVAL, INVALID, REAL, EVAL_EXPIRED, REAL_EXPIRED - }; - - enum Version { - AGPL, CVAL - }; - - enum Validated { - YES, NO, OLD_KEY - }; - - enum Network { - ON, OFF - }; - - enum Compile { - YES, NO - }; - - enum Cached { - YES, NO - }; - - enum Message { - AGPL("AGPL"), VALID(">.* valid"), INVALID("not valid"), NO_LICENSE( - "not found"), NO_VALIDATED("has not been validated"), EXPIRED( - "has expired"), EVALUATION("evaluation"); - - String msg; - - Message(String s) { - msg = s; - } - } - - @Test - public void testUseCases() throws Exception { - useCase(1, License.NONE, Version.AGPL, Validated.NO, Network.OFF, - Compile.YES, Cached.NO, Message.AGPL); - - useCase(2, License.NONE, Version.CVAL, Validated.NO, Network.ON, - Compile.NO, Cached.NO, Message.NO_LICENSE); - - useCase(3, License.NONE, Version.CVAL, Validated.NO, Network.OFF, - Compile.NO, Cached.NO, Message.NO_LICENSE); - - useCase(4, License.EVAL, Version.CVAL, Validated.NO, Network.ON, - Compile.YES, Cached.YES, Message.EVALUATION); - - useCase(5, License.INVALID, Version.CVAL, Validated.NO, Network.OFF, - Compile.YES, Cached.NO, Message.NO_VALIDATED); - - useCase(6, License.INVALID, Version.CVAL, Validated.NO, Network.ON, - Compile.NO, Cached.NO, Message.INVALID); - - useCase(7, License.REAL, Version.CVAL, Validated.NO, Network.ON, - Compile.YES, Cached.YES, Message.VALID); - - useCase(8, License.REAL, Version.CVAL, Validated.NO, Network.OFF, - Compile.YES, Cached.NO, Message.NO_VALIDATED); - - useCase(9, License.REAL, Version.CVAL, Validated.YES, Network.OFF, - Compile.YES, Cached.YES, Message.VALID); - - useCase(10, License.EVAL_EXPIRED, Version.CVAL, Validated.NO, - Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); - - useCase(11, License.EVAL_EXPIRED, Version.CVAL, Validated.YES, - Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); - - useCase(12, License.REAL_EXPIRED, Version.CVAL, Validated.YES, - Network.OFF, Compile.NO, Cached.YES, Message.EXPIRED); - - useCase(13, License.REAL_EXPIRED, Version.CVAL, Validated.NO, - Network.ON, Compile.NO, Cached.YES, Message.EXPIRED); - - useCase(14, License.INVALID, Version.CVAL, Validated.OLD_KEY, - Network.OFF, Compile.YES, Cached.NO, Message.NO_VALIDATED); - } - - @Test - public void testMultipleLicenseUseCases() throws Exception { - addLicensedJarToClasspath("test.foo", VAADIN_CVAL); - System.setProperty(computeLicenseName("test.foo"), VALID_KEY); - - useCase(15, License.REAL, Version.CVAL, Validated.YES, Network.OFF, - Compile.YES, Cached.YES, Message.NO_VALIDATED); - - useCase(16, License.REAL, Version.CVAL, Validated.YES, Network.ON, - Compile.NO, Cached.YES, Message.INVALID); - } - - private void useCase(int number, License lic, Version ver, Validated val, - Network net, Compile res, Cached cached, Message msg) - throws Exception { - - if (ver == Version.AGPL) { - addLicensedJarToClasspath(productNameAgpl, VAADIN_AGPL); - addLicensedJarToClasspath(productNameCval, null); - } else { - addLicensedJarToClasspath(productNameAgpl, null); - addLicensedJarToClasspath(productNameCval, VAADIN_CVAL); - } - - String licenseName = computeLicenseName(productNameCval); - - if (lic == License.NONE) { - System.getProperties().remove(licenseName); - } else if (lic == License.INVALID) { - System.setProperty(licenseName, INVALID_KEY); - } else { - System.setProperty(licenseName, VALID_KEY); - } - - if (val == Validated.NO) { - deleteCache(productNameCval); - } else { - String type = lic == License.EVAL || lic == License.EVAL_EXPIRED ? "evaluation" - : null; - Boolean expired = lic == License.EVAL_EXPIRED - || lic == License.REAL_EXPIRED ? true : null; - String key = val == Validated.OLD_KEY ? "oldkey" : null; - saveCache(productNameCval, key, expired, null, type); - } - - CvalServer licenseProvider = validLicenseProvider; - if (net == Network.OFF) { - licenseProvider = unreachableLicenseProvider; - } else if (lic == License.EVAL_EXPIRED || lic == License.REAL_EXPIRED) { - licenseProvider = expiredLicenseProvider; - } else if (lic == License.EVAL) { - licenseProvider = validEvaluationLicenseProvider; - } - - CvalAddonsChecker addonChecker = new CvalAddonsChecker(); - addonChecker.setLicenseProvider(licenseProvider).setFilter(".*test.*"); - - captureSystemOut(); - - String testNumber = "Test #" + number + " "; - String message; - try { - addonChecker.run(); - message = readSystemOut(); - if (res == Compile.NO) { - Assert.fail(testNumber + "Exception not thrown:" + message); - } - } catch (Exception e) { - restoreSystemOut(); - message = e.getMessage(); - if (res == Compile.YES) { - Assert.fail(testNumber + "Unexpected Exception: " - + e.getMessage()); - } - } - - // System.err.println("\n> " + testNumber + " " + lic + " " + ver + " " - // + val + " " + net + " " + res + " " + cached + "\n" + message); - - Assert.assertTrue(testNumber + "Fail:\n" + message - + "\nDoes not match:" + msg.msg, - message.matches("(?s).*" + msg.msg + ".*")); - - String c = cachedPreferences(productNameCval); - Assert.assertTrue(testNumber + "Fail: cacheExists != " - + (cached == Cached.YES) + "\n " + c, - (c != null) == (cached == Cached.YES)); - } -} diff --git a/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java b/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java deleted file mode 100644 index 6da455b2f6..0000000000 --- a/client-compiler/tests/src/com/vaadin/tools/CvalCheckerTest.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.tools; - -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_LICENSE; -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_NAME; -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_TITLE; -import static com.vaadin.tools.CvalAddonsChecker.VAADIN_ADDON_VERSION; -import static com.vaadin.tools.CvalChecker.GRACE_DAYS_MSECS; -import static com.vaadin.tools.CvalChecker.cacheLicenseInfo; -import static com.vaadin.tools.CvalChecker.deleteCache; -import static com.vaadin.tools.CvalChecker.parseJson; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.prefs.Preferences; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.tools.CvalChecker.CvalInfo; -import com.vaadin.tools.CvalChecker.CvalServer; -import com.vaadin.tools.CvalChecker.InvalidCvalException; -import com.vaadin.tools.CvalChecker.UnreachableCvalServerException; - -/** - * The CvalChecker test. - */ -public class CvalCheckerTest { - - static final String productNameCval = "test.cval"; - static final String productTitleCval = "Vaadin Test"; - static final String productNameAgpl = "test.agpl"; - static final String productTitleAgpl = "Vaadin Test"; - static final String productNameApache = "test.apache"; - static final String VALID_KEY = "valid"; - static final String INVALID_KEY = "invalid"; - - static final String responseJson = "{'licenseKey':'" + VALID_KEY + "'," - + "'licensee':'Test User','type':'normal'," - + "'expiredEpoch':1893511225000," + "'product':{'name':'" - + productNameCval + "', 'version': 2}}"; - - static final String responseJsonWithNullVersion = "{'licenseKey':'" - + VALID_KEY + "'," + "'licensee':'Test User','type':'normal'," - + "'expiredEpoch':1893511225000," + "'product':{'name':'" - + productNameCval + "', 'version': null}}"; - - private static ByteArrayOutputStream outContent; - - // A provider returning a valid license if productKey is valid or null if - // invalid - static final CvalServer validLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - return VALID_KEY.equals(productKey) ? responseJson : null; - } - }; - // A provider returning a valid evaluation license - static final CvalServer validEvaluationLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - return responseJson.replace("normal", "evaluation"); - } - }; - // A provider returning an expired license with a server message - static final CvalServer expiredLicenseProviderWithMessage = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - return responseJson - .replace("'expired", - "'message':'Custom\\\\nServer\\\\nMessage','expired':true,'expired"); - } - }; - // A provider returning an expired license with a server message - static final CvalServer expiredLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - return responseJson.replace("'expired", "'expired':true,'expired"); - } - }; - // A provider returning an expired epoch license - static final CvalServer expiredEpochLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - long ts = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; - return responseJson.replace("1893511225000", "" + ts); - } - }; - // A provider returning an unlimited license - static final CvalServer unlimitedLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) { - return responseJson.replaceFirst("1893511225000", "null"); - } - }; - // An unreachable provider - static final CvalServer unreachableLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) - throws IOException { - // Normally there is no route for this ip in public routers, so we - // should get a timeout. - licenseUrl = "http://localhost:9999/"; - return super.askServer(productName, productKey, 1000); - } - }; - // A provider with 'null' in the version field - static final CvalServer nullVersionLicenseProvider = new CvalServer() { - @Override - String askServer(String productName, String productKey, int timeout) - throws IOException { - return responseJsonWithNullVersion; - } - }; - - private CvalChecker licenseChecker; - private String licenseName; - - @Before - public void setup() { - licenseChecker = new CvalChecker() - .setLicenseProvider(validLicenseProvider); - licenseName = CvalChecker.computeLicenseName(productNameCval); - System.getProperties().remove(licenseName); - deleteCache(productNameCval); - } - - @Test - public void testValidateProduct() throws Exception { - deleteCache(productNameCval); - - // If the license key in our environment is null, throw an exception - try { - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // If the license key is empty, throw an exception - System.setProperty(licenseName, ""); - try { - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // If license key is invalid, throw an exception - System.setProperty(licenseName, "invalid"); - try { - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // Fail if version is bigger - System.setProperty(licenseName, VALID_KEY); - try { - licenseChecker.validateProduct(productNameCval, "3.0", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - assertTrue(expected.getMessage().contains("is not valid")); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // Success if license key and version are valid - System.setProperty(licenseName, VALID_KEY); - licenseChecker - .validateProduct(productNameCval, "2.1", productTitleCval); - Assert.assertTrue(cacheExists(productNameCval)); - - // Success if license and cache file are valid, although the license - // server is offline - licenseChecker.setLicenseProvider(unreachableLicenseProvider); - licenseChecker - .validateProduct(productNameCval, "2.1", productTitleCval); - Assert.assertTrue(cacheExists(productNameCval)); - - // Fail if license key changes although cache file were validated - // previously and it is ok, we are offline - try { - System.setProperty(licenseName, INVALID_KEY); - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - Assert.fail(); - } catch (UnreachableCvalServerException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // Fail with unreachable exception if license has never verified and - // server is offline - try { - System.setProperty(licenseName, VALID_KEY); - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - Assert.fail(); - } catch (UnreachableCvalServerException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertFalse(cacheExists(productNameCval)); - - // Fail when expired flag comes in the server response, although the - // expired is valid. - deleteCache(productNameCval); - licenseChecker.setLicenseProvider(expiredLicenseProviderWithMessage); - try { - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - // Check that we use server customized message if it comes - Assert.assertTrue(expected.getMessage().contains("Custom")); - } - Assert.assertTrue(cacheExists(productNameCval)); - - // Check an unlimited license - deleteCache(productNameCval); - licenseChecker.setLicenseProvider(unlimitedLicenseProvider); - licenseChecker - .validateProduct(productNameCval, "2.1", productTitleCval); - Assert.assertTrue(cacheExists(productNameCval)); - - // Fail if expired flag does not come, but expired epoch is in the past - System.setProperty(licenseName, VALID_KEY); - deleteCache(productNameCval); - licenseChecker.setLicenseProvider(expiredEpochLicenseProvider); - try { - licenseChecker.validateProduct(productNameCval, "2.1", - productTitleCval); - Assert.fail(); - } catch (InvalidCvalException expected) { - assertEquals(productNameCval, expected.name); - } - Assert.assertTrue(cacheExists(productNameCval)); - - deleteCache(productNameCval); - licenseChecker.setLicenseProvider(nullVersionLicenseProvider); - licenseChecker - .validateProduct(productNameCval, "2.1", productTitleCval); - Assert.assertTrue(cacheExists(productNameCval)); - } - - /* - * Creates a new .jar file with a MANIFEST.MF with all vaadin license info - * attributes set, and add the .jar to the classpath - */ - static void addLicensedJarToClasspath(String productName, String licenseType) - throws Exception { - // Create a manifest with Vaadin CVAL license - Manifest testManifest = new Manifest(); - testManifest.getMainAttributes().putValue("Manifest-Version", "1.0"); - testManifest.getMainAttributes().putValue(VAADIN_ADDON_LICENSE, - licenseType); - testManifest.getMainAttributes().putValue(VAADIN_ADDON_NAME, - productName); - testManifest.getMainAttributes().putValue(VAADIN_ADDON_TITLE, - "Test " + productName); - testManifest.getMainAttributes().putValue(VAADIN_ADDON_VERSION, "2"); - - // Create a temporary Jar - File testJarFile = File.createTempFile("vaadin." + productName, "jar"); - testJarFile.deleteOnExit(); - JarOutputStream target = new JarOutputStream(new FileOutputStream( - testJarFile), testManifest); - target.close(); - - // Add the new jar to our classpath (use reflection) - URL url = new URL("file://" + testJarFile.getAbsolutePath()); - final Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", - new Class[] { URL.class }); - addURL.setAccessible(true); - final URLClassLoader urlClassLoader = (URLClassLoader) Thread - .currentThread().getContextClassLoader(); - addURL.invoke(urlClassLoader, new Object[] { url }); - } - - static boolean cacheExists(String productName) { - return cachedPreferences(productName) != null; - } - - static String cachedPreferences(String productName) { - // ~/Library/Preferences/com.apple.java.util.prefs.plist - // .java/.userPrefs/com/google/gwt/dev/shell/prefs.xml - // HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs - Preferences p = Preferences.userNodeForPackage(CvalInfo.class); - return p.get(productName, null); - } - - static void saveCache(String productName, String key, Boolean expired, - Long expireTs, String type) { - String json = responseJson.replace(productNameCval, productName); - if (expired != null && expired) { - expireTs = System.currentTimeMillis() - GRACE_DAYS_MSECS - 1000; - } - if (expireTs != null) { - json = json.replace("1893511225000", "" + expireTs); - } - if (key != null) { - json = json.replace(VALID_KEY, key); - } - if (type != null) { - json = json.replace("normal", type); - - } - cacheLicenseInfo(parseJson(json)); - } - - static void captureSystemOut() { - outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - } - - static String readSystemOut() { - restoreSystemOut(); - return outContent.toString(); - } - - static void restoreSystemOut() { - System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); - } - - @Test(expected = FileNotFoundException.class) - public void testReadKeyFromFile_NonexistingLicenseFile() throws Exception { - licenseChecker.readKeyFromFile(new URL("file:///foobar.baz"), 4); - } - - @Test - public void testReadKeyFromFile_LicenseFileEmpty() throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - - assertNull(licenseChecker.readKeyFromFile(tmpLicenseFile.toURI() - .toURL(), 4)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_LicenseFileHasSingleUnidentifiedKey() - throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("this-is-a-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_LicenseFileHasSingleIdentifiedKey() - throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("4=this-is-a-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_LicenseFileHasMultipleKeys() - throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("4=this-is-a-license"); - out.println("5=this-is-another-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 5)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_LicenseFileHasMultipleKeysWithWhitespace() - throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("4 = this-is-a-license"); - out.println("5 = this-is-another-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 5)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_RequestedVersionMissing() throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("4 = this-is-a-license"); - out.println("5 = this-is-another-license"); - out.close(); - - assertNull(licenseChecker.readKeyFromFile(tmpLicenseFile.toURI() - .toURL(), 3)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_FallbackToDefaultKey() throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("this-is-a-license"); - out.println("5 = this-is-another-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 3)); - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 5)); - - tmpLicenseFile.delete(); - } - - @Test - public void testReadKeyFromFile_FallbackToDefaultKeyReversed() - throws Exception { - File tmpLicenseFile = File.createTempFile("license", "lic"); - PrintWriter out = new PrintWriter(tmpLicenseFile); - out.println("5 = this-is-another-license"); - out.println("this-is-a-license"); - out.close(); - - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 3)); - assertEquals("this-is-a-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 4)); - assertEquals("this-is-another-license", licenseChecker.readKeyFromFile( - tmpLicenseFile.toURI().toURL(), 5)); - - tmpLicenseFile.delete(); - } -} diff --git a/ivysettings.xml b/ivysettings.xml index 8afc679af9..368d4d7fd8 100644 --- a/ivysettings.xml +++ b/ivysettings.xml @@ -46,7 +46,7 @@ + resolver="local-maven" /> 1.6 1.6 + UTF-8 2.4 @@ -50,6 +51,7 @@ push server client + client-compiler diff --git a/uitest/ivy.xml b/uitest/ivy.xml index 9c4d5025bd..a3dfc0e428 100644 --- a/uitest/ivy.xml +++ b/uitest/ivy.xml @@ -61,7 +61,7 @@ + rev="${vaadin.version}" conf="build-provided-> default"> diff --git a/widgets/ivy.xml b/widgets/ivy.xml index 3ee0cfe4af..da9422449b 100644 --- a/widgets/ivy.xml +++ b/widgets/ivy.xml @@ -33,7 +33,7 @@ + rev="${vaadin.version}" conf="build-provided,test->default">