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.LinkedHashMap;
021import java.util.List;
022import java.util.Map;
023
024import javax.xml.bind.annotation.XmlAccessType;
025import javax.xml.bind.annotation.XmlAccessorType;
026import javax.xml.bind.annotation.XmlAttribute;
027import javax.xml.bind.annotation.XmlElement;
028import javax.xml.bind.annotation.XmlRootElement;
029
030import org.apache.camel.model.DataFormatDefinition;
031import org.apache.camel.model.PropertyDefinition;
032import org.apache.camel.spi.Metadata;
033import org.apache.camel.util.CollectionStringBuffer;
034
035/**
036 * Marshal and unmarshal POJOs to/from XML using <a href="https://x-stream.github.io/">XStream</a> library.
037 */
038@Metadata(firstVersion = "1.3.0", label = "dataformat,transformation,xml,json", title = "XStream")
039@XmlRootElement(name = "xstream")
040@XmlAccessorType(XmlAccessType.NONE)
041public class XStreamDataFormat extends DataFormatDefinition {
042    @XmlAttribute
043    private String permissions;
044    @XmlAttribute
045    private String encoding;
046    @XmlAttribute
047    private String driver;
048    @XmlAttribute
049    private String driverRef;
050    @XmlAttribute
051    private String mode;
052
053    @XmlElement(name = "converters")
054    private List<PropertyDefinition> converters;
055    @XmlElement(name = "aliases")
056    private List<PropertyDefinition> aliases;
057    @XmlElement(name = "omitFields")
058    private List<PropertyDefinition> omitFields;
059    @XmlElement(name = "implicitCollections")
060    private List<PropertyDefinition> implicitCollections;
061
062    public XStreamDataFormat() {
063        super("xstream");
064    }
065
066    public XStreamDataFormat(String encoding) {
067        this();
068        setEncoding(encoding);
069    }
070
071    @Override
072    public String getDataFormatName() {
073        return "json".equals(driver) ? "json-xstream" : "xstream";
074    }
075
076    public String getEncoding() {
077        return encoding;
078    }
079
080    /**
081     * Sets the encoding to use
082     */
083    public void setEncoding(String encoding) {
084        this.encoding = encoding;
085    }
086
087    public String getDriver() {
088        return driver;
089    }
090
091    /**
092     * To use a custom XStream driver. The instance must be of type
093     * com.thoughtworks.xstream.io.HierarchicalStreamDriver
094     */
095    public void setDriver(String driver) {
096        this.driver = driver;
097    }
098
099    public String getDriverRef() {
100        return driverRef;
101    }
102
103    /**
104     * To refer to a custom XStream driver to lookup in the registry. The
105     * instance must be of type
106     * com.thoughtworks.xstream.io.HierarchicalStreamDriver
107     */
108    public void setDriverRef(String driverRef) {
109        this.driverRef = driverRef;
110    }
111
112    public String getMode() {
113        return mode;
114    }
115
116    /**
117     * Mode for dealing with duplicate references The possible values are:
118     * <ul>
119     * <li>NO_REFERENCES</li>
120     * <li>ID_REFERENCES</li>
121     * <li>XPATH_RELATIVE_REFERENCES</li>
122     * <li>XPATH_ABSOLUTE_REFERENCES</li>
123     * <li>SINGLE_NODE_XPATH_RELATIVE_REFERENCES</li>
124     * <li>SINGLE_NODE_XPATH_ABSOLUTE_REFERENCES</li>
125     * </ul>
126     */
127    public void setMode(String mode) {
128        this.mode = mode;
129    }
130
131    public List<PropertyDefinition> getConverters() {
132        return converters;
133    }
134
135    public Map<String, String> getConvertersAsMap() {
136        if (converters == null || converters.isEmpty()) {
137            return null;
138        }
139        Map<String, String> answer = new LinkedHashMap<>();
140        for (PropertyDefinition def : converters) {
141            answer.put(def.getKey(), def.getValue());
142        }
143        return answer;
144    }
145
146    /**
147     * List of class names for using custom XStream converters. The classes must
148     * be of type com.thoughtworks.xstream.converters.Converter
149     */
150    public void setConverters(List<PropertyDefinition> converters) {
151        this.converters = converters;
152    }
153
154    public void setConverters(Map<String, String> converters) {
155        this.converters = new ArrayList<>();
156        converters.forEach((k, v) -> this.converters.add(new PropertyDefinition(k, v)));
157    }
158
159    public List<PropertyDefinition> getAliases() {
160        return aliases;
161    }
162
163    public Map<String, String> getAliasesAsMap() {
164        if (aliases == null || aliases.isEmpty()) {
165            return null;
166        }
167        Map<String, String> answer = new LinkedHashMap<>();
168        for (PropertyDefinition def : aliases) {
169            answer.put(def.getKey(), def.getValue());
170        }
171        return answer;
172    }
173
174    /**
175     * Alias a Class to a shorter name to be used in XML elements.
176     */
177    public void setAliases(List<PropertyDefinition> aliases) {
178        this.aliases = aliases;
179    }
180
181    public void setAliases(Map<String, String> aliases) {
182        this.aliases = new ArrayList<>();
183        aliases.forEach((k, v) -> this.aliases.add(new PropertyDefinition(k, v)));
184    }
185
186    public List<PropertyDefinition> getOmitFields() {
187        return omitFields;
188    }
189
190    /**
191     * Prevents a field from being serialized. To omit a field you must always
192     * provide the declaring type and not necessarily the type that is
193     * converted. Multiple values can be separated by comma.
194     */
195    public void setOmitFields(List<PropertyDefinition> omitFields) {
196        this.omitFields = omitFields;
197    }
198
199    public void setOmitFields(Map<String, String> aliases) {
200        this.omitFields = new ArrayList<>();
201        aliases.forEach((k, v) -> this.omitFields.add(new PropertyDefinition(k, v)));
202    }
203
204    public Map<String, String> getOmitFieldsAsMap() {
205        if (omitFields == null || omitFields.isEmpty()) {
206            return null;
207        }
208        Map<String, String> answer = new LinkedHashMap<>();
209        for (PropertyDefinition def : omitFields) {
210            answer.put(def.getKey(), def.getValue());
211        }
212        return answer;
213    }
214
215    public List<PropertyDefinition> getImplicitCollections() {
216        return implicitCollections;
217    }
218
219    /**
220     * Adds a default implicit collection which is used for any unmapped XML tag.
221     * Multiple values can be separated by comma.
222     */
223    public void setImplicitCollections(List<PropertyDefinition> implicitCollections) {
224        this.implicitCollections = implicitCollections;
225    }
226
227    public void setImplicitCollections(Map<String, String> implicitCollections) {
228        this.implicitCollections = new ArrayList<>();
229        implicitCollections.forEach((k, v) -> this.implicitCollections.add(new PropertyDefinition(k, v)));
230    }
231
232    public Map<String, String> getImplicitCollectionsAsMap() {
233        if (implicitCollections == null || implicitCollections.isEmpty()) {
234            return null;
235        }
236        Map<String, String> answer = new LinkedHashMap<>();
237        for (PropertyDefinition def : implicitCollections) {
238            answer.put(def.getKey(), def.getValue());
239        }
240        return answer;
241    }
242
243    public String getPermissions() {
244        return permissions;
245    }
246
247    /**
248     * Adds permissions that controls which Java packages and classes XStream is
249     * allowed to use during unmarshal from xml/json to Java beans.
250     * <p/>
251     * A permission must be configured either here or globally using a JVM
252     * system property. The permission can be specified in a syntax where a plus
253     * sign is allow, and minus sign is deny. <br/>
254     * Wildcards is supported by using <tt>.*</tt> as prefix. For example to
255     * allow <tt>com.foo</tt> and all subpackages then specify
256     * <tt>+com.foo.*</tt>. Multiple permissions can be configured separated by
257     * comma, such as <tt>+com.foo.*,-com.foo.bar.MySecretBean</tt>. <br/>
258     * The following default permission is always included:
259     * <tt>"-*,java.lang.*,java.util.*"</tt> unless its overridden by specifying
260     * a JVM system property with they key
261     * <tt>org.apache.camel.xstream.permissions</tt>.
262     */
263    public void setPermissions(String permissions) {
264        this.permissions = permissions;
265    }
266
267    /**
268     * To add permission for the given pojo classes.
269     *
270     * @param type the pojo class(es) xstream should use as allowed permission
271     * @see #setPermissions(String)
272     */
273    public void setPermissions(Class<?>... type) {
274        CollectionStringBuffer csb = new CollectionStringBuffer(",");
275        for (Class<?> clazz : type) {
276            csb.append("+");
277            csb.append(clazz.getName());
278        }
279        setPermissions(csb.toString());
280    }
281
282}