001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.oxm.support;
018
019import java.io.IOException;
020
021import javax.xml.transform.Source;
022import javax.xml.transform.sax.SAXResult;
023import javax.xml.transform.sax.SAXSource;
024
025import org.xml.sax.ContentHandler;
026import org.xml.sax.DTDHandler;
027import org.xml.sax.EntityResolver;
028import org.xml.sax.ErrorHandler;
029import org.xml.sax.InputSource;
030import org.xml.sax.SAXException;
031import org.xml.sax.SAXNotRecognizedException;
032import org.xml.sax.SAXParseException;
033import org.xml.sax.XMLReader;
034import org.xml.sax.ext.LexicalHandler;
035
036import org.springframework.lang.Nullable;
037import org.springframework.oxm.Marshaller;
038import org.springframework.util.Assert;
039
040/**
041 * {@link Source} implementation that uses a {@link Marshaller}.Can be constructed with a
042 * {@code Marshaller} and an object to be marshalled.
043 *
044 * <p>Even though {@code MarshallingSource} extends from {@code SAXSource}, calling the methods of
045 * {@code SAXSource} is <strong>not supported</strong>. In general, the only supported operation on this class is
046 * to use the {@code XMLReader} obtained via {@link #getXMLReader()} to parse the input source obtained via {@link
047 * #getInputSource()}. Calling {@link #setXMLReader(XMLReader)} or {@link #setInputSource(InputSource)} will result in
048 * {@code UnsupportedOperationException}s.
049 *
050 * @author Arjen Poutsma
051 * @since 3.0
052 * @see javax.xml.transform.Transformer
053 */
054public class MarshallingSource extends SAXSource {
055
056        private final Marshaller marshaller;
057
058        private final Object content;
059
060
061        /**
062         * Create a new {@code MarshallingSource} with the given marshaller and content.
063         * @param marshaller the marshaller to use
064         * @param content the object to be marshalled
065         */
066        public MarshallingSource(Marshaller marshaller, Object content) {
067                super(new MarshallingXMLReader(marshaller, content), new InputSource());
068                Assert.notNull(marshaller, "'marshaller' must not be null");
069                Assert.notNull(content, "'content' must not be null");
070                this.marshaller = marshaller;
071                this.content = content;
072        }
073
074
075        /**
076         * Return the {@code Marshaller} used by this {@code MarshallingSource}.
077         */
078        public Marshaller getMarshaller() {
079                return this.marshaller;
080        }
081
082        /**
083         * Return the object to be marshalled.
084         */
085        public Object getContent() {
086                return this.content;
087        }
088
089        /**
090         * Throws a {@code UnsupportedOperationException}.
091         */
092        @Override
093        public void setInputSource(InputSource inputSource) {
094                throw new UnsupportedOperationException("setInputSource is not supported");
095        }
096
097        /**
098         * Throws a {@code UnsupportedOperationException}.
099         */
100        @Override
101        public void setXMLReader(XMLReader reader) {
102                throw new UnsupportedOperationException("setXMLReader is not supported");
103        }
104
105
106        private static final class MarshallingXMLReader implements XMLReader {
107
108                private final Marshaller marshaller;
109
110                private final Object content;
111
112                @Nullable
113                private DTDHandler dtdHandler;
114
115                @Nullable
116                private ContentHandler contentHandler;
117
118                @Nullable
119                private EntityResolver entityResolver;
120
121                @Nullable
122                private ErrorHandler errorHandler;
123
124                @Nullable
125                private LexicalHandler lexicalHandler;
126
127                private MarshallingXMLReader(Marshaller marshaller, Object content) {
128                        Assert.notNull(marshaller, "'marshaller' must not be null");
129                        Assert.notNull(content, "'content' must not be null");
130                        this.marshaller = marshaller;
131                        this.content = content;
132                }
133
134                @Override
135                public void setContentHandler(@Nullable ContentHandler contentHandler) {
136                        this.contentHandler = contentHandler;
137                }
138
139                @Override
140                @Nullable
141                public ContentHandler getContentHandler() {
142                        return this.contentHandler;
143                }
144
145                @Override
146                public void setDTDHandler(@Nullable DTDHandler dtdHandler) {
147                        this.dtdHandler = dtdHandler;
148                }
149
150                @Override
151                @Nullable
152                public DTDHandler getDTDHandler() {
153                        return this.dtdHandler;
154                }
155
156                @Override
157                public void setEntityResolver(@Nullable EntityResolver entityResolver) {
158                        this.entityResolver = entityResolver;
159                }
160
161                @Override
162                @Nullable
163                public EntityResolver getEntityResolver() {
164                        return this.entityResolver;
165                }
166
167                @Override
168                public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
169                        this.errorHandler = errorHandler;
170                }
171
172                @Override
173                @Nullable
174                public ErrorHandler getErrorHandler() {
175                        return this.errorHandler;
176                }
177
178                @Nullable
179                protected LexicalHandler getLexicalHandler() {
180                        return this.lexicalHandler;
181                }
182
183                @Override
184                public boolean getFeature(String name) throws SAXNotRecognizedException {
185                        throw new SAXNotRecognizedException(name);
186                }
187
188                @Override
189                public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
190                        throw new SAXNotRecognizedException(name);
191                }
192
193                @Override
194                @Nullable
195                public Object getProperty(String name) throws SAXNotRecognizedException {
196                        if ("http://xml.org/sax/properties/lexical-handler".equals(name)) {
197                                return this.lexicalHandler;
198                        }
199                        else {
200                                throw new SAXNotRecognizedException(name);
201                        }
202                }
203
204                @Override
205                public void setProperty(String name, Object value) throws SAXNotRecognizedException {
206                        if ("http://xml.org/sax/properties/lexical-handler".equals(name)) {
207                                this.lexicalHandler = (LexicalHandler) value;
208                        }
209                        else {
210                                throw new SAXNotRecognizedException(name);
211                        }
212                }
213
214                @Override
215                public void parse(InputSource input) throws SAXException {
216                        parse();
217                }
218
219                @Override
220                public void parse(String systemId) throws SAXException {
221                        parse();
222                }
223
224                private void parse() throws SAXException {
225                        SAXResult result = new SAXResult(getContentHandler());
226                        result.setLexicalHandler(getLexicalHandler());
227                        try {
228                                this.marshaller.marshal(this.content, result);
229                        }
230                        catch (IOException ex) {
231                                SAXParseException saxException = new SAXParseException(ex.getMessage(), null, null, -1, -1, ex);
232                                ErrorHandler errorHandler = getErrorHandler();
233                                if (errorHandler != null) {
234                                        errorHandler.fatalError(saxException);
235                                }
236                                else {
237                                        throw saxException;
238                                }
239                        }
240                }
241        }
242
243}