View Javadoc
1   package org.kuali.common.devops.logic.function;
2   
3   import static com.google.common.base.Preconditions.checkNotNull;
4   import static org.kuali.common.util.ReflectionUtils.isOptionalString;
5   import static org.kuali.common.util.base.Exceptions.illegalState;
6   
7   import java.lang.reflect.Field;
8   import java.util.List;
9   import java.util.Locale;
10  import java.util.SortedSet;
11  
12  import org.apache.commons.beanutils.PropertyUtils;
13  import org.kuali.common.devops.table.TableCellDescriptor;
14  import org.kuali.common.util.ReflectionUtils;
15  import org.springframework.core.convert.ConversionService;
16  import org.springframework.core.convert.TypeDescriptor;
17  
18  import com.google.common.base.Function;
19  import com.google.common.base.Optional;
20  import com.google.common.collect.ImmutableList;
21  import com.google.common.collect.Lists;
22  import com.google.common.collect.Sets;
23  import com.google.common.collect.Table;
24  
25  public final class ToListFunction<R, C, V> implements Function<Table<? extends Comparable<R>, ? extends Comparable<C>, TableCellDescriptor<String>>, List<V>> {
26  
27  	private final Class<V> targetType;
28  	private final ConversionService converter;
29  	private final TypeDescriptor sourceType;
30  	private final Locale locale;
31  
32  	@Override
33  	public List<V> apply(Table<? extends Comparable<R>, ? extends Comparable<C>, TableCellDescriptor<String>> table) {
34  		checkNotNull(table, "'table' cannot be null");
35  		SortedSet<Comparable<R>> rowKeys = Sets.newTreeSet(table.rowKeySet());
36  		SortedSet<Comparable<C>> colKeys = Sets.newTreeSet(table.columnKeySet());
37  		List<V> elements = Lists.newArrayList();
38  		org.apache.commons.lang3.builder.Builder<V> builder = ReflectionUtils.newInstance(getBuilderClass(targetType));
39  		for (Comparable<R> rowKey : rowKeys) {
40  			for (Comparable<C> colKey : colKeys) {
41  				TableCellDescriptor<String> descriptor = table.get(rowKey, colKey);
42  				Object value = convertString(descriptor);
43  				setProperty(builder, descriptor.getField().getName(), value);
44  			}
45  			V element = builder.build();
46  			elements.add(element);
47  		}
48  		return ImmutableList.copyOf(elements);
49  	}
50  
51  	protected Object convertString(TableCellDescriptor<String> descriptor) {
52  		try {
53  			TypeDescriptor targetType = new TypeDescriptor(descriptor.getField());
54  			Field field = descriptor.getField();
55  			Optional<String> value = descriptor.getFieldValue();
56  			if (field.getName().equals("tags") && !value.isPresent()) {
57  				System.out.println("yo");
58  			}
59  			Object converted = converter.convert(value.orNull(), sourceType, targetType);
60  			// TODO Spring's converter doesn't do anything if you pass it null
61  			if (isOptionalString(descriptor.getField()) && converted == null) {
62  				converted = Optional.<String> absent();
63  			}
64  			return converted;
65  		} catch (Exception e) {
66  			throw illegalState(e, "unexpected error converting table cell string into a strongly typed object");
67  		}
68  	}
69  
70  	protected void setProperty(Object bean, String name, Object value) {
71  		try {
72  			PropertyUtils.setProperty(bean, name, value);
73  		} catch (Exception e) {
74  			throw illegalState(e, "unexpected error setting bean property %s.%s=[%s]", bean.getClass().getCanonicalName(), name, value);
75  		}
76  	}
77  
78  	protected org.apache.commons.lang3.builder.Builder<V> getBuilder(Class<V> targetType) {
79  		return ReflectionUtils.newInstance(getBuilderClass(targetType));
80  	}
81  
82  	@SuppressWarnings("unchecked")
83  	protected Class<? extends org.apache.commons.lang3.builder.Builder<V>> getBuilderClass(Class<V> targetType) {
84  		Class<?>[] declaredClasses = targetType.getDeclaredClasses();
85  		for (Class<?> declaredClass : declaredClasses) {
86  			if (org.apache.commons.lang3.builder.Builder.class.isAssignableFrom(declaredClass)) {
87  				return (Class<? extends org.apache.commons.lang3.builder.Builder<V>>) declaredClass;
88  			}
89  		}
90  		Object[] args = { org.apache.commons.lang3.builder.Builder.class.getCanonicalName(), targetType.getCanonicalName() };
91  		throw illegalState("[%s] is not assignable from any classes declared in [%s]", args);
92  	}
93  
94  	private ToListFunction(Builder<R, C, V> builder) {
95  		this.targetType = builder.targetType;
96  		this.converter = builder.converter;
97  		this.sourceType = builder.sourceType;
98  		this.locale = builder.locale;
99  	}
100 
101 	public static <R, C, V> ToListFunction<R, C, V> create(Class<V> targetType) {
102 		return new Builder<R, C, V>().targetType(targetType).build();
103 	}
104 
105 	public static <R, C, V> Builder<R, C, V> builder() {
106 		return new Builder<R, C, V>();
107 	}
108 
109 	public static class Builder<R, C, V> implements org.apache.commons.lang3.builder.Builder<ToListFunction<R, C, V>> {
110 
111 		private Class<V> targetType;
112 		private ConversionService converter = ToCsvFunction.getConversionService();
113 		private TypeDescriptor sourceType = TypeDescriptor.valueOf(String.class);
114 		private Locale locale = Locale.getDefault();
115 
116 		public Builder<R, C, V> targetType(Class<V> targetType) {
117 			this.targetType = targetType;
118 			return this;
119 		}
120 
121 		public Builder<R, C, V> converter(ConversionService converter) {
122 			this.converter = converter;
123 			return this;
124 		}
125 
126 		public Builder<R, C, V> sourceType(TypeDescriptor sourceType) {
127 			this.sourceType = sourceType;
128 			return this;
129 		}
130 
131 		public Builder<R, C, V> locale(Locale locale) {
132 			this.locale = locale;
133 			return this;
134 		}
135 
136 		@Override
137 		public ToListFunction<R, C, V> build() {
138 			ToListFunction<R, C, V> instance = new ToListFunction<R, C, V>(this);
139 			validate(instance);
140 			return instance;
141 		}
142 
143 		private static <R, C, V> void validate(ToListFunction<R, C, V> instance) {
144 			checkNotNull(instance.targetType, "'targetType' cannot be null");
145 			checkNotNull(instance.converter, "'converter' cannot be null");
146 			checkNotNull(instance.sourceType, "'sourceType' cannot be null");
147 			checkNotNull(instance.locale, "'locale' cannot be null");
148 		}
149 	}
150 
151 }