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.text.NumberFormat;
021 import java.util.HashMap;
022 import java.util.LinkedHashMap;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.Set;
026
027 import org.apache.camel.dataformat.bindy.util.AnnotationModelLoader;
028 import org.apache.camel.spi.PackageScanClassResolver;
029 import org.apache.camel.util.ObjectHelper;
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032
033 /**
034 * The {@link BindyAbstractFactory} implements what its common to all the formats
035 * supported by camel bindy
036 */
037 public abstract class BindyAbstractFactory implements BindyFactory {
038 private static final transient Log LOG = LogFactory.getLog(BindyAbstractFactory.class);
039 protected Set<Class<?>> models;
040 protected Map<String, List<Field>> annotedLinkFields = new LinkedHashMap<String, List<Field>>();
041 protected String crlf;
042
043 private AnnotationModelLoader modelsLoader;
044 private String[] packageNames;
045
046 public BindyAbstractFactory(PackageScanClassResolver resolver, String... packageNames) throws Exception {
047 this.modelsLoader = new AnnotationModelLoader(resolver);
048 this.packageNames = packageNames;
049
050 if (LOG.isDebugEnabled()) {
051 for (String str : this.packageNames) {
052 LOG.debug("Package name : " + str);
053 }
054 }
055
056 initModel();
057 }
058
059 /**
060 * method uses to initialize the model representing the classes who will
061 * bind the data. This process will scan for classes according to the
062 * package name provided, check the annotated classes and fields.
063 *
064 * @throws Exception
065 */
066 public void initModel() throws Exception {
067 // Find classes defined as Model
068 initModelClasses(this.packageNames);
069 }
070
071 /**
072 * Find all the classes defined as model
073 */
074 private void initModelClasses(String... packageNames) throws Exception {
075 models = modelsLoader.loadModels(packageNames);
076 }
077
078 /**
079 * Find fields annotated in each class of the model
080 */
081 public abstract void initAnnotedFields() throws Exception;
082
083 public abstract void bind(List<String> data, Map<String, Object> model, int line) throws Exception;
084
085 public abstract String unbind(Map<String, Object> model) throws Exception;
086
087 /**
088 * Link objects together
089 */
090 public void link(Map<String, Object> model) throws Exception {
091
092 // Iterate class by class
093 for (String link : annotedLinkFields.keySet()) {
094 List<Field> linkFields = annotedLinkFields.get(link);
095
096 // Iterate through Link fields list
097 for (Field field : linkFields) {
098
099 // Change protection for private field
100 field.setAccessible(true);
101
102 // Retrieve linked object
103 String toClassName = field.getType().getName();
104 Object to = model.get(toClassName);
105
106 ObjectHelper.notNull(to, "No @link annotation has been defined for the oject to link");
107 field.set(model.get(field.getDeclaringClass().getName()), to);
108 }
109 }
110 }
111
112 /**
113 * Factory method generating new instances of the model and adding them to a
114 * HashMap
115 *
116 * @return Map is a collection of the objects used to bind data from
117 * records, messages
118 * @throws Exception can be thrown
119 */
120 public Map<String, Object> factory() throws Exception {
121 Map<String, Object> mapModel = new HashMap<String, Object>();
122
123 for (Class<?> cl : models) {
124 Object obj = ObjectHelper.newInstance(cl);
125
126 // Add instance of the class to the Map Model
127 mapModel.put(obj.getClass().getName(), obj);
128 }
129
130 return mapModel;
131 }
132
133 /**
134 * Generate a unique key
135 *
136 * @param key1 The key of the section number
137 * @param key2 The key of the position of the field
138 * @return the key generated
139 */
140 protected static Integer generateKey(Integer key1, Integer key2) {
141 String key2Formated;
142 String keyGenerated;
143
144 // Test added for ticket - camel-2773
145 if ((key1 != null) && (key2 != null)) {
146 key2Formated = getNumberFormat().format((long) key2);
147 keyGenerated = String.valueOf(key1) + key2Formated;
148 } else {
149 throw new IllegalArgumentException("@Section and/or @KeyValuePairDataField have not been defined !");
150 }
151
152 return Integer.valueOf(keyGenerated);
153 }
154
155 private static NumberFormat getNumberFormat() {
156 // Get instance of NumberFormat
157 NumberFormat nf = NumberFormat.getInstance();
158
159 // set max number of digits to 3 (thousands)
160 nf.setMaximumIntegerDigits(3);
161 nf.setMinimumIntegerDigits(3);
162
163 return nf;
164 }
165
166 public static Object getDefaultValueForPrimitive(Class<?> clazz) throws Exception {
167 if (clazz == byte.class) {
168 return Byte.MIN_VALUE;
169 } else if (clazz == short.class) {
170 return Short.MIN_VALUE;
171 } else if (clazz == int.class) {
172 return Integer.MIN_VALUE;
173 } else if (clazz == long.class) {
174 return Long.MIN_VALUE;
175 } else if (clazz == float.class) {
176 return Float.MIN_VALUE;
177 } else if (clazz == double.class) {
178 return Double.MIN_VALUE;
179 } else if (clazz == char.class) {
180 return Character.MIN_VALUE;
181 } else if (clazz == boolean.class) {
182 return false;
183 } else {
184 return null;
185 }
186
187 }
188
189 /**
190 * Find the carriage return set
191 */
192 public String getCarriageReturn() {
193 return crlf;
194 }
195
196 /**
197 * Format the object into a string according to the format rue defined
198 *
199 * @param format
200 * @param value
201 * @return String
202 * @throws Exception
203 */
204 public String formatString(Format format, Object value) throws Exception {
205
206 String strValue = "";
207
208 if (value != null) {
209
210 // Format field value
211 try {
212 strValue = format.format(value);
213 } catch (Exception e) {
214 throw new IllegalArgumentException("Formatting error detected for the value : " + value, e);
215 }
216
217 }
218
219 return strValue;
220
221 }
222 }