001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.dataformat.bindy;
018
019 import java.lang.reflect.Field;
020 import java.math.BigDecimal;
021 import java.math.BigInteger;
022 import java.text.NumberFormat;
023 import java.util.ArrayList;
024 import java.util.Date;
025 import java.util.HashMap;
026 import java.util.LinkedHashMap;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Set;
030
031 import org.apache.camel.dataformat.bindy.format.BigDecimalFormat;
032 import org.apache.camel.dataformat.bindy.format.BigIntegerFormat;
033 import org.apache.camel.dataformat.bindy.format.ByteFormat;
034 import org.apache.camel.dataformat.bindy.format.BytePatternFormat;
035 import org.apache.camel.dataformat.bindy.format.CharacterFormat;
036 import org.apache.camel.dataformat.bindy.format.DatePatternFormat;
037 import org.apache.camel.dataformat.bindy.format.DoubleFormat;
038 import org.apache.camel.dataformat.bindy.format.DoublePatternFormat;
039 import org.apache.camel.dataformat.bindy.format.FloatFormat;
040 import org.apache.camel.dataformat.bindy.format.FloatPatternFormat;
041 import org.apache.camel.dataformat.bindy.format.IntegerFormat;
042 import org.apache.camel.dataformat.bindy.format.IntegerPatternFormat;
043 import org.apache.camel.dataformat.bindy.format.LongFormat;
044 import org.apache.camel.dataformat.bindy.format.LongPatternFormat;
045 import org.apache.camel.dataformat.bindy.format.ShortFormat;
046 import org.apache.camel.dataformat.bindy.format.ShortPatternFormat;
047 import org.apache.camel.dataformat.bindy.format.StringFormat;
048 import org.apache.camel.dataformat.bindy.util.AnnotationModelLoader;
049 import org.apache.camel.spi.PackageScanClassResolver;
050 import org.apache.camel.util.ObjectHelper;
051 import org.apache.commons.logging.Log;
052 import org.apache.commons.logging.LogFactory;
053
054 /**
055 * The BindyAbstractFactory implements what its common to all the formats
056 * supported by camel bindy
057 */
058 public abstract class BindyAbstractFactory implements BindyFactory {
059 private static final transient Log LOG = LogFactory.getLog(BindyAbstractFactory.class);
060 protected Set<Class> models;
061 protected Map<String, List<Field>> annotedLinkFields = new LinkedHashMap<String, List<Field>>();
062 protected List<Field> linkFields = new ArrayList<Field>();
063 protected String crlf;
064
065 private AnnotationModelLoader modelsLoader;
066 private String[] packageNames;
067
068 public BindyAbstractFactory(PackageScanClassResolver resolver, String... packageNames) throws Exception {
069 this.modelsLoader = new AnnotationModelLoader(resolver);
070 this.packageNames = packageNames;
071
072 if (LOG.isDebugEnabled()) {
073 LOG.debug("Package(s) name : " + packageNames.toString());
074 }
075
076 initModel();
077 }
078
079 /**
080 * method uses to initialize the model representing the classes who will
081 * bind the data. This process will scan for classes according to the
082 * package name provided, check the annotated classes and fields.
083 *
084 * @throws Exception
085 */
086 public void initModel() throws Exception {
087 // Find classes defined as Model
088 initModelClasses(this.packageNames);
089 }
090
091 /**
092 * Find all the classes defined as model
093 */
094 private void initModelClasses(String... packageNames) throws Exception {
095 models = modelsLoader.loadModels(packageNames);
096 }
097
098 /**
099 * Find fields annoted in each class of the model
100 */
101 public abstract void initAnnotedFields() throws Exception;
102
103 public abstract void bind(List<String> data, Map<String, Object> model) throws Exception;
104
105 public abstract String unbind(Map<String, Object> model) throws Exception;
106
107 /**
108 * Link objects together
109 */
110 public void link(Map<String, Object> model) throws Exception {
111
112 // Iterate class by class
113 for (String link : annotedLinkFields.keySet()) {
114 List<Field> linkFields = annotedLinkFields.get(link);
115
116 // Iterate through Link fields list
117 for (Field field : linkFields) {
118
119 // Change protection for private field
120 field.setAccessible(true);
121
122 // Retrieve linked object
123 String toClassName = field.getType().getName();
124 Object to = model.get(toClassName);
125
126 ObjectHelper.notNull(to, "No @link annotation has been defined for the oject to link");
127 field.set(model.get(field.getDeclaringClass().getName()), to);
128
129 }
130 }
131 }
132
133 /**
134 * Factory method generating new instances of the model and adding them to a
135 * HashMap
136 *
137 * @return Map is a collection of the objects used to bind data from
138 * records, messages
139 * @throws Exception
140 * can be thrown
141 */
142 public Map<String, Object> factory() throws Exception {
143 Map<String, Object> mapModel = new HashMap<String, Object>();
144
145 for (Class<?> cl : models) {
146 Object obj = ObjectHelper.newInstance(cl);
147
148 // Add instance of the class to the Map Model
149 mapModel.put(obj.getClass().getName(), obj);
150 }
151
152 return mapModel;
153 }
154
155 /**
156 * Generate a unique key
157 *
158 * @param key1
159 * The key of the section number
160 * @param key2
161 * The key of the position of the field
162 * @return the key generated
163 */
164 protected static Integer generateKey(Integer key1, Integer key2) {
165 String key2Formated = getNumberFormat().format((long)key2);
166 String keyGenerated = String.valueOf(key1) + key2Formated;
167
168 return Integer.valueOf(keyGenerated);
169 }
170
171 /**
172 *
173 * @return NumberFormat
174 */
175 private static NumberFormat getNumberFormat() {
176 // Get instance of NumberFormat
177 NumberFormat nf = NumberFormat.getInstance();
178
179 // set max number of digits to 3 (thousands)
180 nf.setMaximumIntegerDigits(3);
181 nf.setMinimumIntegerDigits(3);
182
183 return nf;
184 }
185
186 public static Object getDefaultValueforPrimitive(Class<?> clazz) throws Exception {
187 if (clazz == byte.class || clazz == Byte.class) {
188 return Byte.MIN_VALUE;
189 } else if (clazz == short.class || clazz == Short.class) {
190 return Short.MIN_VALUE;
191 } else if (clazz == int.class || clazz == Integer.class) {
192 return Integer.MIN_VALUE;
193 } else if (clazz == long.class || clazz == Long.class) {
194 return Long.MIN_VALUE;
195 } else if (clazz == float.class || clazz == Float.class) {
196 return Float.MIN_VALUE;
197 } else if (clazz == double.class || clazz == Double.class) {
198 return Double.MIN_VALUE;
199 } else if (clazz == BigDecimal.class) {
200 return BigDecimal.ZERO;
201 } else if (clazz == BigInteger.class) {
202 return BigInteger.ZERO;
203 } else if (clazz == String.class) {
204 return null;
205 } else if (clazz == Date.class) {
206 return null;
207 } else if (clazz == char.class || clazz == Character.class) {
208 return null;
209 } else if (clazz == boolean.class || clazz == Boolean.class) {
210 return false;
211 } else {
212 throw new IllegalArgumentException("Can not find type corresponding : " + clazz.getCanonicalName());
213 }
214 }
215
216 /**
217 * Find the carriage return set
218 */
219 public String getCarriageReturn() {
220 return crlf;
221 }
222 }