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
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 }