View Javadoc
1   /*
2    * Copyright 2020-2022  the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.xml;
18  
19  import static org.springframework.util.ObjectUtils.isEmpty;
20  
21  import jakarta.xml.bind.Binder;
22  import jakarta.xml.bind.JAXBContext;
23  import jakarta.xml.bind.JAXBException;
24  import jakarta.xml.bind.JAXBIntrospector;
25  import jakarta.xml.bind.Marshaller;
26  import jakarta.xml.bind.SchemaOutputResolver;
27  import jakarta.xml.bind.Unmarshaller;
28  import jakarta.xml.bind.ValidationEventHandler;
29  import jakarta.xml.bind.annotation.adapters.XmlAdapter;
30  import jakarta.xml.bind.attachment.AttachmentMarshaller;
31  import jakarta.xml.bind.attachment.AttachmentUnmarshaller;
32  import java.io.IOException;
33  import java.nio.charset.StandardCharsets;
34  import java.util.List;
35  import java.util.Optional;
36  import java.util.stream.Stream;
37  import javax.xml.validation.Schema;
38  import lombok.EqualsAndHashCode;
39  import lombok.Getter;
40  import lombok.NonNull;
41  import lombok.Setter;
42  import lombok.ToString;
43  import org.springframework.util.Assert;
44  
45  /**
46   * This {@link JAXBContext} will be returned by the {@link JaxbContextBuilder}.
47   *
48   * @author Christian Bremer
49   */
50  @SuppressWarnings("SameNameButDifferent")
51  @EqualsAndHashCode(callSuper = false)
52  @ToString(exclude = {"jaxbContext"})
53  public class JaxbContextWrapper extends JAXBContext {
54  
55    private final JAXBContext jaxbContext;
56  
57    @Getter
58    private final JaxbContextDetails details;
59  
60    @Getter
61    @Setter
62    private boolean formattedOutput;
63  
64    @Getter
65    @Setter
66    private List<XmlAdapter<?, ?>> xmlAdapters;
67  
68    @Getter
69    @Setter
70    private AttachmentMarshaller attachmentMarshaller;
71  
72    @Getter
73    @Setter
74    private AttachmentUnmarshaller attachmentUnmarshaller;
75  
76    @Getter
77    @Setter
78    private ValidationEventHandler validationEventHandler;
79  
80    @Getter
81    @Setter
82    private Schema schema;
83  
84    @Getter
85    @Setter
86    @NonNull
87    private SchemaMode schemaMode = SchemaMode.NEVER;
88  
89    /**
90     * Instantiates a new jaxb context wrapper.
91     *
92     * @param jaxbContext the jaxb context
93     */
94    public JaxbContextWrapper(
95        JAXBContext jaxbContext) {
96      this(jaxbContext, null);
97    }
98  
99    /**
100    * Instantiates a new jaxb context wrapper.
101    *
102    * @param data the data
103    * @param classLoaders the class loaders
104    * @throws JAXBException the jaxb exception
105    */
106   public JaxbContextWrapper(Stream<JaxbContextData> data, ClassLoader... classLoaders)
107       throws JAXBException {
108     Assert.notNull(data, "Stream of jaxb context data must be present.");
109     this.details = data.collect(JaxbContextDetails.contextDataCollector());
110     Assert.isTrue(!details.isEmpty(), "There is no jaxb model.");
111     this.jaxbContext = JAXBContext.newInstance(this.details.getClasses(classLoaders));
112   }
113 
114   /**
115    * Instantiates a new jaxb context wrapper.
116    *
117    * @param jaxbContext the jaxb context
118    * @param details the details
119    */
120   JaxbContextWrapper(
121       JAXBContext jaxbContext,
122       JaxbContextDetails details) {
123     Assert.notNull(jaxbContext, "Jaxb context must be present.");
124     this.jaxbContext = jaxbContext;
125     this.details = Optional.ofNullable(details)
126         .orElseGet(JaxbContextDetails::empty);
127   }
128 
129   @Override
130   public Unmarshaller createUnmarshaller() throws JAXBException {
131     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
132     if (!isEmpty(xmlAdapters)) {
133       xmlAdapters.forEach(unmarshaller::setAdapter);
134     }
135     if (!isEmpty(attachmentUnmarshaller)) {
136       unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);
137     }
138     if (!isEmpty(schema) && (schemaMode == SchemaMode.ALWAYS
139         || schemaMode == SchemaMode.UNMARSHAL
140         || schemaMode == SchemaMode.EXTERNAL_XSD)
141         && !isEmpty(details.getSchemaLocation())) {
142       unmarshaller.setSchema(schema);
143     }
144     if (!isEmpty(validationEventHandler)) {
145       unmarshaller.setEventHandler(validationEventHandler);
146     }
147     return unmarshaller;
148   }
149 
150   @Override
151   public Marshaller createMarshaller() throws JAXBException {
152     Marshaller marshaller = jaxbContext.createMarshaller();
153     marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name());
154     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formattedOutput);
155     if (!isEmpty(details.getSchemaLocation())) {
156       marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, details.getSchemaLocation());
157     }
158     if (!isEmpty(xmlAdapters)) {
159       xmlAdapters.forEach(marshaller::setAdapter);
160     }
161     if (!isEmpty(attachmentMarshaller)) {
162       marshaller.setAttachmentMarshaller(attachmentMarshaller);
163     }
164     if (!isEmpty(schema) && (schemaMode == SchemaMode.ALWAYS
165         || schemaMode == SchemaMode.MARSHAL
166         || schemaMode == SchemaMode.EXTERNAL_XSD)
167         && !isEmpty(details.getSchemaLocation())) {
168       marshaller.setSchema(schema);
169     }
170     if (!isEmpty(validationEventHandler)) {
171       marshaller.setEventHandler(validationEventHandler);
172     }
173     return marshaller;
174   }
175 
176   @Override
177   public <T> Binder<T> createBinder(Class<T> domType) {
178     return jaxbContext.createBinder(domType);
179   }
180 
181   @Override
182   public JAXBIntrospector createJAXBIntrospector() {
183     return jaxbContext.createJAXBIntrospector();
184   }
185 
186   @Override
187   public void generateSchema(SchemaOutputResolver outputResolver) throws IOException {
188     jaxbContext.generateSchema(outputResolver);
189   }
190 
191 }