| /* |
| * Copyright 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.editor.rebind; |
| |
| 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.TypeOracle; |
| import com.google.gwt.dev.generator.NameFactory; |
| import com.google.gwt.dev.util.Name; |
| import com.google.gwt.dev.util.Name.BinaryName; |
| import com.google.gwt.editor.client.Editor; |
| import com.google.gwt.editor.client.impl.DelegateMap; |
| import com.google.gwt.editor.rebind.model.EditorData; |
| import com.google.gwt.editor.rebind.model.EditorModel; |
| import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; |
| import com.google.gwt.user.rebind.SourceWriter; |
| |
| import java.io.PrintWriter; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A base class for generating Editor drivers. |
| */ |
| public abstract class AbstractEditorDriverGenerator extends Generator { |
| |
| private GeneratorContext context; |
| private TreeLogger logger; |
| private EditorModel model; |
| |
| @Override |
| public String generate(TreeLogger logger, GeneratorContext context, |
| String typeName) throws UnableToCompleteException { |
| this.context = context; |
| this.logger = logger; |
| |
| TypeOracle oracle = context.getTypeOracle(); |
| JClassType toGenerate = oracle.findType(typeName).isInterface(); |
| if (toGenerate == null) { |
| logger.log(TreeLogger.ERROR, typeName + " is not an interface type"); |
| throw new UnableToCompleteException(); |
| } |
| |
| String packageName = toGenerate.getPackage().getName(); |
| String simpleSourceName = toGenerate.getName().replace('.', '_') + "Impl"; |
| PrintWriter pw = context.tryCreate(logger, packageName, simpleSourceName); |
| if (pw == null) { |
| return packageName + "." + simpleSourceName; |
| } |
| |
| model = new EditorModel(logger, toGenerate, |
| oracle.findType(getDriverInterfaceType().getName())); |
| |
| ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory( |
| packageName, simpleSourceName); |
| factory.setSuperclass(Name.getSourceNameForClass(getDriverSuperclassType()) |
| + "<" + model.getProxyType().getParameterizedQualifiedSourceName() |
| + ", " + model.getEditorType().getParameterizedQualifiedSourceName() |
| + ">"); |
| factory.addImplementedInterface(typeName); |
| SourceWriter sw = factory.createSourceWriter(context, pw); |
| writeCreateDelegate(sw); |
| writeAdditionalContent(logger, context, model, sw); |
| sw.commit(logger); |
| |
| return factory.getCreatedClassName(); |
| } |
| |
| protected abstract Class<?> getDriverInterfaceType(); |
| |
| protected abstract Class<?> getDriverSuperclassType(); |
| |
| protected String getEditorDelegate(EditorData delegateData) { |
| JClassType edited = delegateData.getEditedType(); |
| JClassType editor = delegateData.getEditorType(); |
| Map<EditorData, String> delegateFields = new IdentityHashMap<EditorData, String>(); |
| NameFactory nameFactory = new NameFactory(); |
| |
| /* |
| * The paramaterization of the editor type is included to ensure that a |
| * correct specialization of a CompositeEditor will be generated. For |
| * example, a ListEditor<Person, APersonEditor> would need a different |
| * delegate from a ListEditor<Person, AnotherPersonEditor>. |
| */ |
| StringBuilder maybeParameterizedName = new StringBuilder( |
| BinaryName.getClassName(editor.getQualifiedBinaryName())); |
| if (editor.isParameterized() != null) { |
| for (JClassType type : editor.isParameterized().getTypeArgs()) { |
| maybeParameterizedName.append("$").append(type.getQualifiedBinaryName()); |
| } |
| } |
| String delegateSimpleName = String.format( |
| "%s_%s", |
| escapedBinaryName(maybeParameterizedName.toString()), |
| BinaryName.getShortClassName(Name.getBinaryNameForClass(getEditorDelegateType()))); |
| |
| String packageName = editor.getPackage().getName(); |
| PrintWriter pw = context.tryCreate(logger, packageName, delegateSimpleName); |
| if (pw != null) { |
| ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory( |
| packageName, delegateSimpleName); |
| factory.setSuperclass(String.format("%s", |
| Name.getSourceNameForClass(getEditorDelegateType()))); |
| SourceWriter sw = factory.createSourceWriter(context, pw); |
| |
| EditorData[] data = model.getEditorData(editor); |
| |
| /* |
| * Declare fields in the generated subclass for the editor and the object |
| * being edited. This decreases casting over having a generic field in the |
| * supertype. |
| */ |
| sw.println("private %s editor;", editor.getQualifiedSourceName()); |
| sw.println("protected %s getEditor() {return editor;}", |
| editor.getQualifiedSourceName()); |
| sw.println( |
| "protected void setEditor(%s editor) {this.editor=(%s)editor;}", |
| Editor.class.getCanonicalName(), editor.getQualifiedSourceName()); |
| sw.println("private %s object;", edited.getQualifiedSourceName()); |
| sw.println("public %s getObject() {return object;}", |
| edited.getQualifiedSourceName()); |
| sw.println( |
| "protected void setObject(Object object) {this.object=(%s)object;}", |
| edited.getQualifiedSourceName()); |
| |
| if (delegateData.isCompositeEditor()) { |
| sw.println("protected %s createComposedDelegate() {", |
| Name.getSourceNameForClass(this.getEditorDelegateType())); |
| sw.indentln("return new %s();", |
| getEditorDelegate(delegateData.getComposedData())); |
| sw.println("}"); |
| } |
| |
| // Fields for the sub-delegates that must be managed |
| for (EditorData d : data) { |
| if (d.isDelegateRequired()) { |
| String fieldName = nameFactory.createName(d.getPropertyName() |
| + "Delegate"); |
| delegateFields.put(d, fieldName); |
| sw.println("%s %s;", |
| Name.getSourceNameForClass(getEditorDelegateType()), fieldName); |
| } |
| } |
| |
| // For each entity property, create a sub-delegate and initialize |
| sw.println("protected void attachSubEditors(%s delegateMap) {", |
| DelegateMap.class.getCanonicalName()); |
| sw.indent(); |
| for (EditorData d : data) { |
| String subDelegateType = getEditorDelegate(d); |
| sw.println("if (editor.%s != null) {", d.getSimpleExpression()); |
| sw.indent(); |
| if (d.isDelegateRequired()) { |
| sw.println("%s = new %s();", delegateFields.get(d), subDelegateType); |
| writeDelegateInitialization(sw, d, delegateFields); |
| sw.println("delegateMap.put(%1$s.getObject(), %1$s);", |
| delegateFields.get(d)); |
| } else if (d.isLeafValueEditor()) { |
| // if (can().access().without().npe()) { editor.subEditor.setValue() } |
| sw.println("if (%4$s) editor.%1$s.setValue(getObject()%2$s.%3$s());", |
| d.getSimpleExpression(), d.getBeanOwnerExpression(), |
| d.getGetterName(), d.getBeanOwnerGuard("getObject()")); |
| // simpleEditor.put("some.path", editor.simpleEditor()); |
| sw.println("simpleEditors.put(\"%s\", editor.%s);", |
| d.getDeclaredPath(), d.getSimpleExpression()); |
| } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // Flush each sub-delegate |
| sw.println("protected void flushSubEditors(%s errorAccumulator) {", |
| List.class.getCanonicalName()); |
| sw.indent(); |
| for (EditorData d : data) { |
| String mutableObjectExpression; |
| if (d.getBeanOwnerExpression().length() > 0) { |
| mutableObjectExpression = mutableObjectExpression(d, |
| String.format("(getObject()%s)", d.getBeanOwnerExpression())); |
| } else { |
| mutableObjectExpression = "getObject()"; |
| } |
| |
| if (d.getSetterName() != null && d.isLeafValueEditor()) { |
| // if (editor.subEditor != null && can().access()) { |
| sw.println("if (editor.%s != null && %s) {", d.getSimpleExpression(), |
| d.getBeanOwnerGuard("getObject()")); |
| sw.indent(); |
| if (d.isDelegateRequired()) { |
| sw.println("%s.flush(errorAccumulator);", delegateFields.get(d)); |
| // mutableObject.setFoo((cast)fooDelegate.getValue()); |
| sw.println("%s.%s((%s)%s.getObject());", mutableObjectExpression, |
| d.getSetterName(), d.getEditedType().getQualifiedSourceName(), |
| delegateFields.get(d)); |
| } else { |
| // mutableObject.setFoo(editor.subEditor.getValue()); |
| sw.println("%s.%s(editor.%s.getValue());", mutableObjectExpression, |
| d.getSetterName(), d.getSimpleExpression()); |
| } |
| sw.outdent(); |
| sw.println("}"); |
| } else if (d.isDelegateRequired()) { |
| // if (fooDelegate != null && can().reach().without().npe()) { |
| sw.println("if (%s != null && %s) {", delegateFields.get(d), |
| d.getBeanOwnerGuard("getObject()")); |
| sw.indent(); |
| // fooDelegate.flush(errorAccumulator); |
| sw.println("%s.flush(errorAccumulator);", delegateFields.get(d)); |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // Flush each sub-delegate |
| sw.println("protected void flushSubEditorErrors(%s errorAccumulator) {", |
| List.class.getCanonicalName()); |
| sw.indent(); |
| for (EditorData d : data) { |
| if (d.isDelegateRequired()) { |
| // if (fooDelegate != null) fooDelegate.flushErrors(accumulator); |
| sw.println("if (%1$s != null) %1$s.flushErrors(errorAccumulator);", |
| delegateFields.get(d)); |
| } |
| } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // Reset the data being displayed |
| sw.println("protected void refreshEditors() {", |
| DelegateMap.class.getCanonicalName()); |
| sw.indent(); |
| for (EditorData d : data) { |
| if (d.isDelegateRequired()) { |
| // if (subEditorDelegate != null) { |
| sw.println("if (%s != null) {", delegateFields.get(d)); |
| sw.indent(); |
| // if (can().access().without().npe()) { |
| sw.println("if (%s) {", d.getBeanOwnerGuard("getObject()")); |
| // subDelegate.refresh(getObject().getFoo().getBar()); |
| sw.indentln("%s.refresh(getObject()%s.%s());", delegateFields.get(d), |
| d.getBeanOwnerExpression(), d.getGetterName()); |
| // } else { subDelegate.refresh(null); } |
| sw.println("} else { %s.refresh(null); }", delegateFields.get(d)); |
| sw.outdent(); |
| sw.println("}"); |
| } else if (d.isLeafValueEditor()) { |
| // if (editor.subEditor != null) { |
| sw.println("if (editor.%s != null) {", d.getSimpleExpression()); |
| sw.indent(); |
| // if (can().access().without().npe()) { editor.subEditor.setValue() } |
| sw.println("if (%4$s) editor.%1$s.setValue(getObject()%2$s.%3$s());", |
| d.getSimpleExpression(), d.getBeanOwnerExpression(), |
| d.getGetterName(), d.getBeanOwnerGuard("getObject()")); |
| // else { editor.subEditor.setValue(null); } |
| sw.println("else { editor.%s.setValue(null); }", |
| d.getSimpleExpression()); |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| sw.outdent(); |
| sw.println("}"); |
| |
| // Write instance delegate to static implementation |
| sw.println("protected void traverse(%s paths) {", |
| List.class.getCanonicalName()); |
| sw.indentln("traverseEditor(getEditor(), \"\", paths);"); |
| sw.println("}"); |
| |
| sw.println("public static void traverseEditor(%s editor," |
| + " String prefix, %s<String> paths) {", |
| editor.getQualifiedSourceName(), List.class.getName()); |
| sw.indent(); |
| for (EditorData d : data) { |
| if (d.isDelegateRequired() || d.isDeclaredPathNested() |
| || d.isCompositeEditor()) { |
| // if (editor.subEditor != null) { |
| sw.println("if (editor.%s != null) {", d.getSimpleExpression()); |
| sw.indent(); |
| // String localPath = appendPath(prefix, "somePath"); |
| sw.println("String localPath = appendPath(prefix, \"%s\");", |
| d.getDeclaredPath()); |
| sw.println("paths.add(localPath);"); |
| |
| if (d.isDelegateRequired()) { |
| // fooDelegate.traverseEditor(editor.subEditor, localPath, paths); |
| sw.println("%s.traverseEditor(editor.%s, localPath, paths);", |
| getEditorDelegate(d), d.getSimpleExpression()); |
| } |
| if (d.isCompositeEditor()) { |
| /* |
| * composedDelegate.traverseEditor(editor.subEditor. |
| * createEditorForTraversal(), localPath, paths); |
| */ |
| sw.println( |
| "%s.traverseEditor(editor.%s.createEditorForTraversal(), localPath, paths);", |
| getEditorDelegate(d.getComposedData()), d.getSimpleExpression()); |
| } |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| sw.outdent(); |
| sw.println("}"); |
| |
| sw.commit(logger); |
| } |
| return packageName + "." + delegateSimpleName; |
| } |
| |
| protected abstract Class<?> getEditorDelegateType(); |
| |
| protected abstract String mutableObjectExpression(EditorData data, |
| String sourceObjectExpression); |
| |
| /** |
| * @param logger |
| * @param context |
| * @param model |
| * @param sw |
| * |
| * @throws UnableToCompleteException |
| */ |
| protected void writeAdditionalContent(TreeLogger logger, |
| GeneratorContext context, EditorModel model, SourceWriter sw) |
| throws UnableToCompleteException { |
| } |
| |
| protected abstract void writeDelegateInitialization(SourceWriter sw, |
| EditorData d, Map<EditorData, String> delegateFields); |
| |
| private String escapedBinaryName(String binaryName) { |
| return binaryName.replace("_", "_1").replace('$', '_').replace('.', '_'); |
| } |
| |
| private void writeCreateDelegate(SourceWriter sw) |
| throws UnableToCompleteException { |
| String editorDelegateName = getEditorDelegate(model.getRootData()); |
| |
| sw.println("protected %s createDelegate() {", |
| Name.getSourceNameForClass(getEditorDelegateType()), |
| model.getProxyType().getQualifiedSourceName(), |
| model.getEditorType().getQualifiedSourceName()); |
| sw.indent(); |
| sw.println("return new %1$s();", editorDelegateName); |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |