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    }