blob: bdee8561b69f6d345a759ebc3792e2f7b78a6242 [file] [log] [blame]
// Copyright 2004 The Apache Software Foundation
//
// Licensed 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.tapestry.form;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.tapestry.Tapestry;
/**
* A utility class often used with the {@link org.apache.tapestry.form.ListEdit} component.
* A ListEditMap is loaded with data objects before the ListEdit renders, and again
* before the ListEdit rewinds. This streamlines the synchronization of the form
* against data on the server. It is most useful when the set of objects is of a manageable
* size (say, no more than a few hundred objects).
*
* <p>
* The map stores a list of keys, and relates each key to a value.
* It also tracks a deleted flag for each key.
*
* <p>
* Usage:
* <br>
* The page or component should implement {@link org.apache.tapestry.event.PageRenderListener}
* and implement {@link org.apache.tapestry.event.PageRenderListener#pageBeginRender(org.apache.tapestry.event.PageEvent)}
* to initialize the map.
*
* <p>
* The external data (from which keys and values are obtained) is queried, and
* each key/value pair is {@link #add(Object, Object) added} to the map, in the order
* that items should be presented.
*
* <p>
* The {@link org.apache.tapestry.form.ListEdit}'s source parameter should be bound
* to the map's {@link #getKeys() keys} property. The value parameter
* should be bound to the map's {@link #setKey(Object) key} property.
*
* <p>
* The {@link org.apache.tapestry.form.ListEdit}'s listener parameter should be
* bound to a listener method to synchronize a property of the component from
* the map.
*
* <pre><code>
* public void synchronize({@link org.apache.tapestry.IRequestCycle} cycle)
* {
* ListEditMap map = ...;
* <i>Type</i> object = (<i>Type</i>)map.getValue();
*
* if (object == null)
* ...
*
* set<i>Property</i>(object);
* }
* </code></pre>
*
* <p>
* You may also connect a {@link org.apache.tapestry.form.Checkbox}'s
* selected parameter to the map's {@link #isDeleted() deleted} property.
*
* <p>
* You may track inclusion in other sets by subclasses and implementing
* new boolean properties. The accessor method should be a call to
* {@link #checkSet(Set)} and the mutator method should be a call
* to {@link #updateSet(Set, boolean)}.
*
* @author Howard Lewis Ship
* @version $Id$
* @since 3.0
*
**/
public class ListEditMap
{
private Map _map = new HashMap();
private List _keys = new ArrayList();
private Set _deletedKeys;
private Object _currentKey;
/**
* Records the key and value into this map.
* The keys may be obtained, in the order in which they are added,
* using {@link #getKeys()}.
* This also sets the current key (so that you
* may invoke {@link #setDeleted(boolean)}, for example).
*
**/
public void add(Object key, Object value)
{
_currentKey = key;
_keys.add(_currentKey);
_map.put(_currentKey, value);
}
/**
* Returns a List of keys, in the order that keys
* were added to the map (using {@link #add(Object, Object)}.
* The caller must not modify the List.
*
**/
public List getKeys()
{
return _keys;
}
/**
* Sets the key for the map. This defines the key used
* with the other methods: {@link #getValue()},
* {@link #isDeleted()}, {@link #setDeleted(boolean)}.
*
**/
public void setKey(Object key)
{
_currentKey = key;
}
/**
* Returns the current key within the map.
*
**/
public Object getKey()
{
return _currentKey;
}
/**
* Returns the value for the key (set using {@link #setKey(Object)}).
* May return null if no such key has been added (this can occur
* if a data object is deleted between the time a form is rendered
* and the time a form is submitted).
*
**/
public Object getValue()
{
return _map.get(_currentKey);
}
/**
* Returns true if the {@link #setKey(Object) current key}
* is in the set of deleted keys.
*
**/
public boolean isDeleted()
{
return checkSet(_deletedKeys);
}
/**
* Returns true if the set contains the
* {@link #getKey() current key}. Returns
* false is the set is null, or doesn't contain
* the current key.
*
**/
protected boolean checkSet(Set set)
{
if (set == null)
return false;
return set.contains(_currentKey);
}
/**
* Adds or removes the {@link #setKey(Object) current key}
* from the set of deleted keys.
*
**/
public void setDeleted(boolean value)
{
_deletedKeys = updateSet(_deletedKeys, value);
}
/**
* Updates the set, adding or removing the {@link #getKey() current key}
* from it. Returns the set passed in.
* If the value is true and the set is null, an new instance
* of {@link HashSet} is created and returned.
*
**/
protected Set updateSet(Set set, boolean value)
{
if (value)
{
if (set == null)
set = new HashSet();
set.add(_currentKey);
}
else
{
if (set != null)
set.remove(_currentKey);
}
return set;
}
/**
* Returns the deleted keys in an unspecified order. May return
* null if no keys are deleted.
*
**/
public List getDeletedKeys()
{
return convertSetToList(_deletedKeys);
}
protected List convertSetToList(Set set)
{
if (Tapestry.isEmpty(set))
return null;
return new ArrayList(set);
}
/**
* Returns all the values stored in the map, in the
* order in which values were added to the map
* using {@link #add(Object, Object)}.
*
**/
public List getAllValues()
{
int count = _keys.size();
List result = new ArrayList(count);
for (int i = 0; i < count; i++)
{
Object key = _keys.get(i);
Object value = _map.get(key);
result.add(value);
}
return result;
}
/**
* Returns all the values stored in the map, excluding
* those whose id has been marked deleted, in the
* order in which values were added to the map
* using {@link #add(Object, Object)}.
*
**/
public List getValues()
{
int deletedCount = Tapestry.size(_deletedKeys);
if (deletedCount == 0)
return getAllValues();
int count = _keys.size();
List result = new ArrayList(count - deletedCount);
for (int i = 0; i < count; i++)
{
Object key = _keys.get(i);
if (_deletedKeys.contains(key))
continue;
Object value = _map.get(key);
result.add(value);
}
return result;
}
}