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

com.sun.jsftemplating.component.dataprovider.MultipleListDataProvider Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.jsftemplating.component.dataprovider;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.sun.data.provider.DataProviderException;
import com.sun.data.provider.FieldKey;
import com.sun.data.provider.RowKey;
import com.sun.data.provider.TransactionalDataListener;
import com.sun.data.provider.impl.IndexRowKey;
import com.sun.data.provider.impl.ObjectFieldKeySupport;
import com.sun.data.provider.impl.ObjectListDataProvider;


/**
 *  

This implementation allows for multiple List objects to * be used to represent table rows. If mulitiple Lists are * used, they must be parallel lists (i.e. have the same # and order of * information).

*/ public class MultipleListDataProvider extends ObjectListDataProvider { private static final long serialVersionUID = 1L; /** *

Default Constructor.

*/ public MultipleListDataProvider() { setIncludeFields(false); } /** *

Constructor that creates initializes the DataProvider with a * single List. Fields are not included by * default.

* * @param list Primary list containing row information. */ public MultipleListDataProvider(List> list) { this(list, false); } /** *

Constructor that creates initializes the DataProvider with a * single List. Fields are included if * includeFields is true.

* * @param lists List<List<Object>> to be * wrapped. * @param includeFields Desired include fields property setting */ public MultipleListDataProvider(List> lists, boolean includeFields) { // Set the lists setLists(lists); // Do not include fields setIncludeFields(false); } /** *

Constructor for an empty DataProvider with a known * type. This constructor is only useful when there is a single * List. Fields are not included.

* * @param objectTypes Desired object type Classes. */ public MultipleListDataProvider(Class [] objectTypes) { this(objectTypes, false); } /** *

Constructor for an empty DataProvider with a known * type. This constructor is only useful when there is a single * List. Fields are included if * includeFields is true.

* * @param objTypes Desired object type of Classes * @param includeFields Desired include fields property setting */ public MultipleListDataProvider(Class [] objTypes, boolean includeFields) { setObjectTypes(objTypes); setIncludeFields(includeFields); } // FIXME: Provide apis to manage multiple lists: // FIXME: addList(String, List) // FIXME: removeList(String)?? /** *

This method returns an array of Object * (Object []) for the given row. Each * array element cooresponds to an Object in one of the * Lists represented by this instance of * MultipleListDataProvider.

* * @throws IndexOutOfBoundsException If row is invalid. */ public Object getObject(RowKey row) { if (!isRowAvailable(row)) { throw new IndexOutOfBoundsException("" + row); } int rowIdx = getRowIndex(row); List> lists = getLists(); Object [] result = new Object[lists.size()]; int idx = 0; for (List list : lists) { result[idx++] = list.get(rowIdx); } return result; } /** *

This method returns an array of array ofObjects * (Object [][]). The first element of the array is the * row, the second cooresponds to the List: * Object[row #][list #]. If there is only one * List represented by this instance of * MultipleListDataProvider, then the list # will be 0; * if there are 2 Lists, then the list # will be 0 or 1, * etc.

*/ public Object [] getObjects() { // Get the array demensions List> lists = getLists(); int numLists = lists.size(); if (numLists == 0) { // This isn't likely, but just in case... return new Object[0][0]; } int numRows = lists.get(0).size(); Object [][] result = new Object[numRows][numLists]; // Fill the array int listNum = 0; for (int rowNum = 0; rowNum < numRows; rowNum++) { listNum = 0; for (List list : lists) { result[rowNum][listNum++] = list.get(rowNum); } } // Return the result (Object[rows][lists]) return result; } /** *

This sets the Object type contained in the * List that this MulitpleObjectDataProvider * represents. This method should only be used when there is one * List held by this * MultipleListDataProvider. In cases where you have * more than one list (and likely you do because you are using this * DataProvider), you should use * {@link #setObjectTypes(Class [])}.

*/ public void setObjectType(Class objectType) { setObjectTypes(new Class [] {objectType}); } /** *

This method sets the Object types for each List that * is represented by this instance of this class. By doing this, you * allow the FieldKeys to be generated. You only need to specify this * information if you have no rows. If there is some data, that data * will be used to determine the type.

* * @param objectTypes The Class types of the row data. */ public void setObjectTypes(Class [] objectTypes) { _types = objectTypes; } /** *

Not supported. This method does not appear to have great value.

*/ public void removeObject(Object object) { throw new UnsupportedOperationException(); } /** *

Not yet supported.

* * @param row The RowKey of the row to check. */ public boolean isRemoved(RowKey row) { return _deletes.contains(row); } /** *

Replace the object at the specified row.

* * @param row The desired row to set the contained object * @param object The new object to set at the specified row */ public void setObject(RowKey row, Object object) { throw new UnsupportedOperationException(this.getClass().getName() + " does not support the setObject(RowKey, Object) method. " + "Instead use setObjects(RowKey, Object [])."); } /** *

This method allows the given row to be replaced with * the given objects.

* * @param row The row to replace. * @param objects The array of Objects to use. */ public void setObjects(RowKey row, Object [] objects) { Object [] previous = (Object []) getObject(row); int rowNum = getRowIndex(row); int cnt = 0; for (List list : getLists()) { list.set(rowNum, objects[cnt++]); } fireValueChanged(null, row, previous, objects); if (getCursorRow() == row) { fireValueChanged(null, previous, objects); } } /** *

Not yet supported...

*/ public void addObject(Object object) { // FIXME: Add api to add the Object*s* (see addObject(object), must have 1 for each List throw new UnsupportedOperationException(); } // ---------------------------------------------------- DataProvider Methods /** * */ public FieldKey getFieldKey(String fieldId) throws DataProviderException { return getSupport().getFieldKey(fieldId); } /** * */ public FieldKey [] getFieldKeys() throws DataProviderException { return getSupport().getFieldKeys(); } /** * */ public Class getType(FieldKey fieldKey) throws DataProviderException { return getSupport().getType(fieldKey); } /** * */ public boolean isReadOnly(FieldKey fieldKey) throws DataProviderException { return getSupport().isReadOnly(fieldKey); } // FIXME: The following method is very difficult to implement correctly b/c // FIXME: the super class uses its own private "getSupport()" method which will // FIXME: return the wrong "support" class. However, the superclass is also // FIXME: managing the "updates" so we must call it... Ask Creator group to // FIXME: properly expose "support" via an interface and get/setSupport() // FIXME: methods. Or ask them to provide access to the updates/deletes/etc. // FIXME: properties. // // FIXME: OK... it appears that they ObjectListDataProvider does not utilize the FieldKey methods in its superclass and instead maintains a private list via its support class. This means I cannot do anything to make this method work with updates!!! /** * */ public Object getValue(FieldKey fieldKey, RowKey rowKey) throws DataProviderException { Object val = null; try { // This will try to get the value, but is unlikely to succeed... val = super.getValue(fieldKey, rowKey); } catch(Exception ex) { // Now check the "right" support class... if (getSupport().getFieldKey(fieldKey.getFieldId()) == null) { throw new IllegalArgumentException("" + fieldKey); } // Make sure it's a valid row key... if (!isRowAvailable(rowKey)) { throw new IndexOutOfBoundsException("" + rowKey); } int index = getRowIndex(rowKey); if (index < getRowCount()) { val = getSupport().getValue( fieldKey, getListForFieldKey(fieldKey).get(index)); } else { // FIXME: Need to manually implement appends, updates, and deletes!! :( // return getSupport().getValue(fieldKey, appends.get(index - getRowCount())); } } return val; } /** * */ public void setValue(FieldKey fieldKey, RowKey rowKey, Object value) throws DataProviderException { if (getSupport().getFieldKey(fieldKey.getFieldId()) == null) { throw new IllegalArgumentException("" + fieldKey); } if (getSupport().isReadOnly(fieldKey)) { throw new IllegalStateException("" + fieldKey); } if (!isRowAvailable(rowKey)) { throw new IndexOutOfBoundsException("" + rowKey); } // Retrieve the previous value and determine if it has changed Object previous = getValue(fieldKey, rowKey); if (((previous == null) && (value == null)) || ((previous != null) && (value != null) && previous.equals(value))) { return; // No change } // Verify type compatibility of the proposed new value if (!getSupport().isAssignable(fieldKey, value)) { throw new IllegalArgumentException(fieldKey + " = " + value); // NOI18N } // FIXME: Need to explicitly support updates (can't get via polymorphism due to bad design)! :( /* // Record a pending change for this row and field Map fieldUpdates = (Map) updates.get(rowKey); if (fieldUpdates == null) { fieldUpdates = new HashMap(); updates.put(rowKey, fieldUpdates); } fieldUpdates.put(fieldKey, value); */ //Remove the following set line and defer until commit() once updates/deletes/etc. is worked out int index = getRowIndex(rowKey); getSupport().setValue( fieldKey, getListForFieldKey(fieldKey).get(index), value); fireValueChanged(fieldKey, rowKey, previous, value); fireValueChanged(fieldKey, previous, value); } /** *

Construct new instances for each List represented by * this class and appended them to each List. */ public RowKey appendRow() throws DataProviderException { // FIXME: Support this! throw new UnsupportedOperationException(); } /** *

This method returns true if rows may be appended.

*/ public boolean canAppendRow() throws DataProviderException { return (isUserResizable() && (getObjectTypes() != null)); } /** *

This method is not supported.

* * @param object Object to be appended. */ public RowKey appendRow(Object object) throws DataProviderException { // FIXME: Support this! throw new UnsupportedOperationException(); } /** *

Remove the object at the specified row from the list.

* * {@inheritDoc} */ public void removeRow(RowKey rowKey) throws DataProviderException { // Verify we can actually remove this row if (!canRemoveRow(rowKey)) { // FIXME: I18N throw new IllegalStateException( "This ObjectListDataProvider is not resizable."); } if (!isRowAvailable(rowKey)) { // FIXME: I18N throw new IllegalArgumentException( "Cannot delete row for row key " + rowKey); } // Record the fact that we are going to delete this row _deletes.add(rowKey); // Fire appropriate events regarding this deletion fireRowRemoved(rowKey); if (getCursorRow() == rowKey) { fireValueChanged(null, getObject(rowKey), null); } } /** *

This method returns the # of rows represented by this * MultipleListDataProvider.

*/ public int getRowCount() throws DataProviderException { List> lists = getLists(); int count = 0; if ((lists != null) && (lists.size() != 0)) { count = lists.get(0).size(); } return count; } /** *

Return true if the specified RowKey * represents a row in the original list, or a row that has been * appended.

* * @param row RowKey to test for availability. */ public boolean isRowAvailable(RowKey row) throws DataProviderException { int idx = getRowIndex(row); if (idx < 0) { return false; } if (idx < (getRowCount() /* + appendCount */)) { return true; } return false; } // --------------------------------------- TransactionalDataProvider Methods /** *

Cause any cached updates to existing field values, as well as * inserted and deleted rows, to be flowed through to the underlying * Lists wrapped by this DataProvider.

*/ public void commitChanges() throws DataProviderException { // FIXME: Do updates here... (see super()) // Commit pending deletes // Iterate backwards so that we correctly modify List RowKey deletes[] = (RowKey[]) _deletes.toArray(new RowKey[_deletes.size()]); int rowIdx = -1; for (int idx = (deletes.length - 1); idx >= 0; idx--) { rowIdx = getRowIndex(deletes[idx]); for (List list : getLists()) { list.remove(rowIdx); } } _deletes.clear(); // FIXME: Deal w/ appends (see super()) // Notify interested listeners that we have committed fireChangesCommitted(); } /** *

Fire a changesCommitted method to all registered * listeners.

*/ protected void fireChangesCommitted() { // FIXME: This method overrides a method by the same name in the superclass that is private! The super() should make this protected. TransactionalDataListener listeners[] = getTransactionalDataListeners(); for (int i = 0; i < listeners.length; i++) { listeners[i].changesCommitted(this); } } /** *

Fire a changesReverted method to all registered * listeners.

*/ private void fireChangesReverted() { // FIXME: This method overrides a method by the same name in the superclass that is private! The super() should make this protected. TransactionalDataListener listeners[] = getTransactionalDataListeners(); for (int i = 0; i < listeners.length; i++) { listeners[i].changesReverted(this); } } /** * */ public void revertChanges() throws DataProviderException { // FIXME: Do updates here... (see super()) //updates.clear(); _deletes.clear(); // FIXME: Deal w/ appends //appends.clear(); // Notify interested listeners that we are reverting fireChangesReverted(); } /** *

Accessor for the List of Lists.

*/ public List> getLists() { return _lists; } /** *

Setter for the List of Lists.

*/ public void setLists(List> lists) { _lists = lists; } /** *

This method returns */ public List getListForFieldKey(FieldKey key) { if ((key != null) && !(key instanceof IndexFieldKey)) { key = support.getFieldKey(key.getFieldId()); } if (key == null) { throw new IllegalArgumentException("Invalid FieldKey: " + key); } return getLists().get(((IndexFieldKey) key).getIndex()); } /** *

The cached support object for field key manipulation. Must be * transient because its content is not Serializable.

*/ private transient MultipleObjectFieldKeySupport support = null; /** *

Return the {@link ObjectFieldKeySupport} instance for the object * class we are wrapping.

*/ private MultipleObjectFieldKeySupport getSupport() { if (support == null) { // Try to get first element of the list to help find FieldKeys Object [] objs = (Object []) getObject(getRowKey(0)); if ((objs != null) && (objs.length > 0)) { support = new MultipleObjectFieldKeySupport( objs, isIncludeFields()); } // else { // FIXME: Add ability to use other meta information (i.e. _types and/or special class to describe info (i.e. for Maps)) to do this. // } } return support; } /** *

This method converts the given rowKey to an * int.

*/ protected int getRowIndex(RowKey rowKey) { // FIXME: This method overrides a private method by the same name... that method shouldn't be private! if (rowKey instanceof IndexRowKey) { return ((IndexRowKey) rowKey).getIndex(); } return -1; } /** *

This method converts the given int to a * RowKey.

*/ protected RowKey getRowKey(int index) { return new IndexRowKey(index); } //////////////////////////////////////////////////////////////////////// // Unsupported Methods //////////////////////////////////////////////////////////////////////// /** *

This method is not supported.

*/ public Class getObjectType() { throw new UnsupportedOperationException(this.getClass().getName() + " does not support the getObjectType() method because it " + "must return multiple types. Please use \"Class [] " + "getObjectTypes()\" instead."); } /** *

This method returns an array of Class [] representing * the Object types represented by this instance of this * class.

*/ public Class [] getObjectTypes() { return _types; } /** *

Storage for our List of Lists.

*/ private List> _lists = new ArrayList>(); /** *

Type information for the LIsts.

*/ private Class [] _types = null; /** *

Set of {@link RowKey}s marked to be deleted. An * Iterator over this set will return the corresponding * {@link RowKey}s in ascending order.

*/ protected Set _deletes = new TreeSet(); }