View Javadoc
1   package org.csveed.bean;
2   
3   import java.beans.BeanInfo;
4   import java.beans.IntrospectionException;
5   import java.beans.Introspector;
6   import java.beans.PropertyDescriptor;
7   import java.lang.reflect.Field;
8   import java.text.NumberFormat;
9   import java.util.ArrayList;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.Locale;
13  import java.util.Map;
14  import java.util.Set;
15  import java.util.TreeMap;
16  
17  import org.csveed.bean.conversion.Converter;
18  import org.csveed.bean.conversion.CustomNumberConverter;
19  import org.csveed.bean.conversion.DateConverter;
20  import org.csveed.bean.conversion.EnumConverter;
21  import org.csveed.common.Column;
22  import org.csveed.report.CsvException;
23  import org.csveed.report.GeneralError;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  public class BeanProperties implements Iterable<BeanProperty> {
28  
29      private static final Logger LOG = LoggerFactory.getLogger(BeanProperties.class);
30  
31      private List<BeanProperty> properties = new ArrayList<>();
32  
33      private Map<Column, BeanProperty> indexToProperty = new TreeMap<>();
34      private Map<Column, BeanProperty> nameToProperty = new TreeMap<>();
35  
36      private Class beanClass;
37  
38      private BeanProperty headerValueProperty;
39      private BeanProperty headerNameProperty;
40  
41      public BeanProperties(Class beanClass) {
42          this.beanClass = beanClass;
43          parseBean(beanClass);
44      }
45  
46      private void parseBean(Class beanClass) {
47          final BeanInfo beanInfo;
48          try {
49              beanInfo = Introspector.getBeanInfo(beanClass);
50          } catch (IntrospectionException err) {
51              throw new RuntimeException(err);
52          }
53  
54          PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
55  
56          // Note that we use getDeclaredFields here instead of the PropertyDescriptor order. The order we now
57          // use is guaranteed to be the declaration order from JDK 6+, which is exactly what we need.
58          for(Field field : beanClass.getDeclaredFields()) {
59              PropertyDescriptor propertyDescriptor = getPropertyDescriptor(propertyDescriptors, field);
60              if (propertyDescriptor == null || propertyDescriptor.getWriteMethod() == null) {
61                  LOG.info("Skipping {}.{}", beanClass.getName(), field.getName());
62                  continue;
63              }
64              addProperty(propertyDescriptor, field);
65          }
66          
67          if (beanClass.getSuperclass() != null) {
68              parseBean(beanClass.getSuperclass());
69          }
70      }
71  
72      @SuppressWarnings("unchecked")
73      private void addProperty(PropertyDescriptor propertyDescriptor, Field field) {
74          BeanPropertyrty">BeanProperty beanProperty = new BeanProperty();
75          beanProperty.setPropertyDescriptor(propertyDescriptor);
76          beanProperty.setField(field);
77          if (Enum.class.isAssignableFrom(propertyDescriptor.getPropertyType())) {
78              beanProperty.setConverter(new EnumConverter(propertyDescriptor.getPropertyType()));
79          }
80          this.properties.add(beanProperty);
81      }
82  
83      private PropertyDescriptor getPropertyDescriptor(PropertyDescriptor[] propertyDescriptors, Field field) {
84          for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
85              if (field.getName().equals(propertyDescriptor.getName())) {
86                  return propertyDescriptor;
87              }
88          }
89          return null;
90      }
91  
92      public void setRequired(String propertyName, boolean required) {
93          get(propertyName).setRequired(required);
94      }
95  
96      public void setDate(String propertyName, String formatText) {
97          setConverter(propertyName, new DateConverter(formatText, true));
98      }
99  
100     public void setLocalizedNumber(String propertyName, Locale locale) {
101         Class<? extends Number> numberClass = get(propertyName).getNumberClass();
102         if (numberClass == null) {
103             throw new CsvException(new GeneralError(
104                     "Property "+beanClass.getName()+"."+propertyName+" is not a java.lang.Number"
105             ));
106         }
107         CustomNumberConverterhtml#CustomNumberConverter">CustomNumberConverter converter = new CustomNumberConverter(numberClass, NumberFormat.getNumberInstance(locale), true);
108         setConverter(propertyName, converter);
109     }
110 
111     public void setConverter(String propertyName, Converter converter) {
112         get(propertyName).setConverter(converter);
113     }
114 
115     protected void removeFromColumnIndex(BeanProperty property) {
116         while (indexToProperty.values().remove(property));
117     }
118 
119     protected void removeFromColumnName(BeanProperty property) {
120         while (nameToProperty.values().remove(property));
121     }
122 
123     public void ignoreProperty(String propertyName) {
124         BeanProperty property = get(propertyName);
125         properties.remove(property);
126         removeFromColumnIndex(property);
127         removeFromColumnName(property);
128     }
129 
130     public void setHeaderValueProperty(String propertyName) {
131         this.headerValueProperty = get(propertyName);
132         this.headerValueProperty.setDynamicColumnProperty(true);
133     }
134 
135     public void setHeaderNameProperty(String propertyName) {
136         this.headerNameProperty = get(propertyName);
137         this.headerNameProperty.setDynamicColumnProperty(true);
138     }
139 
140     public BeanProperty getHeaderValueProperty() {
141         return this.headerValueProperty;
142     }
143 
144     public BeanProperty getHeaderNameProperty() {
145         return this.headerNameProperty;
146     }
147 
148     public void mapIndexToProperty(int columnIndex, String propertyName) {
149         BeanProperty property = get(propertyName);
150         removeFromColumnIndex(property);
151         property.setColumnIndex(columnIndex);
152         indexToProperty.put(new Column(columnIndex), property);
153     }
154 
155     public void mapNameToProperty(String columnName, String propertyName) {
156         BeanProperty property = get(propertyName);
157         removeFromColumnName(property);
158         property.setColumnName(columnName);
159         nameToProperty.put(new Column(columnName.toLowerCase()), property);
160     }
161 
162     protected BeanProperty get(String propertyName) {
163         for (BeanProperty beanProperty : properties) {
164             if (beanProperty.getPropertyName().equals(propertyName)) {
165                 return beanProperty;
166             }
167         }
168         throw new CsvException(new GeneralError(
169                 "Property does not exist: "+ beanClass.getName()+"."+propertyName
170         ));
171     }
172 
173     public BeanProperty fromIndex(Column column) {
174         return indexToProperty.get(column);
175     }
176 
177     public BeanProperty fromName(Column column) {
178         return nameToProperty.get(column);
179     }
180 
181     @SuppressWarnings("unchecked")
182     @Override
183     public Iterator<BeanProperty> iterator() {
184         return ((List<BeanProperty>)((ArrayList<BeanProperty>)this.properties).clone()).iterator();
185     }
186 
187     public Set<Column> columnIndexKeys() {
188         return this.indexToProperty.keySet();
189     }
190 
191     public Set<Column> columnNameKeys() {
192         return this.nameToProperty.keySet();
193     }
194 
195 }