Monday, September 30, 2013

Injecting DBDriver/Logger, AOP in Dart

In order to have GUI component, each component should not have a direct dependency to a specific driver. For instance, in the sample app, AppController has reference to CouchDBClientDao, but we if want to allow other DB driver later, that should not be directly defined in the component class.
This issue can be handled in Java as annotation based injection. JPA entity factory etc can be injected through annotation.

In order to support this, there will be a few approaches.
One approach is to simulate Java's approach. but it requires a special run time lib support for actual injection. So these are actually not within an ordinal programming control, and create some opaqueness for the code behavior, so I actually do not like this idea much.

The other approach is to delegate this injection job to code(programmer) itself. Anyway entry program needs to do some environment setting job normally, adding a task to inject code to appropriate library is not much big deal. and it will allows programmer 100% control over what is happening in program execution time.

then how can we find the location of the class(instance) to inject db driver.
sometimes that component are in deeply nested from top level component node.

That time we may use query feature defined at Component class.
This approach is similar to query in Dart.
To inject to annotated filed for an instance, whenever instance is created, this injection code must be applied. So instance level injection is a bit costly operation.

Or the other hand, static class level injection can be done only once, so this should be efficient.
for instance, dbdriver, logger can be injected to a  static filed.



Saturday, September 28, 2013

Generic GUI Library in Dart (Part 3)

I could run the generic GUI lib application on browsers(in javascript mode).
Since Dart does not support mirror in JavaScript mode, I needed to do this kind of things(see previous post).
The following snapshot shows running the app on Dartium , Chrome and Firefox:


In order to achieve this, I re-factored the code so that it eliminates the direct reference to 'dart:mirrors' by introducing minimal mirror interfaces.

 library mirror_api;

typedef IClassMirror CMirrorFun(Type type);

class ClassMirrorFactory {
  static CMirrorFun _cmf;
  static IClassMirror create(Type type) => _cmf(type);
  static void register(CMirrorFun cmf) { _cmf = cmf; }
}

//
abstract class IClassMirror {
  IInstanceMirror newInstance();
  IInstanceMirror reflect(Object obj);
 
  Map<Symbol, IFieldType> get fieldTypes;
}

abstract class IFieldType {
  Symbol get symbol;
  String get name; // convert symol to string
  Type get type;
}

//
abstract class IInstanceMirror {
  IClassMirror get cmirror;
  Object get reflectee;
 
  IField getField(Symbol name);
}

abstract class IField {
  Symbol get symbol;
  String get name; // convert symol to string
 
  Object get value;
  void set value(Object obj);
 
  Type get type;
}
 Then I provided two implementations of this interfaces:
  •  mirror_dynamic_lib.dart
  •  mirror_static_lib.dart
 mirror_dynamic_lib will use 'dart:mirrors', so this will be useful to run on server side, and no extra information are required to use this lib.
the second one is for javascript mode, it require additional meta  information for the class which is to be used in a generic class.
Following dart file is to provide such information for Expense class.  As shown in the last part, there is a dart2js compiler bug, I could not use the simpler version(commented one), but if we use this code, I could avoid the compiler bug(compiler was crashed).(this bug was fixed after I reported this issue in rev 28044)

library sample_mirror;

import 'mirror_libs/mirror_api_lib.dart';
import 'mirror_libs/mirror_static_lib.dart';
import 'shared/models.dart';

/*
 * This must be invoked before calling any of these simple mirror APIs.
 */

void initClassMirrorFactory() {
  ClassMirrorFactory.register((Type type)=>(type == Expense)?
      new StaticClassMirror(()=>new Expense.Default(), expenseFieldInfo)
  :null);
}

Type _String = String;
Type _ExpenseType = ExpenseType;
Type _DateTime = DateTime;
Type _num = num;
Type _bool = bool;

