Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.VariableMapper;
import javax.faces.FactoryFinder;
import javax.faces.application.ApplicationFactory;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.DataModelListener;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
/**
* Creates a CollectionModel that is sortable.
* All properties that implement java.lang.Comparable are deemed sortable.
*/
public class SortableModel extends CollectionModel
{
/**
* This class provides an enumeration to work with the integer values of the
* {@link Collator} strength values.
*/
public enum Strength
{
/** @see Collator#IDENTICAL */
IDENTICAL(Collator.IDENTICAL),
/** @see Collator#PRIMARY */
PRIMARY(Collator.PRIMARY),
/** @see Collator#SECONDARY */
SECONDARY(Collator.SECONDARY),
/** @see Collator#TERTIARY */
TERTIARY(Collator.TERTIARY);
private Strength(int strength)
{
_strength = strength;
}
public int getIntValue()
{
return _strength;
}
private final int _strength;
}
/**
* This class provides an enumeration to work with the integer values of the
* {@link Collator} decomposition values.
*/
public enum Decomposition
{
/** @see Collator#PRIMARY */
NO_DECOMPOSITION(Collator.NO_DECOMPOSITION),
/** @see Collator#SECONDARY */
CANONICAL_DECOMPOSITION(Collator.CANONICAL_DECOMPOSITION),
/** @see Collator#TERTIARY */
FULL_DECOMPOSITION(Collator.FULL_DECOMPOSITION);
private Decomposition(int decomposition)
{
_decomposition = decomposition;
}
public int getIntValue()
{
return _decomposition;
}
private final int _decomposition;
}
/**
* Create a new SortableModel from the given instance.
* @param model This will be converted into a {@link DataModel}
* @see #setWrappedData
*/
public SortableModel(Object model)
{
setWrappedData(model);
}
/**
* No arg constructor for use as a managed-bean.
* Must call setWrappedData before using this instance.
*/
public SortableModel()
{
}
@Override
public Object getRowData()
{
return _model.getRowData();
}
@Override
public Object getWrappedData()
{
return _wrappedData;
}
@Override
public boolean isRowAvailable()
{
return _model.isRowAvailable();
}
/**
* Sets the underlying data being managed by this instance.
* @param data This Object will be converted into a
* {@link DataModel}.
* @see ModelUtils#toDataModel
*/
@Override
public void setWrappedData(Object data)
{
_baseIndicesList = null;
_model = ModelUtils.toDataModel(data);
_sortCriterion = null;
_sortedIndicesList = null;
_wrappedData = data;
}
@Override
public int getRowCount()
{
return _model.getRowCount();
}
@Override
public void setRowIndex(int rowIndex)
{
int baseIndex = _toBaseIndex(rowIndex);
_model.setRowIndex(baseIndex);
}
@Override
public int getRowIndex()
{
int baseIndex = _model.getRowIndex();
return _toSortedIndex(baseIndex);
}
/**
* Gets the row key of the current row
* @inheritDoc
*/
@Override
public Object getRowKey()
{
return isRowAvailable()
? _model.getRowIndex()
: null;
}
/**
* Finds the row with the matching key and makes it current
* @inheritDoc
*/
@Override
public void setRowKey(Object key)
{
_model.setRowIndex(_toRowIndex(key));
}
public void addDataModelListener(DataModelListener listener)
{
_model.addDataModelListener(listener);
}
public DataModelListener[] getDataModelListeners()
{
return _model.getDataModelListeners();
}
public void removeDataModelListener(DataModelListener listener)
{
_model.removeDataModelListener(listener);
}
/**
* Checks to see if the underlying collection is sortable by the given property.
* @param property The name of the property to sort the underlying collection by.
* @return true, if the property implements java.lang.Comparable
*/
@Override
public boolean isSortable(String property)
{
final int oldIndex = _model.getRowIndex();
try
{
_model.setRowIndex(0);
if (!_model.isRowAvailable())
return false; // if there is no data in the table then nothing is sortable
Object data = _model.getRowData();
try
{
//TODO clean up that _getELXyz() calls
FacesContext context = FacesContext.getCurrentInstance();
ELResolver resolver = _getELResolver(context);
ELContext elContext = _getELContext(context, resolver);
Object propertyValue = evaluateProperty(resolver, elContext, data, property);
// when the value is null, we don't know if we can sort it.
// by default let's support sorting of null values, and let the user
// turn off sorting if necessary:
return (propertyValue instanceof Comparable) ||
(propertyValue == null);
}
catch (RuntimeException e)
{
// don't propagate this exception out. This is because it might break
// the VE.
_LOG.warning(e);
return false;
}
}
finally
{
_model.setRowIndex(oldIndex);
}
}
private Object evaluateProperty(ELResolver resolver, ELContext context, Object base, String property)
{
//simple property -> resolve value directly
if (!property.contains( "." ))
return resolver.getValue(context, base, property );
int index = property.indexOf( '.' );
Object newBase = resolver.getValue(context, base, property.substring( 0, index ) );
return evaluateProperty(resolver, context, newBase, property.substring( index + 1 ) );
}
@Override
public List getSortCriteria()
{
if (_sortCriterion == null)
{
return Collections.emptyList();
}
else
{
return Collections.singletonList(_sortCriterion);
}
}
@Override
public void setSortCriteria(List criteria)
{
if ((criteria == null) || (criteria.isEmpty()))
{
_sortCriterion = null;
// restore unsorted order:
_baseIndicesList = _sortedIndicesList = null;
}
else
{
SortCriterion sc = criteria.get(0);
if ((_sortCriterion == null) || (!_sortCriterion.equals(sc)))
{
// cache the latest sort criterion and do the sorting.
_sortCriterion = sc;
_sort(_sortCriterion);
}
}
}
/**
* Get the comparator associated with the given property.
*
* @param propertyName the property
* @return the comparator or null if one has not been set
*/
public Comparator getComparator(
String propertyName)
{
return _propertyComparators == null ?
null :
_propertyComparators.get(propertyName);
}
/**
* Set a custom comparator to use to sort the given property name.
*
* @param propertyName the property with which to associate the comparator
* @param comparator the comparator to use, or null to remove one
*/
public void setComparator(
String propertyName,
Comparator comparator)
{
assert propertyName != null : "Property name may not be null";
if (comparator == null && _propertyComparators != null)
{
_propertyComparators.remove(propertyName);
if (_propertyComparators.isEmpty())
{
_propertyComparators = null;
}
}
else if (comparator != null)
{
if (_propertyComparators == null)
{
_propertyComparators = new HashMap();
}
_propertyComparators.put(propertyName, comparator);
}
if (_sortCriterion != null && propertyName.equals(_sortCriterion.getProperty()))
{
_sort(_sortCriterion);
}
}
/**
* Convenience method to set a compatator for a property using a {@link Collator} setup with
* the given strength and decomposition values.
*
* @param propertyName the property
* @param collatorStrength the stregth to use or null to leave as the default for the
* default locale
* @param collatorDecomposition the decomposition to use or null to leave as the default for the
* default locale
* @see #setComparator(String, Comparator)
*/
public void setCollator(
String propertyName,
Strength collatorStrength,
Decomposition collatorDecomposition)
{
Locale locale = null;
RequestContext reqCtx = RequestContext.getCurrentInstance();
if (reqCtx != null)
{
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext != null)
{
locale = _getLocale(reqCtx, facesContext);
}
}
Collator collator = locale == null ? Collator.getInstance() : Collator.getInstance(locale);
if (collatorDecomposition != null)
{
collator.setDecomposition(collatorDecomposition.getIntValue());
}
if (collatorStrength != null)
{
collator.setStrength(collatorStrength.getIntValue());
}
setComparator(propertyName, collator);
}
@Override
public String toString()
{
return "SortableModel[" + _model + "]";
}
/**
* Sorts the underlying collection by the given property, in the
* given direction, with the given strength.
* @param sortCriterion sort criterion controlling sort behavior.
* @todo support -1 for rowCount
*/
private void _sort(SortCriterion sortCriterion)
{
//TODO: support -1 for rowCount:
int sz = getRowCount();
if ((_baseIndicesList == null) || (_baseIndicesList.size() != sz))
{
// we do not want to mutate the original data.
// however, instead of copying the data and sorting the copy,
// we will create a list of indices into the original data, and
// sort the indices. This way, when certain rows are made current
// in this Collection, we can make them current in the underlying
// DataModel as well.
_baseIndicesList = new IntList(sz);
}
final int rowIndex = _model.getRowIndex();
_model.setRowIndex(0);
// Make sure the model has that row 0! (It could be empty.)
if (_model.isRowAvailable())
{
FacesContext context = FacesContext.getCurrentInstance();
RequestContext rc = RequestContext.getCurrentInstance();
ELResolver resolver = _getELResolver(context);
ELContext elContext = _getELContext(context, resolver);
Locale locale = _getLocale(rc, context);
Comparator comp =
new Comp(resolver, elContext, locale, sortCriterion.getProperty(),
sortCriterion.getSortStrength());
if (!sortCriterion.isAscending())
comp = new Inverter(comp);
Collections.sort(_baseIndicesList, comp);
_sortedIndicesList = null;
}
_model.setRowIndex(rowIndex);
}
private int _toSortedIndex(int baseIndex)
{
if ((_sortedIndicesList == null) && (_baseIndicesList != null))
{
_sortedIndicesList = (IntList) _baseIndicesList.clone();
for(int i=0; i<_baseIndicesList.size(); i++)
{
Integer base = _baseIndicesList.get(i);
_sortedIndicesList.set(base.intValue(), i);
}
}
return _convertIndex(baseIndex, _sortedIndicesList);
}
private int _toBaseIndex(int sortedIndex)
{
return _convertIndex(sortedIndex, _baseIndicesList);
}
private int _convertIndex(int index, List indices)
{
if (index < 0) // -1 is special
return index;
if ((indices != null) && (indices.size() > index))
{
index = indices.get(index).intValue();
}
return index;
}
private int _toRowIndex(Object rowKey)
{
if (rowKey == null)
return -1;
try
{
return ((Integer)rowKey).intValue();
}
catch (ClassCastException e)
{
_LOG.warning("INVALID_ROWKEY", new Object[]{rowKey , rowKey.getClass()});
_LOG.warning(e);
return -1;
}
}
private static final class IntList extends ArrayList
{
public IntList(int size)
{
super(size);
_expandToSize(size);
}
private void _expandToSize(int desiredSize)
{
for(int i=0; i
{
public Comp(
ELResolver resolver,
ELContext context,
Locale locale,
String property,
SortStrength sortStrength)
{
_resolver = resolver;
_context = context;
// use Collator as comparator whenever locale or strength is available,
// so sorting is natural to that locale.
if (locale != null || sortStrength != null)
{
if (locale != null)
_collator = Collator.getInstance(locale);
else
_collator = Collator.getInstance();
if (sortStrength != null)
_collator.setStrength(sortStrength.getStrength());
}
else
{
_collator = null;
}
_prop = property;
}
@SuppressWarnings("unchecked")
public int compare(
Integer o1,
Integer o2)
{
int index1 = o1.intValue();
int index2 = o2.intValue();
_model.setRowIndex(index1);
Object instance1 = _model.getRowData();
Object value1 = evaluateProperty(_resolver, _context, instance1, _prop );
_model.setRowIndex(index2);
Object instance2 = _model.getRowData();
Object value2 = evaluateProperty(_resolver, _context, instance2, _prop );
if (value1 == null)
return (value2 == null) ? 0 : -1;
if (value2 == null)
return 1;
Comparator comparator = getComparator(_prop);
if (comparator == null)
{
// bug 4545164. Sometimes, isSortable returns true
// even if the underlying object is not a Comparable.
// This happens if the object at rowIndex zero is null.
// So test before we cast:
if (value1 instanceof Comparable)
{
if ((value1 instanceof String) && (value2 instanceof String))
{
return _compare((String) value1, (String) value2);
}
else
{
return ((Comparable