org.richfaces.model.StackingTreeModel.java Source code

Java tutorial

Introduction

Here is the source code for org.richfaces.model.StackingTreeModel.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.model;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.ExtendedDataModel;
import org.ajax4jsf.model.Range;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.FilterIterator;
import org.w3c.dom.NamedNodeMap;

/**
 * That is intended for internal use
 * 
 * @author Nick Belaevski mailto:nbelaevski@exadel.com created 25.07.2007
 * 
 */
public class StackingTreeModel extends AbstractTreeDataModel {

    //ctor arguments
    private String id;
    private String var;
    private StackingTreeModelDataProvider dataProvider;

    //structural elements
    private StackingTreeModel parent;
    private Map<String, StackingTreeModel> models = new LinkedHashMap<String, StackingTreeModel>();

    private Object rowKey;

    private class StackEntry {
        private Object modelKey;
        private Object varObject;
        private StackingTreeModel model;
        private Object rowData;

        public StackEntry(Object varObject, Object modelKey, Object rowData, StackingTreeModel model) {
            super();
            this.varObject = varObject;
            this.modelKey = modelKey;
            this.rowData = rowData;
            this.model = model;
        }
    }

    //   private StackingTreeModel stackingTreeModel;
    private LinkedList<StackEntry> stackEntries = new LinkedList<StackEntry>();

    public ExtendedDataModel getDataModel() {
        Object data = dataProvider.getData();
        ExtendedDataModel dataModel;
        if (data instanceof Map || data instanceof NamedNodeMap) {
            dataModel = new MapDataModel();
        } else {
            dataModel = new SequenceDataModel();
        }

        dataModel.setWrappedData(data);
        return dataModel;
    }

    protected StackingTreeModel getCurrentModel() {
        if (this.rowKey == null) {
            return this;
        }

        if (isRowAvailable()) {
            return ((StackEntry) stackEntries.getLast()).model;
        }

        throw new IllegalStateException("No tree element available or row key not set!");
    }

    public boolean isEmpty() {
        //TODO optimize that
        return getDataModel().getRowCount() == 0;
    }

    private void leaveModel(Iterator<StackEntry> iterator, StackEntry currentEntry, FacesContext context) {
        if (iterator == null) {
            return;
        }

        LinkedList<StackEntry> stack = new LinkedList<StackEntry>();

        StackingTreeModel lastModel = null;
        if (currentEntry != null) {
            iterator.remove();
            stack.addFirst(currentEntry);
            lastModel = currentEntry.model;
        }

        while (iterator.hasNext()) {
            StackEntry entry = (StackEntry) iterator.next();
            if (entry.model != lastModel) {
                //always true for non-recursive models
                lastModel = entry.model;
                stack.addFirst(entry);
            }

            iterator.remove();
        }

        for (Iterator<StackEntry> iterator2 = stack.iterator(); iterator2.hasNext();) {
            StackEntry stackEntry = (StackEntry) iterator2.next();
            stackEntry.model.setupVariable(stackEntry.varObject, context);
        }
    }

    protected StackingTreeModel doSetupKey(Iterator<StackingTreeModelKey> keyIterator,
            Iterator<StackEntry> entriesIterator, FacesContext context, Object modelKey) {
        if (modelKey != null) {
            if (!setupModel(modelKey, context)) {
                //no key is available
                leaveModel(getRoot().stackEntries.iterator(), null, context);
                return null;
            }

            //TODO what's here?
        }

        if (keyIterator != null && keyIterator.hasNext()) {
            StackingTreeModelKey key = keyIterator.next();
            StackingTreeModel stackingTreeModel = this.getInternalModelById(key.modelId);
            Iterator<StackEntry> nextEntriesIterator = null;
            Object nextModelKey = key.modelKey;

            if (entriesIterator != null && entriesIterator.hasNext()) {
                StackEntry entry = entriesIterator.next();
                if (!entry.model.equals(stackingTreeModel) || !entry.modelKey.equals(nextModelKey)) {
                    leaveModel(entriesIterator, entry, context);
                } else {
                    //continue iterating entries, they still lead us by key path
                    nextEntriesIterator = entriesIterator;
                    nextModelKey = null;
                }
            }

            //should not be called when nextEntriesIterator & nextModelKey are both valid
            return stackingTreeModel.doSetupKey(keyIterator, nextEntriesIterator, context, nextModelKey);

        } else {
            leaveModel(entriesIterator, null, context);
            return this;
        }
    }

    protected StackingTreeModel setupKey(Object key, FacesContext context) {
        if (key == this.rowKey) {
            if (stackEntries.isEmpty()) {
                return this;
            } else {
                return (stackEntries.getLast()).model;
            }
        } else {
            Iterator<StackingTreeModelKey> keyIterator = null;
            if (key != null) {
                keyIterator = ((ListRowKey<StackingTreeModelKey>) key).iterator();
            }

            StackingTreeModel model = doSetupKey(keyIterator, stackEntries.iterator(), context, null);
            this.rowKey = key;

            return model;
        }
    }

