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
070    public YAMLDataFormat() {
071        this(YAMLLibrary.SnakeYAML);
072    }
073
074    public YAMLDataFormat(YAMLLibrary library) {
075        super("yaml-" + library.name().toLowerCase());
076        this.library = library;
077    }
078
079    public YAMLDataFormat(YAMLLibrary library, Class<?> unmarshalType) {
080        super("yaml-" + library.name().toLowerCase());
081        this.library = library;
082        this.unmarshalType = unmarshalType;
083    }
084
085    public YAMLLibrary getLibrary() {
086        return library;
087    }
088
089    /**
090     * Which yaml library to use.
091     * <p/>
092     * By default it is SnakeYAML
093     */
094    public void setLibrary(YAMLLibrary library) {
095        this.library = library;
096        setDataFormatName("yaml-" + library.name().toLowerCase());
097    }
098
099    public Class<?> getUnmarshalType() {
100        return unmarshalType;
101    }
102
103    /**
104     * Class of the object to be created
105     */
106    public void setUnmarshalType(Class<?> type) {
107        this.unmarshalType = type;
108    }
109
110    public String getUnmarshalTypeName() {
111        return unmarshalTypeName;
112    }
113
114    /**
115     * Class name of the java type to use when unarmshalling
116     */
117    public void setUnmarshalTypeName(String unmarshalTypeName) {
118        this.unmarshalTypeName = unmarshalTypeName;
119    }
120
121    public ClassLoader getClassLoader() {
122        return classLoader;
123    }
124
125    /**
126     * Set a custom classloader
127     */
128    public void setClassLoader(ClassLoader classLoader) {
129        this.classLoader = classLoader;
130    }
131
132    public String getConstructor() {
133        return constructor;
134    }
135
136    /**
137     * BaseConstructor to construct incoming documents.
138     */
139    public void setConstructor(String constructor) {
140        this.constructor = constructor;
141    }
142
143    public String getRepresenter() {
144        return representer;
145    }
146
147    /**
148     * Representer to emit outgoing objects.
149     */
150    public void setRepresenter(String representer) {
151        this.representer = representer;
152    }
153
154    public String getDumperOptions() {
155        return dumperOptions;
156    }
157
158    /**
159     * DumperOptions to configure outgoing objects.
160     */
161    public void setDumperOptions(String dumperOptions) {
162        this.dumperOptions = dumperOptions;
163    }
164
165    public String getResolver() {
166        return resolver;
167    }
168
169    /**
170     * Resolver to detect implicit type
171     */
172    public void setResolver(String resolver) {
173        this.resolver = resolver;
174    }
175
176    public boolean isUseApplicationContextClassLoader() {
177        return useApplicationContextClassLoader;
178    }
179
180    /**
181     * Use ApplicationContextClassLoader as custom ClassLoader
182     */
183    public void setUseApplicationContextClassLoader(boolean useApplicationContextClassLoader) {
184        this.useApplicationContextClassLoader = useApplicationContextClassLoader;
185    }
186
187    public boolean isPrettyFlow() {
188        return prettyFlow;
189    }
190
191    /**
192     * Force the emitter to produce a pretty YAML document when using the flow
193     * style.
194     */
195    public void setPrettyFlow(boolean prettyFlow) {
196        this.prettyFlow = prettyFlow;
197    }
198
199    public boolean isAllowAnyType() {
200        return allowAnyType;
201    }
202
203    /**
204     * Allow any class to be un-marshaled
205     */
206    public void setAllowAnyType(boolean allowAnyType) {
207        this.allowAnyType = allowAnyType;
208    }
209
210    public List<YAMLTypeFilterDefinition> getTypeFilters() {
211        return typeFilters;
212    }
213
214    /**
215     * Set the types SnakeYAML is allowed to un-marshall
216     */
217    public void setTypeFilters(List<YAMLTypeFilterDefinition> typeFilters) {
218        this.typeFilters = typeFilters;
219    }
220
221    @Override
222    protected DataFormat createDataFormat(RouteContext routeContext) {
223        if (library == YAMLLibrary.SnakeYAML) {
224            setProperty(routeContext.getCamelContext(), this, "dataFormatName", "yaml-snakeyaml");
225        }
226
227        return super.createDataFormat(routeContext);
228    }
229
230    @Override
231    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
232        if (library == YAMLLibrary.SnakeYAML) {
233            configureSnakeDataFormat(dataFormat, camelContext);
234        }
235    }
236
237    protected void configureSnakeDataFormat(DataFormat dataFormat, CamelContext camelContext) {
238        Class<?> yamlUnmarshalType =  unmarshalType;
239        if (yamlUnmarshalType == null && unmarshalTypeName != null) {
240            try {
241                yamlUnmarshalType = camelContext.getClassResolver().resolveMandatoryClass(unmarshalTypeName);
242            } catch (ClassNotFoundException e) {
243                throw ObjectHelper.wrapRuntimeCamelException(e);
244            }
245        }
246
247        setProperty(dataFormat, camelContext, "unmarshalType", yamlUnmarshalType);
248        setProperty(dataFormat, camelContext, "classLoader", classLoader);
249        setProperty(dataFormat, camelContext, "useApplicationContextClassLoader", useApplicationContextClassLoader);
250        setProperty(dataFormat, camelContext, "prettyFlow", prettyFlow);
251        setProperty(dataFormat, camelContext, "allowAnyType", allowAnyType);
252
253        if (typeFilters != null && !typeFilters.isEmpty()) {
254            List<String> typeFilterDefinitions = new ArrayList<>(typeFilters.size());
255            for (YAMLTypeFilterDefinition definition : typeFilters) {
256                String value = definition.getValue();
257
258                if (!value.startsWith("type") && !value.startsWith("regexp")) {
259                    YAMLTypeFilterType type = definition.getType();
260                    if (type == null) {
261                        type = YAMLTypeFilterType.type;
262                    }
263
264                    value = type.name() + ":" + value;
265                }
266
267                typeFilterDefinitions.add(value);
268            }
269
270            setProperty(dataFormat, camelContext, "typeFilterDefinitions", typeFilterDefinitions);
271        }
272
273        setPropertyRef(dataFormat, camelContext, "constructor", constructor);
274        setPropertyRef(dataFormat, camelContext, "representer", representer);
275        setPropertyRef(dataFormat, camelContext, "dumperOptions", dumperOptions);
276        setPropertyRef(dataFormat, camelContext, "resolver", resolver);
277    }
278
279    protected void setProperty(DataFormat dataFormat, CamelContext camelContext, String propertyName, Object propertyValue) {
280        if (ObjectHelper.isNotEmpty(propertyValue)) {
281            setProperty(camelContext, dataFormat, propertyName, propertyValue);
282        }
283    }
284
285    protected void setPropertyRef(DataFormat dataFormat, CamelContext camelContext, String propertyName, String propertyValue) {
286        if (ObjectHelper.isNotEmpty(propertyValue)) {
287            // must be a reference value
288            String ref = propertyValue.startsWith("#") ? propertyValue : "#" + propertyValue;
289            setProperty(camelContext, dataFormat, propertyName, ref);
290        }
291    }
292
293}