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    }