Map<Symbol, FieldInfo> expenseFieldInfo =
[
 new FieldInfo(const Symbol('id'), _String, (Expense e)=>e.id, (Expense e, String value) { e.id = value; }),
 new FieldInfo(const Symbol('rev'), _String, (Expense e)=>e.rev, (Expense e, String value) { e.rev = value; }),
 new FieldInfo(const Symbol('expenseType'), _ExpenseType, (Expense e)=>e.expenseType, (Expense e, ExpenseType value) { e.expenseType = value; }),
 new FieldInfo(const Symbol('date'), _DateTime, (Expense e)=>e.date, (Expense e, DateTime value) { e.date = value; }),
 new FieldInfo(const Symbol('amount'), _num, (Expense e)=>e.amount, (Expense e, num value) { e.amount = value; }),
 new FieldInfo(const Symbol('detail'), _String, (Expense e)=>e.detail, (Expense e, String value) { e.detail = value; }),
 new FieldInfo(const Symbol('isClaimed'), _bool, (Expense e)=>e.isClaimed, (Expense e, bool value) { e.isClaimed = value; })
 ].fold({}, (Map map, FieldInfo fh) => map..[fh.symbol] = fh);

/*
//
// This works for DartVM, but it fails dart2js.
//
Map<Symbol, FieldInfo> expenseFieldInfo =
    [
     new FieldInfo(const Symbol('id'), String, (Expense e)=>e.id, (Expense e, String value) { e.id = value; }),
     new FieldInfo(const Symbol('rev'), String, (Expense e)=>e.rev, (Expense e, String value) { e.rev = value; }),
     new FieldInfo(const Symbol('expenseType'), ExpenseType, (Expense e)=>e.expenseType, (Expense e, ExpenseType value) { e.expenseType = value; }),
     new FieldInfo(const Symbol('date'), DateTime, (Expense e)=>e.date, (Expense e, DateTime value) { e.date = value; }),
     new FieldInfo(const Symbol('amount'), num, (Expense e)=>e.amount, (Expense e, num value) { e.amount = value; }),
     new FieldInfo(const Symbol('detail'), String, (Expense e)=>e.detail, (Expense e, String value) { e.detail = value; }),
     new FieldInfo(const Symbol('isClaimed'), bool, (Expense e)=>e.isClaimed, (Expense e, bool value) { e.isClaimed = value; })
     ].fold({}, (Map map, FieldInfo fh) => map..[fh.symbol] = fh);
*/
From the top level dart file, we can change the  library by just switching to import file as following. I'll attach the entire main dart file for this app. this is  all required codes  to run this application using the generic GUI library. the layout of several components like table, buttons etc, and the controlling logic  are defined here.


 library sample_generic_gui;

import 'dart:html';
import "shared/models.dart";
import "gui_generic_lib/gui_generic_lib.dart";
//import "mirror_libs/mirror_dynamic_lib.dart"; // <== use 'dart:mirrors'
import "sample_mirror_impl.dart";; // <== no use of 'dart:mirrors'

class AppController extends Component {
  static const String APP = "g_app_ctlr";
  final DivElement _uiRoot;
  DivElement _content;
  DivElement _actions;
 
  Table<Expense> table;
  Form<Expense> form;
  ButtonComp newButtom;
  ButtonComp saveButtom;
  ButtonComp deleteButtom;
 
  AppController(Component parent, this._uiRoot, {List<Expense> expenses}): super(parent, const[APP]) {
    table = new Table<Expense>.fromModelType(this, Expense, formatFunctionMap: {ExpenseType: ExpenseTypeComp.format});
    form = new Form<Expense>(this, Expense,
        specialInputCompMap: {ExpenseType: ExpenseTypeComp.inputCompFactory},
        adhocCompMap: {'detail': 'textarea'}  // temp
    );
  
    Expense e = null;
    newButtom = new ButtonComp(this, "New", (_) {
      e = form.create();
      _changeButtonState(false, create: true);
    
    });
    saveButtom = new ButtonComp(this, "Save", (_) {
      Expense e0 = form.save();
      if (e0 != null) {
        e = e0;
        table.addOrUpdate(e);
        _changeButtonState(true);
      }
    });
    deleteButtom = new ButtonComp(this, "Delete", (_) {
      Expense e = form.delete();
      if (e != null) {
        table.delete(e);
        _changeButtonState(true);
      }
    });
    _changeButtonState(true);
  
  
   table.row_listeners.add((ev, row){
      //print("row clicked.. ${ev}, row: ${row}");
      e = row.e;
      form.load(e);
      _changeButtonState(false);
    });
  
    if (expenses != null) {
      table.load(expenses);
    }
    _uiRoot.nodes.add(element); // this 'element' tiggers DOM node creation!
  }
 
  void _changeButtonState(bool isEmpty, {create: false}) {
    (newButtom.element as ButtonElement).disabled = !isEmpty;
    (saveButtom.element as ButtonElement).disabled = isEmpty;
    (deleteButtom.element as ButtonElement).disabled = (create)?true:isEmpty;  
  }

