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.List;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlElement;
026import javax.xml.bind.annotation.XmlRootElement;
027import javax.xml.bind.annotation.XmlTransient;
028
029import org.apache.camel.CamelContext;
030import org.apache.camel.model.DataFormatDefinition;
031import org.apache.camel.spi.DataFormat;
032import org.apache.camel.spi.Metadata;
033import org.apache.camel.spi.RouteContext;
034import org.apache.camel.util.ObjectHelper;
035
036/**
037 * YAML is a data format to marshal and unmarshal Java objects to and from YAML.
038 *
039 * @version
040 */
041@Metadata(firstVersion = "2.17.0", label = "dataformat,transformation,yaml", title = "YAML")
042@XmlRootElement(name = "yaml")
043@XmlAccessorType(XmlAccessType.FIELD)
044public class YAMLDataFormat extends DataFormatDefinition {
045    @XmlAttribute @Metadata(defaultValue = "SnakeYAML")
046    private YAMLLibrary library = YAMLLibrary.SnakeYAML;
047    @XmlTransient
048    private ClassLoader classLoader;
049    @XmlTransient
050    private Class<?> unmarshalType;
051    @XmlAttribute
052    private String unmarshalTypeName;
053    @XmlAttribute
054    private String constructor;
055    @XmlAttribute
056    private String representer;
057    @XmlAttribute
058    private String dumperOptions;
059    @XmlAttribute
060    private String resolver;
061    @XmlAttribute @Metadata(defaultValue = "true")
062    private Boolean useApplicationContextClassLoader = true;
063    @XmlAttribute @Metadata(defaultValue = "false")
064    private Boolean prettyFlow = false;
065    @XmlAttribute @Metadata(defaultValue = "false")
066    private Boolean allowAnyType = false;
067    @XmlElement(name = "typeFilter")
068    private List<YAMLTypeFilterDefinition> typeFilters;
069    @XmlAttribute
070    @Metadata(javaType = "java.lang.Integer", defaultValue = "50")
071    private int maxAliasesForCollections = 50;
072    @XmlAttribute
073    @Metadata(javaType = "java.lang.Boolean", defaultValue = "false")
074    private String allowRecursiveKeys;
075
076    public YAMLDataFormat() {
077        this(YAMLLibrary.SnakeYAML);
078    }
079
080    public YAMLDataFormat(YAMLLibrary library) {
081        super("yaml-" + library.name().toLowerCase());
082        this.library = library;
083    }
084
085    public YAMLDataFormat(YAMLLibrary library, Class<?> unmarshalType) {
086        super("yaml-" + library.name().toLowerCase());
087        this.library = library;
088        this.unmarshalType = unmarshalType;
089    }
090
091    public YAMLLibrary getLibrary() {
092        return library;
093    }
094
095    /**
096     * Which yaml library to use.
097     * <p/>
098     * By default it is SnakeYAML
099     */
100    public void setLibrary(YAMLLibrary library) {
101        this.library = library;
102        setDataFormatName("yaml-" + library.name().toLowerCase());
103    }
104
105    public Class<?> getUnmarshalType() {
106        return unmarshalType;
107    }
108
109    /**
110     * Class of the object to be created
111     */
112    public void setUnmarshalType(Class<?> type) {
113        this.unmarshalType = type;
114    }
115
116    public String getUnmarshalTypeName() {
117        return unmarshalTypeName;
118    }
119
120    /**
121     * Class name of the java type to use when unarmshalling
122     */
123    public void setUnmarshalTypeName(String unmarshalTypeName) {
124        this.unmarshalTypeName = unmarshalTypeName;
125    }
126
127    public ClassLoader getClassLoader() {
128        return classLoader;
129    }
130
131    /**
132     * Set a custom classloader
133     */
134    public void setClassLoader(ClassLoader classLoader) {
135        this.classLoader = classLoader;
136    }
137
138    public String getConstructor() {
139        return constructor;
140    }
141
142    /**
143     * BaseConstructor to construct incoming documents.
144     */
145    public void setConstructor(String constructor) {
146        this.constructor = constructor;
147    }
148
149    public String getRepresenter() {
150        return representer;
151    }
152
153    /**
154     * Representer to emit outgoing objects.
155     */
156    public void setRepresenter(String representer) {
157        this.representer = representer;
158    }
159
160    public String getDumperOptions() {
161        return dumperOptions;
162    }
163
164    /**
165     * DumperOptions to configure outgoing objects.
166     */
167    public void setDumperOptions(String dumperOptions) {
168        this.dumperOptions = dumperOptions;
169    }
170
171    public String getResolver() {
172        return resolver;
173    }
174
175    /**
176     * Resolver to detect implicit type
177     */
178    public void setResolver(String resolver) {
179        this.resolver = resolver;
180    }
181
182    public boolean isUseApplicationContextClassLoader() {
183        return useApplicationContextClassLoader;
184    }
185
186    /**
187     * Use ApplicationContextClassLoader as custom ClassLoader
188     */
189    public void setUseApplicationContextClassLoader(boolean useApplicationContextClassLoader) {
190        this.useApplicationContextClassLoader = useApplicationContextClassLoader;
191    }
192
193    public boolean isPrettyFlow() {
194        return prettyFlow;
195    }
196
197    /**
198     * Force the emitter to produce a pretty YAML document when using the flow
199     * style.
200     */
201    public void setPrettyFlow(boolean prettyFlow) {
202        this.prettyFlow = prettyFlow;
203    }
204
205    public boolean isAllowAnyType() {
206        return allowAnyType;
207    }
208
209    /**
210     * Allow any class to be un-marshaled
211     */
212    public void setAllowAnyType(boolean allowAnyType) {
213        this.allowAnyType = allowAnyType;
214    }
215
216    public List<YAMLTypeFilterDefinition> getTypeFilters() {
217        return typeFilters;
218    }
219
220    public int getMaxAliasesForCollections() {
221        return maxAliasesForCollections;
222    }
223
224    /**
225     * Set the maximum amount of aliases allowed for collections.
226     */
227    public void setMaxAliasesForCollections(int maxAliasesForCollections) {
228        this.maxAliasesForCollections = maxAliasesForCollections;
229    }
230
231    public String getAllowRecursiveKeys() {
232        return allowRecursiveKeys;
233    }
234
235    /**
236     * Set whether recursive keys are allowed.
237     */
238    public void setAllowRecursiveKeys(String allowRecursiveKeys) {
239        this.allowRecursiveKeys = allowRecursiveKeys;
240    }
241
242    /**
243     * Set the types SnakeYAML is allowed to un-marshall
244     */
245    public void setTypeFilters(List<YAMLTypeFilterDefinition> typeFilters) {
246        this.typeFilters = typeFilters;
247    }
248
249    @Override
250    protected DataFormat createDataFormat(RouteContext routeContext) {
251        if (library == YAMLLibrary.SnakeYAML) {
252            setProperty(routeContext.getCamelContext(), this, "dataFormatName", "yaml-snakeyaml");
253        }
254
255        return super.createDataFormat(routeContext);
256    }
257
258    @Override
259    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
260        if (library == YAMLLibrary.SnakeYAML) {
261            configureSnakeDataFormat(dataFormat, camelContext);
262        }
263    }
264
265    protected void configureSnakeDataFormat(DataFormat dataFormat, CamelContext camelContext) {
266        Class<?> yamlUnmarshalType =  unmarshalType;
267        if (yamlUnmarshalType == null && unmarshalTypeName != null) {
268            try {
269                yamlUnmarshalType = camelContext.getClassResolver().resolveMandatoryClass(unmarshalTypeName);
270            } catch (ClassNotFoundException e) {
271                throw ObjectHelper.wrapRuntimeCamelException(e);
272            }
273        }
274
275        setProperty(dataFormat, camelContext, "unmarshalType", yamlUnmarshalType);
276        setProperty(dataFormat, camelContext, "classLoader", classLoader);
277        setProperty(dataFormat, camelContext, "useApplicationContextClassLoader", useApplicationContextClassLoader);
278        setProperty(dataFormat, camelContext, "prettyFlow", prettyFlow);
279        setProperty(dataFormat, camelContext, "allowAnyType", allowAnyType);
280
281        if (typeFilters != null && !typeFilters.isEmpty()) {
282            List<String> typeFilterDefinitions = new ArrayList<>(typeFilters.size());
283            for (YAMLTypeFilterDefinition definition : typeFilters) {
284                String value = definition.getValue();
285
286                if (!value.startsWith("type") && !value.startsWith("regexp")) {
287                    YAMLTypeFilterType type = definition.getType();
288                    if (type == null) {
289                        type = YAMLTypeFilterType.type;
290                    }
291
292                    value = type.name() + ":" + value;
293                }
294
295                typeFilterDefinitions.add(value);
296            }
297
298            setProperty(dataFormat, camelContext, "typeFilterDefinitions", typeFilterDefinitions);
299        }
300
301        setPropertyRef(dataFormat, camelContext, "constructor", constructor);
302        setPropertyRef(dataFormat, camelContext, "representer", representer);
303        setPropertyRef(dataFormat, camelContext, "dumperOptions", dumperOptions);
304        setPropertyRef(dataFormat, camelContext, "resolver", resolver);
305    }
306
307    protected void setProperty(DataFormat dataFormat, CamelContext camelContext, String propertyName, Object propertyValue) {
308        if (ObjectHelper.isNotEmpty(propertyValue)) {
309            setProperty(camelContext, dataFormat, propertyName, propertyValue);
310        }
311    }
312
313    protected void setPropertyRef(DataFormat dataFormat, CamelContext camelContext, String propertyName, String propertyValue) {
314        if (ObjectHelper.isNotEmpty(propertyValue)) {
315            // must be a reference value
316            String ref = propertyValue.startsWith("#") ? propertyValue : "#" + propertyValue;
317            setProperty(camelContext, dataFormat, propertyName, ref);
318        }
319    }
320
321}