001
002 /*
003 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004 *
005 * $Id: JiBXUtil.java 292 2012-02-19 21:14:45Z archie.cobbs $
006 */
007
008 package org.dellroad.stuff.jibx;
009
010 import java.io.BufferedWriter;
011 import java.io.IOException;
012 import java.io.InputStream;
013 import java.io.OutputStream;
014 import java.io.OutputStreamWriter;
015 import java.io.StringWriter;
016 import java.io.Writer;
017 import java.net.URL;
018 import java.util.concurrent.Callable;
019
020 import org.dellroad.stuff.java.IdGenerator;
021 import org.jibx.runtime.BindingDirectory;
022 import org.jibx.runtime.IBindingFactory;
023 import org.jibx.runtime.IMarshallable;
024 import org.jibx.runtime.IMarshallingContext;
025 import org.jibx.runtime.IUnmarshallingContext;
026 import org.jibx.runtime.JiBXException;
027
028 /**
029 * Some simplified API methods for JiBX XML encoding/decoding.
030 */
031 public final class JiBXUtil {
032
033 public static final String XML_ENCODING = "UTF-8";
034
035 private JiBXUtil() {
036 }
037
038 /**
039 * Read in an object encoded as XML.
040 * This method assumes there is exactly one binding for the given class.
041 *
042 * <p>
043 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
044 * (see {@link IdMapper}).
045 *
046 * <p>
047 * The {@code input} is not closed by this method.
048 *
049 * @param targetClass target class
050 * @param input source for the XML document
051 * @throws JiBXException if there is a JiBX parse error
052 * @throws IOException if an error occurs reading from {@code input}
053 */
054 public static <T> T readObject(Class<T> targetClass, InputStream input) throws JiBXException, IOException {
055 return JiBXUtil.readObject(targetClass, null, input);
056 }
057
058 /**
059 * Read in an object encoded as XML.
060 *
061 * <p>
062 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
063 * (see {@link IdMapper}).
064 *
065 * <p>
066 * The {@code input} is not closed by this method.
067 *
068 * @param targetClass target class
069 * @param bindingName binding name, or null to choose the only one
070 * @param input source for the XML document
071 * @throws JiBXException if there is a JiBX parse error
072 * @throws IOException if an error occurs reading from {@code input}
073 */
074 public static <T> T readObject(final Class<T> targetClass, String bindingName, final InputStream input)
075 throws JiBXException, IOException {
076 IBindingFactory bindingFactory = bindingName != null ?
077 BindingDirectory.getFactory(bindingName, targetClass) : BindingDirectory.getFactory(targetClass);
078 final IUnmarshallingContext unmarshallingContext = bindingFactory.createUnmarshallingContext();
079 try {
080 return IdGenerator.run(new Callable<T>() {
081 @Override
082 public T call() throws Exception {
083 return targetClass.cast(unmarshallingContext.unmarshalDocument(input, null));
084 }
085 });
086 } catch (Exception e) {
087 JiBXUtil.unwrapException(e);
088 return null; // not reached
089 }
090 }
091
092 /**
093 * Read in an object encoded as XML from an {@link URL}.
094 * This method assumes there is exactly one binding for the given class.
095 *
096 * <p>
097 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
098 * (see {@link IdMapper}).
099 *
100 * @param targetClass target class
101 * @param url source for the XML document
102 * @throws JiBXException if there is a JiBX parse error
103 * @throws IOException if an error occurs reading the referenced document
104 */
105 public static <T> T readObject(Class<T> targetClass, URL url) throws JiBXException, IOException {
106 return JiBXUtil.readObject(targetClass, null, url);
107 }
108
109 /**
110 * Read in an object encoded as XML from an {@link URL}.
111 *
112 * <p>
113 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
114 * (see {@link IdMapper}).
115 *
116 * @param targetClass target class
117 * @param url source for the XML document
118 * @param bindingName binding name, or null to choose the only one
119 * @throws JiBXException if there is a JiBX parse error
120 * @throws IOException if an error occurs reading the referenced document
121 */
122 public static <T> T readObject(Class<T> targetClass, String bindingName, URL url) throws JiBXException, IOException {
123 InputStream in = url.openStream();
124 try {
125 return JiBXUtil.readObject(targetClass, bindingName, in);
126 } finally {
127 try {
128 in.close();
129 } catch (IOException e) {
130 // ignore
131 }
132 }
133 }
134
135 /**
136 * Write out the given instance encoded as a UTF-8 encoded XML document.
137 * This method assumes there is exactly one binding for the given class.
138 *
139 * <p>
140 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
141 * (see {@link IdMapper}).
142 *
143 * @param obj object to write
144 * @param output output destination; will <b>not</b> be closed by this method
145 * @throws JiBXException if there is a JiBX encoding error
146 * @throws IOException if an error occurs writing to {@code output}
147 */
148 public static <T> void writeObject(T obj, OutputStream output) throws JiBXException, IOException {
149 JiBXUtil.writeObject(obj, null, output);
150 }
151
152 /**
153 * Write out the given instance encoded as a UTF-8 encoded XML document.
154 *
155 * <p>
156 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
157 * (see {@link IdMapper}).
158 *
159 * @param obj object to write
160 * @param bindingName binding name, or null to choose the only one
161 * @param output output destination; will <b>not</b> be closed by this method
162 * @throws JiBXException if there is a JiBX encoding error
163 * @throws IOException if an error occurs writing to {@code output}
164 */
165 public static <T> void writeObject(T obj, String bindingName, OutputStream output) throws JiBXException, IOException {
166 JiBXUtil.writeObject(obj, bindingName, new OutputStreamWriter(output, XML_ENCODING));
167 }
168
169 /**
170 * Write out the given instance encoded as an XML document with "UTF-8" as the declared encoding.
171 * This method assumes there is exactly one binding for the given class.
172 *
173 * <p>
174 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
175 * (see {@link IdMapper}).
176 *
177 * @param obj object to write
178 * @param writer output destination; will <b>not</b> be closed by this method
179 */
180 public static <T> void writeObject(T obj, Writer writer) throws JiBXException, IOException {
181 JiBXUtil.writeObject(obj, null, writer);
182 }
183
184 /**
185 * Write out the given instance encoded as an XML document with "UTF-8" as the declared encoding.
186 *
187 * <p>
188 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
189 * (see {@link IdMapper}).
190 *
191 * @param obj object to write
192 * @param bindingName binding name, or null to choose the only one
193 * @param writer output destination; will <b>not</b> be closed by this method
194 */
195 public static void writeObject(final Object obj, String bindingName, final Writer writer) throws JiBXException, IOException {
196 IBindingFactory bindingFactory = bindingName != null ?
197 BindingDirectory.getFactory(bindingName, obj.getClass()) : BindingDirectory.getFactory(obj.getClass());
198 final IMarshallingContext marshallingContext = bindingFactory.createMarshallingContext();
199 try {
200 IdGenerator.run(new Callable<Void>() {
201 @Override
202 public Void call() throws Exception {
203 BufferedWriter bufferedWriter = new BufferedWriter(writer);
204 marshallingContext.setIndent(4);
205 marshallingContext.setOutput(bufferedWriter);
206 marshallingContext.startDocument(XML_ENCODING, null);
207 ((IMarshallable)obj).marshal(marshallingContext);
208 marshallingContext.getXmlWriter().flush();
209 bufferedWriter.newLine();
210 bufferedWriter.flush();
211 return null;
212 }
213 });
214 } catch (Exception e) {
215 JiBXUtil.unwrapException(e);
216 }
217 }
218
219 /**
220 * Encode the given instance as an XML document and return it as a {@link String}.
221 * This method assumes there is exactly one binding for the given class.
222 *
223 * <p>
224 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
225 * (see {@link IdMapper}).
226 *
227 * @param obj object to encode
228 * @throws JiBXException if there is a JiBX encoding error
229 */
230 public static String toString(Object obj) throws JiBXException {
231 return JiBXUtil.toString(obj, null);
232 }
233
234 /**
235 * Encode the given instance as an XML document and return it as a {@link String}.
236 *
237 * <p>
238 * This method runs within a new invocation of {@link IdGenerator#run(Callable) IdGenerator.run()} to support object references
239 * (see {@link IdMapper}).
240 *
241 * @param obj object to encode
242 * @param bindingName binding name, or null to choose the only one
243 * @throws JiBXException if there is a JiBX encoding error
244 */
245 public static String toString(Object obj, String bindingName) throws JiBXException {
246 final StringWriter w = new StringWriter();
247 try {
248 JiBXUtil.writeObject(obj, bindingName, w);
249 w.close();
250 } catch (IOException e) {
251 throw new JiBXException("unexpected exception", e);
252 }
253 return w.toString();
254 }
255
256 private static void unwrapException(Exception e) throws JiBXException, IOException {
257 if (e instanceof JiBXException)
258 throw (JiBXException)e.getCause();
259 if (e instanceof IOException)
260 throw (IOException)e.getCause();
261 if (e instanceof RuntimeException)
262 throw (RuntimeException)e;
263 throw new RuntimeException(e);
264 }
265 }
266