  Element createElement() => addSubComponents0(newElem("div"));
 
  Element update() => addSubComponents0(initElem());
    
  Element addSubComponents0(Element elm) => addListeners(
      elm
      ..classes.add("section")
      ..nodes.add(new Element.html("<header class='section'>${table.modelType} Table</header>"))
      ..nodes.add(_content = new Element.tag("div")
        ..nodes.add(table.element))
      ..nodes.add(_actions = new Element.tag("div")
        ..id = "actions"
        ..classes.add("section")
        ..nodes.add(newButtom.element)
        ..nodes.add(saveButtom.element)
        ..nodes.add(deleteButtom.element))
      ..nodes.add(form.element)
      ..nodes.add(new Element.html("<footer class='section' id='footer'></footer>")));
}

class ExpenseTypeComp extends SelectComp<ExpenseType> {
  static const String EXPENSE_TYPE_INPUT = "g_expense_type_input";
 
  static final Map<String, ExpenseType> _expenseTypes = {
    "TRV": const ExpenseType("Travel","TRV"),
    "BK": const ExpenseType("Books","BK"),
    "HT": const ExpenseType("Hotel","HT")                        
  };
 
  ExpenseTypeComp(Component parent, String label, {List<String> classes: const [SelectComp.SELECT_INPUT, EXPENSE_TYPE_INPUT]})
    : super(parent, label, ExpenseType, classes: classes);
 
  static ExpenseTypeComp inputCompFactory(Component c, String name, Type t)=>new ExpenseTypeComp(c, name);
  static String format(ExpenseType et) => (et == null)?'':et.name;
 
  Map<String, ExpenseType> get entityTypes => _expenseTypes;
 
  String getCode(ExpenseType et) => et.code;
  String getName(ExpenseType et) => et.name;
}

