/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zkoss.ganttz.timetracker;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.Validate;
import org.zkoss.zk.ui.Component;
import org.zkoss.zul.Row;
import org.zkoss.zul.RowRenderer;
public class OnColumnsRowRenderer<C, T> implements RowRenderer {
private final List<C> columns;
private final ICellForDetailItemRenderer<C, T> cellRenderer;
private Class<T> type;
public static <C, T> OnColumnsRowRenderer<C, T> create(ICellForDetailItemRenderer<C, T> cellRenderer,
Collection<C> columns) {
return create(inferGenericType(cellRenderer), cellRenderer, columns);
}
public static <C, T> OnColumnsRowRenderer<C, T> create(
Class<T> type, ICellForDetailItemRenderer<C, T> cellRenderer, Collection<C> columns) {
return new OnColumnsRowRenderer<>(type, cellRenderer, columns);
}
private static <T> Class<T> inferGenericType(ICellForDetailItemRenderer<?, T> renderer) {
ParameterizedType parametrizedType = findRenderererInterfaceType(renderer);
Type[] actualTypeArguments = parametrizedType.getActualTypeArguments();
final int genericTypePosition = 1;
Type type = actualTypeArguments[genericTypePosition];
if ( !isActualType(type) ) {
informCannotBeInferred(renderer);
}
return (Class<T>) actualTypeArguments[genericTypePosition];
}
private static boolean isActualType(Type t) {
return t instanceof Class;
}
private static ParameterizedType findRenderererInterfaceType(ICellForDetailItemRenderer<?, ?> renderer) {
Type[] genericInterfaces = renderer.getClass().getGenericInterfaces();
for (Type type : genericInterfaces) {
if ( isTypeForInterface(type, ICellForDetailItemRenderer.class) ) {
if ( type instanceof ParameterizedType ) {
return (ParameterizedType) type;
} else {
informCannotBeInferred(renderer);
}
}
}
throw new RuntimeException("shouldn't reach here. Uncovered case for " + renderer);
}
private static boolean isTypeForInterface(Type type, Class<?> interfaceBeingSearched) {
if ( type instanceof ParameterizedType ) {
ParameterizedType p = (ParameterizedType) type;
Type rawType = p.getRawType();
return rawType.equals(interfaceBeingSearched);
}
return type.equals(interfaceBeingSearched);
}
private static void informCannotBeInferred(ICellForDetailItemRenderer<?, ?> renderer) {
throw new IllegalArgumentException(
"the generic type cannot be inferred if actual type parameters are not declared " +
"or implements the raw interface: " + renderer.getClass().getName());
}
private OnColumnsRowRenderer(Class<T> type, ICellForDetailItemRenderer<C, T> cellRenderer, Collection<C> columns) {
Validate.notNull(type);
Validate.notNull(columns);
Validate.notNull(cellRenderer);
Validate.noNullElements(columns);
this.cellRenderer = cellRenderer;
this.columns = new ArrayList<>(columns);
this.type = type;
}
@Override
public void render(Row row, Object o, int index) throws Exception {
if ( !type.isInstance(o) ) {
throw new IllegalArgumentException(o + " is not instance of " + type);
}
for (C item : columns) {
Component child = cellRenderer.cellFor(item, type.cast(o));
child.setParent(row);
}
}
}