Package com.tll.client.ui.field

Source Code of com.tll.client.ui.field.FieldPanel$Styles

/**
* The Logic Lab
* @author jpk Nov 5, 2007
*/
package com.tll.client.ui.field;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.tll.client.bind.Binding;
import com.tll.client.bind.BindingException;
import com.tll.client.model.ModelChangeTracker;
import com.tll.client.validate.Error;
import com.tll.client.validate.ErrorClassifier;
import com.tll.client.validate.ErrorDisplay;
import com.tll.client.validate.IErrorHandler;
import com.tll.client.validate.ValidationException;
import com.tll.common.model.Model;

/**
* FieldPanel - Common base class for {@link Panel}s that display {@link IField}
* s.
* @param <W> The widget type employed for field rendering
* @author jpk
*/
public abstract class FieldPanel<W extends Widget> extends Composite implements IFieldBoundWidget {

  /**
   * Styles (field.css)
   * @author jpk
   */
  public static final class Styles {

    /**
     * Style indicating a UI artifact is a field title.
     */
    public static final String FIELD_TITLE = "fldtitle";

    /**
     * Style indicating a field panel.
     */
    public static final String FIELD_PANEL = "fpnl";
  }

  /**
   * The field group.
   */
  private FieldGroup group;

  /**
   * The model that is updated with the field data.
   */
  private Model model;

  private boolean drawn;

  /**
   * The sole binding.
   */
  private final Binding binding = new Binding();

  /**
   * The sole error handler instance for this binding.
   */
  private IErrorHandler errorHandler;

  /**
   * Tracks which properties have been altered in the model [optional].
   */
  private ModelChangeTracker modelChangeTracker;

  /**
   * Constructor
   */
  public FieldPanel() {
    super();
  }

  /**
   * @return The composite wrapped {@link Widget} the type of which
   *         <em>must</em> be that of the render widget type.
   */
  @SuppressWarnings("unchecked")
  @Override
  public final W getWidget() {
    return (W) super.getWidget();
  }

  @Override
  protected void initWidget(Widget widget) {
    widget.addStyleName(Styles.FIELD_PANEL);
    super.initWidget(widget);
  }

  /**
   * Checks the current state of this field panel ensuring the field group is ok
   * to be generated.
   * @return true/false
   */
  protected boolean canGenerateFieldGroup() {
    if(group == null) {
      if(getIndexedChildren() != null && model == null) {
        // this field panel is dependent on model data for initial display!
        return false;
      }
    }
    return true;
  }

  @Override
  public Model getChangedModel() {
    return (modelChangeTracker == null || model == null) ? null : modelChangeTracker.generateChangeModel(model);
  }

  @Override
  public final IErrorHandler getErrorHandler() {
    return errorHandler;
  }

  @Override
  public final void setErrorHandler(IErrorHandler errorHandler) {
    this.errorHandler = errorHandler;
  }

  @Override
  public ModelChangeTracker getModelChangeTracker() {
    return modelChangeTracker;
  }

  @Override
  public void setModelChangeTracker(ModelChangeTracker modelChangeTracker) {
    this.modelChangeTracker = modelChangeTracker;
  }

  /**
   * Generates the fields in the field group if they haven't been created yet.
   * This guarantees a non-<code> return value.
   * @throws IllegalStateException When the field group can't be generated based
   *         on the current state of this panel.
   */
  @Override
  public final FieldGroup getFieldGroup() throws IllegalStateException {
    if(group == null) {
      if(!canGenerateFieldGroup()) {
        throw new IllegalStateException();
      }
      Log.debug(this + " generating fields..");
      setFieldGroup(generateFieldGroup());
    }
    return group;
  }

  @Override
  public final void setFieldGroup(FieldGroup fields) {
    if(fields == null) throw new IllegalArgumentException("Null fields");
    if(this.group == fields) return;
    if(isBound()) throw new IllegalStateException("Binding already bound");
    this.group = fields;
    this.group.setWidget(this);

    // propagate the binding's error handler and model change tracker
    if(errorHandler != null) {
      Log.debug("Propagating error handler for: " + this);
      group.setErrorHandler(errorHandler);
    }
    final IFieldBoundWidget[] arr = getIndexedChildren();
    if(arr != null) {
      for(final IFieldBoundWidget c : arr) {
        c.setErrorHandler(errorHandler);
        c.setModelChangeTracker(modelChangeTracker);
      }
    }
  }

  @Override
  public final Model getModel() {
    return model;
  }

  /**
   * Sub-classes may override to handle custom bindings.
   * @return A newly created {@link IFieldBindingBuilder} impl.
   */
  protected IFieldBindingBuilder getFieldBindingBuilder() {
    return new DefaultFieldBindingBuilder();
  }

  /**
   * Creates a new {@link Binding} instance employing a
   * {@link DefaultFieldBindingBuilder} instance.
   * @throws BindingException When the bindings are not created for whatever
   *         reason
   */
  private void createBindings() throws BindingException {
    assert !isBound();
    Log.debug("Building binding instance..");
    final IFieldBindingBuilder fbb = getFieldBindingBuilder();
    fbb.set(this);
    fbb.createBindings(binding);
  }

