/*
* Copyright 2010 kk-electronic a/s.
*
* This file is part of KKPortal.
*
* KKPortal is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KKPortal 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KKPortal. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kk_electronic.gwt.rebind;
import java.io.PrintWriter;
import java.util.HashMap;
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.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.inject.client.GinModules;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.kk_electronic.kkportal.core.reflection.ClassMap;
public class ClassMapGenerator extends Generator {
private String packageName;
private String className;
private TypeOracle typeOracle;
private JClassType classType;
private JClassType keyType;
private JClassType valueType;
private TreeLogger logger;
@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
this.logger = logger;
try {
typeOracle = context.getTypeOracle();
classType = typeOracle.getType(typeName);
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
classType.getAnnotation(GinModules.class);
generateClass(logger, context);
} catch (NotFoundException e) {
logger.log(TreeLogger.ERROR, "Exception during ClassMap creation.", e);
throw new UnableToCompleteException();
}
return packageName + "." + className;
}
private void generateClass(TreeLogger logger, GeneratorContext context) throws UnableToCompleteException, NotFoundException {
PrintWriter printWriter = context.tryCreate(logger, packageName, className);
if (printWriter == null){
return;
}
ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName,className);
composer.addImport(HashMap.class.getCanonicalName());
composer.addImplementedInterface(classType.getQualifiedSourceName());
JClassType target = typeOracle.getType(ClassMap.class.getCanonicalName());
for (JClassType interfaze:classType.getImplementedInterfaces()){
if(interfaze.getErasedType().equals(target.getErasedType())){
JClassType[] genericTypes = interfaze.isParameterized().getTypeArgs();
keyType = genericTypes[0];
valueType = genericTypes[1];
}
}
// if (!"java.lang.String".equals(keyType.getQualifiedSourceName())){
// logger.log(TreeLogger.ERROR, "keyType must be a String for now");
// throw new UnableToCompleteException();
// }
composer.addImport(keyType.getQualifiedSourceName());
composer.addImport(valueType.getQualifiedSourceName());
SourceWriter sourceWriter = composer.createSourceWriter(context,printWriter);
writeMethods(sourceWriter);
writeData(sourceWriter);
sourceWriter.outdent();
sourceWriter.println("}");
context.commit(logger, printWriter);
}
private void writeData(SourceWriter sw) throws UnableToCompleteException {
sw.println();
sw.println("{");
sw.indent();
for(JClassType classType:valueType.getSubtypes()){
String key = getKeyFromClass(classType);
sw.println("map.put("+key+","+classType.getQualifiedSourceName()+".class);");
sw.println("reversemap.put("+classType.getQualifiedSourceName()+".class, "+key+");");
}
sw.outdent();
sw.println("}");
}
private void writeMethods(SourceWriter sw) {
sw.print("private static HashMap<"+keyType.getName()+", Class<? extends "+valueType.getName()+">> map");
sw.println("= new HashMap<"+keyType.getName()+", Class<? extends "+valueType.getName()+">>();");
sw.print("private static HashMap<Class<? extends "+valueType.getName()+">,"+keyType.getName()+"> reversemap");
sw.println("= new HashMap<Class<? extends "+valueType.getName()+">,"+keyType.getName()+">();");
sw.println();
sw.println("@Override");
sw.println("public Class<? extends "+valueType.getName()+"> getClassFromKey("+keyType.getName()+" name) {");
sw.println(" return map.get(name);");
sw.println("}");
sw.println();
sw.println("@Override");
sw.println("public "+keyType.getName()+" getKeyFromClass(Class<? extends "+valueType.getName()+"> clazz) {");
sw.println(" return reversemap.get(clazz);");
sw.println("}");
}
private String getKeyFromClass(JClassType j) throws UnableToCompleteException{
MapKey x = j.getAnnotation(MapKey.class);
if(x != null){
return x.value();
}
if( "java.lang.String".equals(keyType.getQualifiedSourceName()) ){
return "\"" + escape(j.getName()) + "\"";
}
if( "java.lang.Integer".equals(keyType.getQualifiedSourceName()) ){
return String.valueOf(j.getName().hashCode());
}
logger.log(TreeLogger.ERROR, "keyType can only be autogenerated for Integers and Strings - supply @MapKey annotation");
throw new UnableToCompleteException();
}
}