    public StackingTreeModel(String id, String var, StackingTreeModelDataProvider dataProvider) {
        super();
        this.id = id;
        this.var = var;
        this.dataProvider = dataProvider;
    }

    public StackingTreeModel() {
        this(null, null, null);
    }

    private Object setupVariable(Object variable, FacesContext context) {
        if (var != null) {
            Map<String, Object> map = context.getExternalContext().getRequestMap();
            return map.put(var, variable);
        }

        return null;
    }

    public boolean setupModel(Object key, FacesContext facesContext) {
        ExtendedDataModel dataModel = getDataModel();
        dataModel.setRowKey(key);

        if (dataModel.isRowAvailable()) {
            Object rowData = dataModel.getRowData();
            //System.out.println("StackingTreeModel.setupModel() " + rowData);
            Object varObject = setupVariable(rowData, facesContext);

            getRoot().stackEntries.add(new StackEntry(varObject, key, rowData, this));

            return true;
        }

        return false;
    }

    public void setParent(StackingTreeModel parent) {
        this.parent = parent;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.richfaces.model.AbstractTreeDataModel#getTreeNode()
     */
    public TreeNode getTreeNode() {
        if (isRowAvailable()) {
            return null;
        }

        throw new IllegalStateException("No tree element available or row key not set!");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.richfaces.model.AbstractTreeDataModel#isLeaf()
     */
    public boolean isLeaf() {
        if (isRowAvailable()) {
            StackEntry lastEntry = (StackEntry) stackEntries.getLast();
            for (Iterator<StackingTreeModel> iterator = lastEntry.model.getInternalModelsIterator(); iterator
                    .hasNext();) {
                StackingTreeModel stackingTreeModel = iterator.next();

                if (!stackingTreeModel.isEmpty()) {
                    return false;
                }
            }

            return true;
        }

        throw new IllegalStateException("No tree element available or row key not set!");
    }

    protected StackingTreeModel getRoot() {
        if (parent != null) {
            return parent.getRoot();
        }

        return this;
    }

    protected void doWalk(FacesContext context, DataVisitor dataVisitor, Range range, ListRowKey argumentKey,
            Object argument, boolean last) throws IOException {

        TreeRange treeRange = (TreeRange) range;

        if (treeRange == null || treeRange.processNode(argumentKey)) {
            StackingTreeModel rootModel = getRoot();

            if (argumentKey != null) {
                processElement(context, dataVisitor, argument, argumentKey, last);
            }

            final ShiftingDataVisitor shiftingDataVisitor = new ShiftingDataVisitor(new Visitor1(dataVisitor));

            if (treeRange == null || treeRange.processChildren(argumentKey)) {

                Object savedRowKey = rootModel.getRowKey();
                //setup key in order for nested components to initialize data models
                rootModel.setRowKey(context, argumentKey);
                Iterator<StackingTreeModel> iterator = this.getInternalModelsIterator();
                rootModel.setRowKey(context, savedRowKey);

                while (iterator.hasNext()) {
                    final StackingTreeModel model = iterator.next();

                    savedRowKey = rootModel.getRowKey();
                    rootModel.setRowKey(context, argumentKey);
                    final ExtendedDataModel scalarModel = model.getDataModel();
                    rootModel.setRowKey(context, savedRowKey);

                    Argument argument2 = new Argument();
                    argument2.listRowKey = argumentKey;
                    argument2.argument = argument;
                    // setup current model
                    argument2.model = model;
                    argument2.range = range;

                    scalarModel.walk(context, new DataVisitor() {

                        public void process(FacesContext context, Object rowKey, Object argument)
                                throws IOException {

                            Object key = scalarModel.getRowKey();
                            scalarModel.setRowKey(rowKey);
                            Object data = scalarModel.getRowData();

                            Object variable = model.setupVariable(data, context);
                            boolean activeData = model.isActiveData();
                            model.setupVariable(variable, context);
                            scalarModel.setRowKey(key);

                            if (activeData) {
                                shiftingDataVisitor.process(context, rowKey, argument);
                            }
                        }

                    }, null, argument2);

                }
            }

            shiftingDataVisitor.end(context);
        }
    }

    private StackingTreeModel getInternalModelById(String id) {
        StackingTreeModel model = getModelById(id);
        if (model.isActive()) {
            return model;
        }

        throw new IllegalStateException();
    }

    public StackingTreeModel getModelById(String id) {
        return (StackingTreeModel) models.get(id);
    }

    private Iterator<StackingTreeModel> getInternalModelsIterator() {
        return new FilterIterator(getModelsIterator(), ACTIVE_MODEL_PREDICATE);
    }

    public Iterator<StackingTreeModel> getModelsIterator() {
        return models.values().iterator();
    }

    public void walk(FacesContext context, DataVisitor dataVisitor, Range range, Object rowKey, Object argument,
            boolean last) throws IOException {
        StackingTreeModel rootModel = getRoot();

        if (rowKey != null) {
            ListRowKey listRowKey = (ListRowKey) rowKey;

            StackingTreeModel treeModel = rootModel.setupKey(listRowKey, context);

            treeModel.doWalk(context, dataVisitor, range, listRowKey, argument, last);

        } else {
            doWalk(context, dataVisitor, range, (ListRowKey) rowKey, argument, last);
        }
    }

    private class Argument {
        private ListRowKey listRowKey;
        private StackingTreeModel model;
        private Range range;
        private Object argument;
    }

    private class Visitor1 implements DataVisitor, LastElementAware {
        private DataVisitor dataVisitor;
        private boolean theLast;

        public Visitor1(DataVisitor dataVisitor) {
            super();
            this.dataVisitor = dataVisitor;
        }

        public void process(FacesContext context, Object rowKey, Object argument) throws IOException {

            Argument a = (Argument) argument;
            ListRowKey listRowKey = new ListRowKey(a.listRowKey, new StackingTreeModelKey(a.model.id, rowKey));
            //System.out.println(".walk() " + (theLast ? " * " : "") + listRowKey);

            a.model.doWalk(context, dataVisitor, a.range, listRowKey, a.argument, theLast);
        }

        public void resetLastElement() {
            theLast = false;
        }

        public void setLastElement() {
            theLast = true;
        }

    }

    private static class ShiftingDataVisitor implements DataVisitor {

        private DataVisitor dataVisitor;

        public ShiftingDataVisitor(DataVisitor dataVisitor) {
            super();
            this.dataVisitor = dataVisitor;
        }

        private Object rowKey;
        private Object argument;
        private boolean shifted = false;

        public void process(FacesContext context, Object rowKey, Object argument) throws IOException {

            if (!shifted) {
                this.rowKey = rowKey;
                this.argument = argument;
                this.shifted = true;
            } else {
                dataVisitor.process(context, this.rowKey, this.argument);
                this.rowKey = rowKey;
                this.argument = argument;
            }
        }

        public void end(FacesContext context) throws IOException {
            if (shifted) {
                try {
                    ((LastElementAware) dataVisitor).setLastElement();
                    dataVisitor.process(context, this.rowKey, argument);
                } finally {
                    ((LastElementAware) dataVisitor).resetLastElement();
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.richfaces.model.AbstractTreeDataModel#walkModel(javax.faces.context.FacesContext,
     *      org.ajax4jsf.model.DataVisitor, org.ajax4jsf.model.Range,
     *      java.lang.Object, java.lang.Object, boolean)
     */
    public void walkModel(FacesContext facesContext, DataVisitor visitor, Range range, Object key, Object argument,
            boolean last) throws IOException {

        walk(facesContext, visitor, range, key, argument, last);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
     */
    public Object getRowKey() {
        return rowKey;
    }

    public void setRowKey(Object key) {
        setRowKey(FacesContext.getCurrentInstance(), key);
    }

    public void setRowKey(FacesContext context, Object key) {
        setupKey(key, context);
    }

    public void addStackingModel(StackingTreeModel model) {
        this.models.put(model.id, model);
        model.setParent(this);
    }

    public void removeStackingModel(StackingTreeModel model) {
        this.models.remove(model.id);
        model.setParent(null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.faces.model.DataModel#getRowData()
     */
    public Object getRowData() {
        if (isRowAvailable()) {
            StackEntry lastEntry = (StackEntry) stackEntries.getLast();
            return lastEntry.rowData;
        }

        throw new IllegalStateException("No tree element available or row key not set!");
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.faces.model.DataModel#isRowAvailable()
     */
    public boolean isRowAvailable() {
        return !stackEntries.isEmpty();
    }

    public StackingTreeModel getParent() {
        return parent;
    }

    protected boolean isActiveData() {
        return true;
    }

    protected boolean isActive() {
        return true;
    }

    private final static Predicate ACTIVE_MODEL_PREDICATE = new Predicate() {

        public boolean evaluate(Object object) {
            StackingTreeModel model = (StackingTreeModel) object;
            if (model == null) {
                return false;
            }

            return model.isActive();
        }

    };

    @Override
    public Object getWrappedData() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setWrappedData(Object data) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object convertToKey(FacesContext context, String keyString, UIComponent component, Converter converter) {

        //force model leave
        setRowKey(context, null);

        String[] strings = ListRowKey.fromString(keyString);
        int l = strings.length / 2;
        List<Object> list = new ArrayList<Object>(l);
        StackingTreeModel model = getRoot();

        for (int i = 0; i < l; i++) {
            int idx = i * 2;

            String modelId = strings[idx];
            model = model.getModelById(modelId);
            if (model.isActive()) {
                Object key = model.convert(context, strings[idx + 1], component, converter);
                if (key == null) {
                    return null;
                }

                list.add(new StackingTreeModelKey(modelId, key));

                if (!model.setupModel(key, context) || !model.isActiveData()) {
                    return null;
                }
            } else {
                return null;
            }
        }

        return new ListRowKey<Object>(list);
    }

    protected Object convert(FacesContext context, String string, UIComponent component, Converter converter) {

        ConvertableKeyModel convertable = (ConvertableKeyModel) getDataModel();
        return convertable.getKeyAsObject(context, string, component, converter);
    }

    public String getId() {
        return id;
    }
}