  @Override
  public void setModel(Model model) {
    // don't spuriously re-apply the same model instance!
    if(this.model != null && model == this.model) {
      return;
    }
    Log.debug(this + " setting model: [" + model + "]..");

    unbind(); // if not bound no-op

    // clear out the field values since we have different model data
    // NOTE: we don't want to lazily instantite the field group here
    if(group != null) {
      Log.debug("Clearing field values..");
      group.clearValue();
    }

    this.model = model;
    Log.debug("model set");

    if(model != null) {
      bind();
      updateFields();

      // apply property metadata and model new flag (sets incremental validation flag)
      Log.debug("Applying prop metadata to fields..");
      getFieldGroup().applyPropertyMetadata(model, model.isNew());
    }
  }

  @Override
  public IIndexedFieldBoundWidget[] getIndexedChildren() {
    // default is null
    return null;
  }

  @Override
  public void reset() {
    if(group == null) return;
    final IIndexedFieldBoundWidget[] indexed = getIndexedChildren();
    if(indexed != null) {
      for(final IIndexedFieldBoundWidget i : indexed) {
        i.reset();
      }
    }
    group.reset();
  }

  @Override
  public void enable(boolean enable) {
    if(group == null) return;
    group.setEnabled(enable);
    final IIndexedFieldBoundWidget[] indexed = getIndexedChildren();
    if(indexed != null) {
      for(final IIndexedFieldBoundWidget i : indexed) {
        i.enable(enable);
      }
    }
  }

  /**
   * Provides the field panel renderer (drawer).
   * @return the renderer
   */
  protected abstract IFieldRenderer<W> getRenderer();

  /**
   * Generates the root {@link FieldGroup} this panel references via
   * {@link #getFieldGroup()}. This method is only called when this panel's
   * field group reference is <code>null</code>. Therefore, this method may be
   * circumvented by manually calling {@link #setFieldGroup(FieldGroup)}.
   * @return A new {@link FieldGroup} instance.
   */
  protected abstract FieldGroup generateFieldGroup();

  /**
   * Responsible for rendering the group in the ui. The default is to employ the
   * provided renderer via {@link #getRenderer()}. Sub-classes may extend this
   * method to circumvent this strategy thus avoiding the call to
   * {@link #getRenderer()}.
   */
  protected void draw() {
    final IFieldRenderer<W> renderer = getRenderer();
    if(renderer != null) {
      Log.debug(this + ": rendering..");
      renderer.render(getWidget(), getFieldGroup());
    }
  }

  @Override
  public final void updateModel() throws NoChangesException, ValidationException, BindingException {
    if(!isBound()) throw new IllegalStateException("Not bound");
    if(errorHandler != null) errorHandler.clear();
    try {
      // validate
      getFieldGroup().validate();

      // update the model
      binding.setLeft();

      if(modelChangeTracker != null && modelChangeTracker.getNumChanges() < 1) {
        throw new NoChangesException();
      }
    }
    catch(final ValidationException e) {
      if(errorHandler != null) errorHandler.handleErrors(e.getErrors(), ErrorDisplay.ALL_FLAGS);
      throw e;
    }
    catch(final BindingException e) {
      String emsg;
      if(e.getCause() != null && e.getCause().getMessage() != null) {
        emsg = e.getCause().getMessage();
      }
      else {
        emsg = e.getMessage();
      }
      if(emsg == null) {
        emsg = "Unknown error occurred.";
      }
      if(errorHandler != null)
        errorHandler.handleError(new Error(ErrorClassifier.CLIENT, null, emsg), ErrorDisplay.ALL_FLAGS);
      throw e;
    }
  }

  @Override
  public void updateFields() throws BindingException {
    if(!isBound()) return;
    if(modelChangeTracker != null) {
      modelChangeTracker.setHandleChanges(false);
    }
    binding.setRight();
    if(modelChangeTracker != null) {
      modelChangeTracker.setHandleChanges(true);
    }
  }

  @Override
  public final void bind() throws BindingException {
    if(isBound()) return;
    if(model == null) {
      throw new IllegalStateException("Can't bind: no model set");
    }
    createBindings();

    Log.debug("Binding: " + this);
    binding.bind();
  }

  @Override
  public final void unbind() {
    if(isBound()) {
      Log.debug("Un-binding: " + this);

      // unbind indexed first
      final IIndexedFieldBoundWidget[] indexedWidgets = getIndexedChildren();
      if(indexedWidgets != null) {
        for(final IIndexedFieldBoundWidget iw : indexedWidgets) {
          Log.debug("Un-binding indexed: " + iw);
          iw.clearIndexed();
        }
      }

      // un-wire the bindings then clear them out
      binding.unbind();
      binding.getChildren().clear();
      if(modelChangeTracker != null) modelChangeTracker.clear();
    }
  }

  @Override
  public final boolean isBound() {
    return binding.getChildren().size() > 0;
  }

  @Override
  protected void onAttach() {
    Log.debug("Attaching " + this + "..");
    super.onAttach();
  }

  @Override
  protected void onLoad() {
    Log.debug("Loading " + toString() + "..");
    super.onLoad();
    if(!drawn) {
      draw();
      drawn = true;
    }
  }

  @Override
  protected void onDetach() {
    Log.debug("Detatching " + toString() + "..");
    super.onDetach();
  }

  @Override
  public String toString() {
    return "FieldPanel[" + (group == null ? "-nofields-" : group.getName()) + "]";
  }
}
TOP

Related Classes of com.tll.client.ui.field.FieldPanel$Styles

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.