/*
* Copyright 2008 Yuxing Huang <felix@webinit.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.webinit.gwt.rebind;
import java.io.PrintWriter;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
public class ObservableObjectGenerator extends Generator {
private static String packageName = "org.webinit.gwt.client";
private static String implementationSuffix = "_" +
org.webinit.gwt.client.Observable.class.getSimpleName() + "Impl";
private JType objectType_;
@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
// retrieve the type oracle
TypeOracle oracle = context.getTypeOracle();
assert oracle != null;
// get the implementation name from the type name
String implementationName;
if (typeName.lastIndexOf('.') == -1) {
implementationName = typeName + implementationSuffix;
}
else {
implementationName = typeName.substring(typeName.lastIndexOf('.')+1) + implementationSuffix;
}
logger.log(TreeLogger.DEBUG, "Generating for "+typeName);
// try to get java.lang.Object
try {
objectType_ = oracle.getType("java.lang.Object");
}
catch (NotFoundException nfe) {
logger.log(TreeLogger.ERROR, typeName, nfe);
return null;
}
JClassType sourceClass = oracle.findType(typeName);
if (sourceClass == null) {
logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + typeName + "'", null);
throw new UnableToCompleteException();
}
// if target class is an interface and it has one or more unknown methods,
// we cannot implement this interface because we cannot implement unknown methods.
if (sourceClass.isInterface() != null) {
JClassType targetInterface = oracle.findType(org.webinit.gwt.client.Observable.class.getCanonicalName());
JMethod[] targetMethods = targetInterface.getMethods();
JMethod[] methods = sourceClass.getMethods();
int known = 0;
for (JMethod method: methods) {
for (JMethod targetMethod: targetMethods) {
if (method.equals(targetMethod)) {
known ++;
}
}
}
if (known < methods.length) {
logger.log(TreeLogger.ERROR, "Unable to implement/extend " + typeName + ", because it has unknown methods.");
throw new UnableToCompleteException();
}
}
// target class is a class,
// if it has unknown abstract methods, we cannot implement it.
// if it has methods with the same signature as the observable interface, we cannot implement it.
else {
JClassType targetInterface = oracle.findType(org.webinit.gwt.client.Observable.class.getCanonicalName());
JMethod[] targetMethods = targetInterface.getMethods();
JMethod[] methods = sourceClass.getMethods();
for (JMethod method: methods) {
for (JMethod targetMethod: targetMethods) {
if (!method.equals(targetMethod) && method.isAbstract()) {
logger.log(TreeLogger.ERROR, "Unable to implement/extend " + typeName + ", because it has unknown method");
throw new UnableToCompleteException();
}
if (method.getReadableDeclaration().equals(targetMethod.getReadableDeclaration()) && !method.isAbstract()) {
logger.log(TreeLogger.ERROR, "Source class contains a conflict method " + method.getName());
throw new UnableToCompleteException();
}
}
}
}
// checking done, get the source writers
ClassSourceFileComposerFactory oocf = new ClassSourceFileComposerFactory(
packageName,
implementationName);
// if the source class is a Class, adds it as a superclass
if (sourceClass.isClass() != null) {
oocf.setSuperclass(typeName);
}
if (sourceClass.isInterface() != null) {
oocf.addImplementedInterface(sourceClass.getQualifiedSourceName());
JClassType[] implementedInterfaces = sourceClass.getImplementedInterfaces();
for (JClassType intf: implementedInterfaces) {
oocf.addImplementedInterface(intf.getQualifiedSourceName());
}
}
// add imports
oocf.addImport("java.util.Map");
oocf.addImport("java.util.Set");
oocf.addImport("java.util.List");
oocf.addImport("java.util.HashMap");
oocf.addImport("java.util.HashSet");
oocf.addImport("java.util.ArrayList");
PrintWriter printWriter = context.tryCreate(logger, packageName, implementationName);
SourceWriter sourceWriter = oocf.createSourceWriter(printWriter);
writeObservableObjectClass(logger, sourceWriter);
context.commit(logger, printWriter);
return packageName + "." + implementationName;
}
private void writeObservableObjectClass(TreeLogger l, SourceWriter w) {
w.println("private Map<String, List<Observer>> observers_ = null;");
// getObservers()
w.println("private Map<String, List<Observer>> getObservers() {");
w.println("if (null == observers_)");
w.println("observers_ = new HashMap<String, List<Observer>>();");
w.println("return observers_;");
w.println("}");
// getObservedEvents
w.println("public Set<String> getObservedEvents() {");
w.println("return getObservers().keySet();");
w.println("}");
w.println("public List<Observer> getObserversOfEvent(String event) {");
w.println("if (getObservers().containsKey(event)) {");
w.println("return getObservers().get(event);");
w.println("} else { return null; }");
w.println("}");
// addObserver
w.println("public void addObserver(String event, Observer observer) {");
w.println("List<Observer> eventObservers;");
w.println("if (getObservers().containsKey(event)) {");
w.println("eventObservers = getObservers().get(event);");
w.println("} else {");
w.println("eventObservers = new ArrayList<Observer>();");
w.println("getObservers().put(event, eventObservers);");
w.println("}");
w.println("eventObservers.add(observer);");
w.println("}");
// removeObserver
w.println("public boolean removeObserver(String event, Observer observer) {");
w.println("List<Observer> eventObservers;");
w.println("if (!getObservers().containsKey(event)) return false;");
w.println("eventObservers = getObservers().get(event);");
w.println("return eventObservers.remove(observer);");
w.println("}");
// trigger
w.println("public boolean trigger(String event, Object object, Object arg) {");
w.println("List<Observer> eventObservers;");
w.println("if (!getObservers().containsKey(event)) return true;");
w.println("eventObservers = getObservers().get(event);");
w.println("boolean result = true;");
w.println("for (Observer ob: eventObservers) result &= ob.observe(event, object, arg);");
w.println("return result;");
w.println("}");
w.println("}");
}
}