/*
* Copyright 2011 cruxframework.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.cruxframework.crux.gwt.rebind;
import java.util.Comparator;
import org.cruxframework.crux.core.client.utils.EscapeUtils;
import org.cruxframework.crux.core.client.utils.StringUtils;
import org.cruxframework.crux.core.rebind.AbstractProxyCreator.SourcePrinter;
import org.cruxframework.crux.core.rebind.CruxGeneratorException;
import org.cruxframework.crux.core.rebind.context.scanner.ResourceNotFoundException;
import org.cruxframework.crux.core.rebind.screen.Event;
import org.cruxframework.crux.core.rebind.screen.EventFactory;
import org.cruxframework.crux.core.rebind.screen.widget.EvtProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.WidgetCreatorContext;
import org.cruxframework.crux.core.rebind.screen.widget.creator.align.AlignmentAttributeParser;
import org.cruxframework.crux.core.rebind.screen.widget.creator.align.HorizontalAlignment;
import org.cruxframework.crux.core.rebind.screen.widget.creator.align.VerticalAlignment;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.ChoiceChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.HasPostProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor;
import org.cruxframework.crux.core.rebind.screen.widget.creator.children.WidgetChildProcessor.HTMLTag;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.DeclarativeFactory;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttribute;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributeDeclaration;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributes;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagAttributesDeclaration;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChild;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagChildren;
import org.cruxframework.crux.core.rebind.screen.widget.declarative.TagConstraints;
import org.cruxframework.crux.core.utils.JClassUtils;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.AsyncHandler;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.google.gwt.user.cellview.client.TextHeader;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.view.client.AbstractDataProvider;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.ListDataProvider;
class CellTableContext extends WidgetCreatorContext
{
String rowDataObject;
JClassType rowDataObjectType;
boolean asyncDataProvider;
String dataProvider = null;;
String header;
String footer;
String colDataObject;
String column;
JType colDataObjectType;
String columnExpression;
void clearColumnInformation()
{
header = null;
footer = null;
colDataObject = null;
colDataObjectType = null;
columnExpression = null;
column = null;
}
}
/**
* @author Thiago da Rosa de Bustamante
*
*/
@DeclarativeFactory(id="cellTable", library="gwt", targetWidget=CellTable.class)
@TagAttributes({
@TagAttribute(value="tableLayoutFixed", type=Boolean.class) //TODO RowStyles??
})
@TagAttributesDeclaration({
@TagAttributeDeclaration(value="dataProviderFactoryMethod", required=true, supportsDataBinding=false),
@TagAttributeDeclaration(value="autoLoad", type=Boolean.class)
})
@TagChildren({
@TagChild(value=CellTableFactory.ColumnsProcessor.class)
})
public class CellTableFactory extends AbstractHasDataFactory<CellTableContext>
{
private static final int DEFAULT_PAGE_SIZE = 15;
@Override
public void processAttributes(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
super.processAttributes(out, context);
context.rowDataObject = getDataObject(context.getChildElement());
context.rowDataObjectType = getContext().getGeneratorContext().getTypeOracle().findType(context.rowDataObject);
String dataProviderFactoryMethod = context.readChildProperty("dataProviderFactoryMethod");
if (!StringUtils.isEmpty(dataProviderFactoryMethod))
{
processDataProvider(out, context, dataProviderFactoryMethod);
}
}
private void processDataProvider(SourcePrinter out, CellTableContext context, String dataProviderFactoryMethod)
{
context.dataProvider = createVariableName("dataProvider");
out.print(AbstractDataProvider.class.getCanonicalName()+"<"+context.rowDataObject+"> " + context.dataProvider + " = ");
EvtProcessor.printEvtCall(out, dataProviderFactoryMethod, "loadDataProvider", (String)null, null, this);
out.println(context.dataProvider+".addDataDisplay("+context.getWidget()+");");
Event event = EventFactory.getEvent("loadDataProvider", dataProviderFactoryMethod);
String controller;
try
{
controller = getContext().getControllers().getController(event.getController(), getDevice());
}
catch (ResourceNotFoundException e)
{
throw new CruxGeneratorException("Can not found the controller ["+event.getController()+"]. Check your classpath and the inherit modules");
}
JClassType controllerClass = getContext().getGeneratorContext().getTypeOracle().findType(controller);
JMethod loadDataProviderMethod = JClassUtils.getMethod(controllerClass, event.getMethod(), new JType[]{});
if (loadDataProviderMethod == null)
{
throw new CruxGeneratorException("DataProvider factory method not found: Controller["+event.getController()+"], Method["+event.getMethod()+"].");
}
JType returnType = loadDataProviderMethod.getReturnType();
if (returnType instanceof JClassType)
{
context.asyncDataProvider =
((JClassType)returnType).isAssignableTo(getContext().getGeneratorContext().getTypeOracle().findType(AsyncDataProvider.class.getCanonicalName()));
}
else
{
context.asyncDataProvider = false;
}
}
@Override
public void postProcess(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String autoLoadProperty = context.readWidgetProperty("autoLoad");
if (!StringUtils.isEmpty(autoLoadProperty))
{
boolean autoLoad = Boolean.parseBoolean(autoLoadProperty);
if (autoLoad)
{
String pageSizeProperty = context.readWidgetProperty("pageSize");
int pageSize;
if (StringUtils.isEmpty(pageSizeProperty))
{
pageSize = DEFAULT_PAGE_SIZE;
}
else
{
pageSize = Integer.parseInt(pageSizeProperty);
}
out.println(context.getWidget()+".setPageSize("+pageSize+");");
}
}
}
@TagConstraints(tagName="column", minOccurs="1", maxOccurs="unbounded")
@TagAttributesDeclaration({
@TagAttributeDeclaration(value="sortable", type=Boolean.class),
@TagAttributeDeclaration(value="property", required=true),
@TagAttributeDeclaration(value="horizontalAlignment", type=HorizontalAlignment.class, defaultValue="defaultAlign"),
@TagAttributeDeclaration(value="verticalAlignment", type=VerticalAlignment.class),
@TagAttributeDeclaration("fieldUpdaterFactoryMethod"),
@TagAttributeDeclaration("width")
})
@TagChildren({
@TagChild(ColumnHeaderProcessor.class),
@TagChild(ColumnCellProcessor.class),
@TagChild(ColumnFooterProcessor.class)
})
public static class ColumnsProcessor extends WidgetChildProcessor<CellTableContext> implements HasPostProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String property = context.readChildProperty("property");
StringBuilder getValueExpression = new StringBuilder();
try
{
context.colDataObjectType = JClassUtils.buildGetValueExpression(getValueExpression, context.rowDataObjectType,
property, "object", true);
context.colDataObject = context.colDataObjectType.getParameterizedQualifiedSourceName();
context.columnExpression = getValueExpression.toString();
}
catch (NoSuchFieldException e)
{
throw new CruxGeneratorException("Can not access property ["+property+"] on row object["+context.rowDataObject+"].");
}
}
public void postProcessChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String columnWidth = context.readChildProperty("width");
if (!StringUtils.isEmpty(columnWidth))
{
out.println(context.getWidget()+".setColumnWidth("+context.column+", "+EscapeUtils.quote(columnWidth)+");");
}
String columnsortable = context.readChildProperty("sortable");
if (!StringUtils.isEmpty(columnsortable))
{
if (Boolean.parseBoolean(columnsortable))
{
setColumnSortable(out, context);
}
}
String columnHorizontalAlignment = context.readChildProperty("horizontalAlignment");
if (!StringUtils.isEmpty(columnHorizontalAlignment))
{
out.println(context.column+".setHorizontalAlignment("+
AlignmentAttributeParser.getHorizontalAlignment(columnHorizontalAlignment, HasHorizontalAlignment.class.getCanonicalName()+".ALIGN_DEFAULT")+");");
}
String columnVerticalAlignment = context.readChildProperty("verticalAlignment");
if (!StringUtils.isEmpty(columnVerticalAlignment))
{
out.println(context.column+".setVerticalAlignment("+AlignmentAttributeParser.getVerticalAlignment(columnVerticalAlignment)+");");
}
String columnFieldUpdaterFactoryMethod = context.readChildProperty("fieldUpdaterFactoryMethod");
if (!StringUtils.isEmpty(columnFieldUpdaterFactoryMethod))
{
String updater = getWidgetCreator().createVariableName("updater");
out.print(FieldUpdater.class.getCanonicalName()+"<"+context.rowDataObject+","+
context.colDataObject+"> "+ updater + " = ("+FieldUpdater.class.getCanonicalName()+"<"+context.rowDataObject+","+
context.colDataObject+">)");
EvtProcessor.printEvtCall(out, columnFieldUpdaterFactoryMethod, "loadFieldUpdater", (String)null, null, getWidgetCreator());
out.println(context.column+".setFieldUpdater("+updater+");");
}
generateAddColumnMethod(out, context);
context.clearColumnInformation();
}
private void generateAddColumnMethod(SourcePrinter out, CellTableContext context)
{
if (context.header != null)
{
if (context.footer != null)
{
out.println(context.getWidget()+".addColumn("+context.column+","+context.header+","+context.footer+");");
}
else
{
out.println(context.getWidget()+".addColumn("+context.column+","+context.header+",("+Header.class.getCanonicalName()+")null);");
}
}
else
{
if (context.footer != null)
{
out.println(context.getWidget()+".addColumn("+context.column+",("+Header.class.getCanonicalName()+")null,"+context.footer+");");
}
else
{
out.println(context.getWidget()+".addColumn("+context.column+");");
}
}
}
private void setColumnSortable(SourcePrinter out, CellTableContext context)
{
if (context.asyncDataProvider)
{
out.println(context.getWidget()+".addColumnSortHandler(new "+AsyncHandler.class.getCanonicalName()+"("+context.getWidget()+"));");
}
else
{
JClassType colType = context.colDataObjectType.isClassOrInterface();
String columnSortHandler = getWidgetCreator().createVariableName("sortHandler");
String listHandlerClassName = ListHandler.class.getCanonicalName()+"<"+context.rowDataObject+">";
out.println(listHandlerClassName+" "+columnSortHandler+" = new "+listHandlerClassName+"((("+
ListDataProvider.class.getCanonicalName()+"<"+context.rowDataObject+">)"+context.dataProvider+").getList());");
out.println(columnSortHandler+".setComparator("+context.column+", new "+Comparator.class.getCanonicalName()+"<"+context.rowDataObject+">() {");
out.println("public int compare("+context.rowDataObject+" o1, "+context.rowDataObject+" o2) {");
out.println("if (o1 == o2) {");
out.println("return 0;");
out.println("}");
out.println("if (o1 != null) {");
out.println("if (o2 != null){");
String property = context.readChildProperty("property");
try
{
if (colType != null && colType.isAssignableTo(getWidgetCreator().getContext().getGeneratorContext().getTypeOracle().findType(Comparable.class.getCanonicalName())))
{
StringBuilder getValueExpression = new StringBuilder();
JClassUtils.buildGetValueExpression(getValueExpression, context.rowDataObjectType, property, "o1", true);
out.println(Comparable.class.getCanonicalName()+" c1 = "+getValueExpression.toString());
getValueExpression = new StringBuilder();
JClassUtils.buildGetValueExpression(getValueExpression, context.rowDataObjectType, property, "o2", true);
out.println(Comparable.class.getCanonicalName()+" c2 = "+getValueExpression.toString());
out.println("if (c1 == c2) {");
out.println("return 0;");
out.println("}");
out.println("if (c1 != null) {");
out.println("return (c2 != null) ? c1.compareTo(c2) : 1;");
out.println("}");
out.println("return -1;");
}
else
{
JPrimitiveType primitive = context.colDataObjectType.isPrimitive();
if (primitive != null)
{
StringBuilder getValueExpression = new StringBuilder();
JClassUtils.buildGetValueExpression(getValueExpression, context.rowDataObjectType, property, "o1", true);
out.println(primitive.getSimpleSourceName()+" c1 = "+getValueExpression.toString());
getValueExpression = new StringBuilder();
JClassUtils.buildGetValueExpression(getValueExpression, context.rowDataObjectType, property, "o2", true);
out.println(primitive.getSimpleSourceName()+" c2 = "+getValueExpression.toString());
out.println("return (c1==c2) ? 0 : (c1<c2) ? -1 : 1;");
}
else
{
throw new CruxGeneratorException("Can not sort column for property ["+property+"] on row object["+context.rowDataObject+"]. Property must have a primitive or Comparable type.");
}
}
}
catch (NoSuchFieldException e)
{
throw new CruxGeneratorException("Can not access property ["+property+"] on row object["+context.rowDataObject+"].");
}
out.println("}");
out.println("return 1;");
out.println("}");
out.println("return -1;");
out.println("}");
out.println("});");
out.println(context.getWidget()+".addColumnSortHandler("+columnSortHandler+");");
}
out.println(context.getWidget()+".getColumnSortList().push("+context.column+");");
out.println(context.column+".setSortable(true);");
}
}
@TagConstraints(tagName="header", minOccurs="0")
@TagChildren({
@TagChild(ColumnHeaderChoiceProcessor.class)
})
public static class ColumnHeaderProcessor extends WidgetChildProcessor<CellTableContext> {}
@TagChildren({
@TagChild(TextColumnHeaderProcessor.class),
@TagChild(HTMLColumnHeaderProcessor.class),
@TagChild(CustomColumnHeaderProcessor.class)
})
public static class ColumnHeaderChoiceProcessor extends ChoiceChildProcessor<CellTableContext> {}
@TagConstraints(tagName="text", type=String.class)
public static class TextColumnHeaderProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String innerText = getWidgetCreator().ensureTextChild(context.getChildElement(),true, context.getWidgetId(), false);
context.header = "new "+TextHeader.class.getCanonicalName()+"("+innerText+")";
}
}
@TagConstraints(tagName="html", type=HTMLTag.class)
public static class HTMLColumnHeaderProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String innerText = getWidgetCreator().ensureHtmlChild(context.getChildElement(),true, context.getWidgetId(), context);
context.header = "new "+SafeHtmlHeader.class.getCanonicalName()+"("+innerText+")";
}
}
@TagConstraints(tagName="custom")
@TagAttributesDeclaration({
@TagAttributeDeclaration(value="factoryMethod", required=true)
})
public static class CustomColumnHeaderProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String factoryMethod = context.getChildElement().optString("factoryMethod");
assert (!StringUtils.isEmpty(factoryMethod));
String header = getWidgetCreator().createVariableName("header");
out.print(Header.class.getCanonicalName()+"<"+context.colDataObject+">"+" "+header+
"=("+Header.class.getCanonicalName()+"<"+context.colDataObject+">)");
EvtProcessor.printEvtCall(out, factoryMethod, "loadHeader", (String)null, null, getWidgetCreator());
context.header = header;
}
}
@TagConstraints(tagName="footer", minOccurs="0", maxOccurs="1")
@TagChildren({
@TagChild(ColumnFooterChoiceProcessor.class)
})
public static class ColumnFooterProcessor extends WidgetChildProcessor<CellTableContext> {}
@TagChildren({
@TagChild(TextColumnFooterProcessor.class),
@TagChild(HTMLColumnFooterProcessor.class),
@TagChild(CustomColumnFooterProcessor.class)
})
public static class ColumnFooterChoiceProcessor extends ChoiceChildProcessor<CellTableContext> {}
@TagConstraints(tagName="text", type=String.class)
public static class TextColumnFooterProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String innerText = getWidgetCreator().ensureTextChild(context.getChildElement(),true, context.getWidgetId(), false);
context.footer = "new "+TextHeader.class.getCanonicalName()+"("+innerText+")";
}
}
@TagConstraints(tagName="html", type=HTMLTag.class)
public static class HTMLColumnFooterProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String innerHTML = getWidgetCreator().ensureHtmlChild(context.getChildElement(),true, context.getWidgetId(), context);
context.footer = "new "+SafeHtmlHeader.class.getCanonicalName()+"("+innerHTML+")";
}
}
@TagConstraints(tagName="custom")
@TagAttributesDeclaration({
@TagAttributeDeclaration(value="factoryMethod", required=true)
})
public static class CustomColumnFooterProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String factoryMethod = context.getChildElement().optString("factoryMethod");
assert (!StringUtils.isEmpty(factoryMethod));
String footer = getWidgetCreator().createVariableName("footer");
out.print(Header.class.getCanonicalName()+"<"+context.colDataObject+">"+" "+footer+
"=("+Header.class.getCanonicalName()+"<"+context.colDataObject+">)");
EvtProcessor.printEvtCall(out, factoryMethod, "loadFooter", (String)null, null, getWidgetCreator());
context.footer = footer;
}
}
@TagConstraints(tagName="cell")
@TagChildren({
@TagChild(value=CellListChildProcessor.class, autoProcess=false)
})
public static class ColumnCellProcessor extends WidgetChildProcessor<CellTableContext>
{
@Override
public void processChildren(SourcePrinter out, CellTableContext context) throws CruxGeneratorException
{
String cell = ((CellTableFactory)getWidgetCreator()).getCell(out, context.getChildElement(), context.getWidgetId());
String column = getWidgetCreator().createVariableName("column");
String colDataObject = context.colDataObject;
if (context.colDataObjectType.isPrimitive() != null)
{
colDataObject = context.colDataObjectType.isPrimitive().getQualifiedBoxedSourceName();
}
out.println(Column.class.getCanonicalName()+"<"+context.rowDataObject+","+colDataObject+">"+" "+column+
"=new "+Column.class.getCanonicalName()+"<"+context.rowDataObject+","+colDataObject+">("+cell+"){");
out.println("public "+colDataObject+" getValue("+context.rowDataObject+" object){");
out.println("return "+context.columnExpression);
out.println("}");
out.println("};");
context.column = column;
}
}
@Override
public CellTableContext instantiateContext()
{
return new CellTableContext();
}
}