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