You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
644 lines
25 KiB
644 lines
25 KiB
/* |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
* |
|
* Copyright (c) 2006-2017 Oracle and/or its affiliates. All rights reserved. |
|
* |
|
* The contents of this file are subject to the terms of either the GNU |
|
* General Public License Version 2 only ("GPL") or the Common Development |
|
* and Distribution License("CDDL") (collectively, the "License"). You |
|
* may not use this file except in compliance with the License. You can |
|
* obtain a copy of the License at |
|
* https://oss.oracle.com/licenses/CDDL+GPL-1.1 |
|
* or LICENSE.txt. See the License for the specific |
|
* language governing permissions and limitations under the License. |
|
* |
|
* When distributing the software, include this License Header Notice in each |
|
* file and include the License file at LICENSE.txt. |
|
* |
|
* GPL Classpath Exception: |
|
* Oracle designates this particular file as subject to the "Classpath" |
|
* exception as provided by Oracle in the GPL Version 2 section of the License |
|
* file that accompanied this code. |
|
* |
|
* Modifications: |
|
* If applicable, add the following below the License Header, with the fields |
|
* enclosed by brackets [] replaced by your own identifying information: |
|
* "Portions Copyright [year] [name of copyright owner]" |
|
* |
|
* Contributor(s): |
|
* If you wish your version of this file to be governed by only the CDDL or |
|
* only the GPL Version 2, indicate your decision by adding "[Contributor] |
|
* elects to include this software in this distribution under the [CDDL or GPL |
|
* Version 2] license." If you don't indicate a single choice of license, a |
|
* recipient has the option to distribute your version of this file under |
|
* either the CDDL, the GPL Version 2 or to extend the choice of license to |
|
* its licensees as provided above. However, if you add GPL Version 2 code |
|
* and therefore, elected the GPL Version 2 license, then the option applies |
|
* only if the new code is made subject to such option by the copyright |
|
* holder. |
|
*/ |
|
|
|
package javax.xml.bind; |
|
|
|
import javax.xml.bind.annotation.XmlRootElement; |
|
import javax.xml.namespace.QName; |
|
import javax.xml.transform.Result; |
|
import javax.xml.transform.Source; |
|
import javax.xml.transform.stream.StreamResult; |
|
import javax.xml.transform.stream.StreamSource; |
|
import java.beans.Introspector; |
|
import java.io.File; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.io.Reader; |
|
import java.io.Writer; |
|
import java.lang.ref.WeakReference; |
|
import java.net.HttpURLConnection; |
|
import java.net.URI; |
|
import java.net.URISyntaxException; |
|
import java.net.URL; |
|
import java.net.URLConnection; |
|
|
|
/** |
|
* Class that defines convenience methods for common, simple use of JAXB. |
|
* |
|
* <p> |
|
* Methods defined in this class are convenience methods that combine several basic operations |
|
* in the {@link JAXBContext}, {@link Unmarshaller}, and {@link Marshaller}. |
|
* |
|
* They are designed |
|
* to be the prefered methods for developers new to JAXB. They have |
|
* the following characterstics: |
|
* |
|
* <ol> |
|
* <li>Generally speaking, the performance is not necessarily optimal. |
|
* It is expected that people who need to write performance |
|
* critical code will use the rest of the JAXB API directly. |
|
* <li>Errors that happen during the processing is wrapped into |
|
* {@link DataBindingException} (which will have {@link JAXBException} |
|
* as its {@link Throwable#getCause() cause}. It is expected that |
|
* people who prefer the checked exception would use |
|
* the rest of the JAXB API directly. |
|
* </ol> |
|
* |
|
* <p> |
|
* In addition, the {@code unmarshal} methods have the following characteristic: |
|
* |
|
* <ol> |
|
* <li>Schema validation is not performed on the input XML. |
|
* The processing will try to continue even if there |
|
* are errors in the XML, as much as possible. Only as |
|
* the last resort, this method fails with {@link DataBindingException}. |
|
* </ol> |
|
* |
|
* <p> |
|
* Similarly, the {@code marshal} methods have the following characteristic: |
|
* <ol> |
|
* <li>The processing will try to continue even if the Java object tree |
|
* does not meet the validity requirement. Only as |
|
* the last resort, this method fails with {@link DataBindingException}. |
|
* </ol> |
|
* |
|
* |
|
* <p> |
|
* All the methods on this class require non-null arguments to all parameters. |
|
* The {@code unmarshal} methods either fail with an exception or return |
|
* a non-null value. |
|
* |
|
* @author Kohsuke Kawaguchi |
|
* @since 1.6, JAXB 2.1 |
|
*/ |
|
public final class JAXB { |
|
/** |
|
* No instanciation is allowed. |
|
*/ |
|
private JAXB() {} |
|
|
|
/** |
|
* To improve the performance, we'll cache the last {@link JAXBContext} used. |
|
*/ |
|
private static final class Cache { |
|
final Class type; |
|
final JAXBContext context; |
|
|
|
public Cache(Class type) throws JAXBException { |
|
this.type = type; |
|
this.context = JAXBContext.newInstance(type); |
|
} |
|
} |
|
|
|
/** |
|
* Cache. We don't want to prevent the {@link Cache#type} from GC-ed, |
|
* hence {@link WeakReference}. |
|
*/ |
|
private static volatile WeakReference<Cache> cache; |
|
|
|
/** |
|
* Obtains the {@link JAXBContext} from the given type, |
|
* by using the cache if possible. |
|
* |
|
* <p> |
|
* We don't use locks to control access to {@link #cache}, but this code |
|
* should be thread-safe thanks to the immutable {@link Cache} and {@code volatile}. |
|
*/ |
|
private static <T> JAXBContext getContext(Class<T> type) throws JAXBException { |
|
WeakReference<Cache> c = cache; |
|
if(c!=null) { |
|
Cache d = c.get(); |
|
if(d!=null && d.type==type) |
|
return d.context; |
|
} |
|
|
|
// overwrite the cache |
|
Cache d = new Cache(type); |
|
cache = new WeakReference<Cache>(d); |
|
|
|
return d.context; |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* Reads the entire file as XML. |
|
*/ |
|
public static <T> T unmarshal( File xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(new StreamSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The resource pointed by the URL is read in its entirety. |
|
*/ |
|
public static <T> T unmarshal( URL xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The URI is {@link URI#toURL() turned into URL} and then |
|
* follows the handling of {@code URL}. |
|
*/ |
|
public static <T> T unmarshal( URI xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The string is first interpreted as an absolute {@code URI}. |
|
* If it's not {@link URI#isAbsolute() a valid absolute URI}, |
|
* then it's interpreted as a {@code File} |
|
*/ |
|
public static <T> T unmarshal( String xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The entire stream is read as an XML infoset. |
|
* Upon a successful completion, the stream will be closed by this method. |
|
*/ |
|
public static <T> T unmarshal( InputStream xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The character stream is read as an XML infoset. |
|
* The encoding declaration in the XML will be ignored. |
|
* Upon a successful completion, the stream will be closed by this method. |
|
*/ |
|
public static <T> T unmarshal( Reader xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
/** |
|
* Reads in a Java object tree from the given XML input. |
|
* |
|
* @param xml |
|
* The XML infoset that the {@link Source} represents is read. |
|
*/ |
|
public static <T> T unmarshal( Source xml, Class<T> type ) { |
|
try { |
|
JAXBElement<T> item = getContext(type).createUnmarshaller().unmarshal(toSource(xml), type); |
|
return item.getValue(); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
|
|
|
|
/** |
|
* Creates {@link Source} from various XML representation. |
|
* See {@link #unmarshal} for the conversion rules. |
|
*/ |
|
private static Source toSource(Object xml) throws IOException { |
|
if(xml==null) |
|
throw new IllegalArgumentException("no XML is given"); |
|
|
|
if (xml instanceof String) { |
|
try { |
|
xml=new URI((String)xml); |
|
} catch (URISyntaxException e) { |
|
xml=new File((String)xml); |
|
} |
|
} |
|
if (xml instanceof File) { |
|
File file = (File) xml; |
|
return new StreamSource(file); |
|
} |
|
if (xml instanceof URI) { |
|
URI uri = (URI) xml; |
|
xml=uri.toURL(); |
|
} |
|
if (xml instanceof URL) { |
|
URL url = (URL) xml; |
|
return new StreamSource(url.toExternalForm()); |
|
} |
|
if (xml instanceof InputStream) { |
|
InputStream in = (InputStream) xml; |
|
return new StreamSource(in); |
|
} |
|
if (xml instanceof Reader) { |
|
Reader r = (Reader) xml; |
|
return new StreamSource(r); |
|
} |
|
if (xml instanceof Source) { |
|
return (Source) xml; |
|
} |
|
throw new IllegalArgumentException("I don't understand how to handle "+xml.getClass()); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* XML will be written to this file. If it already exists, |
|
* it will be overwritten. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, File xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The XML will be {@link URLConnection#getOutputStream() sent} to the |
|
* resource pointed by this URL. Note that not all {@code URL}s support |
|
* such operation, and exact semantics depends on the {@code URL} |
|
* implementations. In case of {@link HttpURLConnection HTTP URLs}, |
|
* this will perform HTTP POST. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, URL xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The URI is {@link URI#toURL() turned into URL} and then |
|
* follows the handling of {@code URL}. See above. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, URI xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The string is first interpreted as an absolute {@code URI}. |
|
* If it's not {@link URI#isAbsolute() a valid absolute URI}, |
|
* then it's interpreted as a {@code File} |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, String xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The XML will be sent to the given {@link OutputStream}. |
|
* Upon a successful completion, the stream will be closed by this method. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, OutputStream xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The XML will be sent as a character stream to the given {@link Writer}. |
|
* Upon a successful completion, the stream will be closed by this method. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, Writer xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* The XML will be sent to the {@link Result} object. |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
public static void marshal( Object jaxbObject, Result xml ) { |
|
_marshal(jaxbObject,xml); |
|
} |
|
|
|
/** |
|
* Writes a Java object tree to XML and store it to the specified location. |
|
* |
|
* <p> |
|
* This method is a convenience method that combines several basic operations |
|
* in the {@link JAXBContext} and {@link Marshaller}. This method is designed |
|
* to be the prefered method for developers new to JAXB. This method |
|
* has the following characterstics: |
|
* |
|
* <ol> |
|
* <li>Generally speaking, the performance is not necessarily optimal. |
|
* It is expected that those people who need to write performance |
|
* critical code will use the rest of the JAXB API directly. |
|
* <li>Errors that happen during the processing is wrapped into |
|
* {@link DataBindingException} (which will have {@link JAXBException} |
|
* as its {@link Throwable#getCause() cause}. It is expected that |
|
* those people who prefer the checked exception would use |
|
* the rest of the JAXB API directly. |
|
* </ol> |
|
* |
|
* @param jaxbObject |
|
* The Java object to be marshalled into XML. If this object is |
|
* a {@link JAXBElement}, it will provide the root tag name and |
|
* the body. If this object has {@link XmlRootElement} |
|
* on its class definition, that will be used as the root tag name |
|
* and the given object will provide the body. Otherwise, |
|
* the root tag name is {@link Introspector#decapitalize(String) infered} from |
|
* {@link Class#getSimpleName() the short class name}. |
|
* This parameter must not be null. |
|
* |
|
* @param xml |
|
* Represents the receiver of XML. Objects of the following types are allowed. |
|
* |
|
* <table><tr> |
|
* <th>Type</th> |
|
* <th>Operation</th> |
|
* </tr><tr> |
|
* <td>{@link File}</td> |
|
* <td>XML will be written to this file. If it already exists, |
|
* it will be overwritten.</td> |
|
* </tr><tr> |
|
* <td>{@link URL}</td> |
|
* <td>The XML will be {@link URLConnection#getOutputStream() sent} to the |
|
* resource pointed by this URL. Note that not all {@code URL}s support |
|
* such operation, and exact semantics depends on the {@code URL} |
|
* implementations. In case of {@link HttpURLConnection HTTP URLs}, |
|
* this will perform HTTP POST.</td> |
|
* </tr><tr> |
|
* <td>{@link URI}</td> |
|
* <td>The URI is {@link URI#toURL() turned into URL} and then |
|
* follows the handling of {@code URL}. See above.</td> |
|
* </tr><tr> |
|
* <td>{@link String}</td> |
|
* <td>The string is first interpreted as an absolute {@code URI}. |
|
* If it's not {@link URI#isAbsolute() a valid absolute URI}, |
|
* then it's interpreted as a {@code File}</td> |
|
* </tr><tr> |
|
* <td>{@link OutputStream}</td> |
|
* <td>The XML will be sent to the given {@link OutputStream}. |
|
* Upon a successful completion, the stream will be closed by this method.</td> |
|
* </tr><tr> |
|
* <td>{@link Writer}</td> |
|
* <td>The XML will be sent as a character stream to the given {@link Writer}. |
|
* Upon a successful completion, the stream will be closed by this method.</td> |
|
* </tr><tr> |
|
* <td>{@link Result}</td> |
|
* <td>The XML will be sent to the {@link Result} object.</td> |
|
* </tr></table> |
|
* |
|
* @throws DataBindingException |
|
* If the operation fails, such as due to I/O error, unbindable classes. |
|
*/ |
|
private static void _marshal( Object jaxbObject, Object xml ) { |
|
try { |
|
JAXBContext context; |
|
|
|
if(jaxbObject instanceof JAXBElement) { |
|
context = getContext(((JAXBElement<?>)jaxbObject).getDeclaredType()); |
|
} else { |
|
Class<?> clazz = jaxbObject.getClass(); |
|
XmlRootElement r = clazz.getAnnotation(XmlRootElement.class); |
|
context = getContext(clazz); |
|
if(r==null) { |
|
// we need to infer the name |
|
jaxbObject = new JAXBElement(new QName(inferName(clazz)),clazz,jaxbObject); |
|
} |
|
} |
|
|
|
Marshaller m = context.createMarshaller(); |
|
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true); |
|
m.marshal(jaxbObject, toResult(xml)); |
|
} catch (JAXBException e) { |
|
throw new DataBindingException(e); |
|
} catch (IOException e) { |
|
throw new DataBindingException(e); |
|
} |
|
} |
|
|
|
private static String inferName(Class clazz) { |
|
return Introspector.decapitalize(clazz.getSimpleName()); |
|
} |
|
|
|
/** |
|
* Creates {@link Result} from various XML representation. |
|
* See {@link #_marshal(Object,Object)} for the conversion rules. |
|
*/ |
|
private static Result toResult(Object xml) throws IOException { |
|
if(xml==null) |
|
throw new IllegalArgumentException("no XML is given"); |
|
|
|
if (xml instanceof String) { |
|
try { |
|
xml=new URI((String)xml); |
|
} catch (URISyntaxException e) { |
|
xml=new File((String)xml); |
|
} |
|
} |
|
if (xml instanceof File) { |
|
File file = (File) xml; |
|
return new StreamResult(file); |
|
} |
|
if (xml instanceof URI) { |
|
URI uri = (URI) xml; |
|
xml=uri.toURL(); |
|
} |
|
if (xml instanceof URL) { |
|
URL url = (URL) xml; |
|
URLConnection con = url.openConnection(); |
|
con.setDoOutput(true); |
|
con.setDoInput(false); |
|
con.connect(); |
|
return new StreamResult(con.getOutputStream()); |
|
} |
|
if (xml instanceof OutputStream) { |
|
OutputStream os = (OutputStream) xml; |
|
return new StreamResult(os); |
|
} |
|
if (xml instanceof Writer) { |
|
Writer w = (Writer)xml; |
|
return new StreamResult(w); |
|
} |
|
if (xml instanceof Result) { |
|
return (Result) xml; |
|
} |
|
throw new IllegalArgumentException("I don't understand how to handle "+xml.getClass()); |
|
} |
|
|
|
}
|
|
|