/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.mantle.rebind;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.apache.commons.lang.StringUtils;
import org.pentaho.mantle.client.events.EventBusUtil;
import java.io.PrintWriter;
import java.util.ArrayList;
public class EventBusUtilGenerator extends Generator {
private String packageName;
private String className;
private TypeOracle typeOracle;
private TreeLogger logger;
@Override
public String generate( TreeLogger logger, GeneratorContext context, String typeName )
throws UnableToCompleteException {
this.logger = logger;
typeOracle = context.getTypeOracle();
try {
// get classType and save instance variables
JClassType classType = typeOracle.getType( typeName );
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
// Generate class source code
generateClass( logger, context );
} catch ( Exception e ) {
// record to logger that Map generation threw an exception
logger.log( TreeLogger.ERROR, "PropertyMap ERROR!!!", e );
}
// return the fully qualified name of the class generated
return packageName + "." + className;
}
private void generateClass( TreeLogger logger, GeneratorContext context ) {
// get print writer that receives the source code
PrintWriter printWriter = null;
printWriter = context.tryCreate( logger, packageName, className );
// print writer if null, source code has ALREADY been generated, return
if ( printWriter == null ) {
return;
}
// init composer, set class properties, create source writer
ClassSourceFileComposerFactory composer = null;
composer = new ClassSourceFileComposerFactory( packageName, className );
composer.addImplementedInterface( EventBusUtil.class.getName() );
composer.addImport( GwtEvent.class.getName() );
composer.addImport( JavaScriptObject.class.getName() );
composer.addImport( EventBus.class.getName() );
composer.addImport( HandlerRegistration.class.getName() );
SourceWriter sourceWriter = null;
sourceWriter = composer.createSourceWriter( context, printWriter );
sourceWriter.indent();
// generator constructor source code
generateConstructor( sourceWriter );
generateMethods( sourceWriter );
// close generated class
sourceWriter.outdent();
sourceWriter.println( "}" );
// commit generated class
context.commit( logger, printWriter );
}
private void generateMethods( SourceWriter sourceWriter ) {
sourceWriter
.println( "public native void invokeEventBusJSO(final JavaScriptObject jso, final String parameterJSON)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
// unfortunately, we can obtain pairs like {"a":"undefined"} or even {"a":undefined}
// the latter will crash the procedure, so let's try to clean up the string
// by removing pairs with 'undefined'
sourceWriter.println( "var tmp = parameterJSON"
// replace ("a":undefined) and ("a":"undefined") with empty space
+ ".replace(/\\\"[\\w]+\\\"\\:[ ]*(\\\")?undefined(\\\")?(,)?/g, '')"
// remove doubled commas
+ ".replace(/,[ ]*,/, ',')"
// remove leading and trailing commas
+ ".replace(/\\{[ ]*,/, '{')"
+ ".replace(/,[ ]*\\}/, '}');" );
sourceWriter.println( "var p = JSON.parse(tmp);" );
sourceWriter.println( "jso.call(this, p)" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println(
"public native String getParameterString(final String paramName, final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println(
"public native Integer getParameterInteger(final String paramName, final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println(
"public native Boolean getParameterBoolean(final String paramName, final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println( "public native Float getParameterFloat(final String paramName, "
+ "final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println(
"public native Double getParameterDouble(final String paramName, final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println( "public native Long getParameterLong(final String paramName, final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
sourceWriter.println();
sourceWriter
.println( "public native Short getParameterShort(final String paramName, "
+ "final JavaScriptObject parameterMap)" );
sourceWriter.println( "/*-{" );
sourceWriter.indent();
sourceWriter.println( "return parameterMap[paramName];" );
sourceWriter.outdent();
sourceWriter.println( "}-*/;" );
// *********************
// FIRE EVENT
// *********************
sourceWriter.println();
sourceWriter.println( "public void fireEvent(final String eventType, final JavaScriptObject parameterMap) { " );
sourceWriter.indent();
try {
// find Command implementors
ArrayList<JClassType> implementingTypes = new ArrayList<JClassType>();
JPackage pack = typeOracle.getPackage( EventBusUtil.class.getPackage().getName() );
JClassType eventSourceType = typeOracle.getType( GwtEvent.class.getName() );
for ( JClassType type : pack.getTypes() ) {
if ( type.isAssignableTo( eventSourceType ) ) {
implementingTypes.add( type );
}
}
sourceWriter.println( "if(false){}" ); // placeholder
for ( JClassType implementingType : implementingTypes ) {
sourceWriter.println( "else if(eventType.equals(\"" + implementingType.getSimpleSourceName() + "\")){" );
sourceWriter.indent();
sourceWriter.println( implementingType.getName() + " event = new " + implementingType.getName() + "();" );
for ( JMethod eventMethod : implementingType.getMethods() ) {
if ( eventMethod.isPublic() && !eventMethod.isStatic() && eventMethod.isConstructor() == null
&& eventMethod.getName().startsWith( "set" ) ) {
String propertyName = eventMethod.getName().substring( 3 );
propertyName = propertyName.substring( 0, 1 ).toLowerCase() + propertyName.substring( 1 );
String simpleType = implementingType.getField( propertyName ).getType().getSimpleSourceName();
if ( "string".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterString(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "integer".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterInteger(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "float".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterFloat(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "double".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterDouble(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "long".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterLong(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "short".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterShort(\"" + propertyName
+ "\", parameterMap));" );
} else if ( "boolean".equalsIgnoreCase( simpleType ) ) {
sourceWriter.println( "event." + eventMethod.getName() + "(getParameterBoolean(\"" + propertyName
+ "\", parameterMap));" );
}
}
}
sourceWriter.println( "EVENT_BUS.fireEvent(event);" );
sourceWriter.outdent();
sourceWriter.println( "}" );
}
} catch ( Exception e ) {
// record to logger that Map generation threw an exception
logger.log( TreeLogger.ERROR, "Error generating BindingContext!!!", e );
}
sourceWriter.outdent();
sourceWriter.println( "}" );
// *********************
// ADD HANDLER
// *********************
sourceWriter.println();
sourceWriter.println( "public HandlerRegistration addHandler(final String eventType, final JavaScriptObject handler) { " );
sourceWriter.indent();
try {
// find Command implementors
JPackage pack = typeOracle.getPackage( EventBusUtil.class.getPackage().getName() );
JClassType eventSourceType = typeOracle.getType( GwtEvent.class.getName() );
sourceWriter.println( "if(false){return null;}" ); // placeholder
for ( JClassType type : pack.getTypes() ) {
if ( type.isAssignableTo( eventSourceType ) ) {
addHandlerElseCondition( sourceWriter, type );
}
}
} catch ( Exception e ) {
// record to logger that Map generation threw an exception
logger.log( TreeLogger.ERROR, "Error generating BindingContext!!!", e );
}
sourceWriter.indent();
sourceWriter.println( "return null;" );
sourceWriter.outdent();
sourceWriter.println( "}" );
}
private void addHandlerElseCondition( SourceWriter writer, JClassType type ) throws NotFoundException {
writer.println( "else if(eventType.equals(\"" + type.getSimpleSourceName() + "\")){" );
writer.indent();
JClassType handlerType =
typeOracle.getType( EventBusUtil.class.getPackage().getName() + "." + type.getName()
+ "Handler" );
writer.println( "HandlerRegistration handlerRegistration = EVENT_BUS.addHandler(" + type.getName() + ".TYPE, new "
+ type.getName() + "Handler() {" );
writer.indent();
for ( JMethod handlerMethod : handlerType.getMethods() ) {
String parameterJSON = addHandlerParamJson( type );
writer.println( "public void " + handlerMethod.getName() + "(" + type.getName()
+ " event) {" );
writer.indent();
writer.println( "invokeEventBusJSO(handler," + parameterJSON + ");" );
writer.outdent();
writer.println( "}" );
}
writer.outdent();
writer.println( "});" );
writer.indent();
writer.println( "return handlerRegistration;" );
writer.outdent();
writer.println( "}" );
}
// visible for testing purposes
String addHandlerParamJson( JClassType type ) {
StringBuilder json = new StringBuilder( 128 );
json.append( '"' ).append( '{' );
JMethod[] methods = type.getMethods();
for ( JMethod eventMethod : methods ) {
if ( eventMethod.isPublic()
&& !eventMethod.isStatic()
&& ( eventMethod.isConstructor() == null )
&& !"void".equalsIgnoreCase( eventMethod.getReturnType().getSimpleSourceName() )
&& !eventMethod.getName().equals( "getAssociatedType" ) ) {
// let's add the property to JSON object
String propertyName = StringUtils.uncapitalize( eventMethod.getName().substring( 3 ) );
String simpleType = type.getField( propertyName ).getType().getSimpleSourceName();
if ( "string".equalsIgnoreCase( simpleType ) ) {
json.append( "\\\"" + propertyName + "\\\":\\\"\" + event." + eventMethod.getName() + "() + \"\\\"" );
} else {
json.append( "\\\"" + propertyName + "\\\":\" + event." + eventMethod.getName() + "() + \"" );
}
json.append( ',' );
}
}
int lastIndex = json.lastIndexOf( "," );
if ( lastIndex == -1 ) {
json.append( '}' );
} else {
json.setCharAt( lastIndex, '}' );
}
return json.append( '"' ).toString();
}
private void generateConstructor( SourceWriter sourceWriter ) {
// start constructor source generation
sourceWriter.println( "public " + className + "() { " );
sourceWriter.indent();
sourceWriter.println( "super();" );
sourceWriter.outdent();
sourceWriter.println( "}" );
}
@SuppressWarnings( "unused" )
private String boxPrimative( JType type ) {
if ( type.isPrimitive() != null ) {
JPrimitiveType primative = type.isPrimitive();
return primative.getQualifiedBoxedSourceName();
} else {
return type.getQualifiedSourceName();
}
}
}