1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.ancientprogramming.fixedformat4j.format.impl;
17
18 import com.ancientprogramming.fixedformat4j.annotation.*;
19 import com.ancientprogramming.fixedformat4j.exception.FixedFormatException;
20 import com.ancientprogramming.fixedformat4j.format.*;
21 import static com.ancientprogramming.fixedformat4j.format.FixedFormatUtil.fetchData;
22 import static com.ancientprogramming.fixedformat4j.format.FixedFormatUtil.getFixedFormatterInstance;
23 import com.ancientprogramming.fixedformat4j.format.data.FixedFormatBooleanData;
24 import com.ancientprogramming.fixedformat4j.format.data.FixedFormatDecimalData;
25 import com.ancientprogramming.fixedformat4j.format.data.FixedFormatNumberData;
26 import com.ancientprogramming.fixedformat4j.format.data.FixedFormatPatternData;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 import static java.lang.String.format;
32 import java.lang.reflect.Constructor;
33 import java.lang.reflect.Method;
34 import java.util.HashMap;
35 import java.util.Set;
36
37
38
39
40
41
42
43 public class FixedFormatManagerImpl implements FixedFormatManager {
44
45 private static final Log LOG = LogFactory.getLog(FixedFormatManagerImpl.class);
46
47 public <T> T load(Class<T> fixedFormatRecordClass, String data) {
48 HashMap<String, Object> foundData = new HashMap<String, Object>();
49 HashMap<String, Class<?>> methodClass = new HashMap<String, Class<?>>();
50
51 getAndAssertRecordAnnotation(fixedFormatRecordClass);
52
53
54 T instance;
55 try {
56 Constructor<T> constructor = fixedFormatRecordClass.getDeclaredConstructor();
57 instance = constructor.newInstance();
58 } catch (NoSuchMethodException e) {
59
60 Class declaringClass = fixedFormatRecordClass.getDeclaringClass();
61 if (declaringClass != null) {
62 try {
63 Object declaringClassInstance = null;
64 try {
65 Constructor declaringClassConstructor = declaringClass.getDeclaredConstructor();
66 declaringClassInstance = declaringClassConstructor.newInstance();
67 } catch (NoSuchMethodException dex) {
68 throw new FixedFormatException(format("Trying to create instance of innerclass %s, but the declaring class %s is missing a default constructor which is nessesary to be loaded through %s", fixedFormatRecordClass.getName(), declaringClass.getName(), getClass().getName()));
69 } catch (Exception de) {
70 throw new FixedFormatException(format("unable to create instance of declaring class %s, which is needed to instansiate %s", declaringClass.getName(), fixedFormatRecordClass.getName()), e);
71 }
72 Constructor<T> constructor = fixedFormatRecordClass.getDeclaredConstructor(declaringClass);
73 instance = constructor.newInstance(declaringClassInstance);
74 } catch (FixedFormatException ex) {
75 throw ex;
76 } catch (NoSuchMethodException ex) {
77 throw new FixedFormatException(format("%s is missing a default constructor which is nessesary to be loaded through %s", fixedFormatRecordClass.getName(), getClass().getName()));
78 } catch (Exception ex) {
79 throw new FixedFormatException(format("unable to create instance of %s", fixedFormatRecordClass.getName()), e);
80 }
81 } else {
82 throw new FixedFormatException(format("%s is missing a default constructor which is nessesary to be loaded through %s", fixedFormatRecordClass.getName(), getClass().getName()));
83 }
84 } catch (Exception e) {
85 throw new FixedFormatException(format("unable to create instance of %s", fixedFormatRecordClass.getName()), e);
86 }
87
88
89 Method[] allMethods = fixedFormatRecordClass.getMethods();
90 for (Method method : allMethods) {
91 String methodName = stripMethodPrefix(method.getName());
92 Field fieldAnnotation = method.getAnnotation(Field.class);
93 Fields fieldsAnnotation = method.getAnnotation(Fields.class);
94 if (fieldAnnotation != null) {
95 Object loadedData = readDataAccordingFieldAnnotation(fixedFormatRecordClass, data, method, fieldAnnotation);
96 foundData.put(methodName, loadedData);
97 methodClass.put(methodName, method.getReturnType());
98 } else if (fieldsAnnotation != null) {
99
100 if (fieldsAnnotation.value() == null || fieldsAnnotation.value().length == 0) {
101 throw new FixedFormatException(format("%s annotation must contain minimum one %s annotation", Fields.class.getName(), Field.class.getName()));
102 }
103 Object loadedData = readDataAccordingFieldAnnotation(fixedFormatRecordClass, data, method, fieldsAnnotation.value()[0]);
104 foundData.put(methodName, loadedData);
105 methodClass.put(methodName, method.getReturnType());
106 }
107 }
108
109 Set<String> keys = foundData.keySet();
110 for (String key : keys) {
111 String setterMethodName = "set" + key;
112
113 Object foundDataObj = foundData.get(key);
114 if (foundDataObj != null) {
115 Class datatype = methodClass.get(key);
116 Method method;
117 try {
118 method = fixedFormatRecordClass.getMethod(setterMethodName, datatype);
119 } catch (NoSuchMethodException e) {
120 throw new FixedFormatException(format("setter method named %s.%s(%s) does not exist", fixedFormatRecordClass.getName(), setterMethodName, datatype));
121 }
122 try {
123 method.invoke(instance, foundData.get(key));
124 } catch (Exception e) {
125 throw new FixedFormatException(format("could not invoke method %s.%s(%s)", fixedFormatRecordClass.getName(), setterMethodName, datatype), e);
126 }
127 }
128
129 }
130 return instance;
131 }
132
133 public <T> String export(String existingData, T fixedFormatRecord) {
134 StringBuffer result = new StringBuffer(existingData);
135 Record record = getAndAssertRecordAnnotation(fixedFormatRecord.getClass());
136
137 Class fixedFormatRecordClass = fixedFormatRecord.getClass();
138 HashMap<Integer, String> foundData = new HashMap<Integer, String>();
139 Method[] allMethods = fixedFormatRecordClass.getMethods();
140 for (Method method : allMethods) {
141 Field fieldAnnotation = method.getAnnotation(Field.class);
142 Fields fieldsAnnotation = method.getAnnotation(Fields.class);
143 if (fieldAnnotation != null) {
144 String exportedData = exportDataAccordingFieldAnnotation(fixedFormatRecord, method, fieldAnnotation);
145 foundData.put(fieldAnnotation.offset(), exportedData);
146 } else if (fieldsAnnotation != null) {
147 Field[] fields = fieldsAnnotation.value();
148 for (Field field : fields) {
149 String exportedData = exportDataAccordingFieldAnnotation(fixedFormatRecord, method, field);
150 foundData.put(field.offset(), exportedData);
151 }
152 }
153 }
154
155 Set<Integer> sortedoffsets = foundData.keySet();
156 for (Integer offset : sortedoffsets) {
157 String data = foundData.get(offset);
158 appendData(result, record.paddingChar(), offset, data);
159 }
160
161 if (record.length() != -1) {
162 while (result.length() < record.length()) {
163 result.append(record.paddingChar());
164 }
165 }
166 return result.toString();
167 }
168
169 public <T> String export(T fixedFormatRecord) {
170 return export("", fixedFormatRecord);
171 }
172
173 private void appendData(StringBuffer result, Character paddingChar, Integer offset, String data) {
174 int zeroBasedOffset = offset - 1;
175 while (result.length() < zeroBasedOffset) {
176 result.append(paddingChar);
177 }
178 int length = data.length();
179 if (result.length() < zeroBasedOffset + length) {
180 result.append(StringUtils.leftPad("", (zeroBasedOffset + length) - result.length(), paddingChar));
181 }
182 result.replace(zeroBasedOffset, zeroBasedOffset + length, data);
183 }
184
185
186 private <T> Record getAndAssertRecordAnnotation(Class<T> fixedFormatRecordClass) {
187 Record recordAnno = fixedFormatRecordClass.getAnnotation(Record.class);
188 if (recordAnno == null) {
189 throw new FixedFormatException(format("%s has to be marked with the record annotation to be loaded", fixedFormatRecordClass.getName()));
190 }
191 return recordAnno;
192 }
193
194 private <T> Object readDataAccordingFieldAnnotation(Class<T> clazz, String data, Method method, Field fieldAnno) throws ParseException {
195 Class datatype = getDatatype(method, fieldAnno);
196
197 FormatContext context = getFormatContext(datatype, fieldAnno);
198 FixedFormatter formatter = getFixedFormatterInstance(context.getFormatter(), context);
199 FormatInstructions formatdata = getFormatInstructions(method, fieldAnno);
200
201 String dataToParse = fetchData(data, formatdata, context);
202 Object loadedData;
203 try {
204 loadedData = formatter.parse(dataToParse, formatdata);
205 } catch (RuntimeException e) {
206 throw new ParseException(data, dataToParse, clazz, method, context, formatdata, e);
207 }
208 if (LOG.isDebugEnabled()) {
209 LOG.debug("the loaded data[" + loadedData + "]");
210 }
211 return loadedData;
212 }
213
214 private Class getDatatype(Method method, Field fieldAnno) {
215 Class datatype;
216 if (followsBeanStandard(method)) {
217 datatype = method.getReturnType();
218 } else {
219 throw new FixedFormatException(format("Cannot annotate method %s, with %s annotation. %s annotations must be placed on methods starting with 'get' or 'is'", method.getName(), fieldAnno.getClass().getName(), fieldAnno.getClass().getName()));
220 }
221 return datatype;
222 }
223
224 private <T> String exportDataAccordingFieldAnnotation(T fixedFormatRecord, Method method, Field fieldAnno) {
225 String result;
226 Class datatype = getDatatype(method, fieldAnno);
227
228 FormatContext context = getFormatContext(datatype, fieldAnno);
229 FixedFormatter formatter = getFixedFormatterInstance(context.getFormatter(), context);
230 FormatInstructions formatdata = getFormatInstructions(method, fieldAnno);
231 Object valueObject;
232 try {
233 valueObject = method.invoke(fixedFormatRecord);
234 } catch (Exception e) {
235 throw new FixedFormatException(format("could not invoke method %s.%s(%s)", fixedFormatRecord.getClass().getName(), method.getName(), datatype), e);
236 }
237 result = formatter.format(valueObject, formatdata);
238 if (LOG.isDebugEnabled()) {
239 LOG.debug(format("exported %s ", result));
240 }
241 return result;
242 }
243
244 private String stripMethodPrefix(String name) {
245
246 if (name.startsWith("get") || name.startsWith("set")) {
247 return name.substring(3);
248 } else if (name.startsWith("is")) {
249 return name.substring(2);
250 } else {
251 return name;
252 }
253
254 }
255
256
257 private FormatContext getFormatContext(Class datatype, Field fieldAnno) {
258 FormatContext context = null;
259 if (fieldAnno != null) {
260 context = new FormatContext(fieldAnno.offset(), datatype, fieldAnno.formatter());
261 }
262 return context;
263
264 }
265
266 private FormatInstructions getFormatInstructions(Method method, Field fieldAnno) {
267 FixedFormatPatternData patternData = getFixedFormatPatternData(method.getAnnotation(FixedFormatPattern.class));
268 FixedFormatBooleanData booleanData = getFixedFormatBooleanData(method.getAnnotation(FixedFormatBoolean.class));
269 FixedFormatNumberData numberData = getFixedFormatNumberData(method.getAnnotation(FixedFormatNumber.class));
270 FixedFormatDecimalData decimalData = getFixedFormatDecimalData(method.getAnnotation(FixedFormatDecimal.class));
271 return new FormatInstructions(fieldAnno.length(), fieldAnno.align(), fieldAnno.paddingChar(), patternData, booleanData, numberData, decimalData);
272 }
273
274 private FixedFormatPatternData getFixedFormatPatternData(FixedFormatPattern annotation) {
275 FixedFormatPatternData result;
276 if (annotation != null) {
277 result = new FixedFormatPatternData(annotation.value());
278 } else {
279 result = FixedFormatPatternData.DEFAULT;
280 }
281 return result;
282 }
283
284 private FixedFormatBooleanData getFixedFormatBooleanData(FixedFormatBoolean annotation) {
285 FixedFormatBooleanData result;
286 if (annotation != null) {
287 result = new FixedFormatBooleanData(annotation.trueValue(), annotation.falseValue());
288 } else {
289 result = FixedFormatBooleanData.DEFAULT;
290 }
291 return result;
292 }
293
294 private FixedFormatNumberData getFixedFormatNumberData(FixedFormatNumber annotation) {
295 FixedFormatNumberData result;
296 if (annotation != null) {
297 result = new FixedFormatNumberData(annotation.sign(), annotation.positiveSign(), annotation.negativeSign());
298 } else {
299 result = FixedFormatNumberData.DEFAULT;
300 }
301 return result;
302 }
303
304 private FixedFormatDecimalData getFixedFormatDecimalData(FixedFormatDecimal annotation) {
305 FixedFormatDecimalData result;
306 if (annotation != null) {
307 result = new FixedFormatDecimalData(annotation.decimals(), annotation.useDecimalDelimiter(), annotation.decimalDelimiter());
308 } else {
309 result = FixedFormatDecimalData.DEFAULT;
310 }
311 return result;
312 }
313
314 private boolean followsBeanStandard(Method method) {
315 String methodName = method.getName();
316 return methodName.startsWith("get") || methodName.startsWith("is");
317 }
318 }