net.databinder.components.hib.DataForm.java Source code

Java tutorial

Introduction

Here is the source code for net.databinder.components.hib.DataForm.java

Source

/*
 * Databinder: a simple bridge from Wicket to Hibernate
 * Copyright (C) 2006  Nathan Hamblen nathan@technically.us
    
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.databinder.components.hib;

import java.io.Serializable;

import net.databinder.models.hib.HibernateObjectModel;

import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.hibernate.Session;

/**
 * Provides default handling for a single {@link HibernateObjectModel} nested 
 * in a {@link CompoundPropertyModel}. This includes saving a new model object to 
 * persistent storage and committing the current transaction when a valid form 
 * is submitted. For forms holding multiple independent persistent objects (when 
 * there is no single parent that cascades saves to the others), subclasses may 
 * override {@link #savePersistentObjectIfNew()} to save all the form's 
 * {@link HibernateObjectModel}s. (Note that automatic {@link #version} tracking 
 * is only available for the primary model.)
 * <p>For very specialized forms it may be necessary to extend this class's parent,
 * {@link DataFormBase}.</p>
 * @author Nathan Hamblen
 */
public class DataForm<T> extends DataFormBase<T> {
    /**
     * Retains the persistent object's version field (if it has one) between
     * requests to detect editing conflicts between users.
     * @see #validate()
     */
    private Serializable version;

    /**
     * Instantiates this form and a new, blank instance of the given class as a persistent model
     * object. By default the model object created is serialized and retained between requests until
     * it is persisted.
     * @param id
     * @param modelClass for the persistent object
     * @see HibernateObjectModel#setRetainUnsaved(boolean)
     */
    public DataForm(String id, Class<T> modelClass) {
        super(id, new CompoundPropertyModel<T>(new HibernateObjectModel<T>(modelClass)));
    }

    public DataForm(String id, HibernateObjectModel<T> model) {
        super(id, new CompoundPropertyModel<T>(model));
        setFactoryKey(model.getFactoryKey());
    }

    /**
     * Instantiates this form with a persistent object of the given class and id.
     * @param id Wicket id
     * @param modelClass for the persistent object
     * @param persistentObjectId id of the persistent object
     */
    public DataForm(String id, Class<T> modelClass, Serializable persistentObjectId) {
        super(id, new CompoundPropertyModel<T>(new HibernateObjectModel<T>(modelClass, persistentObjectId)));
    }

    /**
     * Form that is nested below a component with a compound model containing a Hibernate
     * model.
     * @param id
     */
    public DataForm(String id) {
        super(id);
    }

    /**
     * @param key for the Hibernate session factory to be used with this component
     * @return this 
     */
    @Override
    public DataForm setFactoryKey(Object key) {
        super.setFactoryKey(key);
        getPersistentObjectModel().setFactoryKey(key);
        return this;
    }

    /**
     * @return the single persistent model for this form
     */
    public HibernateObjectModel<T> getPersistentObjectModel() {
        return (HibernateObjectModel<T>) getCompoundModel().getChainedModel();
    }

    /**
     * Change the persistent model object of this form.
     * @param object  to attach to this form
     * @return this form, for chaining
     */
    public DataForm setPersistentObject(T object) {
        getPersistentObjectModel().setObject(object);
        modelChanged();
        return this;
    }

    /**
     * Updates the internal version number to the actual version number. 
     * @see #version
     */
    private void updateVersion() {
        version = getPersistentObjectModel().getVersion();
    }

    /** Late-init version record. */
    @Override
    protected void onBeforeRender() {
        super.onBeforeRender();
        if (version == null)
            updateVersion();
    }

    /** Calls {@link #updateVersion()} when model changes. */
    @Override
    protected void onModelChanged() {
        updateVersion();
    }

    /**
     * Replaces the form's model object with a new, blank (unbound) instance. Does not affect
     * persistent storage.
     * @see HibernateObjectModel#unbind()
     * @return this form, for chaining
     */
    public DataForm clearPersistentObject() {
        getPersistentObjectModel().unbind();
        modelChanged();
        return this;
    }

