All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.trinidad.model.ChildPropertyMenuModel Maven / Gradle / Ivy

The newest version!
/*
 * 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.myfaces.trinidad.model;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.faces.model.DataModel;

import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.CollectionUtils;

/**
 * Creates a MenuModel from a List of beans.
 * To use this class you must have a tree of beans (or Maps).
 * The structure of your tree must be a List (or array) of root beans, and each bean must have a
 * getter method (or Map property) that returns the children List. All elements of your tree must be
 * the same type.
 * 

* Suppose you have a bean called EmpBean that contains the data for a particular employee. Suppose * this bean has a method called getDirectReports() that returns a List of EmpBeans which are the * direct reports of the employee. Suppose there is a List called "founders" which is the root list * of EmpBeans. Now you can construct a MenuModel by calling: *

 * MenuModel model = new ChildPropertyMenuModel(founders, "directReports");
 * 
* Bean rules will be used to find an appropriate getter method for the "directReports" property. * java.util.Maps are also supported instead of beans. *

* Example: Given the following class: *

 * public class Person
 * {
 *    public Person(String name)
 *    {
 *      _name = name;
 *    }
 *
 *    public String getName()
 *    {
 *      return _name;
 *    }
 *
 *    public List getKids()
 *    {
 *      return _kids;
 *    }
 *
 *    private final String _name;
 *    private final List _kids = new ArrayList();
 * }
 * 
* You can construct a tree by: *
 * Person john = new Person("John Smith");
 * Person kim = new Person("Kim Smith");
 * Person tom = new Person("Tom Smith");
 * Person ira = new Person("Ira Wickrememsinghe");
 * Person mallika = new Person("Mallika Wickremesinghe");
 *
 * john.getKids().add(kim);
 * john.getKids().add(tom);
 * ira.getKids().add(mallika);
 *
 * // create the list of root nodes:
 * List people = new ArrayList();
 * people.add(john);
 * people.add(ira);
 * 
* Now you can construct a MenuModel by: *
 * MenuModel model = new ChildPropertyMenuModel(people, "kids", focusRowKey);
 * 
*/ public class ChildPropertyMenuModel extends MenuModel { /** * Creates a MenuModel * @param instance The Collection of root nodes of this tree. * This can be a List or array of beans (or Maps). * This instance is first converted into a CollectionModel (see * {@link ModelUtils#toCollectionModel}). * @param childProperty This property will be used to get at the child Lists from each bean (or * Map). Bean rules will be used to find a getter method that matches this * property. If each node is a Map, this property will be passed in to the * Map's get method to get the child List. * @param focusRowKey null or the row key in focus */ public ChildPropertyMenuModel(Object instance, String childProperty, Object focusRowKey) { this(); setChildProperty(childProperty); setWrappedData(instance); this._focusRowKey = focusRowKey; } /** * No-arg constructor for use with managed-beans. * Must call the {@link #setChildProperty} and * {@link #setWrappedData} methods after constructing this instance. */ public ChildPropertyMenuModel() { Node root = new Node(null); _path.add(root); } /** * Gets the rowKey of the current row. */ @Override public Object getRowKey() { final int sz = _path.size() - 1; Object lastRowkey = _getRowKey(); if ((sz == 0) && (lastRowkey == null)) return null; // root collection // have to clone the path here. otherwise, we have to say that // this tree model cannot be mutated while accessing the path // returned by this method. List path = new ArrayList(sz+1); if (sz > 0) { for(int i=0; i path = (List) rowKey; if ((path == null) || (path.size() == 0)) { setRowIndex(-1); return; } int lastIndex = path.size() - 1; for(int i=0; i path = (List) childKey; if ((path == null) || (path.size() <= 1)) return null; // wrap sublist in a Serializable copy, since sublist usually returns non-Serializable // instances return CollectionUtils.newSerializableList(path.subList(0, path.size() - 1)); } @Override public int getRowCount() { return _getModel().getRowCount(); } @Override public Object getRowData() { return _getModel().getRowData(); } @Override public boolean isRowAvailable() { return _getModel().isRowAvailable(); } @Override public boolean isContainer() { Object rowData = getRowData(); Object value = getChildData(rowData); if (value != null) { if (value instanceof Collection) { return !((Collection)value).isEmpty(); } else if (value.getClass().isArray()) { return Array.getLength(value) > 0; } else if (value instanceof DataModel) { return ((DataModel)value).getRowCount() > 0; } } return value != null; } @Override public void enterContainer() { Object rowData = getRowData(); if (rowData == null) throw new IllegalStateException(_LOG.getMessage("NULL_ROWDATA")); Node node = new Node(rowData); _path.add(node); } @Override public void exitContainer() { int sz = _path.size(); if (sz > 1) _path.remove(sz - 1); else throw new IllegalStateException(_LOG.getMessage("CANNOT_EXIT_ROOT_CONTAINER")); } /** * Gets the instance being wrapped by this MenuModel. */ @Override public Object getWrappedData() { return _wrappedData; } /** * Sets the instance being wrapped by this MenuModel. * Calling this method sets the path to empty. */ @Override public void setWrappedData(Object data) { Node root = _getNode(0); root.childModel = ModelUtils.toCollectionModel(data); setRowKey(null); _wrappedData = data; } /** * Gets the property name used to fetch the children. */ public final String getChildProperty() { return _childProperty; } /** * Sets the property name used to fetch the children. */ public final void setChildProperty(String childProperty) { _childProperty = childProperty; } @Override public int getRowIndex() { return _getModel().getRowIndex(); } @Override public void setRowIndex(int rowIndex) { _getModel().setRowIndex(rowIndex); } @Override public boolean isSortable(String property) { return _getModel().isSortable(property); } @Override public List getSortCriteria() { return _getModel().getSortCriteria(); } @Override public void setSortCriteria(List criteria) { _getModel().setSortCriteria(criteria); } /** * Gets the child data for a node. This child data will be converted into a CollectionModel by * calling {@link #createChildModel}. * @param parentData the node to get the child data from * @return the List or array that is the child data. * must return null for a leaf node. */ protected Object getChildData(Object parentData) { String prop = getChildProperty(); if (prop == null) return null; return SortableModel.__resolveProperty(parentData, prop); } /** * Converts childData into a CollectionModel. * This method calls {@link ModelUtils#toCollectionModel} * @param childData the data to convert. This can be a List or array. */ protected CollectionModel createChildModel(Object childData) { CollectionModel model = ModelUtils.toCollectionModel(childData); model.setRowIndex(-1); return model; } private Object _getRowKey() { return _getModel().getRowKey(); } private void _setRowKey(Object key) { _getModel().setRowKey(key); } private Node _getCurrentNode() { return _getNode(_path.size() - 1); } private Node _getNode(int index) { return _path.get(index); } private CollectionModel _getModel() { Node node = _getCurrentNode(); CollectionModel model = node.childModel; if (model == null) { Object value = getChildData(node.parentData); model = createChildModel(value); node.childModel = model; } return model; } public Object getFocusRowKey() { return _focusRowKey; } public void setFocusRowKey(Object focusRowKey) { _focusRowKey = focusRowKey; } private static final class Node { public Node(Object parentData) { this.parentData = parentData; } public final Object parentData; CollectionModel childModel = null; } private final List _path = new ArrayList(5); private String _childProperty; private Object _wrappedData; private Object _focusRowKey; private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(ChildPropertyMenuModel.class); }