blob: e18b163c26ae937946aec9c2cd95aa0471efda39 [file] [log] [blame]
/*
* $Id$
* $Revision$
* $Date$
*
* ====================================================================
* 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 wicket.extensions.markup.html.repeater.data.sort;
import java.io.Serializable;
import wicket.AttributeModifier;
import wicket.Component;
import wicket.markup.html.link.Link;
import wicket.model.AbstractModel;
import wicket.model.IModel;
import wicket.util.lang.Objects;
import wicket.version.undo.Change;
/**
* A component that represents a sort header. When the link is clicked it will
* toggle the state of a sortable property within the sort state object.
*
* @author Phil Kulak
* @author Igor Vaynberg (ivaynberg)
*/
public class OrderByLink extends Link
{
private static final long serialVersionUID = 1L;
/** sortable property */
private String property;
/** locator for sort state object */
private ISortStateLocator stateLocator;
/**
* Constructor.
*
* @param id
* the component id of the link
* @param property
* the name of the sortable property this link represents. this
* value will be used as parameter for sort state object methods.
* sort state object will be located via the stateLocator
* argument.
* @param stateLocator
* locator used to locate sort state object that this will use to
* read/write state of sorted properties
*/
public OrderByLink(String id, String property, ISortStateLocator stateLocator)
{
this(id, property, stateLocator, DefaultCssProvider.getInstance());
}
/**
* Constructor.
*
* @param id
* the component id of the link
* @param property
* the name of the sortable property this link represents. this
* value will be used as parameter for sort state object methods.
* sort state object will be located via the stateLocator
* argument.
* @param stateLocator
* locator used to locate sort state object that this will use to
* read/write state of sorted properties
* @param cssProvider
* CSS provider that will be used generate the value of class
* attribute for this link
*
* @see OrderByLink.ICssProvider
*
*/
public OrderByLink(String id, String property, ISortStateLocator stateLocator,
ICssProvider cssProvider)
{
super(id);
if (cssProvider == null)
{
throw new IllegalArgumentException("argument [cssProvider] cannot be null");
}
if (property == null)
{
throw new IllegalArgumentException("argument [sortProperty] cannot be null");
}
this.property = property;
this.stateLocator = stateLocator;
add(new CssModifier(this, cssProvider));
}
/**
* @see wicket.markup.html.link.Link
*/
public final void onClick()
{
sort();
onSortChanged();
}
/**
* This method is a hook for subclasses to perform an action after sort has
* changed
*/
protected void onSortChanged()
{
// noop
}
/**
* Re-sort data provider according to this link
*
* @return this
*/
public final OrderByLink sort()
{
if (isVersioned())
{
// version the old state
Change change = new SortStateChange();
addStateChange(change);
}
ISortState state = stateLocator.getSortState();
int oldDir = state.getPropertySortOrder(property);
int newDir = ISortState.ASCENDING;
if (oldDir == ISortState.ASCENDING)
{
newDir = ISortState.DESCENDING;
}
state.setPropertySortOrder(property, newDir);
return this;
}
private final class SortStateChange extends Change
{
private static final long serialVersionUID = 1L;
private final ISortState old = (ISortState)Objects.clone(stateLocator.getSortState());
/**
* @see wicket.version.undo.Change#undo()
*/
public void undo()
{
stateLocator.setSortState(old);
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return "[StateOrderChange old=" + old.toString() + "]";
}
}
/**
* Uses the specified ICssProvider to add css class attributes to the link.
*
* @author Igor Vaynberg ( ivaynberg )
*
*/
public static class CssModifier extends AttributeModifier
{
private static final long serialVersionUID = 1L;
/**
* @param link
* the link this modifier is being added to
* @param provider
* implementation of ICssProvider
*/
public CssModifier(final OrderByLink link, final ICssProvider provider)
{
super("class", true, new AbstractModel()
{
private static final long serialVersionUID = 1L;
public IModel getNestedModel()
{
return null;
}
public Object getObject(Component component)
{
final ISortState sortState = link.stateLocator.getSortState();
return provider.getClassAttributeValue(sortState, link.property);
}
public void setObject(Component component, Object object)
{
throw new UnsupportedOperationException();
}
});
}
/**
* @see wicket.AttributeModifier#isEnabled()
*/
public boolean isEnabled()
{
return getReplaceModel().getObject(null) != null;
}
};
/**
* Interface used to generate values of css class attribute for the anchor
* tag If the generated value is null class attribute will not be added
*
* @author igor
*/
public static interface ICssProvider extends Serializable
{
/**
* @param state
* current sort state
* @param property
* sort property represented by the {@link OrderByLink}
* @return the value of the "class" attribute for the given sort
* state/sort property combination
*/
public String getClassAttributeValue(ISortState state, String property);
}
/**
* Easily constructible implementation of ICSSProvider
*
* @author Igor Vaynberg (ivaynberg)
*
*/
public static class CssProvider implements ICssProvider
{
private static final long serialVersionUID = 1L;
private String ascending;
private String descending;
private String none;
/**
* @param ascending
* css class when sorting is ascending
* @param descending
* css class when sorting is descending
* @param none
* css class when not sorted
*/
public CssProvider(String ascending, String descending, String none)
{
this.ascending = ascending;
this.descending = descending;
this.none = none;
}
/**
* @see wicket.extensions.markup.html.repeater.data.sort.OrderByLink.ICssProvider#getClassAttributeValue(wicket.extensions.markup.html.repeater.data.sort.ISortState,
* java.lang.String)
*/
public String getClassAttributeValue(ISortState state, String property)
{
int dir = state.getPropertySortOrder(property);
if (dir == ISortState.ASCENDING)
{
return ascending;
}
else if (dir == ISortState.DESCENDING)
{
return descending;
}
else
{
return none;
}
}
}
/**
* Convineince implementation of ICssProvider that always returns a null and
* so never adds a class attribute
*
* @author Igor Vaynberg ( ivaynberg )
*/
public static class VoidCssProvider extends CssProvider
{
private static final long serialVersionUID = 1L;
private static ICssProvider instance = new VoidCssProvider();
/**
* @return singleton instance
*/
public static ICssProvider getInstance()
{
return instance;
}
private VoidCssProvider()
{
super("", "", "");
}
}
/**
* Default implementation of ICssProvider
*
* @author Igor Vaynberg ( ivaynberg )
*/
public static class DefaultCssProvider extends CssProvider
{
private static final long serialVersionUID = 1L;
private static DefaultCssProvider instance = new DefaultCssProvider();
private DefaultCssProvider()
{
super("wicket_orderUp", "wicket_orderDown", "wicket_orderNone");
}
/**
* @return singleton instance
*/
public static DefaultCssProvider getInstance()
{
return instance;
}
}
}