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 */
017package org.apache.camel.model.dataformat;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025import javax.xml.bind.annotation.XmlAccessType;
026import javax.xml.bind.annotation.XmlAccessorType;
027import javax.xml.bind.annotation.XmlAttribute;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlRootElement;
030import javax.xml.bind.annotation.XmlTransient;
031import javax.xml.bind.annotation.XmlType;
032import javax.xml.bind.annotation.adapters.XmlAdapter;
033import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
034
035import org.apache.camel.CamelContext;
036import org.apache.camel.model.DataFormatDefinition;
037import org.apache.camel.spi.DataFormat;
038import org.apache.camel.spi.Metadata;
039import org.apache.camel.spi.RouteContext;
040import org.apache.camel.util.CamelContextHelper;
041import org.apache.camel.util.ObjectHelper;
042
043/**
044 * xstream data format
045 *
046 * @version 
047 */
048@Metadata(label = "dataformat,transformation", title = "XStream")
049@XmlRootElement(name = "xstream")
050@XmlAccessorType(XmlAccessType.NONE)
051public class XStreamDataFormat extends DataFormatDefinition {
052    @XmlAttribute
053    private String encoding;
054    @XmlAttribute
055    private String driver;
056    @XmlAttribute
057    private String driverRef;
058    @XmlAttribute
059    private String mode;
060    
061    @XmlJavaTypeAdapter(ConvertersAdapter.class)
062    @XmlElement(name = "converters")
063    private List<String> converters;
064    @XmlJavaTypeAdapter(AliasAdapter.class)
065    @XmlElement(name = "aliases")
066    private Map<String, String> aliases;
067    @XmlJavaTypeAdapter(OmitFieldsAdapter.class)
068    @XmlElement(name = "omitFields")
069    private Map<String, String[]> omitFields;
070    @XmlJavaTypeAdapter(ImplicitCollectionsAdapter.class)
071    @XmlElement(name = "implicitCollections")
072    private Map<String, String[]> implicitCollections;
073
074    public XStreamDataFormat() {
075        super("xstream");
076    }
077    
078    public XStreamDataFormat(String encoding) {
079        this();
080        setEncoding(encoding);
081    }
082    
083    public String getEncoding() {
084        return encoding;
085    }
086
087    /**
088     * Sets the encoding to use
089     */
090    public void setEncoding(String encoding) {
091        this.encoding = encoding;
092    }
093
094    public String getDriver() {
095        return driver;
096    }
097
098    /**
099     * To use a custom XStream driver.
100     * The instance must be of type com.thoughtworks.xstream.io.HierarchicalStreamDriver
101     */
102    public void setDriver(String driver) {
103        this.driver = driver;
104    }
105
106    public String getDriverRef() {
107        return driverRef;
108    }
109
110    /**
111     * To refer to a custom XStream driver to lookup in the registry.
112     * The instance must be of type com.thoughtworks.xstream.io.HierarchicalStreamDriver
113     */
114    public void setDriverRef(String driverRef) {
115        this.driverRef = driverRef;
116    }
117    
118    public String getMode() {
119        return mode;
120    }
121
122    /**
123     * Mode for dealing with duplicate references The possible values are:
124     * <ul>
125     *     <li>NO_REFERENCES</li>
126     *     <li>ID_REFERENCES</li>
127     *     <li>XPATH_RELATIVE_REFERENCES</li>
128     *     <li>XPATH_ABSOLUTE_REFERENCES</li>
129     *     <li>SINGLE_NODE_XPATH_RELATIVE_REFERENCES</li>
130     *     <li>SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES</li>
131     * </ul>
132     */
133    public void setMode(String mode) {
134        this.mode = mode;
135    }
136
137    public List<String> getConverters() {
138        return converters;
139    }
140
141    /**
142     * List of class names for using custom XStream converters.
143     * The classes must be of type com.thoughtworks.xstream.converters.Converter
144     */
145    public void setConverters(List<String> converters) {
146        this.converters = converters;
147    }
148
149    public Map<String, String> getAliases() {
150        return aliases;
151    }
152
153    /**
154     * Alias a Class to a shorter name to be used in XML elements.
155     */
156    public void setAliases(Map<String, String> aliases) {
157        this.aliases = aliases;
158    }
159
160    public Map<String, String[]> getOmitFields() {
161        return omitFields;
162    }
163
164    /**
165     * Prevents a field from being serialized. To omit a field you must always provide the
166     * declaring type and not necessarily the type that is converted.
167     */
168    public void setOmitFields(Map<String, String[]> omitFields) {
169        this.omitFields = omitFields;
170    }
171
172    public Map<String, String[]> getImplicitCollections() {
173        return implicitCollections;
174    }
175
176    /**
177     * Adds a default implicit collection which is used for any unmapped XML tag.
178     */
179    public void setImplicitCollections(Map<String, String[]> implicitCollections) {
180        this.implicitCollections = implicitCollections;
181    }
182
183    @Override
184    protected DataFormat createDataFormat(RouteContext routeContext) {
185        if ("json".equals(this.driver)) {
186            setProperty(routeContext.getCamelContext(), this, "dataFormatName", "json-xstream");
187        }
188        DataFormat answer = super.createDataFormat(routeContext);
189        // need to lookup the reference for the xstreamDriver
190        if (ObjectHelper.isNotEmpty(driverRef)) {
191            setProperty(routeContext.getCamelContext(), answer, "xstreamDriver", CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), driverRef));
192        }
193        return answer;
194    }
195
196    @Override
197    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
198        if (encoding != null) {
199            setProperty(camelContext, dataFormat, "encoding", encoding);
200        }
201        if (this.converters != null) {
202            setProperty(camelContext, dataFormat, "converters", this.converters);
203        }
204        if (this.aliases != null) {
205            setProperty(camelContext, dataFormat, "aliases", this.aliases);
206        }
207        if (this.omitFields != null) {
208            setProperty(camelContext, dataFormat, "omitFields", this.omitFields);
209        }
210        if (this.implicitCollections != null) {
211            setProperty(camelContext, dataFormat, "implicitCollections", this.implicitCollections);
212        }
213        if (this.mode != null) {
214            setProperty(camelContext, dataFormat, "mode", mode);
215        }
216    }
217    
218    
219
220    @XmlTransient
221    public static class ConvertersAdapter extends XmlAdapter<ConverterList, List<String>> {
222        @Override
223        public ConverterList marshal(List<String> v) throws Exception {
224            if (v == null) {
225                return null;
226            }
227
228            List<ConverterEntry> list = new ArrayList<ConverterEntry>();
229            for (String str : v) {
230                ConverterEntry entry = new ConverterEntry();
231                entry.setClsName(str);
232                list.add(entry);
233            }
234            ConverterList converterList = new ConverterList();
235            converterList.setList(list);
236            return converterList;
237        }
238
239        @Override
240        public List<String> unmarshal(ConverterList v) throws Exception {
241            if (v == null) {
242                return null;
243            }
244
245            List<String> list = new ArrayList<String>();
246            for (ConverterEntry entry : v.getList()) {
247                list.add(entry.getClsName());
248            }
249            return list;
250        }
251    }
252
253    @XmlAccessorType(XmlAccessType.NONE)
254    @XmlType(name = "converterList", namespace = "http://camel.apache.org/schema/spring")
255    public static class ConverterList {
256        @XmlElement(name = "converter", namespace = "http://camel.apache.org/schema/spring")
257        private List<ConverterEntry> list;
258
259        public List<ConverterEntry> getList() {
260            return list;
261        }
262
263        public void setList(List<ConverterEntry> list) {
264            this.list = list;
265        }
266    }
267
268    @XmlAccessorType(XmlAccessType.NONE)
269    @XmlType(name = "converterEntry", namespace = "http://camel.apache.org/schema/spring")
270    public static class ConverterEntry {
271        @XmlAttribute(name = "class")
272        private String clsName;
273
274        public String getClsName() {
275            return clsName;
276        }
277
278        public void setClsName(String clsName) {
279            this.clsName = clsName;
280        }
281    }
282
283    @XmlTransient
284    public static class ImplicitCollectionsAdapter 
285            extends XmlAdapter<ImplicitCollectionList, Map<String, String[]>> {
286
287        @Override
288        public ImplicitCollectionList marshal(Map<String, String[]> v) throws Exception {
289            if (v == null || v.isEmpty()) {
290                return null;
291            }
292
293            List<ImplicitCollectionEntry> list = new ArrayList<ImplicitCollectionEntry>();
294            for (Entry<String, String[]> e : v.entrySet()) {
295                ImplicitCollectionEntry entry = new ImplicitCollectionEntry(e.getKey(), e.getValue());
296                list.add(entry);
297            }
298
299            ImplicitCollectionList collectionList = new ImplicitCollectionList();
300            collectionList.setList(list);
301
302            return collectionList;
303        }
304
305        @Override
306        public Map<String, String[]> unmarshal(ImplicitCollectionList v) throws Exception {
307            if (v == null) {
308                return null;
309            }
310
311            Map<String, String[]> map = new HashMap<String, String[]>();
312            for (ImplicitCollectionEntry entry : v.getList()) {
313                map.put(entry.getClsName(), entry.getFields());
314            }
315            return map;
316        }
317    }
318
319    @XmlAccessorType(XmlAccessType.NONE)
320    @XmlType(name = "implicitCollectionList", namespace = "http://camel.apache.org/schema/spring")
321    public static class ImplicitCollectionList {
322        @XmlElement(name = "class", namespace = "http://camel.apache.org/schema/spring")
323        private List<ImplicitCollectionEntry> list;
324
325        public List<ImplicitCollectionEntry> getList() {
326            return list;
327        }
328
329        public void setList(List<ImplicitCollectionEntry> list) {
330            this.list = list;
331        }
332    }
333
334    @XmlAccessorType(XmlAccessType.NONE)
335    @XmlType(name = "implicitCollectionEntry", namespace = "http://camel.apache.org/schema/spring")
336    public static class ImplicitCollectionEntry {
337        @XmlAttribute(name = "name")
338        private String clsName;
339
340        @XmlElement(name = "field", namespace = "http://camel.apache.org/schema/spring")
341        private String[] fields;
342
343        public ImplicitCollectionEntry() {
344        }
345
346        public ImplicitCollectionEntry(String clsName, String[] fields) {
347            this.clsName = clsName;
348            this.fields = fields;
349        }
350
351        public String getClsName() {
352            return clsName;
353        }
354
355        public void setClsName(String clsName) {
356            this.clsName = clsName;
357        }
358
359        public String[] getFields() {
360            return fields;
361        }
362
363        public void setFields(String[] fields) {
364            this.fields = fields;
365        }
366
367        @Override
368        public String toString() {
369            return "Alias[ImplicitCollection=" + clsName + ", fields=" + Arrays.asList(this.fields) + "]";
370        }
371    }
372
373    @XmlTransient
374    public static class AliasAdapter extends XmlAdapter<AliasList, Map<String, String>> {
375
376        @Override
377        public AliasList marshal(Map<String, String> value) throws Exception {
378            if (value == null || value.isEmpty()) {
379                return null;
380            }
381
382            List<AliasEntry> ret = new ArrayList<AliasEntry>(value.size());
383            for (Map.Entry<String, String> entry : value.entrySet()) {
384                ret.add(new AliasEntry(entry.getKey(), entry.getValue()));
385            }
386            AliasList jaxbMap = new AliasList();
387            jaxbMap.setList(ret);
388            return jaxbMap;
389        }
390
391        @Override
392        public Map<String, String> unmarshal(AliasList value) throws Exception {
393            if (value == null || value.getList() == null || value.getList().isEmpty()) {
394                return null;
395            }
396
397            Map<String, String> answer = new HashMap<String, String>();
398            for (AliasEntry alias : value.getList()) {
399                answer.put(alias.getName(), alias.getClsName());
400            }
401            return answer;
402        }
403    }
404
405    @XmlAccessorType(XmlAccessType.NONE)
406    @XmlType(name = "aliasList", namespace = "http://camel.apache.org/schema/spring")
407    public static class AliasList {
408        @XmlElement(name = "alias", namespace = "http://camel.apache.org/schema/spring")
409        private List<AliasEntry> list;
410
411        public List<AliasEntry> getList() {
412            return list;
413        }
414
415        public void setList(List<AliasEntry> list) {
416            this.list = list;
417        }
418    }
419
420    @XmlAccessorType(XmlAccessType.NONE)
421    @XmlType(name = "aliasEntry", namespace = "http://camel.apache.org/schema/spring")
422    public static class AliasEntry {
423
424        @XmlAttribute
425        private String name;
426
427        @XmlAttribute(name = "class")
428        private String clsName;
429
430        public AliasEntry() {
431        }
432
433        public AliasEntry(String key, String clsName) {
434            this.name = key;
435            this.clsName = clsName;
436        }
437
438        public String getName() {
439            return name;
440        }
441
442        public void setName(String name) {
443            this.name = name;
444        }
445
446        public String getClsName() {
447            return clsName;
448        }
449
450        public void setClsName(String clsName) {
451            this.clsName = clsName;
452        }
453
454        @Override
455        public String toString() {
456            return "Alias[name=" + name + ", class=" + clsName + "]";
457        }
458    }
459
460    @XmlTransient
461    public static class OmitFieldsAdapter
462            extends XmlAdapter<OmitFieldList, Map<String, String[]>> {
463
464        @Override
465        public OmitFieldList marshal(Map<String, String[]> v) throws Exception {
466            if (v == null || v.isEmpty()) {
467                return null;
468            }
469
470            List<OmitFieldEntry> list = new ArrayList<OmitFieldEntry>();
471            for (Entry<String, String[]> e : v.entrySet()) {
472                OmitFieldEntry entry = new OmitFieldEntry(e.getKey(), e.getValue());
473                list.add(entry);
474            }
475
476            OmitFieldList collectionList = new OmitFieldList();
477            collectionList.setList(list);
478
479            return collectionList;
480        }
481
482        @Override
483        public Map<String, String[]> unmarshal(OmitFieldList v) throws Exception {
484            if (v == null || v.getList() == null || v.getList().isEmpty()) {
485                return null;
486            }
487
488            Map<String, String[]> map = new HashMap<String, String[]>();
489            for (OmitFieldEntry entry : v.getList()) {
490                map.put(entry.getClsName(), entry.getFields());
491            }
492            return map;
493        }
494    }
495
496    @XmlAccessorType(XmlAccessType.NONE)
497    @XmlType(name = "omitFieldList", namespace = "http://camel.apache.org/schema/spring")
498    public static class OmitFieldList {
499        @XmlElement(name = "omitField", namespace = "http://camel.apache.org/schema/spring")
500        private List<OmitFieldEntry> list;
501
502        public List<OmitFieldEntry> getList() {
503            return list;
504        }
505
506        public void setList(List<OmitFieldEntry> list) {
507            this.list = list;
508        }
509    }
510
511    @XmlAccessorType(XmlAccessType.NONE)
512    @XmlType(name = "omitFieldEntry", namespace = "http://camel.apache.org/schema/spring")
513    public static class OmitFieldEntry {
514
515        @XmlAttribute(name = "class")
516        private String clsName;
517
518        @XmlElement(name = "field", namespace = "http://camel.apache.org/schema/spring")
519        private String[] fields;
520
521        public OmitFieldEntry() {
522        }
523
524        public OmitFieldEntry(String clsName, String[] fields) {
525            this.clsName = clsName;
526            this.fields = fields;
527        }
528
529        public String getClsName() {
530            return clsName;
531        }
532
533        public void setClsName(String clsName) {
534            this.clsName = clsName;
535        }
536
537        public String[] getFields() {
538            return fields;
539        }
540
541        public void setFields(String[] fields) {
542            this.fields = fields;
543        }
544
545        @Override
546        public String toString() {
547            return "OmitField[" + clsName + ", fields=" + Arrays.asList(this.fields) + "]";
548        }
549    }
550
551}