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.util.Iterator;
021 import java.util.LinkedHashMap;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.TreeMap;
025
026 import org.apache.camel.dataformat.bindy.annotation.KeyValuePairField;
027 import org.apache.camel.dataformat.bindy.annotation.Link;
028 import org.apache.camel.dataformat.bindy.annotation.Message;
029 import org.apache.camel.dataformat.bindy.util.Converter;
030 import org.apache.camel.spi.PackageScanClassResolver;
031 import org.apache.camel.util.ObjectHelper;
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034
035 /**
036 * The BindyKeyValuePairFactory is the class who allows to bind data of type
037 * key value pair. Such format exist in financial messages FIX.
038 * This class allows to generate a model associated to message, bind data from a message
039 * to the POJOs, export data of POJOs to a message and format data
040 * into String, Date, Double, ... according to the format/pattern defined
041 */
042 public class BindyKeyValuePairFactory extends BindyAbstractFactory implements BindyFactory {
043
044 private static final transient Log LOG = LogFactory.getLog(BindyKeyValuePairFactory.class);
045
046 private Map<Integer, KeyValuePairField> mapKeyValuePairField = new LinkedHashMap<Integer, KeyValuePairField>();
047 private Map<Integer, Field> mapAnnotedField = new LinkedHashMap<Integer, Field>();
048
049 private String keyValuePairSeparator;
050 private String pairSeparator;
051
052 public BindyKeyValuePairFactory(PackageScanClassResolver resolver, String packageName) throws Exception {
053
054 super(resolver, packageName);
055
056 // Initialize what is specific to Key Value Pair model
057 initKeyValuePairModel();
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 package
063 * name provided, check the classes and fields annoted. Next, we retrieve the
064 * parameters required like : Pair Separator & key value pair separator
065 *
066 * @throws Exception
067 */
068 public void initKeyValuePairModel() throws Exception {
069
070 // Find annotated KeyValuePairfields declared in the Model classes
071 initAnnotedFields();
072
073 // Initialize key value pair parameter(s)
074 initMessageParameters();
075
076 }
077
078
079 public void initAnnotedFields() {
080
081 for (Class<?> cl : models) {
082
083 for (Field field : cl.getDeclaredFields()) {
084 KeyValuePairField keyValuePairField = field.getAnnotation(KeyValuePairField.class);
085 if (keyValuePairField != null) {
086 if (LOG.isDebugEnabled()) {
087 LOG.debug("Key declared in the class : " + cl.getName() + ", key : "
088 + keyValuePairField.tag() + ", Field : " + keyValuePairField.toString());
089 }
090 mapKeyValuePairField.put(keyValuePairField.tag(), keyValuePairField);
091 mapAnnotedField.put(keyValuePairField.tag(), field);
092 }
093
094 Link linkField = field.getAnnotation(Link.class);
095
096 if (linkField != null) {
097 if (LOG.isDebugEnabled()) {
098 LOG.debug("Class linked : " + cl.getName() + ", Field" + field.toString());
099 }
100 mapAnnotedLinkField.put(cl.getName(), field);
101 }
102 }
103
104 }
105 }
106
107
108 public void bind(List<String> data, Map<String, Object> model) throws Exception {
109
110 int pos = 0;
111
112 if (LOG.isDebugEnabled()) {
113 LOG.debug("Data : " + data);
114 }
115
116 while (pos < data.size()) {
117
118 if (!data.get(pos).equals("")) {
119
120 // Separate the key from its value
121 // e.g 8=FIX 4.1 --> key = 8 and Value = FIX 4.1
122 ObjectHelper.notNull(this.keyValuePairSeparator, "Key Value Pair not defined in the @Message annotation");
123 String[] keyValuePair = data.get(pos).split(this.getKeyValuePairSeparator());
124
125 int tag = Integer.parseInt(keyValuePair[0]);
126 String value = keyValuePair[1];
127
128 if (LOG.isDebugEnabled()) {
129 LOG.debug("Key : " + tag + ", value : " + value);
130 }
131
132 KeyValuePairField keyValuePairField = mapKeyValuePairField.get(tag);
133 ObjectHelper.notNull(keyValuePairField, "No tag defined for the field : " + tag);
134
135 Field field = mapAnnotedField.get(tag);
136 field.setAccessible(true);
137
138 if (LOG.isDebugEnabled()) {
139 LOG.debug("Tag : " + tag + ", Data : " + value + ", Field type : " + field.getType());
140 }
141
142 Format<?> format;
143 String pattern = keyValuePairField.pattern();
144
145 format = FormatFactory.getFormat(field.getType(), pattern, keyValuePairField.precision());
146 field.set(model.get(field.getDeclaringClass().getName()), format.parse(value));
147
148 }
149
150 pos++;
151 }
152
153 }
154
155 public String unbind(Map<String, Object> model) throws Exception {
156
157 StringBuilder builder = new StringBuilder();
158
159 Map<Integer, KeyValuePairField> keyValuePairFields = new TreeMap<Integer, KeyValuePairField>(mapKeyValuePairField);
160 Iterator<Integer> it = keyValuePairFields.keySet().iterator();
161
162 // Check if separator exists
163 ObjectHelper.notNull(this.pairSeparator,
164 "The pair separator has not been instantiated or property not defined in the @Message annotation");
165
166 char separator = Converter.getCharDelimitor(this.getPairSeparator());
167
168 if (LOG.isDebugEnabled()) {
169 LOG.debug("Separator converted : '0x" + Integer.toHexString(separator) + "', from : " + this.getPairSeparator());
170 }
171
172 while (it.hasNext()) {
173
174 KeyValuePairField keyValuePairField = mapKeyValuePairField.get(it.next());
175 ObjectHelper.notNull(keyValuePairField, "KeyValuePair is null !");
176
177 // Retrieve the field
178 Field field = mapAnnotedField.get(keyValuePairField.tag());
179 // Change accessibility to allow to read protected/private fields
180 field.setAccessible(true);
181
182 if (LOG.isDebugEnabled()) {
183 LOG.debug("Tag : " + keyValuePairField.tag() + ", Field type : " + field.getType()
184 + ", class : " + field.getDeclaringClass().getName());
185 }
186
187 // Retrieve the format associated to the type
188 Format format;
189
190 String pattern = keyValuePairField.pattern();
191 format = FormatFactory.getFormat(field.getType(), pattern, keyValuePairField.precision());
192
193 Object obj = model.get(field.getDeclaringClass().getName());
194
195 if (LOG.isDebugEnabled()) {
196 LOG.debug("Model object : " + obj.toString());
197 }
198
199 // Convert the content to a String and append it to the builder
200 // Add the tag followed by its key value pair separator
201 // the data and finish by the pair separator
202 builder.append(keyValuePairField.tag() + this.getKeyValuePairSeparator()
203 + format.format(field.get(obj)) + separator);
204 }
205
206 return builder.toString();
207 }
208
209 /**
210 * Find the pair separator used to delimit the key value pair fields
211 */
212 public String getPairSeparator() {
213 return pairSeparator;
214 }
215
216 /**
217 * Find the key value pair separator used to link the key with its value
218 */
219 public String getKeyValuePairSeparator() {
220 return keyValuePairSeparator;
221 }
222
223 /**
224 * Get parameters defined in @Message annotation
225 */
226 private void initMessageParameters() {
227
228 if ((pairSeparator == null) || (keyValuePairSeparator == null)) {
229
230 for (Class<?> cl : models) {
231
232 // Get annotation @Message from the class
233 Message message = cl.getAnnotation(Message.class);
234
235 if (message != null) {
236
237 // Get Pair Separator parameter
238 ObjectHelper.notNull(message.pairSeparator(),
239 "No Pair Separator has been defined in the @Message annotation !");
240 pairSeparator = message.pairSeparator();
241 if (LOG.isDebugEnabled()) {
242 LOG.debug("Pair Separator defined for the message : " + pairSeparator);
243 }
244
245 // Get KeyValuePair Separator parameter
246 ObjectHelper.notNull(message.keyValuePairSeparator(),
247 "No Key Value Pair Separator has been defined in the @Message annotation !");
248 keyValuePairSeparator = message.keyValuePairSeparator();
249 if (LOG.isDebugEnabled()) {
250 LOG.debug("Key Value Pair Separator defined for the message : "
251 + keyValuePairSeparator);
252 }
253
254 // Get carriage return parameter
255 crlf = message.crlf();
256 if (LOG.isDebugEnabled()) {
257 LOG.debug("Carriage return defined for the message : " + crlf);
258 }
259 }
260 }
261 }
262 }
263 }