org.apache.cocoon.forms.binding.RepeaterJXPathCollection.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cocoon.forms.binding.RepeaterJXPathCollection.java

Source

/*
 * 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

import org.apache.commons.jxpath.JXPathContext;

/**
 * Implements a collection that takes care about removed, updated and inserted
 * elements, obtaining from a {@link RepeaterAdapter} all the needed objects.
 *
 * @version $Id: RepeaterJXPathCollection.java 479295 2006-11-26 06:15:25Z antonio $
 */
public class RepeaterJXPathCollection {

    private JXPathContext storageContext;

    private Map updatedRows;
    private Set deletedRows;
    private List insertedRows;

    private int collectionSize;

    private RepeaterSorter sorter = null;
    private RepeaterFilter filter = null;
    private RepeaterAdapter adapter = null;

    private List itemsCache = new ArrayList();

    public void init(JXPathContext storageContext, String rowpath, RepeaterAdapter adapter) {
        this.storageContext = storageContext;
        collectionSize = 0;
        Object value = storageContext.getValue(rowpath);
        if (value != null) {
            if (value instanceof Collection) {
                collectionSize = ((Collection) value).size();
            } else {
                collectionSize = ((Double) storageContext.getValue("count(" + rowpath + ")")).intValue();
            }
        }

        this.updatedRows = new HashMap();
        this.deletedRows = new HashSet();
        this.insertedRows = new ArrayList();
        this.adapter = adapter;
        this.sorter = adapter.sortBy(null);
    }

    private int getStartIndex(int start) {
        int i = start;
        RepeaterItem item = adapter.getItem(i);
        // In case start is after the end of the collection try to go back
        // until a valid item is found
        while (item == null && i > 0) {
            i--;
            item = adapter.getItem(i);
        }
        if (item == null)
            return 0;
        // Now move the index ahead of one for each deleted item "before"
        // the desired one
        for (Iterator iter = deletedRows.iterator(); iter.hasNext();) {
            RepeaterItem delitem = (RepeaterItem) iter.next();
            if (sorter.compare(delitem, item) < 0) {
                i++;
            }
        }
        // And move it backward for each inserted row before the actual index
        for (Iterator iter = insertedRows.iterator(); iter.hasNext();) {
            RepeaterItem insitem = (RepeaterItem) iter.next();
            if (sorter.compare(insitem, item) < 0) {
                i--;
            }
        }
        if (i < 0)
            return 0;
        // Now we should have the correct start
        return i;
    }

    public List getItems(int start, int length) {
        List ret = new ArrayList();
        int rlength = length;
        int rstart = getStartIndex(start);
        RepeaterItem startItem = null;
        if (rstart > 0) {
            // Try to fetch one element before, so that we can distinguish
            // where we started after inferring added elements.
            startItem = getItem(rstart - 1);
        }
        if (startItem != null) {
            ret.add(startItem);
        }
        int i = rstart;
        RepeaterItem item;
        while (length > 0) {
            item = getItem(i);
            if (item == null)
                break;
            // skip deleted items
            while (isDeleted(item)) {
                i++;
                item = getItem(i);
                if (item == null)
                    break;
            }
            if (filter != null) {
                while (!filter.shouldDisplay(item)) {
                    i++;
                    item = getItem(i);
                    if (item == null)
                        break;
                }
            }
            if (item == null)
                break;
            ret.add(item);
            i++;
            length--;
        }
        // Infer the inserted rows.
        if (this.insertedRows.size() > 0) {
            if (filter != null) {
                for (Iterator iter = this.insertedRows.iterator(); iter.hasNext();) {
                    RepeaterItem acitem = (RepeaterItem) iter.next();
                    if (filter.shouldDisplay(acitem)) {
                        ret.add(acitem);
                    }
                }
            } else {
                ret.addAll(this.insertedRows);
            }
            Collections.sort(ret, this.sorter);
        }
        if (startItem != null) {
            // Now get from the element after our start element.
            int pos = ret.indexOf(startItem);
            for (int j = 0; j <= pos; j++) {
                ret.remove(0);
            }
        }
        while (ret.size() > rlength)
            ret.remove(ret.size() - 1);

        this.itemsCache.clear();
        this.itemsCache.addAll(ret);
        return ret;
    }

    public List getCachedItems() {
        return this.itemsCache;
    }

    public void flushCachedItems() {
        this.itemsCache.clear();
    }

    private RepeaterItem getItem(int i) {
        // Take the element from the original collection and check if it was updated
        RepeaterItem item = this.adapter.getItem(i);
        if (item == null)
            return null;
        if (isUpdated(item)) {
            item = (RepeaterItem) this.updatedRows.get(item.getHandle());
        }
        return item;
    }

    public void updateRow(RepeaterItem item) {
        if (!isInserted(item) && !isDeleted(item)) {
            this.updatedRows.put(item.getHandle(), item);
        }
    }

    public void deleteRow(RepeaterItem item) {
        if (isInserted(item)) {
            this.insertedRows.remove(item);
            return;
        } else if (isUpdated(item)) {
            this.updatedRows.remove(item);
        }
        this.deletedRows.add(item);
    }

    public void addRow(RepeaterItem item) {
        this.insertedRows.add(item);
    }

    public int getOriginalCollectionSize() {
        return collectionSize;
    }

    public int getActualCollectionSize() {
        return getOriginalCollectionSize() - this.deletedRows.size() + this.insertedRows.size();
    }

    /*
     * convenience methods to search the cache
     */

    private boolean isUpdated(RepeaterItem item) {
        return this.updatedRows.containsKey(item.getHandle());
    }

    private boolean isDeleted(RepeaterItem item) {
        return this.deletedRows.contains(item);
    }

    private boolean isInserted(RepeaterItem item) {
        return this.insertedRows.contains(item);
    }

    public JXPathContext getStorageContext() {
        return storageContext;
    }

    public List getDeletedRows() {
        // FIXME we should sort by natural order
        List ret = new ArrayList(this.deletedRows);
        Collections.sort(ret, this.sorter);
        Collections.reverse(ret);
        return ret;
    }

    public List getInsertedRows() {
        return insertedRows;
    }

    public Collection getUpdatedRows() {
        return updatedRows.values();
    }

    public RepeaterAdapter getAdapter() {
        return this.adapter;
    }

    public void addRow(RepeaterRow row) {
        RepeaterItem item = this.adapter.generateItem(row);
        this.addRow(item);
    }

    public void sortBy(String field) {
        this.sorter = this.adapter.sortBy(field);
    }

    public void filter(String field, Object value) {
        if (filter == null) {
            filter = this.adapter.getFilter();
        }
        filter.setFilter(field, value);
    }
}