    /** 
     * @return the effective compound model for this form, which may be
     * attached to a parent component
     */
    protected CompoundPropertyModel getCompoundModel() {
        IModel model = getModel();
        Component cur = this;
        while (cur != null) {
            model = cur.getDefaultModel();
            if (model != null && model instanceof CompoundPropertyModel)
                return (CompoundPropertyModel) model;
            cur = cur.getParent();
        }
        throw new WicketRuntimeException("DataForm has no parent compound model");
    }

    /** Default implementation calls {@link #commitFormIfValid()}. */
    @Override
    protected void onSubmit() {
        commitFormIfValid();
    }

    /**
     * Commits a valid form's data to persistent storage. If no errors are 
     * registered for any form component, this method calls 
     * {@link #savePersistentObjectIfNew()}
     * {@link #commitTransactionIfValid()}, and {@link #updateVersion()}.
     * @return true if committed
     */
    protected boolean commitFormIfValid() {
        if (!hasError()) {
            savePersistentObjectIfNew();
            commitTransactionIfValid(); // flush and commit session
            // if version is present it should have changed
            if (version != null) {
                updateVersion();
            }
            return true;
        }
        return false;
    }

    /**
     * Saves persistent model object if it is not already contained in the session.
     * If the a sub-class is responsible for more than one {@link HibernateObjectModel},
     * it may override to call {@link #saveIfNew(HibernateObjectModel)} on each.  
     * @return true if object was newly saved 
     */
    protected boolean savePersistentObjectIfNew() {
        return saveIfNew(getPersistentObjectModel());
    }

    /**
     * Saves model's entity if it is not already contained in the session.
     * @return true if object was newly saved 
     */
    protected boolean saveIfNew(HibernateObjectModel<T> model) {
        Session session = getHibernateSession();
        if (!session.contains(model.getObject())) {
            onBeforeSave(model);
            session.save(model.getObject());
            // updating binding status; though it will happen on detach
            // some UI components may like to know sooner.
            getPersistentObjectModel().checkBinding();
            return true;
        }
        return false;
    }

    /**
     * Called before saving any new object by {@link #saveIfNew(HibernateObjectModel)}.
     * This is a good time to make last minute changes to new objects that
     * couldn't be easily serialized (adding relationships to existing persistent 
     * entities, for example).
     * @param generally, the persistent model for this form (but subclasses may also call saveIfNew)
     */
    protected void onBeforeSave(HibernateObjectModel<T> model) {
    };

    /**
     * Checks that the version number, if present, is the last known version number.
     * If it does not match, validation fails and will continue to fail until the form is
     * reloaded with the updated data and version number. This allows the user to
     * preserve her unsaved changes while preventing overwrites. <p> <b>Note:</b> although
     * timestamp versions are supported, beware of rounding errors. equals() must return true
     * when comparing the retained version object to the one loaded from persistent storage.
     */
    @Override
    protected void onValidate() {
        if (version != null) {
            Serializable currentVersion = getPersistentObjectModel().getVersion();
            if (!version.equals(currentVersion))
                error(getString("version.mismatch", null)); // report error
            // do not update version number as old data still appears in form
        }
        super.onValidate();
    }

    /**
     * @return persistent storage version number if available, null otherwise
     */
    protected Serializable getVersion() {
        return version;
    }

    /**
     * Deletes the form's model object from persistent storage. Flushes change so that
     * queries executed in the same request (e.g., in a HibernateListModel) will not return
     * this object.
     * @return true if the object was deleted, false if it did not exist
     */
    protected boolean deletePersistentObject() {
        Session session = getHibernateSession();
        Object modelObject = getPersistentObjectModel().getObject();
        if (!session.contains(modelObject))
            return false;
        session.delete(modelObject);
        session.flush();
        return true;
    }

    /**
     * Instances of this nested class call #{@link DataForm#clearPersistentObject()}
     * on their instantiating DataForm when clicked.
     */
    public class ClearLink extends Link {
        public ClearLink(String id) {
            super(id);
        }

        /** @return true if visible and the form's perisistent model is bound */
        @Override
        public boolean isEnabled() {
            return !DataForm.this.isVisibleInHierarchy() || getPersistentObjectModel().isBound();
        }

        @Override
        public void onClick() {
            clearPersistentObject();
            DataForm.this.setVisible(true);
        }
    }
}