package org.ovirt.engine.ui.common.binding;
import java.util.LinkedHashSet;
import java.util.Set;
import org.ovirt.engine.ui.common.editor.AbstractUiCommonModelEditorDriver;
import org.ovirt.engine.ui.common.editor.UiCommonEditorDriver;
import org.ovirt.engine.ui.common.editor.UiCommonEventMap;
import org.ovirt.engine.ui.common.editor.UiCommonListenerMap;
import org.ovirt.engine.ui.common.editor.UiCommonModelEditorDelegate;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.editor.rebind.AbstractEditorDriverGenerator;
import com.google.gwt.editor.rebind.model.EditorData;
import com.google.gwt.editor.rebind.model.EditorModel;
import com.google.gwt.user.rebind.SourceWriter;
public class UiCommonEditorDriverGenerator extends AbstractEditorDriverGenerator {
private JClassType baseModelType;
private JClassType entityModelType;
private JClassType listModelType;
private JClassType hasCleanupType;
private TreeLogger logger;
private EditorModel model;
private SourceWriter sw;
@Override
protected Class<?> getDriverInterfaceType() {
return UiCommonEditorDriver.class;
}
@Override
protected Class<?> getDriverSuperclassType() {
return AbstractUiCommonModelEditorDriver.class;
}
@Override
protected Class<?> getEditorDelegateType() {
return UiCommonModelEditorDelegate.class;
}
@Override
protected String mutableObjectExpression(EditorData data,
String sourceObjectExpression) {
return sourceObjectExpression;
}
/**
* Implement additional methods defined in {@link AbstractUiCommonModelEditorDriver}
*/
@Override
protected void writeAdditionalContent(TreeLogger logger,
GeneratorContext context,
EditorModel model,
SourceWriter sw)
throws UnableToCompleteException {
TypeOracle typeOracle = context.getTypeOracle();
baseModelType = eraseType(typeOracle.findType("org.ovirt.engine.ui.uicommonweb.models.Model")); //$NON-NLS-1$
entityModelType = eraseType(typeOracle.findType("org.ovirt.engine.ui.uicommonweb.models.EntityModel")); //$NON-NLS-1$
listModelType = eraseType(typeOracle.findType("org.ovirt.engine.ui.uicommonweb.models.ListModel")); //$NON-NLS-1$
hasCleanupType = eraseType(typeOracle.findType("org.ovirt.engine.ui.uicommonweb.HasCleanup")); //$NON-NLS-1$
this.logger = logger;
this.model = model;
this.sw = sw;
logger.log(Type.DEBUG, "Starting to write additional Driver code"); //$NON-NLS-1$
writeListenerMap();
writeEventMap();
writeOwnerModels();
writeCleanup();
}
/**
* Writes the UiCommonListenerMap for the edited model
*/
private void writeListenerMap() {
logger.log(Type.DEBUG, "Starting to write ListenerMap"); //$NON-NLS-1$
sw.println();
sw.println("@Override"); //$NON-NLS-1$
sw.println("protected " + UiCommonListenerMap.class.getName() + " getListenerMap() {"); //$NON-NLS-1$ //$NON-NLS-2$
sw.indent();
sw.println(UiCommonListenerMap.class.getName() + " listenerMap = new " + UiCommonListenerMap.class.getName() //$NON-NLS-1$
+ "();"); //$NON-NLS-1$
sw.println();
logger.log(Type.DEBUG, "Looking for top-level Editor Fields"); //$NON-NLS-1$
for (EditorData editorData : model.getEditorData()) {
logger.log(Type.DEBUG, "Going over Field: " + editorData); //$NON-NLS-1$
String path = editorData.getPath();
// Change first letter to Upper to comply with UiCommon Property Names
path = Character.toUpperCase(path.charAt(0)) + path.substring(1, path.length());
if (path.length() == 0) {
continue;
}
// only relevant for top-level properties
if (!editorData.isDeclaredPathNested()) {
logger.log(Type.DEBUG, "Found top-level Field: " + editorData); //$NON-NLS-1$
sw.println("listenerMap.addListener(\"%s\", \"PropertyChanged\", new org.ovirt.engine.ui.uicompat.IEventListener() {", //$NON-NLS-1$
path);
sw.indent();
sw.println("@Override"); //$NON-NLS-1$
sw.println("public void eventRaised(org.ovirt.engine.ui.uicompat.Event ev, Object sender, org.ovirt.engine.ui.uicompat.EventArgs args) {"); //$NON-NLS-1$
sw.indent();
sw.println("getEditor().%s.setValue(getObject()%s);", //$NON-NLS-1$
editorData.getExpression(),
editorData.getGetterExpression());
sw.outdent();
sw.println("}"); //$NON-NLS-1$
sw.outdent();
sw.println("});"); //$NON-NLS-1$
sw.println();
}
}
sw.println("return listenerMap;"); //$NON-NLS-1$
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
/**
* Writes the UiCommonEventMap for the edited model
*/
private void writeEventMap() {
logger.log(Type.DEBUG, "Starting to write EventMap"); //$NON-NLS-1$
sw.println();
sw.println("@Override"); //$NON-NLS-1$
sw.println("protected " + UiCommonEventMap.class.getName() + " getEventMap() {"); //$NON-NLS-1$ //$NON-NLS-2$
sw.indent();
sw.println(UiCommonEventMap.class.getName() + " eventMap = new " + UiCommonEventMap.class.getName() + "();"); //$NON-NLS-1$ //$NON-NLS-2$
logger.log(Type.DEBUG, "Looking for Model Fields"); //$NON-NLS-1$
for (EditorData editorData : model.getEditorData()) {
logger.log(Type.DEBUG, "Going over Field: " + editorData); //$NON-NLS-1$
String path = editorData.getPath();
if (path.length() == 0) {
continue;
}
JClassType propertyOwnerType = eraseType(editorData.getPropertyOwnerType());
if (propertyOwnerType == entityModelType) {
logger.log(Type.DEBUG, "Found EntityModel Field: " + editorData); //$NON-NLS-1$
sw.println("eventMap.addEvent(\"%s\", \"EntityChanged\", getObject()%s.getEntityChangedEvent());", //$NON-NLS-1$
path, editorData.getBeanOwnerExpression());
} else if (propertyOwnerType == listModelType) {
logger.log(Type.DEBUG, "Found ListModel Field: " + editorData); //$NON-NLS-1$
sw.println("eventMap.addEvent(\"%s\", \"ItemsChanged\", getObject()%s.getItemsChangedEvent());", //$NON-NLS-1$
path, editorData.getBeanOwnerExpression());
sw.println("eventMap.addEvent(\"%s\", \"SelectedItemsChanged\", getObject()%s.getSelectedItemsChangedEvent());", //$NON-NLS-1$
path,
editorData.getBeanOwnerExpression());
sw.println("eventMap.addEvent(\"%s\", \"SelectedItemChanged\", getObject()%s.getSelectedItemChangedEvent());", //$NON-NLS-1$
path,
editorData.getBeanOwnerExpression());
}
}
sw.println("return eventMap;"); //$NON-NLS-1$
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
/**
* Writes the map of the owner Models
*/
private void writeOwnerModels() {
logger.log(Type.DEBUG, "Starting to write OwnerModels"); //$NON-NLS-1$
sw.println();
sw.println("@Override"); //$NON-NLS-1$
sw.println("protected java.util.Map<String, org.ovirt.engine.ui.uicommonweb.models.Model> getOwnerModels() {"); //$NON-NLS-1$
sw.indent();
sw.println("java.util.Map<String, org.ovirt.engine.ui.uicommonweb.models.Model> regs = new java.util.HashMap<String, org.ovirt.engine.ui.uicommonweb.models.Model>();"); //$NON-NLS-1$
logger.log(Type.DEBUG, "Going over Editor Fields"); //$NON-NLS-1$
for (EditorData editorData : model.getEditorData()) {
logger.log(Type.DEBUG, "Going over Field: " + editorData); //$NON-NLS-1$
String path = editorData.getPath();
if (path.length() == 0) {
continue;
}
JClassType propertyOwnerType = eraseType(editorData.getPropertyOwnerType());
if (propertyOwnerType == listModelType || propertyOwnerType == entityModelType) {
logger.log(Type.DEBUG, "Found owner Model Field: " + editorData); //$NON-NLS-1$
sw.println("regs.put(\"%s\", getObject()%s);", path, editorData.getBeanOwnerExpression()); //$NON-NLS-1$
}
}
sw.println("return regs;"); //$NON-NLS-1$
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
private void writeCleanup() {
logger.log(Type.DEBUG, "Starting to write cleanup impl. for editor " //$NON-NLS-1$
+ model.getEditorType().getQualifiedSourceName());
sw.println();
sw.println("@Override"); //$NON-NLS-1$
sw.println("public void cleanup() {"); //$NON-NLS-1$
sw.indent();
// 1. clean up the Editor instance
Set<String> editorFieldExpressions = getEditorFieldCleanupExpressions();
for (String expr : editorFieldExpressions) {
sw.println(String.format("if (%s != null) {", expr)); //$NON-NLS-1$
sw.indent();
sw.println(String.format("%s.cleanup();", expr)); //$NON-NLS-1$
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
// 2. clean up the edited Model object
Set<String> modelExpressions = getModelCleanupExpressions();
if (!modelExpressions.isEmpty()) {
sw.println("if (getObject() != null) {"); //$NON-NLS-1$
sw.indent();
for (String expr : modelExpressions) {
sw.println(String.format("%s.cleanup();", expr)); //$NON-NLS-1$
}
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
sw.outdent();
sw.println("}"); //$NON-NLS-1$
}
private Set<String> getModelCleanupExpressions() {
Set<String> result = new LinkedHashSet<>();
// top-level Model
if (model.getProxyType().isAssignableTo(hasCleanupType)) {
result.add("getObject()"); //$NON-NLS-1$
}
// all Models edited through the top-level Model
for (EditorData editorData : model.getEditorData()) {
if (editorData.getPropertyOwnerType().isAssignableTo(hasCleanupType)) {
result.add(String.format(
"getObject()%s", //$NON-NLS-1$
editorData.getBeanOwnerExpression()));
}
}
return result;
}
private Set<String> getEditorFieldCleanupExpressions() {
Set<String> result = new LinkedHashSet<>();
for (JClassType typeCandidate : model.getEditorType().getFlattenedSupertypeHierarchy()) {
JClassType classType = typeCandidate.isClass();
if (classType != null) {
for (JField field : classType.getFields()) {
JClassType fieldClassOrInterfaceType = field.getType().isClassOrInterface();
if (fieldClassOrInterfaceType != null
// field type assignable to HasCleanup ..
&& fieldClassOrInterfaceType.isAssignableTo(hasCleanupType)
// .. but not assignable to Model
&& !fieldClassOrInterfaceType.isAssignableTo(baseModelType)) {
result.add(String.format(
"getEditor().%s", //$NON-NLS-1$
field.getName()));
}
}
}
}
return result;
}
private JClassType eraseType(JClassType classType) {
return classType == null ? null : classType.getErasedType();
}
}