class SwingTree extends AbstractSwingContainer implements XulTree {

  public <T> void setElements( Collection<T> elements ) {
    suppressEvents = true;
    this.elements = elements;
    this.getRootChildren().removeAll();

    // active editor needs updating, but won't if still active
    if ( table != null ) {
      CellEditor ce = table.getCellEditor();
      if ( ce != null ) {
        ce.stopCellEditing();
      }
    }

    if ( elements == null ) {
      if ( table != null ) {
        table.updateUI();
      } else {
        tree.updateUI();
      }
      changeSupport.firePropertyChange( "selectedRows", null, getSelectedRows() ); //$NON-NLS-1$
      changeSupport.firePropertyChange( "absoluteSelectedRows", null, getAbsoluteSelectedRows() ); //$NON-NLS-1$
      return;
    }
    try {

      if ( table != null ) {
        for ( T o : elements ) {
          XulTreeRow row = this.getRootChildren().addNewRow();

          for ( int x = 0; x < this.getColumns().getChildNodes().size(); x++ ) {
            XulComponent col = this.getColumns().getColumn( x );
            final XulTreeCell cell = (XulTreeCell) getDocument().createElement( "treecell" ); //$NON-NLS-1$
            XulTreeCol column = (XulTreeCol) col;

            for ( InlineBindingExpression exp : ( (XulTreeCol) col ).getBindingExpressions() ) {
              logger
                  .debug( "applying binding expression [" + exp + "] to xul tree cell [" + cell + "] and model [" + o + "]" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

              String colType = column.getType();
              if ( StringUtils.isEmpty( colType ) == false && colType.equalsIgnoreCase( "dynamic" ) ) { //$NON-NLS-1$
                colType = extractDynamicColType( o, x );
              }

              if ( colType == null ) {
                if ( StringUtils.isNotEmpty( exp.getModelAttr() ) ) {
                  Binding binding = createBinding( (XulEventSource) o, exp.getModelAttr(), cell, exp.getXulCompAttr() );
                  if ( !this.editable ) {
                    binding.setBindingType( Binding.Type.ONE_WAY );
                  }
                  domContainer.addBinding( binding );
                  binding.fireSourceChanged();
                }
              } else if ( ( colType.equalsIgnoreCase( "combobox" ) || colType.equalsIgnoreCase( "editablecombobox" ) ) && column.getCombobinding() != null ) { //$NON-NLS-1$ //$NON-NLS-2$
                Binding binding = createBinding( (XulEventSource) o, column.getCombobinding(), cell, "value" ); //$NON-NLS-1$
                binding.setBindingType( Binding.Type.ONE_WAY );
                domContainer.addBinding( binding );
                binding.fireSourceChanged();

                binding = createBinding( (XulEventSource) o, ( (XulTreeCol) col ).getBinding(), cell, "selectedIndex" ); //$NON-NLS-1$
                binding.setConversion( new BindingConvertor<Object, Integer>() {

                  @Override
                  public Integer sourceToTarget( Object value ) {
                    int index = ( (Vector) cell.getValue() ).indexOf( value );
                    return index > -1 ? index : 0;
                  }

                  @Override
                  public Object targetToSource( Integer value ) {
                    return ( (Vector) cell.getValue() ).get( value );
                  }

                } );
                domContainer.addBinding( binding );
                binding.fireSourceChanged();

                if ( colType.equalsIgnoreCase( "editablecombobox" ) ) { //$NON-NLS-1$

                  binding = createBinding( (XulEventSource) o, exp.getModelAttr(), cell, exp.getXulCompAttr() );
                  if ( !this.editable ) {
                    binding.setBindingType( Binding.Type.ONE_WAY );
                  } else {
                    binding.setBindingType( Binding.Type.BI_DIRECTIONAL );
                  }
                  domContainer.addBinding( binding );
                }

              } else if ( colType.equalsIgnoreCase( "checkbox" ) ) { //$NON-NLS-1$

                if ( StringUtils.isNotEmpty( exp.getModelAttr() ) ) {
                  Binding binding = createBinding( (XulEventSource) o, exp.getModelAttr(), cell, "value" ); //$NON-NLS-1$

                  if ( !this.editable ) {
                    binding.setBindingType( Binding.Type.ONE_WAY );
                  }
                  domContainer.addBinding( binding );
                  binding.fireSourceChanged();
                }

              } else if ( colType != null && this.customEditors.containsKey( colType ) ) {

                Binding binding = createBinding( (XulEventSource) o, exp.getModelAttr(), cell, "value" ); //$NON-NLS-1$
                binding.setBindingType( Binding.Type.BI_DIRECTIONAL );
                domContainer.addBinding( binding );
                binding.fireSourceChanged();

              } else {
                if ( StringUtils.isNotEmpty( exp.getModelAttr() ) ) {
                  Binding binding = createBinding( (XulEventSource) o, exp.getModelAttr(), cell, exp.getXulCompAttr() );
                  if ( !this.editable ) {
                    binding.setBindingType( Binding.Type.ONE_WAY );
                  }
                  domContainer.addBinding( binding );
                  binding.fireSourceChanged();
                }
              }

            }
            if ( column.getDisabledbinding() != null ) {
              String prop = column.getDisabledbinding();
              Binding bind = createBinding( (XulEventSource) o, column.getDisabledbinding(), cell, "disabled" ); //$NON-NLS-1$
              bind.setBindingType( Binding.Type.ONE_WAY );
              domContainer.addBinding( bind );
              bind.fireSourceChanged();
            }

            row.addCell( cell );
          }
        }

        // Setup context menu.. must do this here, as elements may not have been initialized in the layout() method.
        if ( popupListener == null ) {

          // The newItemBinding must be populated to get the context menu - and once you have it, you have the whole
          // set of menu options (no coded alternatives today). Review this logic when there is a requirement for
          // fine-grained control of which menu items are available to the table.

          if ( StringUtils.isNotEmpty( newItemBinding ) ) {
            if ( isCollectionManaged() ) {

              popupListener = new PopupListener();
              table.addMouseListener( popupListener );
              table.getTableHeader().addMouseListener( popupListener );

            } else {
              logger
                  .error( "Operations associated with newitembinding attribute not allowed on an unbound collection. Refactor your model "
                      + "to use a managed collection (for an example, see AbstractModelList)." );
            }
          }
        }
      } else {
        // tree

        for ( T o : elements ) {
          XulTreeRow row = this.getRootChildren().addNewRow();
          addTreeChild( o, row );
        }

      }

      if ( table != null ) {
        table.updateUI();
      } else {
        setupTree();
        tree.updateUI();
      }
      suppressEvents = false;

      // treat as a selection change
      changeSupport.firePropertyChange( "selectedRows", null, getSelectedRows() ); //$NON-NLS-1$
      changeSupport.firePropertyChange( "absoluteSelectedRows", null, getAbsoluteSelectedRows() ); //$NON-NLS-1$
    } catch ( XulException e ) {
      logger.error( "error adding elements", e ); //$NON-NLS-1$
    } catch ( Exception e ) {
      logger.error( "error adding elements", e ); //$NON-NLS-1$
    }
  }

  public  SwingTree( Element self, XulComponent parent, XulDomContainer domContainer, String tagName );

}