├── .gitignore ├── LICENSE.txt ├── README.md ├── README.txt ├── core ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── timepedia │ │ └── exporter │ │ ├── Exporter.gwt.xml │ │ ├── client │ │ ├── Export.java │ │ ├── ExportAfterCreateMethod.java │ │ ├── ExportClosure.java │ │ ├── ExportConstructor.java │ │ ├── ExportInstanceMethod.java │ │ ├── ExportJsInitMethod.java │ │ ├── ExportOverlay.java │ │ ├── ExportPackage.java │ │ ├── ExportStaticMethod.java │ │ ├── Exportable.java │ │ ├── Exporter.java │ │ ├── ExporterBaseActual.java │ │ ├── ExporterBaseImpl.java │ │ ├── ExporterUtil.java │ │ ├── Getter.java │ │ ├── NoExport.java │ │ ├── SOptional.java │ │ ├── SType.java │ │ ├── Setter.java │ │ ├── StructuralType.java │ │ └── test │ │ │ └── JsTestUtil.java │ │ ├── doclet │ │ └── JsDoclet.java │ │ └── rebind │ │ ├── ClassExporter.java │ │ ├── DispatchTable.java │ │ ├── ExportableTypeOracle.java │ │ ├── ExporterGenerator.java │ │ ├── ExporterGeneratorNoExport.java │ │ ├── JExportOverlayClassType.java │ │ ├── JExportable.java │ │ ├── JExportableArrayType.java │ │ ├── JExportableClassType.java │ │ ├── JExportableConstructor.java │ │ ├── JExportableField.java │ │ ├── JExportableMethod.java │ │ ├── JExportableParameter.java │ │ ├── JExportablePrimitiveType.java │ │ ├── JExportableType.java │ │ ├── JStructuralTypeField.java │ │ └── Property.java │ └── test │ └── java │ └── org │ └── timepedia │ └── exporter │ ├── Test.gwt.xml │ └── test │ ├── CoreTestGwt.java │ ├── ExporterGwtSuiteTest.java │ └── issues │ ├── Issue25aTestGwt.java │ ├── Issue25bTestGwt.java │ ├── Issue33TestGwt.java │ ├── Issue34TestGwt.java │ ├── Issue35TestGwt.java │ ├── Issue38TestGwt.java │ ├── Issue44TestGwt.java │ ├── Issue45TestGwt.java │ ├── Issue48TestGwt.java │ ├── Issue49TestGwt.java │ ├── Issue50TestGwt.java │ ├── Issue51TestGwt.java │ └── MyExportable.java └── samples ├── export-widgets ├── README.txt ├── pom.xml └── src │ └── main │ ├── java │ └── org │ │ └── gquery │ │ └── ewidget │ │ ├── ExportWidget.gwt.xml │ │ ├── client │ │ └── ExportWidget.java │ │ └── public │ │ └── ExportWidget.html │ └── webapp │ ├── WEB-INF │ └── web.xml │ └── index.html ├── exporterdemo ├── pom.xml └── src │ └── main │ ├── java │ └── exporterdemo │ │ ├── ExporterDemo.gwt.xml │ │ ├── client │ │ ├── Employee.java │ │ ├── ExportManager.java │ │ ├── ExporterDemo.java │ │ ├── Manager.java │ │ ├── MyClickListener.java │ │ └── Person.java │ │ └── public │ │ └── ExporterDemo.html │ └── webapp │ ├── WEB-INF │ └── web.xml │ └── index.html └── testdemo ├── pom.xml └── src └── main ├── java └── testdemo │ ├── TestDemo.gwt.xml │ ├── client │ └── TestDemo.java │ └── public │ └── TestDemo.html └── webapp ├── WEB-INF └── web.xml └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | gwt-unitCache 3 | war 4 | *~ 5 | .settings 6 | .project 7 | .classpath 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2007 Timepedia.org 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, 10 | software distributed under the License is distributed on 11 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 12 | either express or implied. See the License for the specific 13 | language governing permissions and limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is gwt-exporter? # 2 | The project is a GWT module which contains a generator capable of taking GWT classes and exporting them as ordinary Javascript APIs callable from hand-written Javascript, without having to deal with JSNI, and suitable for use in mashups. 3 | 4 | Develop an application or library in GWT and use gwtexporter annotations to make classes and methods available from javascript. 5 | 6 | The project includes a class to generate the Api documentation of the exported javascript. 7 | 8 | # Documentation # 9 | Take a look to the [Getting Started Guide](../../wiki/GettingStarted) to learn how to use it. 10 | 11 | [Here](#articles-and-posts) you have are a set of useful links you can read as well. 12 | 13 | 14 | # gwt-exporter in action # 15 | * In your java code modify your classes to implement the `Exportable` interface, and add some extra annotations 16 | ``` 17 | package com.example; 18 | 19 | @Export() /* [1] */ 20 | public class Person implements /* [2] */ Exportable { 21 | private String name; 22 | public Person(String name) { 23 | this.name = name; 24 | } 25 | public String execute(String key, long[] values) { 26 | return key + " : " + values.length; 27 | } 28 | public String foo(Foo foo) { 29 | return foo.foo(); 30 | } 31 | } 32 | 33 | public class Foo implements /* [3] */ Exportable { 34 | public String bar() { 35 | return "bar,"; 36 | } 37 | } 38 | 39 | // [1] Export this class and all its public methods. 40 | // [2] [3] Mark classes as exportable: use the gwt-exporter generator. 41 | // [3] because this class is not annotated with @Export, it will be exported 42 | // only in the case it was used in a exported method. 43 | ``` 44 | 45 | * In your `onModuleLoad` you have to say to the compiler which classes to export 46 | ``` 47 | public void onModuleLoad() { 48 | GWT.exportAll(); // Export all Exportable classes, otherwise use GWT.create per class. 49 | } 50 | ``` 51 | * **IMPORTANT:** The exported stuff will not be available in JS until your GWT app is fully loaded. 52 | * In compile time, gwt-exporter will decorate your classes with the necessary jsni code so as they are callable from javascript: 53 | ``` 54 | 55 | var person = new com.example.Person('manolo') 56 | var foo = new com.example.Person.Foo() 57 | foo.bar(); 58 | person.foo(foo) 59 | person.execute('baz', [1,2,3]) 60 | 61 | ``` 62 | 63 | * Additionally gwt-exporter's plenty of features to make your js api look better 64 | ``` 65 | 66 | @ExportPackage("pkg") // Change the js namespace from "com.example" to "pkg" 67 | @Export("person") // Change the class name from "Person" to "person" 68 | public class Person implements Exportable { 69 | [...] 70 | @Export("baz") // Rename methods 71 | public String execute(String key, long[] values) { 72 | [...] 73 | } 74 | 75 | public String run(Order callback) { // Run js functions 76 | callback.success("ok"); 77 | } 78 | } 79 | 80 | @ExportClosure // Convert js functions to java objects 81 | public interface Order implements Exportable { 82 | void success(String msg); 83 | } 84 | 85 | ``` 86 | ``` 87 | 88 | var person = new pkg.person('manolo') 89 | person.baz('baz', [1,2,3]) 90 | person.run(function(msg){ 91 | alert('msg') 92 | }); 93 | 94 | ``` 95 | # Articles and posts # 96 | 97 | Here you have a set of articles, tutorials and links related with GwtExporter: 98 | 99 | * The [Getting started guide](../../wiki/GettingStarted) is the official documentation of the library. 100 | * [English](../../wiki/Exporting-GWT-to-JS) and [Spanish](../../wiki/Exportando-GWT-a-JS) tutorials. 101 | * [jsQuery](http://code.google.com/p/gwtquery/wiki/JsQuery) is a clone of jquery written in gwt and exported with Gwt. You can take a look to [this presentation](http://www.slideshare.net/dodotis/gquery-a-jquery-clone-for-gwt-rivieradev-2011) to get more information. 102 | * A list of some posts about gwtexporter in Ray's blog. 103 | * [Announcemen of a doclet class able to generate js-api documentation using javadoc comments](http://timepedia.blogspot.com/2009/01/gwt-exporter-now-generates-javascript.html) 104 | * [Ray's thoughts about the future of gwt exporter in March,2003](http://timepedia.blogspot.com/2009/03/whats-next-for-gwt-exporter.html) 105 | * [New features in 2.05](http://timepedia.blogspot.com/2009/03/gwt-exporter-205-released.html) 106 | * [Bug in 2.05](http://timepedia.blogspot.com/2009/04/small-update-to-gwt-exporter.html) 107 | * [New features in 2.09](http://timepedia.blogspot.com/2009/12/gwt-exporter-209-released.html) 108 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | This is the GWT Exporter module courtesy of Timepedia.org. This module 3 | allows you to expose GWT classes, methods, and fields as Javascript 4 | objects and takes care of marshalling GWT objects back and forth 5 | between the Javascript interface and the GWT interface. 6 | 7 | Feel free to use, modify, and redistribute this code according to the 8 | provisions of the Apache License 2.0. 9 | 10 | If you want to contribute, feel free to submit issues to the issue 11 | tracker on http://code.google.com/gwt-exporter or submit patches. 12 | You can email to the mailing list gwt-exporter@googlecode.com as well. 13 | 14 | Build files are in maven central repositories, or in the download 15 | section of the web site. 16 | 17 | There is some good wiki pages and articles in the web site and you 18 | can take a look to our test cases to learn usage. 19 | 20 | You can also read the Timepedia blog, http://timepedia.blogspot.com 21 | for a discussion of how everything works. 22 | 23 | -Ray Cromwell 24 | 25 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.timepedia.exporter 4 | gwtexporter 5 | jar 6 | 2.6.0-SNAPSHOT 7 | GWT Javascript API Exporter 8 | http://timepedia.org 9 | 10 | 11 | org.sonatype.oss 12 | oss-parent 13 | 7 14 | 15 | 16 | 17 | UTF-8 18 | 2.5.1-rc1 19 | 20 | 21 | 22 | junit 23 | junit 24 | 3.8.1 25 | test 26 | 27 | 28 | com.google.gwt 29 | gwt-user 30 | ${gwtversion} 31 | provided 32 | 33 | 34 | com.google.gwt 35 | gwt-dev 36 | ${gwtversion} 37 | provided 38 | 39 | 40 | com.sun 41 | tools 42 | 1.6 43 | system 44 | true 45 | ${java.home}/../lib/tools.jar 46 | 47 | 48 | 49 | 50 | 51 | 52 | ${basedir}/src/main/java 53 | 54 | 55 | ${basedir}/src/main/resources 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-source-plugin 62 | 63 | 64 | package 65 | 66 | jar 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 75 | 1.6 76 | 1.6 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-surefire-plugin 82 | 83 | 84 | ${basedir}/src/main/java 85 | ${basedir}/src/test/java 86 | 87 | 88 | 89 | false 90 | 91 | -runStyle HtmlUnit:FF3,IE6 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-gpg-plugin 98 | 99 | 100 | sign-artifacts 101 | verify 102 | 103 | sign 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-javadoc-plugin 111 | 112 | 113 | javadoc 114 | package 115 | 116 | jar 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-release-plugin 124 | 2.3.2 125 | 126 | 127 | default 128 | 129 | perform 130 | 131 | 132 | core/pom.xml 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | maven2-repository.dev.java.net 143 | Java.net Repository for Maven 144 | http://download.java.net/maven/2/ 145 | 146 | 147 | timefire 148 | http://timefire-repository.googlecode.com/svn/mavenrepo/ 149 | 150 | 151 | 152 | 153 | scm:git:https://code.google.com/p/gwt-exporter/ 154 | scm:git:git@github.com:manolo/gwt-exporter.git 155 | http://code.google.com/p/gwt-exporter/source/browse/ 156 | gwtexporter-2.6.1 157 | 158 | 159 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/Exporter.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/Export.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks a method, field, constructor, or class as exportable and allows 10 | * its Javascript name to be overriden. 11 | * 12 | * When a class is marked, all public methods will be exported unless them 13 | * are marked with NoExport 14 | */ 15 | @Target( 16 | {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE}) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | public @interface Export { 19 | 20 | String value() default ""; 21 | 22 | boolean all() default false; 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportAfterCreateMethod.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Mark a static method in a class so as it will run after the class 10 | * has been created. 11 | * It is run just once and it is useful to redefine name-spaces or 12 | * whatever we needed to do after the class has been exported. 13 | */ 14 | @Target({ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ExportAfterCreateMethod { 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportClosure.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Indicates that an interface allows JS functions to be automatically promoted 10 | * to its type when it appears as an argument to an exported function. 11 | * For example: 12 | *
13 | @Export
14 | @ExportPackage("jsc")
15 | @ExportClosure
16 | public interface JsClosure extends Exportable {
17 |   public void execute(String par1, String par2);
18 | }
19 | 
20 | @Export
21 | @ExportPackage("jsc")
22 | public class MyClass implements Exportable {
23 |   public executeJsClosure(JsClosure closure){
24 |     closure.execute("Hello", "Friend");
25 |   }
26 | }
27 |  * 
28 | */ 29 | 30 | 31 | @Target(ElementType.TYPE) 32 | @Retention(RetentionPolicy.RUNTIME) 33 | public @interface ExportClosure { 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportConstructor.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Mark a static method to be exported as a constructor of the exported 10 | * class. 11 | * The method should return the exported class instance. 12 | * 13 | */ 14 | @Target({ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ExportConstructor { 17 | String value() default ""; 18 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportInstanceMethod.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Mark a static method in a class annotated with ExportOverlay to 10 | * be exported as an instance method of the original class. 11 | * The first argument of the method must be the instance of the original class. 12 | */ 13 | @Target({ElementType.METHOD}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ExportInstanceMethod { 16 | String value() default ""; 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportJsInitMethod.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Mark an instance method in a class so as it will return the 10 | * JavascriptObject which should be used when wrapping the class. 11 | */ 12 | @Target({ElementType.METHOD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ExportJsInitMethod { 15 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportOverlay.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | /** 4 | * Implements a decoupled Exportable interface. Type T will be exported. 5 | * 6 | * Classes implementing this interface, should be marked with Export annotation and 7 | * have to implement stub methods and constructor of the methods in the original class 8 | * to export. 9 | * 10 | * This classes could have static implementations of constructors and methods of the 11 | * original class which should be marked with ExportConstructor or ExportInstanceMethod. 12 | * 13 | */ 14 | public interface ExportOverlay extends Exportable { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportPackage.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | */ 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ExportPackage { 14 | 15 | String value(); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExportStaticMethod.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Mark a static method in a class annotated with ExportOverlay to 10 | * be exported as an static method of the original class. 11 | */ 12 | @Target({ElementType.METHOD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ExportStaticMethod { 15 | String value() default ""; 16 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/Exportable.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | /** 4 | * Marker interface. To export a class to Javascript, perform the following 5 | * steps: 6 | * 7 | *
  1. add Exportable as an implemented interface
  2. Choose between a 8 | * whitelist or blacklist export policy
  3. If blacklist, add @gwt.export to the 9 | * JavaDoc of the class definition and use @gwt.noexport for any methods you do 10 | * not wish to be exported. By default, all public methods and static final 11 | * fields are exported.
  4. If whitelist, add @gwt.export to each method, field, 12 | * constructor you wish to export.
  5. use @gwt.exportPackage foo.bar to 13 | * override the generated Javascript package hierarchy
  6. If two methods have 14 | * the same export name, you can resolve the conflict by renaming the exported 15 | * method, e.g. "@gwt.export addDouble" will export method add(double, double) 16 | * as addDouble(double,double)
17 | * 18 | *

Finally, somewhere in your entry point class, perform the following: 19 | *

Exporter exporter = (Exporter)GWT.create(MyClass.class); 20 | * exporter.export(); 21 | * 22 | * @author Ray Cromwell <ray@timepedia.org> 23 | */ 24 | public interface Exportable { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/Exporter.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import com.google.gwt.core.client.JavaScriptObject; 4 | 5 | /** 6 | * Exportable classes passed to GWT.create() will return an implementation of 7 | * Exporter. Invoke the export() method to export the JavaScript bridge classes 8 | * and methods. 9 | * 10 | * @author Ray Cromwell <ray@timepedia.org> 11 | */ 12 | public interface Exporter { 13 | 14 | @Deprecated 15 | /** 16 | * Invoking GWT.create() on an exportable class is sufficient to export it. 17 | */ 18 | void export(); 19 | 20 | /** 21 | * Invoked to synchronize Java object with underlying structural type JS 22 | * object after return from a Java method. 23 | */ 24 | // void sync(); 25 | 26 | boolean isAssignable(Object o); 27 | 28 | JavaScriptObject getJsConstructor(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExporterBaseImpl.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.util.Date; 4 | 5 | import com.google.gwt.core.client.JavaScriptObject; 6 | import com.google.gwt.core.client.JsArray; 7 | import com.google.gwt.core.client.JsArrayNumber; 8 | import com.google.gwt.core.client.JsArrayString; 9 | 10 | /** 11 | * No-op implementation when export is disabled. 12 | */ 13 | public class ExporterBaseImpl { 14 | 15 | public void setWrapper(Object instance, JavaScriptObject wrapper) { 16 | } 17 | 18 | public JavaScriptObject typeConstructor(Exportable type) { 19 | return null; 20 | } 21 | 22 | public JavaScriptObject typeConstructor(String type) { 23 | return null; 24 | } 25 | 26 | public JavaScriptObject wrap(Object type) { 27 | return null; 28 | } 29 | 30 | public JavaScriptObject wrap(Object[] type) { 31 | return null; 32 | } 33 | 34 | public JavaScriptObject wrap(JavaScriptObject[] type) { 35 | return null; 36 | } 37 | 38 | public JavaScriptObject wrap(double[] type) { 39 | return null; 40 | } 41 | 42 | public JavaScriptObject wrap(float[] type) { 43 | return null; 44 | } 45 | 46 | public JavaScriptObject wrap(int[] type) { 47 | return null; 48 | } 49 | 50 | public JavaScriptObject wrap(char[] type) { 51 | return null; 52 | } 53 | 54 | public JavaScriptObject wrap(byte[] type) { 55 | return null; 56 | } 57 | 58 | public JavaScriptObject wrap(long[] type) { 59 | return null; 60 | } 61 | 62 | public JavaScriptObject wrap(short[] type) { 63 | return null; 64 | } 65 | 66 | public JavaScriptObject declarePackage(String qualifiedExportName) { 67 | return null; 68 | } 69 | 70 | public JavaScriptObject runDispatch(Object instance, Class clazz, int meth, 71 | JsArray arguments, boolean isStatic, boolean isVarArgs) { 72 | return null; 73 | } 74 | 75 | public void registerDispatchMap(Class clazz, JavaScriptObject dispMap, boolean isStatic) { 76 | } 77 | 78 | public JavaScriptObject wrap(String[] type) { 79 | return null; 80 | } 81 | 82 | public JavaScriptObject wrap(Date[] type) { 83 | return null; 84 | } 85 | 86 | public String[] toArrString(JsArrayString type) { 87 | return null; 88 | } 89 | 90 | public double[] toArrDouble(JsArrayNumber type) { 91 | return null; 92 | } 93 | 94 | public float[] toArrFloat(JsArrayNumber type) { 95 | return null; 96 | } 97 | 98 | public int[] toArrInt(JsArrayNumber type) { 99 | return null; 100 | } 101 | 102 | public byte[] toArrByte(JsArrayNumber type) { 103 | return null; 104 | } 105 | 106 | public char[] toArrChar(JsArrayNumber type) { 107 | return null; 108 | } 109 | 110 | public long[] toArrLong(JsArrayNumber type) { 111 | return null; 112 | } 113 | 114 | public T[] toArrObject(JavaScriptObject type, T[] ret) { 115 | return null; 116 | } 117 | 118 | public Exportable[] toArrExport(JavaScriptObject p) { 119 | return null; 120 | } 121 | 122 | public Date[] toArrDate(JavaScriptObject j) { 123 | return null; 124 | } 125 | 126 | public Object[] toArrJsObject(JavaScriptObject type) { 127 | return null; 128 | } 129 | 130 | public JavaScriptObject unshift(Object o, JavaScriptObject arr){ 131 | return null; 132 | } 133 | 134 | public JavaScriptObject dateToJsDate(Date d) { 135 | return null; 136 | } 137 | 138 | public Date jsDateToDate(JavaScriptObject jd) { 139 | return null; 140 | } 141 | 142 | public Object gwtInstance(Object o) { 143 | return null; 144 | } 145 | 146 | public void addExporter(Classc, T o) { 147 | } 148 | 149 | } 150 | 151 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/ExporterUtil.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.util.Date; 4 | 5 | import org.timepedia.exporter.client.ExporterBaseActual.JsArrayObject; 6 | 7 | import com.google.gwt.core.client.GWT; 8 | import com.google.gwt.core.client.JavaScriptObject; 9 | import com.google.gwt.core.client.JsArray; 10 | import com.google.gwt.core.client.JsArrayNumber; 11 | import com.google.gwt.core.client.JsArrayString; 12 | 13 | /** 14 | * Holds utility methods and wrapper state 15 | * 16 | * @author Ray Cromwell 17 | */ 18 | public class ExporterUtil { 19 | 20 | public static abstract class ExportAll implements Exportable, Exporter { 21 | public void export() { 22 | export(false); 23 | } 24 | public abstract void export(boolean all); 25 | } 26 | 27 | private static ExporterBaseImpl impl = GWT.create(ExporterBaseImpl.class); 28 | static { 29 | } 30 | 31 | public static JavaScriptObject declarePackage(String qualifiedExportName) { 32 | return impl.declarePackage(qualifiedExportName); 33 | } 34 | 35 | /** 36 | * Automatically export all instantiable and public classes marked with the Exportable 37 | * interface. 38 | * 39 | * @param all, when this parameter is true it will export additionally all non-instantiable 40 | * classes marked with Exportable 41 | */ 42 | public static void export(boolean all) { 43 | ExportAll export = GWT.create(ExportAll.class); 44 | export.export(all); 45 | } 46 | 47 | /** 48 | * Export all classes marked with the Exportable interface, including those which are not default 49 | * instantiable (interfaces, abstracts, without constructor, etc). 50 | * 51 | * Use ExporterUtil.export(false) to export just the set of instantiable classes and save some 52 | * js size, gwt-exporter will take care of exporting dependent classes when used. 53 | * 54 | */ 55 | public static void exportAll() { 56 | export(true); 57 | } 58 | 59 | // public static void exportAllAsync() { 60 | // GWT.runAsync(new RunAsyncCallback() { 61 | // public void onFailure(Throwable reason) { 62 | // throw new RuntimeException(reason); 63 | // } 64 | // 65 | // public void onSuccess() { 66 | // GWT.create(ExportAll.class); 67 | // onexport(); 68 | // } 69 | // 70 | // private native void onexport() /*-{ 71 | // $wnd.onexport(); 72 | // }-*/; 73 | // }); 74 | // } 75 | 76 | public static JavaScriptObject runDispatch(Object instance, Class clazz, int meth, 77 | JsArray arguments, boolean isStatic, boolean isVarArgs) { 78 | return impl.runDispatch(instance, clazz, meth, arguments, isStatic, isVarArgs); 79 | } 80 | 81 | public static native byte getStructuralFieldbyte(JavaScriptObject jso, 82 | String field) /*-{ 83 | return jso[field]; 84 | }-*/; 85 | 86 | public static native char getStructuralFieldchar(JavaScriptObject jso, 87 | String field) /*-{ 88 | return jso[field]; 89 | }-*/; 90 | 91 | public static native double getStructuralFielddouble(JavaScriptObject jso, 92 | String field) /*-{ 93 | return jso[field]; 94 | }-*/; 95 | 96 | public static native float getStructuralFieldfloat(JavaScriptObject jso, 97 | String field) /*-{ 98 | return jso[field]; 99 | }-*/; 100 | 101 | public static native int getStructuralFieldint(JavaScriptObject jso, 102 | String field) /*-{ 103 | return jso[field]; 104 | }-*/; 105 | 106 | public static long getStructuralFieldlong(JavaScriptObject jso, 107 | String field) { 108 | return (long) getStructuralFielddouble(jso, field); 109 | } 110 | 111 | public static native T getStructuralFieldObject(JavaScriptObject jso, 112 | String field) /*-{ 113 | return jso[field]; 114 | }-*/; 115 | 116 | public static native short getStructuralFieldshort(JavaScriptObject jso, 117 | String field) /*-{ 118 | return jso[field]; 119 | }-*/; 120 | 121 | public static void registerDispatchMap(Class clazz, JavaScriptObject dispMap, 122 | boolean isStatic) { 123 | impl.registerDispatchMap(clazz, dispMap, isStatic); 124 | } 125 | 126 | public static native void setStructuralField(JavaScriptObject jso, 127 | String field, Object val) /*-{ 128 | jso[field]=type; 129 | }-*/; 130 | 131 | public static void setWrapper(Object instance, JavaScriptObject wrapper) { 132 | impl.setWrapper(instance, wrapper); 133 | } 134 | 135 | public static JavaScriptObject typeConstructor(Exportable type) { 136 | return impl.typeConstructor(type); 137 | } 138 | 139 | public static JavaScriptObject typeConstructor(String type) { 140 | return impl.typeConstructor(type); 141 | } 142 | 143 | public static JavaScriptObject wrap(Object type) { 144 | return impl.wrap(type); 145 | } 146 | 147 | public static JavaScriptObject wrap(JavaScriptObject[] type) { 148 | return impl.wrap(type); 149 | } 150 | 151 | public static JavaScriptObject wrap(Object[] type) { 152 | return impl.wrap(type); 153 | } 154 | 155 | public static JavaScriptObject wrap(Exportable[] type) { 156 | return wrap((Object[])type); 157 | } 158 | 159 | public static JavaScriptObject wrap(String[] type) { 160 | return impl.wrap(type); 161 | } 162 | 163 | public static JavaScriptObject wrap(double[] type) { 164 | return impl.wrap(type); 165 | } 166 | 167 | public static JavaScriptObject wrap(float[] type) { 168 | return impl.wrap(type); 169 | } 170 | 171 | public static JavaScriptObject wrap(int[] type) { 172 | return impl.wrap(type); 173 | } 174 | 175 | public static JavaScriptObject wrap(char[] type) { 176 | return impl.wrap(type); 177 | } 178 | 179 | public static JavaScriptObject wrap(byte[] type) { 180 | return impl.wrap(type); 181 | } 182 | 183 | public static JavaScriptObject wrap(long[] type) { 184 | return impl.wrap(type); 185 | } 186 | 187 | public static JavaScriptObject wrap(short[] type) { 188 | return impl.wrap(type); 189 | } 190 | 191 | public static JavaScriptObject wrap(Date[] type) { 192 | return impl.wrap(type); 193 | } 194 | 195 | public static String[] toArrString(JavaScriptObject type) { 196 | return impl.toArrString(type.cast()); 197 | } 198 | 199 | public static double[] toArrDouble(JavaScriptObject type) { 200 | return impl.toArrDouble(type.cast()); 201 | } 202 | 203 | public static float[] toArrFloat(JavaScriptObject type) { 204 | return impl.toArrFloat(type.cast()); 205 | } 206 | 207 | public static int[] toArrInt(JavaScriptObject type) { 208 | return impl.toArrInt(type.cast()); 209 | } 210 | 211 | public static byte[] toArrByte(JavaScriptObject type) { 212 | return impl.toArrByte(type.cast()); 213 | } 214 | 215 | public static char[] toArrChar(JavaScriptObject type) { 216 | return impl.toArrChar(type.cast()); 217 | } 218 | 219 | public static long[] toArrLong(JavaScriptObject type) { 220 | return impl.toArrLong(type.cast()); 221 | } 222 | 223 | public static T[] toArrObject(JavaScriptObject type, T[] ret) { 224 | return impl.toArrObject(type, ret); 225 | } 226 | 227 | public static Object[] toArrJsObject(JavaScriptObject type) { 228 | return impl.toArrJsObject(type); 229 | } 230 | 231 | public static Date[] toArrDate(JavaScriptObject type) { 232 | return impl.toArrDate(type); 233 | } 234 | 235 | // Although in Compiled mode we could cast an Exportable[] to any other T[] array 236 | // In hosted mode it is not possible, so we only support Exportable[] parameter 237 | public static Exportable[] toArrExport(JavaScriptObject type) { 238 | return impl.toArrExport(type); 239 | } 240 | 241 | public static JavaScriptObject unshift(Object o, JavaScriptObject arr) { 242 | return impl.unshift(o, arr); 243 | } 244 | 245 | public static JavaScriptObject dateToJsDate(Date d) { 246 | return impl.dateToJsDate(d); 247 | } 248 | 249 | public static Date jsDateToDate(JavaScriptObject jd) { 250 | return impl.jsDateToDate(jd); 251 | } 252 | 253 | public static int length(JavaScriptObject o) { 254 | return o.cast().length(); 255 | } 256 | 257 | public static Object gwtInstance(Object o) { 258 | return impl.gwtInstance(o); 259 | } 260 | 261 | public static void addExporter(Classc, T o) { 262 | impl.addExporter(c, o); 263 | } 264 | 265 | } 266 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/Getter.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks a method as a getter for a readable property 10 | * its Javascript name to be overriden else the default java convention will be used (get/set/is removed and lowercase) 11 | * 12 | * @see Setter for also generating the set method for a writeable property 13 | */ 14 | @Target({ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Getter { 17 | 18 | String value() default ""; 19 | 20 | boolean all() default false; 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/NoExport.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * If a class is marked for export, used this method to selectively disable 10 | * certain methods or fields from being exported. 11 | */ 12 | @Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface NoExport { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/SOptional.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Indicates a structural type field which may be optional. 10 | */ 11 | @Target( 12 | {ElementType.METHOD, ElementType.FIELD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface SOptional { 15 | 16 | public abstract boolean value() default false; 17 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/SType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Structural type field. Used to tell the @StructuralType annotation which 10 | * fields are important for matching, and what their names are. By default, 11 | * all Javabean properties must be matched. @SType can be used to expand the 12 | * fields which must be matched, or rename them. 13 | */ 14 | @Target( 15 | {ElementType.METHOD, ElementType.FIELD}) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | public @interface SType { 18 | 19 | public abstract String value() default ""; 20 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/Setter.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks a method as a setter for a writable property 10 | * its Javascript name to be overriden else the default java convention will be used (get/set/is removed and lowercase) 11 | * 12 | * @see Getter for also generating the get method for a read/write property 13 | */ 14 | @Target({ElementType.METHOD}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface Setter { 17 | 18 | String value() default ""; 19 | 20 | boolean all() default false; 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/StructuralType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Indicates that a class or interface may have Javascript objects automatically 10 | * promoted it its type when such objects are passed as arguments in situations 11 | * where the class type is expected. In the class of a class, the JS object must 12 | * contain properties whose names match java bean or @SType properties of the 13 | * class. Any classes that allow structural types must possess a no-arg 14 | * constructor and be non-final. JavaBean property setters must be non-final. 15 | * 16 | * In the case of interface types, each method in the interface must have 17 | * a corresponding property in the JS object whose value is a JS function. The 18 | * JS object will be automatically promoted to a Java implementation of the 19 | * interface which delegates the method implementations to the bound JS 20 | * functions on the JS object. 21 | */ 22 | @Target(ElementType.TYPE) 23 | @Retention(RetentionPolicy.RUNTIME) 24 | public @interface StructuralType { 25 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/client/test/JsTestUtil.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.client.test; 2 | 3 | /** 4 | * Class used to run assert-equals in Jsni tests. 5 | * 6 | * This class is packet with gwt-exporter so as we can use it either 7 | * in tests and samples modules. 8 | * 9 | * @author manolo 10 | */ 11 | public class JsTestUtil { 12 | 13 | public final String name; 14 | 15 | public JsTestUtil(Class clz) { 16 | this.name = clz.getName().replaceAll("^.+\\.", ""); 17 | } 18 | 19 | private String failed = ""; 20 | 21 | public boolean isFailed() { 22 | return !failed.isEmpty(); 23 | } 24 | 25 | public String getFailed() { 26 | return failed; 27 | } 28 | 29 | private native void print(T s) /*-{ 30 | if ($wnd.console && $wnd.console.log) { 31 | $wnd.console.log(s); 32 | } else { 33 | $wnd.alert(s); 34 | } 35 | }-*/; 36 | 37 | private native String arrToStr(T o) /*-{ 38 | var ret = ''; 39 | for ( var i = 0; i < o.length; i++) 40 | ret += (i > 0 ? "," : "") + String(o[i]); 41 | return ret; 42 | }-*/; 43 | 44 | /** 45 | * Checks that both parameters have the same string representation. 46 | * 47 | * In case of arrays, it converts them to a comma separated string. 48 | * In case of numbers, it removes .0 part. 49 | * In case of classNames it compares just the unqualified name of the class. 50 | */ 51 | public void assertEquals(Object a, Object b) { 52 | String sb = (b != null && b.toString().matches(".*(Array|\\[).*")) 53 | ? arrToStr(b) : b == null ? "null" : b.toString(); 54 | String ss = ""; 55 | while (!ss.equals(sb)) { 56 | ss = sb; 57 | sb = sb.replaceAll("(^|,)[^,]+[\\$\\.]([A-Z][\\w]+)\\$*(,|$)", "$1$2$3"); 58 | } 59 | sb = sb.replaceAll("\\.0+([^\\d])", "$1"); 60 | if (a == null && b == null || a != null 61 | && a.toString().equals(sb.toString())) { 62 | print("OK -> " + sb); 63 | } else { 64 | String btype = (b != null ? b.getClass().getName() : String.valueOf(b)); 65 | String amsg = a == null ? "" : ("[" + a.getClass().getName() + "] " + a + " <=> [" + btype + "] "); 66 | String msg = "ERROR -> " + amsg + sb; 67 | print(msg); 68 | failed += (failed.isEmpty() ? "" : "\n") + msg; 69 | } 70 | } 71 | 72 | public void assertEqualsNumber(double a, double b) { 73 | if (a == b) { 74 | print("OK -> " + a); 75 | } else { 76 | String msg = "ERROR -> " + a + " <=> " + b; 77 | failed += (failed.isEmpty() ? "" : "\n") + msg; 78 | } 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/doclet/JsDoclet.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.doclet; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.StringTokenizer; 10 | 11 | import com.sun.javadoc.AnnotationDesc; 12 | import com.sun.javadoc.ClassDoc; 13 | import com.sun.javadoc.ConstructorDoc; 14 | import com.sun.javadoc.ExecutableMemberDoc; 15 | import com.sun.javadoc.MethodDoc; 16 | import com.sun.javadoc.PackageDoc; 17 | import com.sun.javadoc.ParamTag; 18 | import com.sun.javadoc.Parameter; 19 | import com.sun.javadoc.RootDoc; 20 | import com.sun.javadoc.Tag; 21 | import com.sun.javadoc.Type; 22 | import com.sun.tools.doclets.formats.html.ConfigurationImpl; 23 | import com.sun.tools.doclets.formats.html.HtmlDoclet; 24 | import com.sun.tools.doclets.formats.html.HtmlDocletWriter; 25 | import com.sun.tools.doclets.internal.toolkit.util.ClassTree; 26 | import com.sun.tools.doclets.internal.toolkit.util.DocletConstants; 27 | import com.sun.tools.doclets.internal.toolkit.util.PackageListWriter; 28 | import com.sun.tools.doclets.internal.toolkit.util.Util; 29 | 30 | /** 31 | * Generates Js and Gss docs. 32 | */ 33 | public class JsDoclet extends HtmlDoclet { 34 | 35 | public static boolean start(RootDoc rootDoc) { 36 | JsDoclet jsDoclet = new JsDoclet(); 37 | try { 38 | return jsDoclet.startGeneration3(rootDoc); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | return false; 43 | } 44 | 45 | private boolean startGeneration3(RootDoc root) throws Exception { 46 | configuration = ConfigurationImpl.getInstance(); 47 | configuration.root = root; 48 | 49 | if (root.classes().length == 0) { 50 | configuration.message. 51 | error("doclet.No_Public_Classes_To_Document"); 52 | return false; 53 | } 54 | configuration.setOptions(); 55 | //configuration.getDocletSpecificMsg().notice("doclet.build_version", 56 | //configuration.getDocletSpecificBuildDate()); 57 | ClassTree classtree = new ClassTree(configuration, 58 | configuration.nodeprecated); 59 | 60 | generateClassFiles(root, classtree); 61 | if (configuration.sourcepath != null 62 | && configuration.sourcepath.length() > 0) { 63 | StringTokenizer pathTokens = new StringTokenizer(configuration.sourcepath, 64 | String.valueOf(File.pathSeparatorChar)); 65 | boolean first = true; 66 | while (pathTokens.hasMoreTokens()) { 67 | Util.copyDocFiles(configuration, 68 | pathTokens.nextToken() + File.separator, 69 | DocletConstants.DOC_FILES_DIR_NAME, first); 70 | first = false; 71 | } 72 | } 73 | 74 | PackageListWriter.generate(configuration); 75 | generatePackageFiles(classtree); 76 | 77 | generateOtherFiles(root, classtree); 78 | configuration.tagletManager.printReport(); 79 | return true; 80 | } 81 | 82 | // FIXME: exportOverlay is not implemented yet 83 | Map classTypeMap = new HashMap(); 84 | 85 | @Override 86 | protected void generateOtherFiles(RootDoc rootDoc, ClassTree classTree) 87 | throws Exception { 88 | super.generateOtherFiles(rootDoc, classTree); 89 | HtmlDocletWriter writer = new HtmlDocletWriter(configuration, "jsdoc.html"); 90 | 91 | writer.html(); 92 | writer.head(); 93 | writer.println(""); 94 | writer.println(getCSS()); 95 | writer.headEnd(); 96 | writer.body("white", true); 97 | 98 | ClassDoc[] classes = rootDoc.classes(); 99 | Arrays.sort(classes); 100 | 101 | // Document static methods 102 | List smethods = new ArrayList(); 103 | for (ClassDoc clz : classes) { 104 | if (isExportable(clz)) { 105 | 106 | // FIXME: implement exportoverlay somehow. 107 | classTypeMap.put(clz.simpleTypeName(), getExportedName(clz, false, true)); 108 | 109 | if (hasStaticMethods(clz)) { 110 | for (MethodDoc md : clz.methods()) { 111 | if (md.isStatic() && isExportable(md)) { 112 | smethods.add(md); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | if (smethods.size() > 0) { 119 | writer.h1("Exported JavaScript-API: Index of static functions"); 120 | writer.table(1, "100%", 0, 0); 121 | java.util.Collections.sort(smethods); 122 | for (MethodDoc md: smethods) { 123 | writeMethod(writer, false, true, md); 124 | } 125 | writer.tableEnd(); 126 | } 127 | 128 | List eclasses = new ArrayList(); 129 | for (ClassDoc clz : classes) { 130 | if (isExportable(clz) && hasClassMethods(clz) && !isExportedClosure(clz)) { 131 | eclasses.add(clz); 132 | } 133 | } 134 | 135 | if (eclasses.size() > 0) { 136 | // Write an index of classes 137 | writer.h1("Exported JavaScript-API: Index of Classes"); 138 | writer.ul(); 139 | for (ClassDoc clz : eclasses) { 140 | writer.li(); 141 | writer.println(getExportedName(clz, true, true)); 142 | } 143 | writer.ulEnd(); 144 | 145 | // Write each class 146 | for (ClassDoc clz : eclasses) { 147 | String className = getExportedName(clz, false, true); 148 | writer.h2("
"+ className + "
"); 149 | String comments = clz.commentText().trim(); 150 | if (!comments.isEmpty()) { 151 | writer.println("
" + filter(clz.commentText()) + "
"); 152 | } 153 | 154 | writer.table(1, "100%", 0, 0); 155 | writeConstructors(writer, clz); 156 | writeMethods(writer, clz, true, isExportedAll(clz), new ArrayList()); 157 | writer.tableEnd(); 158 | } 159 | } 160 | 161 | writer.bodyEnd(); 162 | writer.htmlEnd(); 163 | writer.flush(); 164 | writer.close(); 165 | } 166 | 167 | private void writeConstructors(HtmlDocletWriter writer, ClassDoc clz) { 168 | String cName = getExportedName(clz, false, true); 169 | boolean firstcon = true; 170 | for (ConstructorDoc cd : clz.constructors()) { 171 | if (isExportable(cd)) { 172 | if (firstcon) { 173 | writer.tr(); 174 | writer.tdColspanBgcolorStyle(2, "", "jsdocHeader"); 175 | writer.print("Constructors"); 176 | firstcon = false; 177 | writer.tdEnd(); 178 | writer.trEnd(); 179 | } 180 | writer.tr(); 181 | writer.tdVAlignClass("top", "jsdocRetType"); 182 | writer.print(" "); 183 | writer.tdEnd(); 184 | writer.tdVAlignClass("top", "jsdocMethod"); 185 | writer.print("" + cName + "("); 186 | writeParameters(writer, cd.parameters()); 187 | writer.print(")"); 188 | writer.br(); 189 | 190 | String comment = filter(cd.commentText()); 191 | comment += getCommentTags(cd); 192 | writer.print("" + comment + ""); 193 | 194 | writer.tdEnd(); 195 | writer.trEnd(); 196 | } 197 | } 198 | } 199 | 200 | private void writeMethods(HtmlDocletWriter writer, ClassDoc clz, boolean firstcon, boolean all, List visited) { 201 | if (clz == null) { 202 | return; 203 | } 204 | for (MethodDoc md : clz.methods()) { 205 | if (!md.isStatic() && md.isPublic() && !md.isAbstract()) { 206 | String sig = getSignatureMethod(md); 207 | if (!visited.contains(sig)) { 208 | if (all || isExportable(md)) { 209 | writeMethod(writer, firstcon, false, md); 210 | firstcon = false; 211 | } 212 | visited.add(sig); 213 | } 214 | } 215 | } 216 | writeMethods(writer, clz.superclass(), firstcon, all, visited); 217 | } 218 | 219 | private MethodDoc getMethodInInterface(MethodDoc md) { 220 | if (!md.containingClass().isInterface()) { 221 | String sig = getSignatureMethod(md); 222 | for (ClassDoc cd: md.containingClass().interfaces()) { 223 | for (MethodDoc m: cd.methods()) { 224 | if (sig.equals(getSignatureMethod(m)) && isExportable(m)) { 225 | return m; 226 | } 227 | } 228 | } 229 | } 230 | return null; 231 | } 232 | 233 | private MethodDoc getMethodInSuperclass(ClassDoc superc, MethodDoc md) { 234 | if (superc != null) { 235 | String sig = getSignatureMethod(md); 236 | for (MethodDoc m : superc.methods()) { 237 | if (sig.equals(getSignatureMethod(m))) { 238 | return m; 239 | } 240 | } 241 | return getMethodInSuperclass(superc.superclass(), md); 242 | } 243 | return null; 244 | } 245 | 246 | private String getSignatureMethod (MethodDoc md) { 247 | return md.returnType() + " " + md.name() + md.signature(); 248 | } 249 | 250 | private void writeMethod(HtmlDocletWriter writer, boolean firstcon, boolean writePakage, 251 | MethodDoc md) { 252 | ClassDoc cd = md.containingClass(); 253 | String pkg = writePakage ? getExportedName(cd, false, true) : ""; 254 | String name = getExportedName(md); 255 | if (name.startsWith("$wnd")) { 256 | pkg = ""; 257 | name = name.replaceFirst("^\\$wnd\\.", ""); 258 | } 259 | if (!pkg.isEmpty()) { 260 | pkg += "."; 261 | } 262 | 263 | if (firstcon) { 264 | writer.tr(); 265 | writer.tdColspanBgcolorStyle(2, "", "jsdocHeader"); 266 | writer.print("Methods"); 267 | firstcon = false; 268 | writer.tdEnd(); 269 | writer.trEnd(); 270 | } 271 | writer.tr(); 272 | writer.tdVAlignClass("top", "jsdocRetType"); 273 | writer.print(getExportedName(md.returnType(), true)); 274 | 275 | writer.tdEnd(); 276 | writer.tdVAlignClass("top", "jsdocMethod"); 277 | writer.print( 278 | "" + pkg + name + "" 279 | + "("); 280 | writeParameters(writer, md.parameters()); 281 | writer.print(")"); 282 | writer.br(); 283 | 284 | String comments = md.commentText(); 285 | if (!md.isStatic() && comments.isEmpty()) { 286 | MethodDoc id = getMethodInInterface(md); 287 | if (id != null) { 288 | comments = id.commentText(); 289 | } 290 | if (comments.isEmpty()) { 291 | id = getMethodInSuperclass(cd.superclass(), md); 292 | } else { 293 | md = id; 294 | } 295 | if (id != null) { 296 | comments = id.commentText(); 297 | md = id; 298 | } 299 | } 300 | comments += getCommentTags(md); 301 | if (!comments.isEmpty()) { 302 | writer.print("" + filter(comments) + ""); 303 | } 304 | writer.tdEnd(); 305 | writer.trEnd(); 306 | } 307 | 308 | private String getCommentTags(ExecutableMemberDoc doc) { 309 | String ret = ""; 310 | if (doc != null) { 311 | ret += "
    "; 312 | for (ParamTag t : doc.paramTags() ){ 313 | ret +="
  • " + t.parameterName() + " " + t.parameterComment(); 314 | } 315 | for (Tag t : doc.tags("return") ){ 316 | ret += "
  • return " + t.text(); 317 | } 318 | ret += "
"; 319 | } 320 | return ret; 321 | } 322 | 323 | private boolean hasClassMethods(ClassDoc clz) { 324 | if (clz != null && !clz.isInterface() && !clz.isAbstract()) { 325 | for (ConstructorDoc cd : clz.constructors()) { 326 | if (isExportable(cd)) { 327 | return true; 328 | } 329 | } 330 | for (MethodDoc md : clz.methods()) { 331 | if (!md.isStatic()) { 332 | boolean exportable = isExportable(md); 333 | if (!exportable && (md = getMethodInInterface(md)) != null) { 334 | exportable = isExportable(md); 335 | } 336 | if (exportable) { 337 | return true; 338 | } 339 | } 340 | } 341 | return hasClassMethods(clz.superclass()); 342 | } 343 | return false; 344 | } 345 | 346 | private boolean hasStaticMethods(ClassDoc clz) { 347 | int countExportedMethods = 0; 348 | for (MethodDoc md : clz.methods()) { 349 | if (isExportable(md) && md.isStatic()) { 350 | countExportedMethods++; 351 | } 352 | } 353 | return countExportedMethods > 0; 354 | } 355 | 356 | private String getExportedName(MethodDoc cd) { 357 | String ename = cd.name(); 358 | for (AnnotationDesc a : cd.annotations()) { 359 | if (a.annotationType().name().equals("Export")) { 360 | for (AnnotationDesc.ElementValuePair p : a.elementValues()) { 361 | ename = p.value().toString().trim(); 362 | break; 363 | } 364 | } 365 | } 366 | return ename.replaceAll("\"", ""); 367 | } 368 | 369 | protected String filter(String s) { 370 | if (s.startsWith("Created")) { 371 | return ""; 372 | } 373 | s = s.replaceAll("(?s)\\{@link\\s[^\\}]*?#(.+)\\}", "$1"); 374 | s = s.replaceAll("(?s)\\{@link\\s[^\\}]*?([^\\.\\}]+)\\}", "$1"); 375 | return s; 376 | } 377 | 378 | private String getExportedName(Type clz, boolean link) { 379 | return (clz.isPrimitive() ? "void".equals(clz.typeName()) ? " " 380 | : clz.typeName() : getExportedName(clz.asClassDoc(), link, false)) + clz.dimension(); 381 | } 382 | 383 | private void writeParameters(HtmlDocletWriter writer, Parameter[] ps) { 384 | writer.print(getParameterString(ps)); 385 | } 386 | 387 | private boolean isExportable(ConstructorDoc cd) { 388 | boolean export = isExported(cd.containingClass()); 389 | for (AnnotationDesc a : cd.annotations()) { 390 | if (a.annotationType().name().equals("Export")) { 391 | export = true; 392 | } 393 | if (a.annotationType().name().equals("NoExport")) { 394 | export = false; 395 | } 396 | } 397 | return export; 398 | } 399 | 400 | private boolean isExportable(MethodDoc md) { 401 | if (md == null) { 402 | return false; 403 | } 404 | boolean export = isExported(md.containingClass()); 405 | for (AnnotationDesc a : md.annotations()) { 406 | if (a.annotationType().name().equals("Export")) { 407 | return true; 408 | } 409 | if (a.annotationType().name().equals("NoExport")) { 410 | return false; 411 | } 412 | } 413 | if (!export) { 414 | export = isExportable(getMethodInInterface(md)); 415 | } 416 | return export; 417 | } 418 | 419 | 420 | private boolean isExportedClosure(ClassDoc clz) { 421 | for (AnnotationDesc a : clz.annotations()) { 422 | String aname = a.annotationType().name(); 423 | if (aname.equals("ExportClosure")) { 424 | return true; 425 | } 426 | } 427 | return false; 428 | } 429 | 430 | private boolean isExportedAll(ClassDoc clz) { 431 | for (AnnotationDesc a : clz.annotations()) { 432 | String aname = a.annotationType().name(); 433 | if (aname.equals("Export") || aname.equals("ExportClosure")) { 434 | for (AnnotationDesc.ElementValuePair p : a.elementValues()) { 435 | if ("all".equals(p.element().name())) { 436 | return "true".equals(p.value().toString()); 437 | } 438 | } 439 | } 440 | } 441 | return false; 442 | } 443 | 444 | private boolean isExported(ClassDoc clz) { 445 | for (AnnotationDesc a : clz.annotations()) { 446 | String aname = a.annotationType().name(); 447 | if (aname.equals("Export") || aname.equals("ExportClosure")) { 448 | return true; 449 | } 450 | } 451 | return false; 452 | } 453 | 454 | private String getExportedPackage(ClassDoc clz) { 455 | if (clz == null) { 456 | return ""; 457 | } 458 | 459 | PackageDoc cpkg = clz.containingPackage(); 460 | String pkg = cpkg == null ? "" : (cpkg.name().trim()); 461 | 462 | for (AnnotationDesc a : clz.annotations()) { 463 | if (a.annotationType().name().equals("ExportPackage")) { 464 | for (AnnotationDesc.ElementValuePair p : a.elementValues()) { 465 | pkg = p.value().toString().replaceAll("\"", ""); 466 | if (!pkg.isEmpty()) { 467 | pkg += "."; 468 | } 469 | break; 470 | } 471 | } 472 | } 473 | return pkg; 474 | } 475 | 476 | private String getExportedName(ClassDoc clz, boolean withLink, boolean withPkg) { 477 | if (clz == null) { 478 | return ""; 479 | } 480 | 481 | PackageDoc cpkg = clz.containingPackage(); 482 | String pkg = cpkg == null ? "" : cpkg.name(); 483 | String name = clz.name(); 484 | 485 | boolean isClosure = false; 486 | boolean isEnclosed = clz.containingClass() != null; 487 | boolean isExportEnclosed = isEnclosed && isExportable(clz.containingClass()); 488 | boolean removeEnclosedPart = false; 489 | 490 | if (isExportEnclosed) { 491 | pkg = getExportedName(clz.containingClass(), false, true); 492 | removeEnclosedPart = true; 493 | } 494 | 495 | for (AnnotationDesc a : clz.annotations()) { 496 | if (a.annotationType().name().equals("ExportPackage")) { 497 | for (AnnotationDesc.ElementValuePair p : a.elementValues()) { 498 | pkg = p.value().toString().trim(); 499 | removeEnclosedPart = true; 500 | break; 501 | } 502 | } 503 | if (a.annotationType().name().equals("Export")) { 504 | for (AnnotationDesc.ElementValuePair p : a.elementValues()) { 505 | if ("value".equals(p.element().name())) { 506 | name = p.value().toString().trim().replaceAll("\"", ""); 507 | if (!name.isEmpty()) { 508 | removeEnclosedPart = false; 509 | break; 510 | } 511 | } 512 | } 513 | } 514 | if (a.annotationType().name().equals("ExportClosure")) { 515 | isClosure = true; 516 | name = "function("; 517 | name += getParameterString(clz.methods()[0].parameters()); 518 | name += ")"; 519 | pkg = ""; 520 | } 521 | } 522 | if (removeEnclosedPart) { 523 | name = name.replaceFirst("^.+\\.", ""); 524 | } 525 | 526 | pkg = pkg.replaceAll("\"", "").replaceFirst("\\.+$", ""); 527 | if (!pkg.isEmpty()) { 528 | pkg += "."; 529 | } 530 | 531 | if (withLink && !isClosure && 532 | !name.matches("String|JavaScriptObject|Object|Exportable|Class|Date")) { 533 | if (withPkg) { 534 | name = pkg + "" + name + ""; 535 | } else { 536 | name = "" + name + ""; 537 | } 538 | } else if (withPkg) { 539 | name = pkg + name; 540 | } 541 | return name; 542 | } 543 | 544 | private String getParameterString(Parameter[] ps) { 545 | String result = ""; 546 | for (int i = 0; i < ps.length; i++) { 547 | Type type = ps[i].type(); 548 | String ename = getExportedName(type, true); 549 | String pname = ename.contains("function") ? "{}" : ps[i].name(); 550 | result += "" + ename 551 | + " " + pname 552 | + ""; 553 | if (i < ps.length - 1) { 554 | result += ", "; 555 | } 556 | } 557 | return result; 558 | } 559 | 560 | 561 | 562 | private static boolean isExportable(ClassDoc clz) { 563 | if (clz == null) { 564 | return false; 565 | } 566 | for (ClassDoc i : clz.interfaces()) { 567 | if (i.name().contains("Exportable")) { 568 | return true; 569 | } 570 | for (ClassDoc j : i.interfaces()) { 571 | if (j.name().contains("Exportable")) { 572 | return true; 573 | } 574 | } 575 | } 576 | for (MethodDoc m : clz.methods()) { 577 | if (!m.isStatic()) { 578 | for (AnnotationDesc a : m.annotations()) { 579 | if (a.annotationType().name().equals("Export")) { 580 | return true; 581 | } 582 | } 583 | } 584 | } 585 | return isExportable(clz.superclass()); 586 | } 587 | 588 | public String getCSS() { 589 | return ""; 645 | } 646 | 647 | } 648 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/DispatchTable.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | import com.google.gwt.core.ext.typeinfo.JArrayType; 11 | import com.google.gwt.core.ext.typeinfo.JType; 12 | 13 | /** 14 | * Information to assist quick overloaded type resolution at runtime. 15 | */ 16 | public class DispatchTable { 17 | 18 | public DispatchTable(ExportableTypeOracle x) { 19 | xTypeOracle = x; 20 | } 21 | 22 | private boolean isOverloaded; 23 | private static ExportableTypeOracle xTypeOracle; 24 | 25 | /** 26 | * Add a signature to the dispatch table. Returns false if the same signature 27 | * occurs more than once. 28 | */ 29 | public boolean addSignature(JExportableMethod method) { 30 | 31 | JExportableParameter[] exportableParameters = method.getExportableParameters(); 32 | 33 | isOverloaded |= method.isVarArgs(); 34 | 35 | Set sigs = sigMap.get(exportableParameters.length); 36 | if (sigs == null) { 37 | sigs = new HashSet(); 38 | sigMap.put(exportableParameters.length, sigs); 39 | } 40 | isOverloaded |= sigMap.size() > 1; 41 | 42 | // When one argument is Object we have to handle the method via dispatcher, 43 | // which is able to cast primitive arguments to Object 44 | if (!isOverloaded) { 45 | for (JExportableParameter p : exportableParameters) { 46 | isOverloaded |= "java.lang.Object".equals(p.getTypeName()); 47 | } 48 | } 49 | 50 | Signature sig = new Signature(method, exportableParameters); 51 | if (sigs.contains(sig)) { 52 | return false; 53 | } else { 54 | sigs.add(sig); 55 | } 56 | isOverloaded |= sigs.size() > 1; 57 | return true; 58 | } 59 | 60 | public int maxArity() { 61 | return Collections.max(sigMap.keySet()).intValue(); 62 | } 63 | 64 | public boolean isOverloaded() { 65 | return isOverloaded; 66 | } 67 | 68 | public static String toJSON(HashMap dispatchMap) { 69 | StringBuilder sb = new StringBuilder(); 70 | sb.append("{\n"); 71 | int i = 0; 72 | for (Map.Entry e : dispatchMap.entrySet()) { 73 | if (e.getValue().isOverloaded()) { 74 | // We use a number instead of the method name, so as the generated 75 | // js is small. 76 | sb.append(" " + i++ + ":" + e.getValue().toJSON() + ",\n"); 77 | } 78 | } 79 | sb.append("}"); 80 | return sb.toString(); 81 | } 82 | 83 | public static class Signature { 84 | 85 | private JExportableMethod method; 86 | 87 | private JExportableParameter[] exportableParameters; 88 | 89 | public Signature(JExportableMethod method, 90 | JExportableParameter[] exportableParameters) { 91 | this.method = method; 92 | this.exportableParameters = exportableParameters; 93 | } 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (this == o) { 98 | return true; 99 | } 100 | if (o == null || getClass() != o.getClass()) { 101 | return false; 102 | } 103 | 104 | Signature signature = (Signature) o; 105 | 106 | if (!Arrays 107 | .equals(exportableParameters, signature.exportableParameters)) { 108 | return false; 109 | } 110 | 111 | return true; 112 | } 113 | 114 | @Override 115 | public int hashCode() { 116 | return exportableParameters != null ? Arrays 117 | .hashCode(exportableParameters) : 0; 118 | } 119 | 120 | public String toJSON() { 121 | StringBuilder sb = new StringBuilder(); 122 | sb.append("["); 123 | String functionRef = "@" + method.getJSNIReference(); 124 | if (method.isStatic() || method.needsWrapper()) { 125 | sb.append(functionRef); 126 | } else { 127 | sb.append("function() { return this." + functionRef+".apply(this, arguments); }"); 128 | } 129 | sb.append(","); 130 | 131 | String wrap = method.getExportableReturnType(false) == null ? null : method.getExportableReturnType(false).getWrapperFunc(); 132 | sb.append(wrap + "," + generateWrapArgumentsFunction(method) + ","); 133 | for (JExportableParameter param : exportableParameters) { 134 | String jsType = param.getJsTypeOf(); 135 | if (jsType.equals("number") || jsType.equals("object") || 136 | jsType.equals("string") || jsType.equals("boolean") || 137 | jsType.equals("array") ) { 138 | jsType = "\""+jsType+"\""; 139 | } 140 | sb.append(jsType + ","); 141 | } 142 | sb.append("]"); 143 | return sb.toString(); 144 | } 145 | } 146 | 147 | public String toJSON() { 148 | StringBuilder json = new StringBuilder(); 149 | json.append("{\n"); 150 | for (Integer arity : sigMap.keySet()) { 151 | json.append(" " + arity + ":" + toJSON(sigMap.get(arity)) + ",\n"); 152 | } 153 | json.append(" }"); 154 | return json.toString(); 155 | } 156 | 157 | static boolean isAnyOverridden(HashMap dispatchMap) { 158 | for (Map.Entry e : dispatchMap.entrySet()) { 159 | if (e.getValue().isOverloaded()) { 160 | return true; 161 | } 162 | } 163 | return false; 164 | } 165 | private String toJSON(Set signatures) { 166 | StringBuilder sb = new StringBuilder(); 167 | sb.append("["); 168 | for (Signature s : signatures) { 169 | sb.append(s.toJSON() + ","); 170 | } 171 | sb.append("]"); 172 | return sb.toString(); 173 | } 174 | 175 | private Map> sigMap 176 | = new HashMap>(); 177 | 178 | private static String generateWrapArgumentsFunction(JExportableMethod method) { 179 | String args = "["; 180 | JExportableParameter params[] = method.getExportableParameters(); 181 | boolean hasClosures = false; 182 | for (int i = 0; i < params.length; i++) { 183 | args += (i > 0 ? "," : ""); 184 | String argName = "args[" + i + "]"; 185 | JType t = params[i].getParam().getType(); 186 | JArrayType a = t.isArray(); 187 | if (a != null) { 188 | JExportableClassType requestedType = xTypeOracle.findExportableClassType(a.getComponentType().getQualifiedSourceName()); 189 | if (xTypeOracle.isClosure(requestedType)) { 190 | hasClosures = true; 191 | args += argName 192 | + " == null ? null :function(a) {for (var i = 0; i < a.length ; i++) {a[i] = a[i].constructor == $wnd." 193 | + requestedType.getJSQualifiedExportName() 194 | + " ? a[i]." + ClassExporter.GWT_INSTANCE + " : @" 195 | + requestedType.getQualifiedExporterImplementationName() 196 | + "::makeClosure(Lcom/google/gwt/core/client/JavaScriptObject;)(a[i]);}return a;}(" 197 | + argName + ")"; 198 | } else { 199 | args += argName; 200 | } 201 | } else { 202 | JExportableClassType requestedType = xTypeOracle.findExportableClassType(t.getQualifiedSourceName()); 203 | if (xTypeOracle.isClosure(requestedType)) { 204 | hasClosures = true; 205 | args += argName 206 | + " == null ? null : function(a) { a = a.constructor == $wnd." 207 | + requestedType.getJSQualifiedExportName() 208 | + " ? a." + ClassExporter.GWT_INSTANCE + " : @" 209 | + requestedType.getQualifiedExporterImplementationName() 210 | + "::makeClosure(Lcom/google/gwt/core/client/JavaScriptObject;)(a); return a;}(" 211 | + argName + ")"; 212 | } else { 213 | args += argName; 214 | } 215 | } 216 | } 217 | args += "]"; 218 | String unshift = "@org.timepedia.exporter.client.ExporterUtil::unshift(Ljava/lang/Object;Lcom/google/gwt/core/client/JavaScriptObject;)"; 219 | boolean needsUnshift = !method.isStatic() && method.needsWrapper(); 220 | if (hasClosures) { 221 | String ret = "function(instance, args){return "; 222 | if (needsUnshift) { 223 | return ret + unshift + "(instance, " + args + ")}"; 224 | } else { 225 | return ret + args + "}"; 226 | } 227 | } else { 228 | return needsUnshift ? unshift : ""; 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/ExportableTypeOracle.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.timepedia.exporter.client.Export; 9 | import org.timepedia.exporter.client.ExportClosure; 10 | import org.timepedia.exporter.client.ExportConstructor; 11 | import org.timepedia.exporter.client.Getter; 12 | import org.timepedia.exporter.client.NoExport; 13 | import org.timepedia.exporter.client.Setter; 14 | import org.timepedia.exporter.client.StructuralType; 15 | 16 | import com.google.gwt.core.ext.TreeLogger; 17 | import com.google.gwt.core.ext.typeinfo.JAbstractMethod; 18 | import com.google.gwt.core.ext.typeinfo.JClassType; 19 | import com.google.gwt.core.ext.typeinfo.JConstructor; 20 | import com.google.gwt.core.ext.typeinfo.JField; 21 | import com.google.gwt.core.ext.typeinfo.JMethod; 22 | import com.google.gwt.core.ext.typeinfo.JType; 23 | import com.google.gwt.core.ext.typeinfo.TypeOracle; 24 | import com.google.gwt.core.ext.typeinfo.TypeOracleException; 25 | 26 | /** 27 | * 28 | */ 29 | public class ExportableTypeOracle { 30 | 31 | public static final String JSO_CLASS 32 | = "com.google.gwt.core.client.JavaScriptObject"; 33 | 34 | static final String EXPORTER_CLASS = "org.timepedia.exporter.client.Exporter"; 35 | 36 | static final String EXPORTABLE_CLASS 37 | = "org.timepedia.exporter.client.Exportable"; 38 | 39 | static final String EXPORTALL_CLASS = "org.timepedia.exporter.client.ExporterUtil.ExportAll"; 40 | 41 | static final String EXPORT_OVERLAY_CLASS 42 | = "org.timepedia.exporter.client.ExportOverlay"; 43 | 44 | private static final String STRING_CLASS = "java.lang.String"; 45 | private static final String DATE_CLASS = "java.util.Date"; 46 | 47 | private JClassType exportAllType; 48 | 49 | public boolean isConstructor(JAbstractMethod method, JExportableClassType type) { 50 | return method.isConstructor() != null || method.getAnnotation(ExportConstructor.class) !=null; 51 | } 52 | 53 | public boolean isExportable(JField field) { 54 | return field.isStatic() && field.isPublic() && field.isFinal() && ( 55 | isExportable(field.getAnnotation(Export.class)) || ( 56 | isExportable(field.getEnclosingType()) && !isNotExportable( 57 | field.getAnnotation(NoExport.class)))); 58 | } 59 | 60 | public boolean isExportable(JClassType type) { 61 | return isExportable(type.getAnnotation(Export.class)) || 62 | (type.isInterface() != null && 63 | isExportable(type.getAnnotation(ExportClosure.class))); 64 | } 65 | 66 | public boolean isExportable(JExportableClassType type) { 67 | return isExportable(type.getRequestedType()); 68 | } 69 | 70 | public static boolean isExportable(Export annotation) { 71 | return annotation != null; 72 | } 73 | 74 | public static boolean isExportable(Getter annotation) { 75 | return annotation != null; 76 | } 77 | 78 | public static boolean isExportable(Setter annotation) { 79 | return annotation != null; 80 | } 81 | 82 | public boolean isExportable(JAbstractMethod method, JExportableClassType type) { 83 | boolean export = false; 84 | // Only public methods are exported 85 | if (method.isPublic()) { 86 | Export e; 87 | if (method instanceof JConstructor && isInstantiable(method.getEnclosingType()) && method.getParameters().length == 0) { 88 | // zero-arg constructors always exportable 89 | export = true; 90 | } else if (isNotExportable(method.getAnnotation(NoExport.class))) { 91 | // Do not export methods annotated as NoExport, although the 92 | // method is marked as export in an interface or the entire class 93 | // is annotated as Export 94 | export = false; 95 | } else if (isExportable(method.getAnnotation(Export.class)) || isExportable(method.getAnnotation(Setter.class)) || isExportable(method.getAnnotation(Getter.class))) { 96 | // Export this method if has the Export annotation 97 | export = true; 98 | } else if (isExportable(method.getEnclosingType())) { 99 | // Export all method in a class annotated as Export 100 | export = true; 101 | } else if (type != null && (e = type.getType().getAnnotation(Export.class)) != null && e.all()) { 102 | // Export this method if the class has the Export.all attribute set 103 | // Filter some generic methods present in Object 104 | export = !method.getName().matches("getClass|hashCode|equals|notify|notifyAll|wait"); 105 | } else { 106 | // Export methods which are annotated in implemented interfaces 107 | for (JClassType c : method.getEnclosingType().getImplementedInterfaces()) { 108 | for (JMethod m : c.getMethods()) { 109 | if (!isNotExportable(m.getAnnotation(NoExport.class)) 110 | && (isExportable(c) || isExportable(m.getAnnotation(Export.class)) || isExportable(method.getAnnotation(Setter.class)) || isExportable(method.getAnnotation(Getter.class))) 111 | && m.getName().equals(method.getName())) { 112 | if (m.getReadableDeclaration(true, true, false, true, true).equals( 113 | ((JMethod) method).getReadableDeclaration(true, true, false, 114 | true, true))) { 115 | export = true; 116 | break; 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | return export; 124 | } 125 | 126 | private static boolean isExportable(ExportClosure annotation) { 127 | return annotation != null; 128 | } 129 | 130 | private static boolean isNotExportable(NoExport annotation) { 131 | return annotation != null; 132 | } 133 | 134 | private static boolean isExportable(ExportConstructor annotation) { 135 | return annotation != null; 136 | } 137 | 138 | public boolean isExportableFactoryMethod(JMethod method, JType retClass) { 139 | if (isExportable(method, null) && isExportable(method.getAnnotation(ExportConstructor.class))) { 140 | if (method.isStatic() && retClass.equals(method.getReturnType())) { 141 | return true; 142 | } 143 | throw new RuntimeException("Method " + method.getEnclosingType() + " " + method 144 | + " ExportConstructor but it is not static or it does not return a " + retClass); 145 | } 146 | return false; 147 | } 148 | 149 | private TypeOracle typeOracle; 150 | 151 | private TreeLogger log; 152 | 153 | private JClassType exportableType = null; 154 | 155 | private JClassType jsoType = null; 156 | 157 | private JClassType stringType = null; 158 | private JClassType dateType = null; 159 | 160 | private JClassType exportOverlayType; 161 | 162 | private Map overlayTypes 163 | = new HashMap(); 164 | 165 | public ExportableTypeOracle(TypeOracle typeOracle, TreeLogger log) { 166 | this.typeOracle = typeOracle; 167 | this.log = log; 168 | exportableType = typeOracle.findType(EXPORTABLE_CLASS); 169 | exportOverlayType = typeOracle.findType(EXPORT_OVERLAY_CLASS); 170 | exportAllType = typeOracle.findType(EXPORTALL_CLASS); 171 | 172 | jsoType = typeOracle.findType(JSO_CLASS); 173 | stringType = typeOracle.findType(STRING_CLASS); 174 | dateType = typeOracle.findType(DATE_CLASS); 175 | assert exportableType != null; 176 | assert exportOverlayType != null; 177 | assert jsoType != null; 178 | assert stringType != null; 179 | 180 | for (JClassType t : typeOracle.getTypes()) { 181 | if (t.isAssignableTo(exportOverlayType) && !t.equals(exportOverlayType)) { 182 | JClassType targetType = getExportOverlayType(t); 183 | overlayTypes.put(targetType.getQualifiedSourceName(), 184 | new JExportOverlayClassType(this, t)); 185 | } 186 | } 187 | } 188 | 189 | public JExportableClassType findExportableClassType(String requestedClass) { 190 | JClassType requestedType = typeOracle.findType(requestedClass); 191 | if (requestedType != null) { 192 | if (requestedType.isAssignableTo(exportOverlayType)) { 193 | return new JExportOverlayClassType(this, requestedType); 194 | } else if (requestedType.isAssignableTo(exportableType)) { 195 | return new JExportableClassType(this, requestedType); 196 | } 197 | JExportOverlayClassType exportOverlay = overlayTypes.get(requestedClass); 198 | return exportOverlay; 199 | } 200 | return null; 201 | } 202 | 203 | public JExportableType findExportableType(String paramTypeName) { 204 | try { 205 | JType type = typeOracle.parse(paramTypeName); 206 | JClassType cType = type != null ? type.isClassOrInterface() : null; 207 | if (type.isPrimitive() != null) { 208 | return new JExportablePrimitiveType(this, type.isPrimitive()); 209 | } else if (type.isArray() != null) { 210 | return new JExportableArrayType(this, type.isArray()); 211 | } else if (overlayTypes.containsKey(paramTypeName)) { 212 | return overlayTypes.get(paramTypeName); 213 | } else if (cType.isAssignableTo(exportOverlayType)) { 214 | return new JExportOverlayClassType(this, type.isClassOrInterface()); 215 | } else if (cType != null && (cType.isAssignableTo(exportableType) 216 | || cType.isAssignableTo(stringType) 217 | || cType.isAssignableTo(dateType) 218 | || cType.isAssignableTo(jsoType))) { 219 | return new JExportableClassType(this, type.isClassOrInterface()); 220 | } else { 221 | return null; 222 | } 223 | } catch (TypeOracleException e) { 224 | return null; 225 | } 226 | } 227 | 228 | public JClassType getExportOverlayType(JClassType requestedType) { 229 | JClassType[] inf = requestedType.getImplementedInterfaces(); 230 | for (JClassType i : inf) { 231 | if (isExportOverlay(i)) { 232 | return i.isParameterized().getTypeArgs()[0]; 233 | } 234 | } 235 | return null; 236 | } 237 | 238 | public boolean isArray(JExportableClassType jExportableClassType) { 239 | return jExportableClassType.getType().isArray() != null; 240 | } 241 | 242 | // We maintain a cache with overlay classes exported via export closure 243 | static HashMap closuresCache = new HashMap(); 244 | 245 | public boolean isClosure(JExportableClassType jExportableClassType) { 246 | if (jExportableClassType == null) { 247 | return false; 248 | } 249 | 250 | String cType = jExportableClassType.getType().getQualifiedSourceName(); 251 | if (closuresCache.containsKey(cType)) { 252 | return true; 253 | } 254 | 255 | JClassType rType = jExportableClassType.getRequestedType(); 256 | if (rType != null && rType.isAssignableTo(exportableType)) { 257 | ExportClosure ann = rType.getAnnotation(ExportClosure.class); 258 | if (ann != null && rType.isInterface() != null) { 259 | if (rType.getMethods().length > 0) { 260 | closuresCache.put(rType.getQualifiedSourceName(), jExportableClassType); 261 | closuresCache.put(cType, jExportableClassType); 262 | return true; 263 | } 264 | } 265 | } 266 | return false; 267 | } 268 | 269 | public boolean isExportOverlay(JClassType i) { 270 | return i.isAssignableTo(exportOverlayType); 271 | } 272 | 273 | public boolean isInstantiable(JClassType i) { 274 | return !i.isAbstract() && i.isInterface() == null; 275 | } 276 | 277 | public boolean isJavaScriptObject(JExportableClassType type) { 278 | return type.getType().isAssignableTo(jsoType); 279 | } 280 | 281 | public boolean isString(JExportableClassType type) { 282 | return type.getType().isAssignableTo(stringType); 283 | } 284 | 285 | public boolean isDate(JExportableClassType type) { 286 | return type.getType().isAssignableTo(dateType); 287 | } 288 | 289 | public boolean isString(JType type) { 290 | return type.isClass() != null && type.isClass().isAssignableTo(stringType); 291 | } 292 | 293 | public boolean isJavaScriptObject(JType type) { 294 | return type.isClass() != null && type.isClass().isAssignableTo(jsoType); 295 | } 296 | 297 | public boolean isExportAll(String requestedClass) { 298 | return typeOracle.findType(requestedClass).isAssignableTo(exportAllType); 299 | } 300 | 301 | public boolean hasExportableMethods(JClassType type) { 302 | return new JExportableClassType(this, type).getExportableMethods().length > 0; 303 | } 304 | 305 | public List findAllExportableTypes() { 306 | ArrayList types = new ArrayList(); 307 | // Closures should be exported the first 308 | for (JClassType t : typeOracle.getTypes()) { 309 | if (t.isAssignableTo(exportableType) && isClosure(t)) { 310 | types.add(t); 311 | } 312 | } 313 | // ExportOverlays should be exported before other classes 314 | for (JClassType t : typeOracle.getTypes()) { 315 | if (types.contains(t) || t.equals(exportAllType) 316 | || t.equals(exportableType) || t.equals(exportOverlayType)) { 317 | continue; 318 | } 319 | if (t.isAssignableTo(exportOverlayType)) { 320 | types.add(t); 321 | } 322 | } 323 | // Finally all exportable classes 324 | for (JClassType t : typeOracle.getTypes()) { 325 | if (types.contains(t) || t.equals(exportAllType) 326 | || t.equals(exportableType) || t.equals(exportOverlayType)) { 327 | continue; 328 | } 329 | // Apart from implementing the Exportable interface, exportable 330 | // classes must have the @Export annotation in either the class 331 | // declaration or in one or more methods. 332 | if (t.isAssignableTo(exportableType)) { 333 | if (isExportable(t) || hasExportableMethods(t)) { 334 | types.add(t); 335 | } 336 | } 337 | } 338 | return types; 339 | } 340 | 341 | public boolean isClosure(JClassType type) { 342 | return type.getAnnotation(ExportClosure.class) != null; 343 | } 344 | 345 | public boolean isStructuralType(JClassType type) { 346 | // always false for now until enabled 347 | return false && type.getAnnotation(StructuralType.class) != null; 348 | } 349 | 350 | public String getJsTypeOf(JClassType type) { 351 | if (type.isAssignableTo(stringType)) { 352 | return "string"; 353 | } else if (type.isAssignableTo(jsoType)) { 354 | return "object"; 355 | } 356 | return "@" + type.getQualifiedSourceName() + "::class"; 357 | } 358 | 359 | public JClassType getExportableType() { 360 | return exportableType; 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/ExporterGenerator.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.Generator; 4 | import com.google.gwt.core.ext.GeneratorContext; 5 | import com.google.gwt.core.ext.TreeLogger; 6 | import com.google.gwt.core.ext.UnableToCompleteException; 7 | 8 | public class ExporterGenerator extends Generator { 9 | 10 | public String generate(TreeLogger logger, GeneratorContext ctx, 11 | String requestedClass) throws UnableToCompleteException { 12 | ClassExporter classExporter = new ClassExporter(logger, ctx); 13 | String generatedClass = classExporter.exportClass(requestedClass, true); 14 | return generatedClass; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/ExporterGeneratorNoExport.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.Generator; 4 | import com.google.gwt.core.ext.GeneratorContext; 5 | import com.google.gwt.core.ext.TreeLogger; 6 | import com.google.gwt.core.ext.UnableToCompleteException; 7 | 8 | public class ExporterGeneratorNoExport extends Generator { 9 | 10 | public String generate(TreeLogger logger, GeneratorContext ctx, 11 | String requestedClass) throws UnableToCompleteException { 12 | ClassExporter classExporter = new ClassExporter(logger, ctx); 13 | String generatedClass = classExporter.exportClass(requestedClass, false); 14 | return generatedClass; 15 | } 16 | } -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportOverlayClassType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JClassType; 4 | 5 | /** 6 | * 7 | */ 8 | public class JExportOverlayClassType extends JExportableClassType { 9 | 10 | private JClassType exportType; 11 | 12 | public JExportOverlayClassType(ExportableTypeOracle exportableTypeOracle, 13 | JClassType requestedType) { 14 | super(exportableTypeOracle, requestedType); 15 | exportType = exportableTypeOracle.getExportOverlayType(requestedType); 16 | } 17 | 18 | @Override 19 | public JClassType getType() { 20 | return exportType; 21 | } 22 | 23 | public JClassType getOverlayType() { 24 | return super.getType(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportable.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | /** 4 | * Interface for all exportable elements. Maps Java package, field, and method 5 | * names into JS equivalents 6 | * 7 | * @author Ray Cromwell 8 | */ 9 | public interface JExportable { 10 | 11 | public String getJSQualifiedExportName(); 12 | 13 | public String getJSNIReference(); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableArrayType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JArrayType; 4 | import com.google.gwt.core.ext.typeinfo.JType; 5 | 6 | import org.timepedia.exporter.client.ExporterUtil; 7 | 8 | /** 9 | * 10 | */ 11 | public class JExportableArrayType extends JExportableClassType 12 | implements JExportableType { 13 | 14 | public JExportableArrayType(ExportableTypeOracle exportableTypeOracle, 15 | JArrayType array) { 16 | super(exportableTypeOracle, array); 17 | this.exportableTypeOracle = exportableTypeOracle; 18 | this.array = array; 19 | } 20 | 21 | private ExportableTypeOracle exportableTypeOracle; 22 | 23 | private JArrayType array; 24 | 25 | public boolean needsExport() { 26 | return true; 27 | } 28 | 29 | public String getQualifiedSourceName() { 30 | return array.getQualifiedSourceName(); 31 | } 32 | 33 | @Override 34 | public String getWrapperFunc() { 35 | JType type = array.getComponentType(); 36 | return "@" + ExporterUtil.class.getName() + "::wrap([" 37 | + type.getJNISignature() + ")"; 38 | } 39 | 40 | public JExportableType getComponentType() { 41 | return exportableTypeOracle 42 | .findExportableType(array.getComponentType().getQualifiedSourceName()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableClassType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.Comparator; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import org.timepedia.exporter.client.Export; 13 | import org.timepedia.exporter.client.ExportPackage; 14 | import org.timepedia.exporter.client.ExporterUtil; 15 | import org.timepedia.exporter.client.Getter; 16 | import org.timepedia.exporter.client.SType; 17 | import org.timepedia.exporter.client.Setter; 18 | 19 | import com.google.gwt.core.ext.typeinfo.JClassType; 20 | import com.google.gwt.core.ext.typeinfo.JConstructor; 21 | import com.google.gwt.core.ext.typeinfo.JField; 22 | import com.google.gwt.core.ext.typeinfo.JMethod; 23 | import com.google.gwt.core.ext.typeinfo.JType; 24 | 25 | /** 26 | * 27 | */ 28 | public class JExportableClassType implements JExportable, JExportableType { 29 | 30 | private static final String IMPL_EXTENSION = "_ExporterImpl"; 31 | 32 | private ExportableTypeOracle exportableTypeOracle; 33 | 34 | private JClassType type; 35 | 36 | public JExportableClassType(ExportableTypeOracle exportableTypeOracle, 37 | JClassType type) { 38 | this.exportableTypeOracle = exportableTypeOracle; 39 | 40 | this.type = type; 41 | } 42 | 43 | public boolean equals(Object o) { 44 | if (this == o) { 45 | return true; 46 | } 47 | if (o == null || getClass() != o.getClass()) { 48 | return false; 49 | } 50 | 51 | JExportableClassType that = (JExportableClassType) o; 52 | 53 | return getQualifiedSourceName().equals(that.getQualifiedSourceName()); 54 | } 55 | 56 | public String[] getEnclosingClasses() { 57 | String[] enc = type.getName().split("\\."); 58 | String[] enclosingTypes = new String[enc.length - 1]; 59 | if (enc.length > 1) { 60 | System.arraycopy(enc, 0, enclosingTypes, 0, enclosingTypes.length); 61 | } 62 | return enclosingTypes; 63 | } 64 | 65 | public JExportableConstructor[] getExportableConstructors() { 66 | ArrayList exportableCons = new ArrayList(); 67 | for (JConstructor method : type.getConstructors()) { 68 | if (exportableTypeOracle.isExportable(method, this)) { 69 | exportableCons.add(new JExportableConstructor(this, method)); 70 | } 71 | } 72 | return exportableCons.toArray(new JExportableConstructor[0]); 73 | } 74 | 75 | public JExportableMethod[] getExportableFactoryMethods() { 76 | ArrayList exportableMethods = new ArrayList(); 77 | JType retClass = getTypeToExport(); 78 | for (JMethod method : type.getMethods()) { 79 | if (exportableTypeOracle.isExportableFactoryMethod(method, retClass)) { 80 | exportableMethods.add(new JExportableMethod(this, method)); 81 | } 82 | } 83 | return exportableMethods.toArray(new JExportableMethod[0]); 84 | } 85 | 86 | public JExportableField[] getExportableFields() { 87 | ArrayList exportableFields 88 | = new ArrayList(); 89 | 90 | for (JField field : type.getFields()) { 91 | if (exportableTypeOracle.isExportable(field)) { 92 | exportableFields.add(new JExportableField(this, field)); 93 | } 94 | } 95 | return exportableFields.toArray(new JExportableField[0]); 96 | } 97 | 98 | private JExportableMethod[] exportableMethods; 99 | 100 | private JExportableMethod jsInitMethod; 101 | private JExportableMethod afterCreateMethod; 102 | 103 | public JExportableMethod[] getExportableMethods() { 104 | if (exportableMethods == null) { 105 | ArrayList ret = new ArrayList(); 106 | 107 | // Create a set with all methods in this class 108 | HashSet classMethods = new HashSet(); 109 | classMethods.addAll(Arrays.asList(type.getMethods())); 110 | classMethods.addAll(Arrays.asList(type.getInheritableMethods())); 111 | List sortedList = new ArrayList(classMethods); 112 | 113 | // We sort methods so as the user can modify the order they are exported via 114 | // the export annotation. It is useful when we want export methods whose namespace 115 | // depends on previous methods, ie: 116 | // @Export("$wnd.$") method1() 117 | // @Export("$wnd.$.method") method2() 118 | Collections.sort(sortedList, new Comparator() { 119 | String exportableMethodname(JMethod m) { 120 | String ret = m.getName(); 121 | Export e = m.getAnnotation(Export.class); 122 | if (e != null && e.value() != null && !e.value().isEmpty()) { 123 | ret = e.value(); 124 | } 125 | return ret; 126 | } 127 | public int compare(JMethod o1, JMethod o2) { 128 | return exportableMethodname(o1).compareTo(exportableMethodname(o2)); 129 | } 130 | }); 131 | 132 | for (JMethod method : sortedList) { 133 | if (exportableTypeOracle.isConstructor(method, this)) { 134 | continue; 135 | } 136 | 137 | if (exportableTypeOracle.isExportable(method, this)) { 138 | JExportableMethod m = new JExportableMethod(this, method); 139 | if (m.isExportJsInitMethod() 140 | && exportableTypeOracle.isJavaScriptObject(method.getReturnType()) 141 | ) { 142 | jsInitMethod = m; 143 | } 144 | if (m.isExportAfterCreateMethod()) { 145 | afterCreateMethod = m; 146 | } else { 147 | ret.add(m); 148 | } 149 | } 150 | } 151 | exportableMethods = ret.toArray(new JExportableMethod[0]); 152 | } 153 | return exportableMethods; 154 | } 155 | 156 | public Property[] getExportableProperties() { 157 | Map properties = new HashMap(); 158 | JExportableMethod[] methods = getExportableMethods(); 159 | for (JExportableMethod method : methods) { 160 | Getter getter = method.getMethod().getAnnotation(Getter.class); 161 | if (getter != null) { 162 | Property property = getProperty(properties, method, getter.value()); 163 | if (property != null) { 164 | property.setGetter(method); 165 | } 166 | } 167 | Setter setter = method.getMethod().getAnnotation(Setter.class); 168 | if (setter != null) { 169 | Property property = getProperty(properties, method, setter.value()); 170 | if (property != null) { 171 | property.setSetter(method); 172 | } 173 | } 174 | 175 | } 176 | return properties.values().toArray(new Property[properties.size()]); 177 | } 178 | 179 | /** 180 | * @param properties 181 | * @param method 182 | * @param name 183 | */ 184 | private Property getProperty(Map properties, 185 | JExportableMethod method, String name) { 186 | Property property = null; 187 | if ("".equals(name)) { 188 | String methodName = method.getName(); 189 | if (methodName.startsWith("get")) { 190 | methodName = methodName.substring(3); 191 | } 192 | else if (methodName.startsWith("is")) { 193 | methodName = methodName.substring(2); 194 | } 195 | else if (methodName.startsWith("set")) { 196 | methodName = methodName.substring(3); 197 | } 198 | methodName = Character.toLowerCase(methodName.charAt(0)) + methodName.substring(1); 199 | name = methodName; 200 | } 201 | property = properties.get(name); 202 | if (property == null) { 203 | property = new Property(name); 204 | properties.put(name, property); 205 | } 206 | return property; 207 | } 208 | 209 | public JExportableMethod getJsInitMethod() { 210 | return jsInitMethod; 211 | } 212 | 213 | public JExportableMethod getAfterCreateMethod() { 214 | return afterCreateMethod; 215 | } 216 | 217 | public ExportableTypeOracle getExportableTypeOracle() { 218 | return exportableTypeOracle; 219 | } 220 | 221 | public String getExporterImplementationName() { 222 | return type.getName().replace(".", "_") + IMPL_EXTENSION; 223 | } 224 | 225 | public String getHostedModeJsTypeCast() { 226 | return null; 227 | } 228 | 229 | public String getJsTypeOf() { 230 | return exportableTypeOracle.getJsTypeOf(getType()); 231 | } 232 | 233 | public String getJSConstructor() { 234 | String pkg = getJSExportPackage().trim(); 235 | if (!pkg.isEmpty()) { 236 | pkg += "."; 237 | } 238 | return pkg + getJSExportName(); 239 | } 240 | 241 | String exportName; 242 | public String getJSExportName () { 243 | if (exportName == null) { 244 | // By default we use the name of the class. 245 | exportName = getTypeToExport().getName(); 246 | 247 | Export expAnnotation = type.getAnnotation(Export.class); 248 | if (expAnnotation != null && !expAnnotation.value().trim().isEmpty()) { 249 | // use the annotation value 250 | exportName = expAnnotation.value().trim(); 251 | } else if (type.getEnclosingType() != null) { 252 | // Check if the class is defined inside another exported type 253 | JExportableClassType encType = exportableTypeOracle 254 | .findExportableClassType( 255 | type.getEnclosingType().getQualifiedSourceName()); 256 | 257 | // Check if the class has a pkgAnnotation 258 | ExportPackage pkgAnnotation = type.getAnnotation(ExportPackage.class); 259 | 260 | // Remove the enclosing part from the class name 261 | if (encType != null || pkgAnnotation != null) { 262 | exportName = exportName.replaceFirst("^.+\\.", ""); 263 | } 264 | 265 | 266 | } 267 | } 268 | return exportName; 269 | } 270 | 271 | String exportPackage; 272 | public String getJSExportPackage() { 273 | if (exportPackage == null) { 274 | // By default we use the java namespace, but removing the 'client' part 275 | exportPackage = getPrefix(); 276 | // Check for the @ExportPackage annotation 277 | ExportPackage ann = type.getAnnotation(ExportPackage.class); 278 | JClassType type = getTypeToExport(); 279 | if (ann != null) { 280 | exportPackage = ann.value().trim(); 281 | } else if (type.getEnclosingType() != null) { 282 | // Check if the class is defined inside another exported type to use it's name as base 283 | JExportableClassType encType = exportableTypeOracle 284 | .findExportableClassType( 285 | type.getEnclosingType().getQualifiedSourceName()); 286 | if (encType != null) { 287 | exportPackage = encType.getJSQualifiedExportName(); 288 | } 289 | } 290 | } 291 | return exportPackage; 292 | } 293 | 294 | public String getJSNIReference() { 295 | return type.getJNISignature(); 296 | } 297 | 298 | public String getJSQualifiedExportName() { 299 | return getJSConstructor(); 300 | } 301 | 302 | public String getPackageName() { 303 | return type.getPackage().getName(); 304 | } 305 | 306 | public String getPrefix() { 307 | String prefix = ""; 308 | boolean firstClientPackage = true; 309 | for (String pkg : type.getPackage().getName().split("\\.")) { 310 | if (firstClientPackage && pkg.equals("client")) { 311 | firstClientPackage = false; 312 | continue; 313 | } 314 | prefix += pkg; 315 | prefix += '.'; 316 | } 317 | // remove trailing . 318 | return prefix.substring(0, prefix.length() - 1); 319 | } 320 | 321 | public String getQualifiedExporterImplementationName() { 322 | return getPackageName() + "." + getExporterImplementationName(); 323 | } 324 | 325 | public String getQualifiedSourceName() { 326 | return getType().getQualifiedSourceName(); 327 | } 328 | 329 | public JStructuralTypeField[] getStructuralTypeFields() { 330 | if (!isStructuralType()) { 331 | return new JStructuralTypeField[0]; 332 | } else { 333 | ArrayList fields 334 | = new ArrayList(); 335 | for (JMethod method : type.getMethods()) { 336 | if (method.getName().startsWith("set") 337 | && Character.isUpperCase(method.getName().charAt(3)) 338 | && method.getParameters().length == 1 339 | || method.getAnnotation(SType.class) != null) { 340 | fields.add(new JStructuralTypeField(this, method)); 341 | } 342 | } 343 | return fields.toArray(new JStructuralTypeField[0]); 344 | } 345 | } 346 | 347 | public JClassType getType() { 348 | return type; 349 | } 350 | 351 | public JClassType getRequestedType() { 352 | return type; 353 | } 354 | 355 | public JClassType getTypeToExport() { 356 | return exportableTypeOracle.isExportOverlay(type) 357 | ? exportableTypeOracle.getExportOverlayType(type) : type; 358 | } 359 | 360 | public String getWrapperFunc() { 361 | if (!needsExport()) { 362 | return null; 363 | } 364 | return "@" + ExporterUtil.class.getName() 365 | + "::wrap(Ljava/lang/Object;)"; 366 | } 367 | 368 | public int hashCode() { 369 | return getQualifiedSourceName().hashCode(); 370 | } 371 | 372 | public boolean isPrimitive() { 373 | return type.isPrimitive() != null; 374 | } 375 | 376 | public boolean isStructuralType() { 377 | return exportableTypeOracle.isStructuralType(this.getType()); 378 | } 379 | 380 | public boolean isTransparentType() { 381 | return exportableTypeOracle.isJavaScriptObject(this) 382 | || exportableTypeOracle.isString(this) 383 | || exportableTypeOracle.isDate(this) 384 | || exportableTypeOracle.isArray(this); 385 | } 386 | 387 | public String getJsniSigForArrays() { 388 | if (exportableTypeOracle.isJavaScriptObject(this)) { 389 | return "[Lcom/google/gwt/core/client/JavaScriptObject;"; 390 | } else if (isTransparentType()){ 391 | return "[" + type.getJNISignature(); 392 | } else if (exportableTypeOracle.isExportOverlay(type)) { 393 | // Fixes issue #35 394 | return "[Ljava/lang/Object;"; 395 | } else { 396 | return "[Lorg/timepedia/exporter/client/Exportable;"; 397 | } 398 | } 399 | 400 | public boolean needsExport() { 401 | return !isPrimitive() && !isTransparentType(); 402 | } 403 | 404 | public boolean isInstantiable() { 405 | return exportableTypeOracle.isInstantiable(type); 406 | } 407 | 408 | } 409 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableConstructor.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JAbstractMethod; 4 | import com.google.gwt.core.ext.typeinfo.JParameter; 5 | import com.google.gwt.core.ext.typeinfo.JType; 6 | 7 | /** 8 | * Wrapper utility class for dealing with exportable constructors 9 | * 10 | * @author Ray Cromwell (ray@timepedia.org) 11 | */ 12 | public class JExportableConstructor extends JExportableMethod { 13 | 14 | private static final String STATIC_FACTORY_NAME = "___create"; 15 | 16 | public JExportableConstructor(JExportableClassType exportableEnclosingType, 17 | JAbstractMethod method) { 18 | super(exportableEnclosingType, method); 19 | } 20 | 21 | private void assertExportable(JType param) { 22 | 23 | } 24 | 25 | public String getJSNIReference() { 26 | String reference = exportableEnclosingType.getQualifiedSourceName() + "::" 27 | + getStaticFactoryMethodName() + "("; 28 | JParameter[] params = method.getParameters(); 29 | for (int i = 0; i < params.length; i++) { 30 | reference += params[i].getType().getJNISignature(); 31 | } 32 | reference += ")"; 33 | return reference; 34 | } 35 | 36 | public String getStaticFactoryMethodName() { 37 | return STATIC_FACTORY_NAME; 38 | } 39 | 40 | public JExportableType getExportableReturnType() { 41 | return exportableEnclosingType; 42 | } 43 | 44 | public String getStaticFactoryJSNIReference() { 45 | String reference = 46 | exportableEnclosingType.getQualifiedExporterImplementationName() + "::" 47 | + getStaticFactoryMethodName() + "("; 48 | JParameter[] params = method.getParameters(); 49 | for (int i = 0; i < params.length; i++) { 50 | reference += params[i].getType().getJNISignature(); 51 | } 52 | reference += ")"; 53 | return reference; 54 | } 55 | 56 | public String getJSQualifiedExportName() { 57 | return exportableEnclosingType.getJSQualifiedExportName(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableField.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JField; 4 | 5 | import org.timepedia.exporter.client.Export; 6 | 7 | /** 8 | * 9 | */ 10 | public class JExportableField { 11 | 12 | private JExportableClassType enclosingExportType; 13 | 14 | private JField field; 15 | 16 | private String exportName; 17 | 18 | public JExportableField(JExportableClassType enclosingExportType, 19 | JField field) { 20 | this.enclosingExportType = enclosingExportType; 21 | this.field = field; 22 | Export ann = field.getAnnotation(Export.class); 23 | 24 | if (ann != null && ann.value().length() > 0) { 25 | exportName = ann.value(); 26 | } else { 27 | exportName = field.getName(); 28 | } 29 | } 30 | 31 | public String getJSExportName() { 32 | return exportName; 33 | } 34 | 35 | public String getJSQualifiedExportName() { 36 | return enclosingExportType.getJSQualifiedExportName() + "." 37 | + getJSExportName(); 38 | } 39 | 40 | public String getJSNIReference() { 41 | return field.getEnclosingType().getQualifiedSourceName() + "::" + field 42 | .getName(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableMethod.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportAfterCreateMethod; 5 | import org.timepedia.exporter.client.ExportConstructor; 6 | import org.timepedia.exporter.client.ExportInstanceMethod; 7 | import org.timepedia.exporter.client.ExportJsInitMethod; 8 | import org.timepedia.exporter.client.ExportStaticMethod; 9 | 10 | import com.google.gwt.core.ext.typeinfo.JAbstractMethod; 11 | import com.google.gwt.core.ext.typeinfo.JArrayType; 12 | import com.google.gwt.core.ext.typeinfo.JConstructor; 13 | import com.google.gwt.core.ext.typeinfo.JMethod; 14 | import com.google.gwt.core.ext.typeinfo.JParameter; 15 | import com.google.gwt.core.ext.typeinfo.JType; 16 | 17 | /** 18 | * 19 | */ 20 | public class JExportableMethod implements JExportable { 21 | 22 | public static final String WRAPPER_PREFIX = "__static_wrapper_"; 23 | 24 | protected JExportableClassType exportableEnclosingType; 25 | 26 | protected JAbstractMethod method; 27 | 28 | private String exportName; 29 | 30 | private String qualifiedExportName; 31 | 32 | JExportableParameter[] exportableParameters; 33 | 34 | public JExportableMethod(JExportableClassType exportableEnclosingType, 35 | JAbstractMethod method) { 36 | this.exportableEnclosingType = exportableEnclosingType; 37 | this.method = method; 38 | 39 | String anValue = ""; 40 | if (isExportInstanceMethod()) { 41 | anValue = method.getAnnotation(ExportInstanceMethod.class).value(); 42 | } else if (isExportStaticMethod()) { 43 | anValue = method.getAnnotation(ExportStaticMethod.class).value(); 44 | } else { 45 | Export ann = method.getAnnotation(Export.class); 46 | anValue = ann != null ? ann.value().trim() : ""; 47 | } 48 | 49 | if (!anValue.isEmpty()) { 50 | exportName = anValue; 51 | 52 | // Static methods can be assigned to any name-space if it starts with $wnd 53 | if (isStatic() && anValue.startsWith("$wnd.")) { 54 | qualifiedExportName = anValue.replaceFirst("\\$wnd\\.", ""); 55 | } 56 | } else { 57 | exportName = method.getName(); 58 | } 59 | 60 | if (qualifiedExportName == null) { 61 | qualifiedExportName = getEnclosingExportType().getJSQualifiedExportName() 62 | + "." + getUnqualifiedExportName(); 63 | } 64 | 65 | JParameter[] params = method.getParameters(); 66 | exportableParameters = new JExportableParameter[params.length]; 67 | for (int i = 0; i < params.length; i++) { 68 | exportableParameters[i] = new JExportableParameter(this, params[i]); 69 | } 70 | } 71 | 72 | public JAbstractMethod getMethod() { 73 | return method; 74 | } 75 | 76 | public String getUnqualifiedExportName() { 77 | return exportName; 78 | } 79 | 80 | public String getJSQualifiedExportName() { 81 | return qualifiedExportName; 82 | } 83 | 84 | public JExportableType getExportable(JType type, boolean searchComponent) { 85 | ExportableTypeOracle xTypeOracle = getExportableTypeOracle(); 86 | JArrayType a = type.isArray(); 87 | if (searchComponent && a != null) { 88 | return xTypeOracle.findExportableType(a.getComponentType().getQualifiedSourceName()); 89 | } else { 90 | return xTypeOracle.findExportableType(type.getQualifiedSourceName()); 91 | } 92 | } 93 | 94 | public JExportableType getExportableReturnType(boolean b) { 95 | return method instanceof JMethod ? getExportable(((JMethod)method).getReturnType(), b) : null; 96 | } 97 | 98 | public JExportableParameter[] getExportableParameters() { 99 | return exportableParameters; 100 | } 101 | 102 | public JExportableClassType getEnclosingExportType() { 103 | return exportableEnclosingType; 104 | } 105 | 106 | public String getEnclosingTypeQualifiedSourceName() { 107 | if (isExportOverlay()) { 108 | return ((JExportOverlayClassType)exportableEnclosingType).getOverlayType().getQualifiedSourceName(); 109 | } else { 110 | return exportableEnclosingType.getQualifiedSourceName(); 111 | } 112 | } 113 | 114 | public String getJSNIReference() { 115 | String reference = ""; 116 | // we export the original method or the static wrapper 117 | if (!needsWrapper()) { 118 | // Use static ExportConstructor and ExportMethod methods in the overlay class 119 | if (isExportConstructor() || isExportInstanceMethod() || isExportStaticMethod() || isExportAfterCreateMethod()) { 120 | reference = getEnclosingTypeQualifiedSourceName(); 121 | } else { 122 | reference = exportableEnclosingType.getQualifiedSourceName(); 123 | } 124 | reference += "::" + method.getName() + "("; 125 | } else { 126 | reference = exportableEnclosingType.getQualifiedExporterImplementationName() + "::" + WRAPPER_PREFIX + method.getName() + "("; 127 | // Wrappers are static, so we pass the instance in the first argument. 128 | if (!isStatic()) { 129 | reference += exportableEnclosingType.getTypeToExport().getJNISignature(); 130 | } 131 | } 132 | 133 | int len = exportableParameters.length; 134 | for (int i = 0; i < len; i++) { 135 | String signature = exportableParameters[i].getJNISignature(); 136 | // Here we replace 137 | // - long by double signature 138 | // - arrays and date by javascriptobject 139 | if ("J".equals(signature)) { 140 | signature = "D"; 141 | } else if ( 142 | signature.startsWith("[") 143 | || signature.equals("Ljava/util/Date;")) { 144 | signature = "Lcom/google/gwt/core/client/JavaScriptObject;"; 145 | } 146 | reference += signature; 147 | } 148 | 149 | reference += ")"; 150 | return reference; 151 | } 152 | 153 | private Boolean wrap = null; 154 | 155 | // return true if this method needs a static wrapper. 156 | // - methods which have a 'long' parameter or return 'long' 157 | // - methods which have array parameters 158 | // - methods with variable arguments 159 | public boolean needsWrapper() { 160 | if (wrap == null) { 161 | wrap = false; 162 | if (method.isVarArgs()) { 163 | wrap = true; 164 | } else if (isExportInstanceMethod()) { 165 | wrap = true; 166 | } else if (getExportableReturnType(false) != null && 167 | "long".equals(getExportableReturnType(false).getQualifiedSourceName())) { 168 | wrap = true; 169 | } else for (JExportableParameter p : getExportableParameters()) { 170 | if (p.getTypeName().matches("(long|.*\\[\\])$|java.util.Date")) { 171 | wrap = true; 172 | break; 173 | } 174 | } 175 | } 176 | return wrap; 177 | } 178 | 179 | public boolean isInStaticMap() { 180 | return isStatic() && !isExportInstanceMethod(); 181 | } 182 | 183 | public boolean isStatic() { 184 | if (method instanceof JConstructor) { 185 | return false; 186 | } else { 187 | return ((JMethod) method).isStatic(); 188 | } 189 | } 190 | 191 | public boolean isExportOverlay() { 192 | return exportableEnclosingType instanceof JExportOverlayClassType; 193 | } 194 | 195 | public boolean isExportInstanceMethod() { 196 | return isStatic() 197 | && isExportOverlay() 198 | && method.getAnnotation(ExportInstanceMethod.class) != null; 199 | } 200 | 201 | public boolean isExportStaticMethod() { 202 | return isStatic() 203 | && isExportOverlay() 204 | && method.getAnnotation(ExportStaticMethod.class) != null; 205 | } 206 | 207 | public boolean isExportConstructor() { 208 | return isStatic() 209 | && isExportOverlay() 210 | && method.getAnnotation(ExportConstructor.class) != null; 211 | } 212 | 213 | public boolean isExportJsInitMethod() { 214 | return isStatic() == false 215 | && method.getParameters().length == 0 216 | && method.getAnnotation(ExportJsInitMethod.class) != null; 217 | } 218 | 219 | public boolean isExportAfterCreateMethod() { 220 | return isStatic() 221 | && method.getParameters().length == 0 222 | && method.getAnnotation(ExportAfterCreateMethod.class) != null; 223 | } 224 | 225 | public ExportableTypeOracle getExportableTypeOracle() { 226 | return getEnclosingExportType().getExportableTypeOracle(); 227 | } 228 | 229 | public String toString() { 230 | String str = exportableEnclosingType.getQualifiedSourceName() + "." 231 | + method.getName() + "("; 232 | JExportableParameter[] params = getExportableParameters(); 233 | for (int i = 0; i < params.length; i++) { 234 | str += params[i]; 235 | if (i < params.length - 1) { 236 | str += ", "; 237 | } 238 | } 239 | return str + ")"; 240 | } 241 | 242 | public String getName() { 243 | return method.getName(); 244 | } 245 | 246 | public boolean isVarArgs() { 247 | return method.isVarArgs(); 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableParameter.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JParameter; 4 | import com.google.gwt.core.ext.typeinfo.JPrimitiveType; 5 | 6 | /** 7 | * 8 | */ 9 | public class JExportableParameter { 10 | 11 | private JParameter param; 12 | private JExportableMethod method; 13 | private ExportableTypeOracle xTypeOracle; 14 | 15 | public JParameter getParam() { 16 | return param; 17 | } 18 | 19 | 20 | public JExportableClassType getExportableEnclosingType() { 21 | return method.getEnclosingExportType(); 22 | } 23 | 24 | public JExportableParameter(JExportableMethod exportableMethod, 25 | JParameter param) { 26 | this.param = param; 27 | this.method = exportableMethod; 28 | xTypeOracle = getExportableEnclosingType().getExportableTypeOracle(); 29 | } 30 | 31 | public String getTypeName() { 32 | return param.getType().getQualifiedSourceName(); 33 | } 34 | 35 | public String getJNISignature() { 36 | return param.getType().getJNISignature(); 37 | } 38 | 39 | public String getExportParameterValue(String argName) { 40 | 41 | String paramTypeName = param.getType().getQualifiedSourceName(); 42 | JExportableType type = xTypeOracle.findExportableType(paramTypeName); 43 | 44 | if (type != null && type.needsExport()) { 45 | JExportableClassType cType = (JExportableClassType) type; 46 | if (xTypeOracle.isClosure((JExportableClassType) type)) { 47 | return argName + " == null ? null : (" + argName + ".constructor == $wnd." 48 | + cType.getJSQualifiedExportName() + " ? " + argName 49 | + "." + ClassExporter.GWT_INSTANCE + " : " 50 | + "@" + cType.getQualifiedExporterImplementationName() + "::" 51 | + "makeClosure(Lcom/google/gwt/core/client/JavaScriptObject;)(" 52 | + argName 53 | + "))"; 54 | } else if (xTypeOracle.isExportable((JExportableClassType) type)) { 55 | return "(" + argName + " == null ? null : " + argName + "." + ClassExporter.GWT_INSTANCE + ")"; 56 | } 57 | } 58 | 59 | if (param.getType().isClass() != null 60 | && !xTypeOracle.isJavaScriptObject(param.getType()) 61 | && !xTypeOracle.isString(param.getType())) { 62 | return "@org.timepedia.exporter.client.ExporterUtil::gwtInstance(Ljava/lang/Object;)(" 63 | + argName + ")"; 64 | } 65 | 66 | return argName; 67 | } 68 | 69 | @Override 70 | public boolean equals(Object o) { 71 | if (this == o) { 72 | return true; 73 | } 74 | if (o == null || getClass() != o.getClass()) { 75 | return false; 76 | } 77 | 78 | JExportableParameter that = (JExportableParameter) o; 79 | return getJsTypeOf().equals(that.getJsTypeOf()); 80 | } 81 | 82 | public String getJsTypeOf() { 83 | 84 | if (param == null) { 85 | return "null"; 86 | } else if (param.getType().isArray() != null) { 87 | return "array"; 88 | } else if (param.getType().isPrimitive() != null) { 89 | JPrimitiveType prim = param.getType().isPrimitive(); 90 | return prim == JPrimitiveType.BOOLEAN ? "boolean" : "number"; 91 | } else if (xTypeOracle.isString(param.getType())) { 92 | return "string"; 93 | } else if (xTypeOracle.isJavaScriptObject(param.getType())) { 94 | return "object"; 95 | } else { 96 | String paramTypeName = param.getType().getQualifiedSourceName(); 97 | JExportableType type = xTypeOracle.findExportableType(paramTypeName); 98 | if (type != null && type instanceof JExportableClassType 99 | && xTypeOracle.isClosure((JExportableClassType) type)) { 100 | return "'function'"; 101 | } 102 | return "@" + param.getType().getQualifiedSourceName() + "::class"; 103 | } 104 | } 105 | 106 | public boolean isExportable() { 107 | String js = getJsTypeOf(); 108 | return !js.contains("@") || getExportableType(false) != null; 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return param != null ? getJsTypeOf().hashCode() : 0; 114 | } 115 | 116 | public String toString() { 117 | return param.getType().getSimpleSourceName(); 118 | } 119 | 120 | public JExportableType getExportableType(boolean b) { 121 | return method.getExportable(param.getType(), b); 122 | } 123 | 124 | public String getToArrayFunc(String qsn, String argName) { 125 | String ret = "ExporterUtil."; 126 | String after = ")"; 127 | if (qsn.equals("java.lang.String[]")) { 128 | ret += "toArrString" ; 129 | } else if (qsn.equals("java.util.Date[]")) { 130 | ret += "toArrDate" ; 131 | } else if (qsn.equals("double[]")) { 132 | ret += "toArrDouble" ; 133 | } else if (qsn.equals("float[]")) { 134 | ret += "toArrFloat" ; 135 | } else if (qsn.equals("long[]")) { 136 | ret += "toArrLong" ; 137 | } else if (qsn.equals("int[]")) { 138 | ret += "toArrInt" ; 139 | } else if (qsn.equals("byte[]")) { 140 | ret += "toArrByte" ; 141 | } else if (qsn.equals("char[]")) { 142 | ret += "toArrChar" ; 143 | } else { 144 | ret += "toArrObject"; 145 | after = ", new " + qsn.replace("]", "ExporterUtil.length(" + argName + ")]") + after; 146 | } 147 | return ret + "(" + argName + after; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportablePrimitiveType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JPrimitiveType; 4 | 5 | /** 6 | * 7 | */ 8 | public class JExportablePrimitiveType implements JExportableType { 9 | 10 | private ExportableTypeOracle exportableTypeOracle; 11 | 12 | private JPrimitiveType primitive; 13 | 14 | public JExportablePrimitiveType(ExportableTypeOracle exportableTypeOracle, 15 | JPrimitiveType primitive) { 16 | this.exportableTypeOracle = exportableTypeOracle; 17 | this.primitive = primitive; 18 | } 19 | 20 | public boolean needsExport() { 21 | return false; 22 | } 23 | 24 | public String getQualifiedSourceName() { 25 | return primitive.getQualifiedSourceName(); 26 | } 27 | 28 | public String getHostedModeJsTypeCast() { 29 | return primitive.getSimpleSourceName().equals("Boolean") ? "Boolean" 30 | : "Number"; 31 | } 32 | 33 | public String getWrapperFunc() { 34 | return null; 35 | } 36 | 37 | public String getJsTypeOf() { 38 | JPrimitiveType prim = primitive.isPrimitive(); 39 | return prim == JPrimitiveType.BOOLEAN ? "boolean" : "number"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JExportableType.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | /** 4 | * Represents types that might be exported. 5 | */ 6 | public interface JExportableType { 7 | 8 | /** 9 | * True if this type needs export to work in Javascript. 10 | */ 11 | boolean needsExport(); 12 | /* 13 | * The Java qualified name of this type. 14 | */ 15 | String getQualifiedSourceName(); 16 | 17 | /** 18 | * The name of a JS type cast operation that may be required to debox 19 | * Java values for Javascript in hosted mode. 20 | */ 21 | String getHostedModeJsTypeCast(); 22 | 23 | String getJsTypeOf(); 24 | 25 | String getWrapperFunc(); 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/JStructuralTypeField.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | import com.google.gwt.core.ext.typeinfo.JMethod; 4 | import com.google.gwt.core.ext.typeinfo.JPrimitiveType; 5 | 6 | import org.timepedia.exporter.client.SType; 7 | 8 | /** 9 | * Represents a bean property or method which is a structural type field. 10 | */ 11 | public class JStructuralTypeField { 12 | 13 | private JExportableClassType exportableClassType; 14 | 15 | private JMethod setterMethod; 16 | 17 | public JStructuralTypeField(JExportableClassType exportableClassType, 18 | JMethod setterMethod) { 19 | 20 | this.exportableClassType = exportableClassType; 21 | this.setterMethod = setterMethod; 22 | } 23 | 24 | public String JavaDeclaration() { 25 | return setterMethod.getReturnType().getQualifiedSourceName() + " " 26 | + setterMethod.getName() + "(" 27 | + setterMethod.getParameters()[0].getType().getQualifiedSourceName() 28 | + " arg)"; 29 | } 30 | 31 | public boolean isVoidReturn() { 32 | return setterMethod.getReturnType().equals(JPrimitiveType.VOID); 33 | } 34 | 35 | public String getMethodName() { 36 | return setterMethod.getName(); 37 | } 38 | 39 | public String getReturnType() { 40 | return setterMethod.getReturnType().getQualifiedSourceName(); 41 | } 42 | 43 | public String getFieldValueCast() { 44 | return setterMethod.getParameters()[0].getType().isPrimitive() != null ? 45 | "(double)": "(Object)"; 46 | } 47 | public String getFieldJSNIType() { 48 | return setterMethod.getParameters()[0].getType().isPrimitive() != null ? 49 | "D" : "Ljava/lang/Object;"; 50 | } 51 | 52 | public String getName() { 53 | SType st = setterMethod.getAnnotation(SType.class); 54 | if(st != null) { 55 | return st.value(); 56 | } 57 | return beanize(setterMethod.getName()); 58 | } 59 | 60 | private String beanize(String name) { 61 | String prop = name.startsWith("set") ? name.substring(3) : name; 62 | return Character.toLowerCase(prop.charAt(0))+prop.substring(1); 63 | } 64 | 65 | public String getFieldLowestType() { 66 | JPrimitiveType type = setterMethod.getParameters()[0].getType() 67 | .isPrimitive(); 68 | return type != null ? type.getQualifiedSourceName() : "Object"; 69 | } 70 | 71 | public String getFieldType() { 72 | return setterMethod.getParameters()[0].getType().getQualifiedSourceName(); 73 | } 74 | 75 | public JExportableType getExportableType() { 76 | return exportableClassType.getExportableTypeOracle().findExportableType(getFieldType()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/main/java/org/timepedia/exporter/rebind/Property.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.rebind; 2 | 3 | public class Property { 4 | 5 | private String name; 6 | private JExportableMethod getter; 7 | private JExportableMethod setter; 8 | 9 | public Property(String name) { 10 | this.name = name; 11 | } 12 | 13 | public void setGetter(JExportableMethod getter) { 14 | this.getter = getter; 15 | } 16 | 17 | public void setSetter(JExportableMethod setter) { 18 | this.setter = setter; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public JExportableMethod getGetter() { 26 | return getter; 27 | } 28 | 29 | public JExportableMethod getSetter() { 30 | return setter; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/Test.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/ExporterGwtSuiteTest.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test; 2 | 3 | import junit.framework.Test; 4 | 5 | import org.timepedia.exporter.test.issues.*; 6 | 7 | import com.google.gwt.junit.tools.GWTTestSuite; 8 | public class ExporterGwtSuiteTest extends GWTTestSuite 9 | { 10 | public static Test suite() 11 | { 12 | GWTTestSuite suite = new GWTTestSuite( "Exporter Suite" ); 13 | suite.addTestSuite(CoreTestGwt.class ); 14 | suite.addTestSuite(Issue25aTestGwt.class); 15 | suite.addTestSuite(Issue25bTestGwt.class); 16 | suite.addTestSuite(Issue33TestGwt.class); 17 | suite.addTestSuite(Issue34TestGwt.class); 18 | suite.addTestSuite(Issue35TestGwt.class); 19 | suite.addTestSuite(Issue38TestGwt.class); 20 | suite.addTestSuite(Issue44TestGwt.class); 21 | suite.addTestSuite(Issue48TestGwt.class); 22 | suite.addTestSuite(Issue49TestGwt.class); 23 | suite.addTestSuite(Issue50TestGwt.class); 24 | suite.addTestSuite(Issue51TestGwt.class); 25 | return suite; 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue25aTestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import com.google.gwt.core.client.GWT; 4 | import com.google.gwt.junit.client.GWTTestCase; 5 | 6 | public class Issue25aTestGwt extends GWTTestCase{ 7 | 8 | @Override 9 | public String getModuleName() { 10 | return "org.timepedia.exporter.Test"; 11 | } 12 | 13 | @Override 14 | protected void gwtSetUp() throws Exception { 15 | GWT.create(MyExportable.class); 16 | } 17 | 18 | public native String evalJsTest(String jsCode) /*-{ 19 | try { 20 | return eval(jsCode); 21 | } catch(e) { 22 | return String(e); 23 | } 24 | }-*/; 25 | 26 | public void test1() { 27 | String jsCode = "var v1 = new $wnd.gwt.MyExportable(); String(v1.getId());"; 28 | assertEquals("no-id", evalJsTest(jsCode)); 29 | } 30 | 31 | public void test2() { 32 | String jsCode = "var v1 = new $wnd.gwt.MyExportable('mid'); String(v1.getId());"; 33 | assertEquals("mid", evalJsTest(jsCode)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue25bTestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import com.google.gwt.core.client.GWT; 4 | import com.google.gwt.junit.client.GWTTestCase; 5 | 6 | public class Issue25bTestGwt extends GWTTestCase{ 7 | 8 | @Override 9 | public String getModuleName() { 10 | return "org.timepedia.exporter.Test"; 11 | } 12 | 13 | @Override 14 | protected void gwtSetUp() throws Exception { 15 | GWT.create(MyExportable.class); 16 | } 17 | 18 | public native String evalJsTest(String jsCode) /*-{ 19 | try { 20 | return eval(jsCode); 21 | } catch(e) { 22 | return String(e); 23 | } 24 | }-*/; 25 | 26 | public void test1() { 27 | String jsCode = "var v1 = new $wnd.gwt.MyExportable(); String(v1.getId());"; 28 | assertEquals("no-id", evalJsTest(jsCode)); 29 | } 30 | 31 | public void test2() { 32 | String jsCode = "var v1 = new $wnd.gwt.MyExportable('mid'); String(v1.getId());"; 33 | assertEquals("mid", evalJsTest(jsCode)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue33TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | 7 | import com.google.gwt.core.client.GWT; 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue33TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | @Override 18 | protected void gwtSetUp() throws Exception { 19 | GWT.create(EvenMoreSpecificType.class); 20 | GWT.create(FurtherSubClass.class); 21 | } 22 | 23 | @ExportPackage("tst") 24 | @Export 25 | public static class Test implements Exportable { 26 | public int[] method(int i) { 27 | return new int[]{1}; 28 | } 29 | 30 | public int[] method(String s) { 31 | return new int[]{1}; 32 | } 33 | } 34 | 35 | public static class SomeType { 36 | public String getType() { 37 | return "type"; 38 | } 39 | } 40 | 41 | public static class SomeMoreSpecificType extends SomeType { 42 | } 43 | 44 | @ExportPackage("tst") 45 | @Export(value="EvenMoreSpecificType", all = true) 46 | public static class EvenMoreSpecificType extends SomeMoreSpecificType 47 | implements Exportable { 48 | } 49 | 50 | public static abstract class SuperClass { 51 | public String doSomething(T t) { 52 | return t.getType(); 53 | } 54 | } 55 | 56 | public static abstract class SubClass extends 57 | SuperClass { 58 | @Override 59 | public String doSomething(SomeMoreSpecificType t) { 60 | return super.doSomething(t); 61 | } 62 | } 63 | 64 | @ExportPackage("tst") 65 | @Export(value="FurtherSubClass", all = true) 66 | public static class FurtherSubClass extends SubClass 67 | implements Exportable { 68 | @Override 69 | public String doSomething(SomeMoreSpecificType t) { 70 | return super.doSomething(t); 71 | } 72 | } 73 | 74 | public static native String runJs() /*-{ 75 | var e = new $wnd.tst.EvenMoreSpecificType(); 76 | var v = new $wnd.tst.FurtherSubClass(); 77 | return v.doSomething(e); 78 | }-*/; 79 | 80 | public void testIssue() { 81 | assertEquals("type", runJs()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue34TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | 7 | import com.google.gwt.core.client.GWT; 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue34TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | @Override 18 | protected void gwtSetUp() throws Exception { 19 | GWT.create(Test.class); 20 | } 21 | 22 | @ExportPackage("tst") 23 | @Export("Test34") 24 | public static class Test implements Exportable { 25 | public int[] method(int i) { return new int[]{1}; } 26 | public int[] method(String s) { return new int[]{2}; } 27 | } 28 | 29 | public static native String runJs() /*-{ 30 | var ret = "" 31 | ret += (new $wnd.tst.Test34()).method(1)[0]; 32 | ret += '-'; 33 | ret += (new $wnd.tst.Test34()).method('')[0]; 34 | return ret; 35 | }-*/; 36 | 37 | public void testIssue() { 38 | assertEquals("1-2", runJs()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue35TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportConstructor; 5 | import org.timepedia.exporter.client.ExportOverlay; 6 | import org.timepedia.exporter.client.ExportPackage; 7 | 8 | import com.google.gwt.core.client.GWT; 9 | import com.google.gwt.junit.client.GWTTestCase; 10 | 11 | public class Issue35TestGwt extends GWTTestCase { 12 | 13 | @Override 14 | public String getModuleName() { 15 | return "org.timepedia.exporter.Test"; 16 | } 17 | 18 | @Override 19 | protected void gwtSetUp() throws Exception { 20 | GWT.create(CoordinateOverlay.class); 21 | GWT.create(GeometryOverlay.class); 22 | } 23 | 24 | public static class Coordinate { 25 | int x, y; 26 | public Coordinate() { 27 | } 28 | public Coordinate(int x, int y) { 29 | this.x = x; 30 | this.y = y; 31 | } 32 | public String toString() { 33 | return x + "x" + y; 34 | } 35 | } 36 | 37 | public static class Geometry { 38 | public Geometry() { 39 | } 40 | public Geometry(String geometryType, int srid, int precision) { 41 | } 42 | Coordinate[] coordinates; 43 | public Coordinate[] getCoordinates() { 44 | return coordinates; 45 | } 46 | public void setCoordinates(Coordinate[] coordinates) { 47 | this.coordinates = coordinates; 48 | } 49 | } 50 | 51 | @Export("Coordinate") 52 | @ExportPackage("sp") 53 | public static class CoordinateOverlay implements ExportOverlay { 54 | @ExportConstructor 55 | public static Coordinate constructor(int x, int y) { 56 | return new Coordinate(x, y); 57 | } 58 | public String toString() { 59 | return ""; 60 | } 61 | } 62 | 63 | @Export("Geometry") 64 | @ExportPackage("sp") 65 | public static class GeometryOverlay implements ExportOverlay { 66 | @ExportConstructor 67 | public static Geometry constructor(String geometryType, int srid, int precision) { 68 | return new Geometry(geometryType, srid, precision); 69 | } 70 | public Coordinate[] getCoordinates() { 71 | return null; 72 | } 73 | public void setCoordinates(Coordinate[] coordinates) { 74 | } 75 | } 76 | 77 | public static native String runJs() /*-{ 78 | var geometry = new $wnd.sp.Geometry("Point", 0, 0); 79 | geometry.setCoordinates([ new $wnd.sp.Coordinate(10, 10), new $wnd.sp.Coordinate(20, 20) ]); 80 | return "" + geometry.getCoordinates(); 81 | }-*/; 82 | 83 | public void testIssue() { 84 | assertEquals("10x10,20x20", runJs()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue38TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | import org.timepedia.exporter.client.ExporterUtil; 7 | 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue38TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | // Should be exported as pkg.Person1 18 | @ExportPackage("pkg") 19 | @Export(all = true, value = "Person1") 20 | public static class Person implements Exportable { 21 | 22 | // Should be exported as pkg.Person1.Foo 23 | @Export 24 | public static class Foo implements Exportable { 25 | public String bar() { 26 | return "foo-bar,"; 27 | } 28 | } 29 | 30 | private String name; 31 | public Person(String name) { 32 | this.name = name; 33 | } 34 | public String method() { 35 | return "Hello: " + name + ","; 36 | } 37 | 38 | /** 39 | * Just an empty method used to test JsDoclet 40 | * @param theName Any string should work 41 | * @return always null. 42 | */ 43 | public String method(String theName) { 44 | return null; 45 | } 46 | } 47 | 48 | // Should be exported as pkg.Person2 49 | @ExportPackage("pkg") 50 | @Export(all = true) 51 | public static class Person2 extends Person { 52 | /** 53 | * This comment is here to test JsDoclet 54 | * 55 | * @param name the name of the person 56 | */ 57 | public Person2(String name) { 58 | super(name); 59 | } 60 | } 61 | 62 | // Should be exported as org.timepedia.exporter.test.issues.Issue38TestGwt.Person3 63 | @Export(all = true) 64 | public static class Person3 extends Person { 65 | public Person3(String name) { 66 | super(name); 67 | } 68 | } 69 | 70 | public static native String runJs() /*-{ 71 | try { 72 | ret = ""; 73 | if ($wnd.pkg && $wnd.pkg.Person2) { 74 | ret += new $wnd.pkg.Person1("A").method(); 75 | ret += new $wnd.pkg.Person1.Foo().bar(); 76 | ret += new $wnd.pkg.Person2("B").method(); 77 | ret += new $wnd.org.timepedia.exporter.test.issues.Issue38TestGwt.Person3("C").method(); 78 | } 79 | return ret; 80 | } catch(e) { 81 | return("Js Exception : " + e); 82 | } 83 | }-*/; 84 | 85 | public void testIssue() { 86 | // export(false) export classes which are default-instantiable (excluding interfaces, abstract, no-zero-constructor) 87 | ExporterUtil.export(false); 88 | assertEquals("", runJs()); 89 | 90 | // exportAll exports all Exportable classes marked with @Export 91 | ExporterUtil.exportAll(); 92 | assertEquals("Hello: A,foo-bar,Hello: B,Hello: C,", runJs()); 93 | } 94 | } -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue44TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | 7 | import com.google.gwt.core.client.GWT; 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue44TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | @Override 18 | protected void gwtSetUp() throws Exception { 19 | GWT.create(Impl.class); 20 | GWT.create(ServiceImpl.class); 21 | } 22 | 23 | @ExportPackage("tst") 24 | @Export("Interface") 25 | public interface Interface extends Exportable { 26 | public String method(); 27 | } 28 | 29 | @ExportPackage("tst") 30 | @Export("Impl") 31 | public static class Impl implements Interface, Exportable { 32 | 33 | @Override 34 | public String method() { 35 | return "method"; 36 | } 37 | 38 | } 39 | 40 | @ExportPackage("tst") 41 | @Export("Service") 42 | public interface Service extends Exportable { 43 | 44 | public String accept(Interface itf); 45 | } 46 | 47 | @ExportPackage("tst") 48 | @Export("ServiceImpl") 49 | public static class ServiceImpl implements Service, Exportable { 50 | 51 | @Override 52 | public String accept(Interface itf) { 53 | return itf.method(); 54 | } 55 | 56 | } 57 | 58 | public static native String runJs() /*-{ 59 | var service = new $wnd.tst.ServiceImpl(); 60 | var ret = ""; 61 | ret += service.accept(new $wnd.tst.Impl()); 62 | return ret; 63 | }-*/; 64 | 65 | public void testIssue() { 66 | assertEquals("method", runJs()); 67 | } 68 | } -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue45TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | import org.timepedia.exporter.client.ExporterUtil; 7 | 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue45TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | public void test() { 18 | ExporterUtil.exportAll(); 19 | publish(); 20 | assertEquals("bar,java.lang.ClassCastException,bar,foo", runJs()); 21 | } 22 | 23 | @ExportPackage("module") 24 | @Export(value="Chart") 25 | public static class Chart implements Exportable { 26 | String name; 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public static Chart create() { 35 | return new Chart(); 36 | } 37 | } 38 | 39 | public static class ChartManager implements Exportable { 40 | public static void addChart(Chart chart) { 41 | chart.setName("foo"); 42 | } 43 | } 44 | 45 | private static native void publish() /*-{ 46 | // Export some Jsni objects without using gwt-exporter 47 | $wnd.ChartManager = { 48 | addChart : function(chrt) { $entry( @org.timepedia.exporter.test.issues.Issue45TestGwt.ChartManager::addChart(*)(chrt)); } 49 | ,newChart : function(){ return @org.timepedia.exporter.test.issues.Issue45TestGwt.Chart::create(); } 50 | ,addChartFailure: function(msg) {$wnd.alert('no implementation err: ' + msg); } 51 | ,addChartSuccess: function(chart) {$wnd.alert('addChartSuccess not implemented'); } 52 | }; 53 | }-*/; 54 | 55 | private static native String runJs() /*-{ 56 | var ret = ""; 57 | 58 | // Create a new Chart exported by gwt-exporter 59 | // actually it is not a gwt object but a js object with 60 | // wraps a reference to the real gwt instance. 61 | var cht = new $wnd.module.Chart(); 62 | cht.setName('bar'); 63 | ret += cht.getName(); // bar 64 | 65 | // If we pass this gwt-exporter object to a jsni method it should raise 66 | // a cast exception 67 | ret += "," 68 | try { 69 | $wnd.ChartManager.addChart(cht); 70 | } catch(e) { 71 | ret += e; //java.lang.ClassCastException 72 | } 73 | ret += "," 74 | ret += cht.getName(); // bar 75 | 76 | // We have to pass the appropriate gwt instance of the Chart object 77 | // The gwt instance is stored in the .g property of the js exported object. 78 | $wnd.ChartManager.addChart(cht.g); 79 | ret += "," 80 | ret += cht.getName(); // foo 81 | 82 | return ret; 83 | }-*/; 84 | } -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue48TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportClosure; 5 | import org.timepedia.exporter.client.ExportOverlay; 6 | import org.timepedia.exporter.client.ExportPackage; 7 | import org.timepedia.exporter.client.Exportable; 8 | import org.timepedia.exporter.client.ExporterUtil; 9 | import org.timepedia.exporter.client.test.JsTestUtil; 10 | 11 | import com.google.gwt.core.client.JavaScriptObject; 12 | import com.google.gwt.event.shared.EventHandler; 13 | import com.google.gwt.event.shared.SimpleEventBus; 14 | import com.google.gwt.junit.client.GWTTestCase; 15 | import com.google.web.bindery.event.shared.Event; 16 | import com.google.web.bindery.event.shared.EventBus; 17 | import com.google.web.bindery.event.shared.HandlerRegistration; 18 | 19 | public class Issue48TestGwt extends GWTTestCase { 20 | 21 | JsTestUtil jsTest = new JsTestUtil(Issue48TestGwt.class); 22 | 23 | @Override 24 | public String getModuleName() { 25 | return "org.timepedia.exporter.Test"; 26 | } 27 | 28 | public void test() { 29 | ExporterUtil.exportAll(); 30 | runJsTests(jsTest); 31 | if (jsTest.isFailed()) fail(jsTest.getFailed()); 32 | } 33 | 34 | public native JavaScriptObject runJsTests(JsTestUtil jsTest) /*-{ 35 | // Utility function to assert equals numbers 36 | var assertEq = function(a, b) {jsTest.@org.timepedia.exporter.client.test.JsTestUtil::assertEqualsNumber(*)(a, b);} 37 | 38 | // We increase this ver each time the handler is called 39 | var i = 0; 40 | var login_handler = function() {i++}; 41 | 42 | // Register the handler 43 | var reg_handler = $wnd.My.Handlers.addLoginEventHandler(login_handler); 44 | assertEq(0, i); 45 | 46 | // each call to fireLogin should increment the counter 47 | $wnd.My.Handlers.fireLoginEvent(); 48 | assertEq(1, i); 49 | 50 | $wnd.My.Handlers.fireLoginEvent(); 51 | assertEq(2, i); 52 | 53 | // We remove the handler from the eventBus, so the counter should not be incremented 54 | reg_handler.removeHandler(); 55 | 56 | // The counter should remain the same 57 | $wnd.My.Handlers.fireLoginEvent(); 58 | assertEq(2, i); 59 | }-*/; 60 | 61 | @Export 62 | @ExportClosure 63 | @ExportPackage("_internal") 64 | public static interface JsFunction extends Exportable { 65 | public void execute(); 66 | } 67 | 68 | @ExportPackage("My") 69 | @Export("Handlers") 70 | public static class HandlersExporter implements Exportable { 71 | private static EventBus eventBus = new SimpleEventBus(); 72 | 73 | public static HandlerRegistration addLoginEventHandler(final JsFunction function) { 74 | return eventBus.addHandler(LoginEvent.TYPE, new LogInEventHandler() { 75 | public void onLogIn(LoginEvent event) { 76 | function.execute(); 77 | } 78 | }); 79 | } 80 | 81 | public static void fireLoginEvent() { 82 | eventBus.fireEvent(new LoginEvent()); 83 | } 84 | } 85 | 86 | @Export 87 | @ExportPackage("_internal") 88 | public interface JsHandlerRegistration extends ExportOverlay { 89 | void removeHandler(); 90 | } 91 | 92 | public interface LogInEventHandler extends EventHandler { 93 | void onLogIn(LoginEvent event); 94 | } 95 | 96 | public static class LoginEvent extends Event { 97 | public static final Type TYPE = new Type(); 98 | 99 | public Type getAssociatedType() { 100 | return TYPE; 101 | } 102 | 103 | protected void dispatch(LogInEventHandler handler) { 104 | handler.onLogIn(null); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue49TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | import org.timepedia.exporter.client.ExporterUtil; 7 | 8 | import com.google.gwt.junit.client.GWTTestCase; 9 | 10 | public class Issue49TestGwt extends GWTTestCase { 11 | 12 | @Override 13 | public String getModuleName() { 14 | return "org.timepedia.exporter.Test"; 15 | } 16 | 17 | @Override 18 | protected void gwtSetUp() throws Exception { 19 | ExporterUtil.exportAll(); 20 | } 21 | 22 | @Export 23 | public interface ObjectInterface extends Exportable { 24 | public String method(); 25 | } 26 | 27 | @ExportPackage("tst") 28 | @Export("ObjectFactory") 29 | public static class ObjectFactory implements Exportable { 30 | public ObjectInterface create() { 31 | return new ObjectImpl(); 32 | } 33 | } 34 | 35 | @ExportPackage("tst") 36 | @Export("ObjectUser") 37 | public static class ObjectUser implements Exportable { 38 | public String executeMethod(ObjectInterface object) { 39 | return object.method(); 40 | } 41 | } 42 | 43 | public static class ObjectImpl implements ObjectInterface, Exportable { 44 | @Override 45 | public String method() { 46 | return "object"; 47 | } 48 | } 49 | 50 | public static native String runJs() /*-{ 51 | var factory = new $wnd.tst.ObjectFactory(); 52 | var ret = ""; 53 | var object = factory.create(); 54 | var objectUser = new $wnd.tst.ObjectUser(); 55 | ret += objectUser.executeMethod(object); 56 | return ret; 57 | }-*/; 58 | 59 | public void testIssue() { 60 | assertEquals("object", runJs()); 61 | } 62 | } -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue50TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | import org.timepedia.exporter.client.Getter; 7 | import org.timepedia.exporter.client.Setter; 8 | 9 | import com.google.gwt.core.client.GWT; 10 | import com.google.gwt.junit.client.GWTTestCase; 11 | 12 | public class Issue50TestGwt extends GWTTestCase { 13 | 14 | @Override public String getModuleName() { 15 | return "org.timepedia.exporter.Test"; 16 | } 17 | 18 | @Override protected void gwtSetUp() throws Exception { 19 | GWT.create(Test.class); 20 | } 21 | 22 | @ExportPackage("tst") 23 | @Export("Test50") 24 | public static class Test implements Exportable { 25 | private String name = ""; 26 | 27 | @Getter public String getName() { 28 | return name; 29 | } 30 | @Setter public void setName(String s) { 31 | this.name = s + "-exporter"; 32 | } 33 | } 34 | 35 | public static native String runJs() /*-{ 36 | var obj = new $wnd.tst.Test50(); 37 | obj.name = "gwt"; 38 | return obj.name; 39 | }-*/; 40 | 41 | public void testIssue() { 42 | assertEquals("gwt-exporter", runJs()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/Issue51TestGwt.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | import org.timepedia.exporter.client.Getter; 7 | import org.timepedia.exporter.client.Setter; 8 | 9 | import com.google.gwt.core.client.GWT; 10 | import com.google.gwt.junit.client.GWTTestCase; 11 | 12 | public class Issue51TestGwt extends GWTTestCase { 13 | 14 | @Override public String getModuleName() { 15 | return "org.timepedia.exporter.Test"; 16 | } 17 | 18 | @Override protected void gwtSetUp() throws Exception { 19 | GWT.create(Test.class); 20 | } 21 | 22 | /** 23 | * An external interface that can't be Exportable by itself 24 | */ 25 | public static interface ExternalInterface { 26 | public static final String MY_CONSTANT = "MY_CONSTANT"; 27 | 28 | public String getName(); 29 | public void setName(String s); 30 | } 31 | 32 | /** 33 | * A marker interface that extends the external interface an implements the Exportable to let the 34 | * system know that it does need to export all fields of this interface and the ones it extend. 35 | */ 36 | public static interface MyInternalInterface extends ExternalInterface, Exportable { 37 | 38 | } 39 | 40 | @ExportPackage("tst") 41 | @Export("Test51") 42 | public static class Test implements MyInternalInterface { 43 | private String name = ""; 44 | public String getName() { 45 | return name; 46 | } 47 | public void setName(String s) { 48 | this.name = s + "-exporter"; 49 | } 50 | } 51 | 52 | public static native String runJs() /*-{ 53 | var obj = new $wnd.tst.Test51(); 54 | obj.setName($wnd.tst.Test51.MY_CONSTANT); 55 | return obj.getName(); 56 | }-*/; 57 | 58 | public void testIssue() { 59 | assertEquals("MY_CONSTANT-exporter", runJs()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /core/src/test/java/org/timepedia/exporter/test/issues/MyExportable.java: -------------------------------------------------------------------------------- 1 | package org.timepedia.exporter.test.issues; 2 | 3 | import org.timepedia.exporter.client.Export; 4 | import org.timepedia.exporter.client.ExportPackage; 5 | import org.timepedia.exporter.client.Exportable; 6 | 7 | @ExportPackage("gwt") 8 | @Export 9 | public class MyExportable implements Exportable { 10 | 11 | String n = "no-id"; 12 | 13 | public MyExportable() { 14 | } 15 | 16 | public MyExportable(String id) { 17 | n = id; 18 | } 19 | 20 | public String getId() { 21 | return n; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/export-widgets/README.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is the project ExportWidget which uses GwtQuery and GwtExporter libraries. 4 | 5 | It tries to show how to look for widgets from js and how to use methods in 6 | those found widgets. 7 | 8 | - Assuming you have installed maven, compile and install it just running: 9 | $ mvn clean install 10 | 11 | - Run it in development mode: 12 | $ mvn gwt:run 13 | 14 | - Import and run in Eclipse: 15 | 16 | The archetype generates a project ready to be used in eclipse, 17 | but before importing it you have to install the following plugins: 18 | 19 | * Google plugin for eclipse (update-site: http://dl.google.com/eclipse/plugin/3.7 or 3.6 or 3.5) 20 | * Sonatype Maven plugin (update-site: http://m2eclipse.sonatype.org/site/m2e) 21 | 22 | Then you can import the project in your eclipse workspace: 23 | 24 | * File -> Import -> Existing Projects into Workspace 25 | 26 | Finally you should be able to run the project in development mode and to run the gwt test unit. 27 | 28 | * Right click on the project -> Run as -> Web Application 29 | * Right click on the test class -> Run as -> GWT JUnit Test 30 | 31 | - Although the project has the files .classpath and .project, you could generate them running any 32 | of the following commands: 33 | 34 | $ mvn eclipse:m2eclipse (if you like to use m2eclipse) 35 | $ mvn eclipse:eclipse (to use the project without m2eclipse) 36 | -------------------------------------------------------------------------------- /samples/export-widgets/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | ExportWidget gwtquery project 6 | org.gquery 7 | ewidget 8 | war 9 | 1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 1.6 14 | 1.6 15 | 1.3.1 16 | 2.5.0-SNAPSHOT 17 | 2.5.1-rc1 18 | 2.5.1-rc1 19 | 20 | 21 | 22 | 23 | central 24 | http://repo1.maven.org/maven2 25 | 26 | 27 | gwtquery-plugins 28 | http://gwtquery-plugins.googlecode.com/svn/mavenrepo 29 | 30 | 31 | sonatype 32 | http://oss.sonatype.org/content/repositories/snapshots 33 | true 34 | false 35 | 36 | 37 | m.g.o 38 | http://maven.glassfish.org/content/groups/public/ 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | junit 51 | junit 52 | 3.8.1 53 | test 54 | 55 | 56 | com.google.gwt 57 | gwt-user 58 | ${gwtversion} 59 | provided 60 | 61 | 62 | com.google.gwt 63 | gwt-dev 64 | ${gwtversion} 65 | provided 66 | 67 | 68 | com.google.gwt 69 | gwt-servlet 70 | ${gwtversion} 71 | runtime 72 | 73 | 74 | com.googlecode.gwtquery 75 | gwtquery 76 | ${gQueryVersion} 77 | provided 78 | 79 | 80 | org.timepedia.exporter 81 | gwtexporter 82 | ${gExporterVersion} 83 | provided 84 | 85 | 86 | 87 | javax.validation 88 | validation-api 89 | 1.0.0.GA 90 | 91 | 92 | javax.validation 93 | validation-api 94 | 1.0.0.GA 95 | sources 96 | 97 | 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-compiler-plugin 103 | 2.1 104 | 105 | 1.6 106 | 1.6 107 | 108 | 109 | 110 | org.codehaus.mojo 111 | gwt-maven-plugin 112 | ${gwtmaven} 113 | 114 | ${gwt.loglevel} 115 | 116 | ${gwtversion} 117 | true 118 | ExportWidget/ExportWidget.html 119 | ${project.build.directory}/${project.build.finalName} 120 | 121 | 122 | 123 | prepare-package 124 | 125 | compile 126 | 127 | 128 | 129 | 130 | 131 | maven-surefire-plugin 132 | 2.8.1 133 | 134 | 135 | \${project.build.sourceDirectory} 136 | \${project.build.testSourceDirectory} 137 | 138 | false 139 | always 140 | 141 | 142 | gwt.args 143 | -out target/gwt-tests 144 | 145 | 146 | 147 | gwt.persistentunitcache 148 | false 149 | 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-eclipse-plugin 156 | 2.7 157 | 158 | true 159 | false 160 | 161 | com.google.gwt.eclipse.core.gwtProjectValidator 162 | org.eclipse.wst.common.modulecore.ComponentStructuralBuilder 163 | org.eclipse.jdt.core.javabuilder 164 | org.eclipse.wst.common.modulecore.ComponentStructuralBuilderDependencyResolver 165 | 166 | 167 | com.google.gwt.eclipse.core.gwtNature 168 | org.eclipse.jdt.core.javanature 169 | org.eclipse.wst.common.modulecore.ModuleCoreNature 170 | 171 | 172 | com.google.gwt.eclipse.core.GWT_CONTAINER 173 | org.eclipse.jdt.launching.JRE_CONTAINER 174 | org.eclipse.jdt.junit.JUNIT_CONTAINER/3 175 | 176 | 177 | 178 | 179 | 180 | maven-resources-plugin 181 | 2.5 182 | 183 | 184 | copy-resources 185 | generate-sources 186 | 187 | copy-resources 188 | 189 | 190 | ${project.build.directory}/${project.build.finalName} 191 | 192 | 193 | src/main/webapp 194 | true 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | ${project.build.directory}/${project.build.finalName}/WEB-INF/classes 203 | 204 | 205 | -------------------------------------------------------------------------------- /samples/export-widgets/src/main/java/org/gquery/ewidget/ExportWidget.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/export-widgets/src/main/java/org/gquery/ewidget/client/ExportWidget.java: -------------------------------------------------------------------------------- 1 | package org.gquery.ewidget.client; 2 | import static com.google.gwt.query.client.GQuery.$; 3 | 4 | import org.timepedia.exporter.client.Export; 5 | import org.timepedia.exporter.client.ExportOverlay; 6 | import org.timepedia.exporter.client.ExportPackage; 7 | import org.timepedia.exporter.client.Exportable; 8 | import org.timepedia.exporter.client.ExporterUtil; 9 | 10 | import com.google.gwt.core.client.EntryPoint; 11 | import com.google.gwt.event.dom.client.ClickEvent; 12 | import com.google.gwt.event.dom.client.ClickHandler; 13 | import com.google.gwt.user.client.ui.Button; 14 | import com.google.gwt.user.client.ui.Label; 15 | import com.google.gwt.user.client.ui.RootPanel; 16 | import com.google.gwt.user.client.ui.Widget; 17 | 18 | /** 19 | * App example to show how to mix gquery and gwtexporter 20 | * 21 | * After compiling this app and loading in your html document, 22 | * you can call exported methods from javascript: 23 |
 24 |    label = js.getLabel(".gwt-Label");
 25 |    label.setText("Hi");
 26 |    
 27 |    widget = js.getWidget(".gwt-Label");
 28 |    label = new js.Label(widget);
 29 |    label.setText("Bye");
 30 |    
 31 |    button = js.getButtons("*")[0];
 32 |    button.click();
 33 | 
 34 |    widget = js.getWidget(".gwt-Button");
 35 |    button = new js.Button(widget);
 36 |    button.click();
 37 |  
38 | */ 39 | public class ExportWidget implements EntryPoint { 40 | 41 | public void onModuleLoad() { 42 | ExporterUtil.exportAll(); 43 | 44 | final Label label = new Label("Hello world"); 45 | final Button button = new Button("Click me", new ClickHandler() { 46 | public void onClick(ClickEvent event) { 47 | $(label) 48 | .animate("fontSize: '+=10px', color:'blue'") 49 | .delay(1000) 50 | .animate("fontSize: '-=10px', color: 'black'"); 51 | } 52 | }); 53 | 54 | RootPanel.get().add(label); 55 | RootPanel.get().add(button); 56 | } 57 | 58 | /** 59 | * We use a class to expose some static methods which 60 | * will return the widgets we want calling to gquery. 61 | */ 62 | @ExportPackage("") 63 | @Export("js") 64 | public static class Exposer implements Exportable { 65 | public static Label getLabel(String selector) { 66 | return $(selector).widgets(Label.class).get(0); 67 | } 68 | 69 | public static Button[] getButtons(String selector) { 70 | return $(selector).widgets(Button.class).toArray(new Button[0]); 71 | } 72 | 73 | public static Widget getWidget(String selector) { 74 | return $(selector).widget(); 75 | } 76 | 77 | public static Widget[] getWidgets(String selector) { 78 | return $(selector).widgets().toArray(new Widget[0]); 79 | } 80 | } 81 | 82 | /** 83 | * We export the widgets we want to use so as we can 84 | * access their instance methods. 85 | * 86 | * We can use either interfaces or abstract classes. 87 | */ 88 | @ExportPackage("js") 89 | @Export 90 | public static abstract class LabelOverLay implements ExportOverlay