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 can be thrown
140 */
141 public Map<String, Object> factory() throws Exception {
142 Map<String, Object> mapModel = new HashMap<String, Object>();
143
144 for (Class<?> cl : models) {
145 Object obj = ObjectHelper.newInstance(cl);
146
147 // Add instance of the class to the Map Model
148 mapModel.put(obj.getClass().getName(), obj);
149 }
150
151 return mapModel;
152 }
153
154 /**
155 * Generate a unique key
156 *
157 * @param key1 The key of the section number
158 * @param key2 The key of the position of the field
159 * @return the key generated
160 */
161 protected static Integer generateKey(Integer key1, Integer key2) {
162 String key2Formated = getNumberFormat().format((long)key2);
163 String keyGenerated = String.valueOf(key1) + key2Formated;
164
165 return Integer.valueOf(keyGenerated);
166 }
167
168 /**
169 * @return NumberFormat
170 */
171 private static NumberFormat getNumberFormat() {
172 // Get instance of NumberFormat
173 NumberFormat nf = NumberFormat.getInstance();
174
175 // set max number of digits to 3 (thousands)
176 nf.setMaximumIntegerDigits(3);
177 nf.setMinimumIntegerDigits(3);
178
179 return nf;
180 }
181
182 /**
183 * Return Default value for primitive type
184 *
185 * @param clazz
186 * @return
187 * @throws Exception
188 */
189 public static Object getDefaultValueforPrimitive(Class<?> clazz) throws Exception {
190
191 if (clazz == byte.class) {
192 return Byte.MIN_VALUE;
193 } else if (clazz == short.class) {
194 return Short.MIN_VALUE;
195 } else if (clazz == int.class) {
196 return Integer.MIN_VALUE;
197 } else if (clazz == long.class) {
198 return Long.MIN_VALUE;
199 } else if (clazz == float.class) {
200 return Float.MIN_VALUE;
201 } else if (clazz == double.class) {
202 return Double.MIN_VALUE;
203 } else if (clazz == char.class) {
204 return Character.MIN_VALUE;
205 } else if (clazz == boolean.class) {
206 return false;
207 } else {
208 return null;
209 }
210
211 }
212
213 /**
214 * Find the carriage return set
215 */
216 public String getCarriageReturn() {
217 return crlf;
218 }
219 }