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