Package org.apache.cocoon.forms.binding

Source Code of org.apache.cocoon.forms.binding.TempRepeaterJXPathBinding

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.cocoon.forms.binding;

import java.util.Iterator;

import org.apache.avalon.framework.logger.Logger;

import org.apache.cocoon.forms.formmodel.Repeater;
import org.apache.cocoon.forms.formmodel.Widget;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.Pointer;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Experimental simple binding for repeaters:
*   on save, first deletes the target data before recreating it from scratch.
* Based on code from SimpleRepeater.
* <p>
* For a smarter binding that avoids deletion and recreation, consider
* {@link org.apache.cocoon.forms.binding.RepeaterJXPathBinding}
*
* @version $Id: TempRepeaterJXPathBinding.java 517733 2007-03-13 15:37:22Z vgritsenko $
*/
public class TempRepeaterJXPathBinding extends JXPathBindingBase {

    private final String repeaterId;
    private final String repeaterPath;
    private final String rowPath;
    private final String rowPathInsert;
    private final boolean clearOnLoad;
    private final JXPathBindingBase rowBinding;
    private final JXPathBindingBase insertRowBinding;
    private final boolean deleteIfEmpty;
    private final boolean virtualRows;


    public TempRepeaterJXPathBinding(JXPathBindingBuilderBase.CommonAttributes commonAtts,
                                     String repeaterId,
                                     String repeaterPath,
                                     String rowPath,
                                     String rowPathInsert,
                                     boolean virtualRows,
                                     boolean clearOnLoad,
                                     boolean deleteIfEmpty,
                                     JXPathBindingBase rowBinding,
                                     JXPathBindingBase insertBinding) {
        super(commonAtts);
        this.repeaterId = repeaterId;
        this.repeaterPath = repeaterPath;
        this.rowPath = rowPath;
        this.rowPathInsert = rowPathInsert;
        this.rowBinding = rowBinding;
        this.rowBinding.setParent(this);
        this.insertRowBinding = insertBinding;
        this.insertRowBinding.setParent(this);
        this.virtualRows = virtualRows;
        this.clearOnLoad = clearOnLoad;
        this.deleteIfEmpty = deleteIfEmpty;
    }

    public void enableLogging(Logger logger) {
        super.enableLogging(logger);
        if (this.insertRowBinding != null) {
            this.insertRowBinding.enableLogging(logger);
        }
        this.rowBinding.enableLogging(logger);
    }

    public String getId() { return repeaterId; }
    public String getRepeaterPath() { return repeaterPath; }
    public String getRowPath() { return rowPath; }
    public String getRowPathInsert() { return rowPathInsert; }
    public boolean getVirtualRows() { return virtualRows; }
    public boolean getClearOnLoad() { return clearOnLoad; }
    public boolean getDeleteIfEmpty() { return deleteIfEmpty; }
    public JXPathBindingBase[] getChildBindings() { return ((ComposedJXPathBindingBase)rowBinding).getChildBindings(); }
    public JXPathBindingBase[] getInsertChildBindings() { return ((ComposedJXPathBindingBase)insertRowBinding).getChildBindings(); }

    public void doLoad(Widget frmModel, JXPathContext jctx) throws BindingException {
        // (There should be a general widget type checker for all the bindings to use,
        // coupled with a general informative exception class to throw if the widget is
        // of the wrong type or null.)
        Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);
        if (repeater == null) {
            String fullId = frmModel.getRequestParameterName();
            if (fullId == null || fullId.length() == 0) {
                fullId = "";
            } else {
                fullId = fullId + ".";
            }
            throw new RuntimeException(
                "TempRepeaterJXPathBinding: Repeater \"" + fullId + this.repeaterId +
                "\" does not exist (" + frmModel.getLocation() + ")");
        }

        // Start by clearing the repeater, if necessary.
        if (this.clearOnLoad) {
            repeater.clear();
        }

        // Find the location of the repeater data.
        Pointer repeaterPointer = jctx.getPointer(this.repeaterPath);