main() {
  // register reflection factory
  initClassMirrorFactory();
 
  List<Expense> expenses = [
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random()];
  Element uiContainer = document.query("#sample_generic_gui");
  AppController app = new AppController(null, uiContainer);
  app.table.load(expenses);
-------------------------------

here is the implementation classes:

library dynamic_mirror;

import 'dart:mirrors';
import 'mirror_api_lib.dart';

/*
 * This must be invoked before calling any of these simple mirror APIs.
 */
void initClassMirrorFactory() {
  ClassMirrorFactory.register((Type type)=>new DynamicClassMirror.reflectClass(type));
}

//
// IClassMirror implementations
//
class DynamicClassMirror implements IClassMirror {
  static Map<Type, DynamicClassMirror>  cmirrs = {};
 
  final Type type;
  ClassMirror _cmirror;
  MethodMirror _ctor;
  Map<Symbol, IFieldType> _fieldTypes = {};
 
  DynamicClassMirror(this.type) {
    _cmirror = reflectClass(type);
   
    reflectClass(type).constructors.forEach((k, v){
      if (v.parameters.length == 0 && (_ctor == null || getSymbolName(k) == "${type}.Default")) {
        _ctor = v;
      }
    });
   
    _cmirror.getters.forEach((Symbol symbol, MethodMirror md){
      _fieldTypes[symbol] = new DynamicFieldType(symbol, md);
    });
  }
 
  factory DynamicClassMirror.reflectClass(Type type) {
    DynamicClassMirror cmirr = cmirrs[type];
    if (cmirr == null) {
      cmirr = new DynamicClassMirror(type);
    }
    return cmirr;
  }
 
  IInstanceMirror newInstance() =>
      reflect(_cmirror.newInstance(_ctor.constructorName, []).reflectee);

  IInstanceMirror reflect(Object obj) => new DynamicInstanceMirror(this, obj);

  Map<Symbol, IFieldType> get fieldTypes => _fieldTypes;

}

class DynamicFieldType implements IFieldType {
  Symbol _symbol;
  String _name;
  MethodMirror _md;
 
  DynamicFieldType(this._symbol, this._md) {
    _name = getSymbolName(_symbol);
  }
 
  Symbol get symbol => _symbol;
  String get name => _name;
  Type get type => (_md.returnType as ClassMirror).reflectedType;
}

//
// IInstanceMirror implementations
//
class DynamicInstanceMirror implements IInstanceMirror {
  Map<Symbol, DynamicField>  dfs = {};
 
  final IClassMirror _cmirror;
  InstanceMirror _imirror;
 
  DynamicInstanceMirror(this._cmirror, Object obj) {
    _imirror = reflect(obj);
  }
 
  IClassMirror get cmirror => _cmirror;
 
  Object get reflectee => _imirror.reflectee;
  IField getField(Symbol name) => new DynamicField.create(name, this);
}

class DynamicField implements IField {
  DynamicInstanceMirror _parent;
  Symbol _symbol;
  String _name;
 
  DynamicField(Symbol this._symbol, this._parent) {
    _name = getSymbolName(_symbol);
  }
 
  factory DynamicField.create(Symbol symbol, DynamicInstanceMirror _parent) {
    DynamicField df = _parent.dfs[symbol];
    if (df == null) {
      _parent.dfs[symbol] = df = new DynamicField(symbol, _parent);
    }
    return df;
  }
 
  Symbol get symbol => _symbol;
  String get name => _name;
 
  Object get value => _parent._imirror.getField(_symbol).reflectee;
  void set value(Object obj) { _parent._imirror.setField(_symbol, obj); }
 
  Type get type => _parent._cmirror.fieldTypes[_symbol].type;
 }

//
// utils
//
String getSymbolName(Symbol symbol) => symbol.toString().substring('Symbol("'.length, symbol.toString().length-2);
 ----------------------------
 library static_mirror;

import 'mirror_api_lib.dart';

//
// IClassMirror implementations
//
typedef Object Getter(Object entity);
typedef void Setter(Object entity, Object value);

class FieldInfo implements IFieldType {
  final Symbol _symbol;
  String _name;
  final Type _type;
 
  final Getter getter;
  final Setter setter;
 
  FieldInfo(this._symbol, this._type, this.getter, this.setter) {
    _name = getSymbolName(_symbol);
  }
 
  Symbol get symbol => _symbol;
  String get name => _name;
  Type get type => _type;
}

typedef Object DefaultConstructor();

class StaticClassMirror implements IClassMirror {
  final DefaultConstructor ctor;
  final Map<Symbol, FieldInfo> fieldInfos;
 
  StaticClassMirror(this.ctor, this.fieldInfos);
 
  IInstanceMirror newInstance() => reflect(ctor());
  IInstanceMirror reflect(Object obj) => new StaticInstanceMirror(this, obj);

  Map<Symbol, IFieldType> get fieldTypes => fieldInfos;
}

//
// IInstanceMirror implementations
//
class StaticInstanceMirror implements IInstanceMirror {
  Map<Symbol, StaticField>  dfs = {};
 
  final IClassMirror _cmirror;
 
  final Object _obj;
 
  StaticInstanceMirror(this._cmirror, Object this._obj);
 
  IClassMirror get cmirror => _cmirror;
 
  Object get reflectee => _obj;
  IField getField(Symbol name) => new StaticField.create(name, this);
}

class StaticField implements IField {
  StaticInstanceMirror _parent;
  Symbol _symbol;
  String _name;
 
  StaticField(Symbol this._symbol, this._parent) {
    _name = getSymbolName(_symbol);
  }
 
  factory StaticField.create(Symbol symbol, StaticInstanceMirror _parent) {
    StaticField df = _parent.dfs[symbol];
    if (df == null) {
      _parent.dfs[symbol] = df = new StaticField(symbol, _parent);
    }
    return df;
  }
 
  Symbol get symbol => _symbol;
  String get name => _name;
 
  Object get value => _cmirror.fieldInfos[symbol].getter(_parent.reflectee);
 
  void set value(Object obj) { _cmirror.fieldInfos[symbol].setter(_parent.reflectee, obj); }
 
  Type get type => _cmirror.fieldInfos[symbol].type;
 
  StaticClassMirror get _cmirror => (_parent._cmirror as StaticClassMirror);
 }

//
// utils
//
String getSymbolName(Symbol symbol) => symbol.toString().substring('Symbol("'.length, symbol.toString().length-2);

Entity annotations for GUI/Persistence

It is useful to use annotations to excludes hints information from the main source code and to avoid cluttering files which are used in program but not integrated in language source code file system.
Such files will become maintenance problem later.

There are mainly two usage cases for entity class.
  • GUI annotation. for instance, we may want to use customized label name other than default field name, we can attach such information as annotation for the getter definition.
  • Persistence mapping. This is essentially the same as JPA. we may want to more info how OR or DB scheme is associated with the entity.


CouchDB and generic DAO class

Generic GUI and CouchDB will be a good match.
Since Couch does not require scheme definition, it will be possible to develop generic  class to support CRUD features.

class DAO<T> {
 void  save(T t);
 void update(T t);
void delete(T t);
Iterable<T> query(String condition);

}

In particular, such application will be running on server side, we will not have constraint using mirror.

Actually I already created such library for DartExpenseServer mentioned in earlier post.
One of the issue was to support a mapping to/from Json object for the entity class.
In the application, this mapping methods were hand coded. But it is desireble to automate this step by a library.

Such Library should be easily implemented with mirror.

Restriction of Mirror in JS mode

I almost finished developing CRUD features for generic GUI library.



It just consists from Table and Form, and additional supporting components for Input.
Although there are more rooms for refinements, but it's working and written as I hoped.

The size of code is quite small, and is general enough.
For this type of library to be meaningful, it must be easily customized without touching the library itself.
In order to do so, it provides mechanism to inject features from outside.

Also it defines some basic patterns, like create, update to follow so that recursive operation would work properly.

But when I tried running it with javascript mode, I found out it does not run.
It looks like such mirror features are not yet supported in javascript mode.(there is a note on 5/2013).

Also there is an issue for the generated javascript code.
if we include dart:mirrors, the current code generates  7238 total methods!(impressive, I will never able to write such lines(methods) of javascript code)

Out of it,  5318 methods retained for use by dart:mirrors.
(But still my codes generated 2000 methods!)

So it seems not good idea to use dart:mirror for javascript mode.

One of the approach to overcome this restriction, and run generic GUI library application on browser is to avoid using mirror library itself.
This can be done if we generate an implementation class for some mirror like API  from concrete classes which is used in a application.

And we can also use two versions of implementations, one is for Dartium mode using the mirror library,  and the other is for javascript mode, the implementation classes are to be generated by a Dart application in Dartium mode.

Sunday, September 22, 2013

Generic GUI Library in Dart (Part 2)

In the Part 1 of this post, I used Polymer to create the web page, or to construct the page DOM object.(here is Part3)
In this revised version(generic-gui-v1), I removed such dependency to Polymer, and directly created DOM in Dart classes.



The major reason for doing this was to support component style GUI development.
In Polymer, or some other template mechanism based on some extension of HTML, the logic associated with such GUI element and actual code which would be implemented in a Dart class are separated, and basically things are getting messy.

Component will keep such DOM creation logic in itself. therefore we can construct a new component using the DOM associated with the sub components. This is the power of integrating them rather than delegating to a separate template files.

Another idea introduced here is automatic component management.
when components are created, implicitly corresponding tree node structure is created. This will provide better Query feature against components.
Although DOM can be found by Dart's query method for DOM, but we cannot identify a component associated with the DOM node. So this should be very useful.
On the other hand, if we have a component, we can have the corresponding DOM node immediately.

Following Component and TopComponent classes were introduced to support such abstract component structures.
The Component class will be used as super class of all UI component class like Table, Row etc.
It contains DOM element and its sub components.

The management of these structure are almost automatic, so each component class need not have to aware of these underlying structure building.

Fine point is the order of component creation and Element creation. To create a component, the parent component must be created first, but in order to create a DOM element, we need to create from the children.

So this means we cannot create element inside of component constructor.
But this will be taken care by init method in TopComponent. Namely this class is top level component, and only for this object, init is supported. The constructor of the subclass of TopComponent should invoke init in the end of the constructor.

 /*
 * Component should be created from top down, while DOM node should be created from bottom up
 */

abstract class Component {
  static int _gid = 0;
 
  final int id = _gid++;
  final List<String> classes;
  Element _element;
 
  final Component parent;
  final List<Component> children = [];
 
  Component(Component this.parent, List<String> this.classes) {
    if (parent != null) parent.children.add(this);
  }
 
  // DOM
  Element get element => (_element == null)?_element = _createElement():_element;
   
  // invoked from top level node.
  void _init() {
    children.forEach((Component child)=>child._init());
    _element = _createElement();
  }

  Element _createElement() =>
    createElement()
      ..id = "id-${id}"
      ..classes.addAll(classes);

  // this should not be invoked execpt from _init!
  Element createElement();
 
  //
  // utils
  //

  TopComponent topComponent() =>  (parent == null)?(this as TopComponent):parent.topComponent();
 
  Component findById(int id0) {
    if (id0 == id) {
      return this;
    }
    for (Component c in children) {
      Component c0 = c.findById(id0);
      if (c0 != null) {
        return c0;
      }
    }
    return null;
  }
 
  dynamic fold(var initialValue, dynamic combine(var previousValue, Component child)) =>
    children.fold(combine(initialValue, this), (pr, child) => child.fold(pr, combine));

}

abstract class TopComponent extends Component {
  static const String TOP_NODE = "g_top";
 
  TopComponent(): super(null, [TOP_NODE]);
 
  void init() {
    _init();
  }
}
The Table classes have been revised as followings:

class HeaderCell extends Component {
  static const String HCELL = "g_hcell";
  TableHeader tableHeader;
  Symbol symbol;
  Type type;
 
  // GUI
  String label;
 
  HeaderCell(TableHeader parent, this.symbol, this.type, this.label, {List<String> classes: const [HCELL]}): super(parent, classes), this.tableHeader = parent;
 
  factory HeaderCell.fromSymbol(TableHeader tableHeader, Symbol symbol, Type type) =>
    new HeaderCell(tableHeader, symbol, type, MirrorSystem.getName(symbol));
 
  RowCell defaultRowCell(Row row) => new RowCell(row, this);
 
  // DOM
  Element createElement() => new Element.td()..text = label;
}

class RowCell extends Component {
  static const String RCELL = "g_rcell";
  HeaderCell headerCell;
  Object value = null;
 
  RowCell(Row parent, this.headerCell, {Object init, List<String> classes : const [RCELL]}): super(parent, classes) {
    value = init;
  }
 
  // DOM
  Element createElement() => new Element.td()..text = (value == null)?'':value.toString();
}

class TableHeader<E> extends Component {
  static const String TH = "g_th";
  final Table<E> table;
  final List<HeaderCell> headerCells;
 
  TableHeader(Table parent, this.headerCells, {List<String> classes: const [TH]}): super(parent, classes), table = parent;
 
  factory TableHeader.fromType(Table parent, Type modelType) {
    var th = new TableHeader(parent, []);
    reflectClass(modelType).getters.forEach((Symbol symbol, MethodMirror md){
      //print(">> symbol: ${symbol}, returnType: ${(md.returnType as ClassMirror).reflectedType}");
      th.headerCells.add(new HeaderCell.fromSymbol(th, symbol, (md.returnType as ClassMirror).reflectedType));
    });
    return th;
  }
 
  // DOM
  Element createElement() => headerCells.fold(new Element.tag("thead"), (Element thead, HeaderCell hc)=>thead..nodes.add(hc.element));
}

class Row<E> extends Component {
  static const String TR = "g_tr";
  final Table<E> table;
  final E e;
  final List<RowCell> rowCells = [];

  Row(Table parent, this.e, {List<String> classes: const [TR]}): super(parent, classes), table = parent;
   
  factory Row.defaultRow(Table<E> _table) =>
    table.theader.headerCells.fold(new Row(table, null), (Row row, HeaderCell hc) => row..rowCells.add(hc.defaultRowCell(row)));
 
  factory Row.fromEntity(Table<E> table, E e) {
    InstanceMirror imirr = reflect(e);
    return table.theader.headerCells.fold(new Row(table, e), (Row row, HeaderCell hc) => row..rowCells.add(new RowCell(row, hc, init: imirr.getField(hc.symbol).reflectee)));
  }
 
  // DOM
  TableRowElement createElement() => rowCells.fold(new Element.tag("tr"), (TableRowElement row, RowCell rc)=>row..nodes.add(rc.element));
}

class Table<E> extends Component {
  static const String TABLE = "g_table";
  Type modelType;
  String name;
  TableHeader theader;
  final List<Row<E>> rows;
 
  Table(Component parent, this.modelType, {List<String>  classes: const [TABLE]}): super(parent, classes), rows = [];
 
  factory Table.fromModelType(Component parent, Type modelType) =>
    [new Table(parent, modelType)].fold(null, (p, tbl)=>tbl..theader = new TableHeader.fromType(tbl, modelType)); 
 
  //
  void newRow() => rows.add(new Row<E>.defaultRow(this));
 
  void addRow(Row<E> row) {
    // check row.tabel == this
    if (row.table != this) {
      print(">> addRow error");
      throw new Exception("Table<1>");
    }
    rows.add(row);
    element.nodes.add(row.element);
  }
 
  void addRowFromEntity(E e) => addRow(new Row<E>.fromEntity(this, e));
 
  void clear() {
    element.nodes.clear();
    element.nodes.add(theader.element);
    rows.clear();
  }
 
  void load(Iterable<E> es) {
    clear();
    es.forEach(addRowFromEntity);
  }
 
  // DOM
  Element createElement() => rows.fold(new Element.tag("table"), (Element tab, Row row)=>tab..nodes.add(row.element));
}
This is the sample application using these library.
 class AppController extends TopComponent {
  final DivElement _uiRoot;
  DivElement _content;
  DivElement _actions;
 
  Table<Expense> _table;
 
  AppController(this._uiRoot, {List<Expense> expenses}) {
    _table = new Table<Expense>.fromModelType(this, Expense);
    if (expenses != null) {
      _table.load(expenses);
    }
    init();
    _uiRoot.nodes.add(element);
  }

  Element createElement() =>
    new Element.tag("div")
      ..classes.add("section")
      ..nodes.add(new Element.html("<header class='section'>Generic Table</header>"))
      ..nodes.add(_content = new Element.tag("div")
        ..nodes.add(_table.element))
      ..nodes.add(_actions = new Element.tag("div")
        ..id = "actions"
        ..classes.add("section"))
      ..nodes.add(new Element.html("<footer class='section' id='footer'></footer>"));
 
  Table<Expense> get table => _table;
}

main() {
  List<Expense> expenses = [
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random()];
  Element uiContainer = document.query("#generic_table");
  AppController app = new AppController(uiContainer);
  app.table.load(expenses);

}
 The html file is very simple.
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample app</title>
    <link rel="stylesheet" href="generic_table.css">
  </head>
  <body>
    <div id="generic_table"></div>
    <script type="application/dart" src="generic_table.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>


Saturday, September 21, 2013

Generic GUI library in Dart (Part 1)

Most of Web application depend on  table view, and edit view.
Often these are related to database application.

These views are used to support CRUD operation.
The table view is essentially for query, to find a data.
The editor view is to create a new entity or edit existing entity.
Often these entities  reside in a remote database.

To achieve these thing, like Ruby on Rails, often code generation approach has been used. But this approach is not component oriented since generating code make it difficult to reuse codes

The better approach would be to develop generic GUI library.

The reason why this has not been used had several reasons coming from language short coming.

In order to support this approach, following language features are essential.
  • Class
  • Generics
  • Reflection
  • Annotation
  • direct DOM manipulation
Essentially, Java cannot  support direct DOM manipulation, and JavaScript doe not have all of class related feature as standard language feature.

So only Dart can support all of this.
In Java, it might be possible to use Pivot and running with Applet on browser.
But applet is not popular these days, and if we have more suitable language for web application, this approach is not so attractive. The Dart's concise syntax, and server side usage make it much simpler to develop web application than Java.

So I developed  first such application(generic-table-v1) in Dart.
Since this is rather primitive level of implementation, it will be easier to understand the basic of this approach.
In order to add more feature, it will be better not to rely on polymer.
These improvement will be the next step.

The following short code is all we need to have a class specific table view.
 main() {
  List<Expense> expenses = [
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random(),
                            new Expense.random()];
  Table table = new Table.fromModelType(Expense)
    ..load(expenses)
    ..newRow()
    ;
 
  query('#tmpl').model = table;
}
and I used polymer to create corresponding template:
  <body>
    <h1>Generic table</h1>
    <template id="tmpl" bind>
      <table border="1"> 
        <tr>    
          <th template repeat="{{ theader.headerCells }}">
            {{label}}
          </th> 
        </tr>         
        <tr template repeat="{{ rows }}">
          <td template repeat="{{ rowCells }}"><input type="text" value="{{value}}"></td>
        </tr>
      </table>
    </template> 
    <script type="application/dart" src="generic_table.dart"></script>
  </body>
This is the shapshot of this table.


The point of this approach is if we have some entity class like Expense, we just need to pass this class to have different Table view.

A specific table view associated with an entity class is  implemented by the same generic class. So no code generation are required.
Even if we have 500 of entity tables, we still don't need to generate 500 of JSP files or something similar. just a single generic class can handle all of these cases.
This would reduce the build time as well as code size.

But the main advantage of this approach  is to allow developing component based GUI library.  If properly designed, subcomponents of table, like row, cell may be specialized using separate GUI libraries.
Reusing code will be as usual as normal software development.

This Table class was written to prove the feasibility of generic GUI approach in Dart,  but there are more potentials in this approach to enhance sophisticated interaction between GUI DOM node operations,  entities and GUI views. Also these can be combined with backend server as well.

 Since the code is still manageable size, I will show all the codes.
 library generic_gui;

import 'dart:mirrors';


class Cell {
}


class HeaderCell extends Cell {

  TableHeader tableHeader;
  String label;
  Type type;
  Symbol symbol;
 
  HeaderCell(this.tableHeader, this.symbol, this.type, this.label);
 
  factory HeaderCell.fromSymbol(TableHeader tableHeader, Symbol symbol, Type type) =>
    new HeaderCell(tableHeader, symbol, type,MirrorSystem.getName(symbol));
 
  RowCell defaultRowCell() => new RowCell(this);
}

class RowCell extends Cell {

  HeaderCell headerCell;
  Object value = null;
  RowCell(this.headerCell, {Object initv}) {
    if (initv != null) {
      value = initv;
    } else {
      // we may introduce  default value assignment depending on the type.
    }
  }
}

class TableHeader<E> {

  Table<E> table;
  List<HeaderCell> headerCells;
 
  TableHeader(this.headerCells);
 
  factory TableHeader.fromType(Type modelType) {
    var th = new TableHeader([]);
    reflectClass(modelType).getters.forEach((Symbol symbol, MethodMirror md){
      th.headerCells.add(new HeaderCell.fromSymbol(th, symbol, (md.returnType as ClassMirror).reflectedType));
    });
    return th;
  }
}

class Row<E> {

  final Table<E> _table;
  final E e;
  final List<RowCell> rowCells;

  Row(this._table, this.e, this.rowCells);

    
// I like this kind of single expression style. 
factory Row.defaultRow(Table<E> _table) => new Row(_table, null,

    _table.theader.headerCells.fold([], (rowCells, HeaderCell hcell)=>rowCells..add(hcell.defaultRowCell())));
// '..' syntax is perfect match with this fold function.
  // Sometimes, we need to declare variable, then we must use block statement, but if Dart supports FAMOUS 'let' syntax of ML(or F#), then this can be written as a single expression, s.t. ' => let var imirr = ... in ... end;'
  factory Row.fromEntity(Table<E> table, E e) {
    InstanceMirror imirr = reflect(e);
    return new Row(table, e, table.theader.headerCells.fold([], (cells, HeaderCell hc)=>
        cells..add(new RowCell(hc, init: imirr.getField(hc.symbol).reflectee))));
  }

  Table get table => _table;

}

class Table<E> {

  Type modelType;
  String name;
  TableHeader _theader;
  final List<Row<E>> rows;
  List<E> _es;
 
  Table(this.modelType, this._theader): rows = [], _es = [] {
    _theader.table = this;
  }
 
  factory Table.fromModelType(Type modelType) {
    TableHeader th = new TableHeader.fromType(modelType);
    Table tbl = new Table(modelType, th);
    th.table = tbl;
    return tbl;
  }
   
  TableHeader get theader => _theader;
 
  void newRow() => rows.add(new Row<E>.defaultRow(this));
 
  void addRow(Row<E> row) {
    // check row.tabel == this
    if (row.table != this) {
      print(">> addRow error");
      throw new Exception("Table<1>");
    }
    rows.add(row);
  }
 
  Row<E> addRowFromEntity(E e) {
    addRow(new Row<E>.fromEntity(this, e));
  }
  // there should be more methods like these, but they should be associated with DOM operation.
  void clear() {
    _es.clear();
    rows.clear();
  }
 
  load(Iterable<E> es) {
    clear();
    _es.addAll(es);
    for (E e in es) {
      addRowFromEntity(e);
    }
  }
}

//

String getMethodName(MethodMirror md) =>MirrorSystem.getName(md.simpleName);

List<Symbol> getSymbols(Type t) => reflectClass(t).getters.values.

  fold([], (List symbs, MethodMirror mtd) =>(mtd.isGetter)?(symbs..add(mtd.simpleName)):symbs);