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