        // Check if there is data present.
        //
        // (Otherwise, should we check the leniency config option
        // to decide whether to be silent or throw an exception?)
        if (repeaterPointer != null) {

            // Narrow to repeater context.
            JXPathContext repeaterContext = jctx.getRelativeContext(repeaterPointer);

            // Build a jxpath iterator for the repeater row pointers.
            Iterator rowPointers = repeaterContext.iteratePointers(this.rowPath);

            // Iterate through the rows of data.
            int rowNum = 0;
            while (rowPointers.hasNext()) {

                // Get or create a row widget.
                Repeater.RepeaterRow thisRow;
                if (repeater.getSize() > rowNum) {
                    thisRow = repeater.getRow(rowNum);
                } else {
                    thisRow = repeater.addRow();
                }
                rowNum++;

                // Narrow to the row context.
                Pointer rowPointer = (Pointer) rowPointers.next();
                JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);

                // If virtual rows are requested, place a deep clone of the row data
                // into a temporary node, and narrow the context to this virtual row.
                //
                // (A clone of the data is used to prevent modifying the source document.
                // Otherwise, the appendChild method would remove the data from the source
                // document.  Is this protection worth the penalty of a deep clone?)
                //
                // (This implementation of virtual rows currently only supports DOM
                // bindings, but could easily be extended to support other bindings.)

                if (virtualRows) {
                    Node repeaterNode = (Node)repeaterPointer.getNode();
                    Node virtualNode = repeaterNode.getOwnerDocument().createElementNS(null, "virtual");
                    Node node = (Node)rowPointer.getNode();
                    Node clone = node.cloneNode(true);
                    Node fakeDocElement = node.getOwnerDocument().getDocumentElement().cloneNode(false);
                    virtualNode.appendChild(clone);
                    fakeDocElement.appendChild(virtualNode);
                    rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement);
                    rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual"));
                }

                // Finally, perform the load row binding.
                this.rowBinding.loadFormFromModel(thisRow, rowContext);
            }
        }

        if (getLogger().isDebugEnabled())
            getLogger().debug("done loading rows " + this);
    }

    public void doSave(Widget frmModel, JXPathContext jctx) throws BindingException {
        // (See comment in doLoad about type checking and throwing a meaningful exception.)
        Repeater repeater = (Repeater) selectWidget(frmModel, this.repeaterId);

        // Perform shortcut binding if the repeater is empty
        // and the deleteIfEmpty config option is selected.
        if (repeater.getSize() == 0 && this.deleteIfEmpty) {
            // Delete all of the old data for this repeater.
            jctx.removeAll(this.repeaterPath);

        // Otherwise perform the normal save binding.
        } else {

            // Narrow to the repeater context, creating the path if it did not exist.
            JXPathContext repeaterContext = jctx.getRelativeContext(jctx.createPath(this.repeaterPath));

            // Start by deleting all of the old row data.
            repeaterContext.removeAll(this.rowPath);

            // Verify that repeater is not empty and has an insert row binding.
            if(repeater.getSize() > 0) {
                if (this.insertRowBinding != null) {

                    //register the factory!
                    //this.insertRowBinding.saveFormToModel(repeater, repeaterContext);

                    // Iterate through the repeater rows.
                    for (int i = 0; i < repeater.getSize(); i++) {

                        // Narrow to the repeater row context.
                        Pointer rowPointer = repeaterContext.getPointer(this.rowPathInsert);
                        JXPathContext rowContext = repeaterContext.getRelativeContext(rowPointer);

                        // Variables used for virtual rows.
                        // They are initialized here just to keep the compiler happy.
                        Node rowNode = null;
                        Node virtualNode = null;

                        // If virtual rows are requested, create a temporary node and
                        // narrow the context to this initially empty new virtual row.
                        if (virtualRows) {
                            rowNode = (Node)rowContext.getContextBean();
                            Document document = rowNode.getOwnerDocument();
                            virtualNode = document.createElementNS(null, "virtual");
                            Node fakeDocElement = document.getDocumentElement().cloneNode(false);
                            fakeDocElement.appendChild(virtualNode);
                            rowContext = JXPathContext.newContext(repeaterContext, fakeDocElement);
                            rowContext = rowContext.getRelativeContext(rowContext.getPointer("virtual"));
                        }

                        // Perform the insert row binding
                        this.insertRowBinding.saveFormToModel(repeater, rowContext);

                        // Perform the save row binding.
                        this.rowBinding.saveFormToModel(repeater.getRow(i), rowContext);

                        // If virtual rows are requested, finish by appending the
                        // children of the virtual row to the real context node.
                        if (virtualRows) {
                            NodeList list = virtualNode.getChildNodes();
                            int count = list.getLength();
                            for (int j = 0; j < count; j++) {
                                // The list shrinks when a child is appended to the context
                                // node, so we always reference the first child in the list.
                                rowNode.appendChild(list.item(0));
                            }
                        }
                        getLogger().debug("bound new row");
                    }
                } else {
                    getLogger().warn("TempRepeaterBinding has detected rows to insert, " +
                        "but misses the <on-insert-row> binding to do it.");
                }
            }
        }
    }

    public String toString() {
        return "TempRepeaterJXPathBinding [widget=" + this.repeaterId + ", xpath=" + this.repeaterPath + "]";
    }
}
TOP

Related Classes of org.apache.cocoon.forms.binding.TempRepeaterJXPathBinding

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.