se.jguru.shared.jaxb.spi.metro.MetroMarshallerAndUnmarshaller.kt Maven / Gradle / Ivy
/*-
* #%L
* Nazgul Project: jguru-shared-jaxb-spi-metro
* %%
* Copyright (C) 2018 jGuru Europe AB
* %%
* Licensed under the jGuru Europe AB license (the "License"), based
* on Apache License, Version 2.0; you may not use this file except
* in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package se.jguru.shared.jaxb.spi.metro
import com.sun.xml.bind.marshaller.NamespacePrefixMapper
import com.sun.xml.bind.v2.ContextFactory
import se.jguru.shared.algorithms.api.introspection.Introspection
import se.jguru.shared.algorithms.api.xml.MarshallingFormat
import se.jguru.shared.algorithms.api.xml.NamespacePrefixResolver
import se.jguru.shared.algorithms.api.xml.SimpleNamespacePrefixResolver
import se.jguru.shared.jaxb.spi.shared.AbstractMarshallerAndUnmarshaller
import java.io.StringReader
import javax.xml.bind.JAXBContext
import javax.xml.bind.JAXBException
import javax.xml.transform.stream.StreamSource
/**
* [AbstractMarshallerAndUnmarshaller] implementation using the Reference (Metro) implementation.
*
* @param typeInformation a [MutableList] containing classes which should be made available to the [JAXBContext]
* synthesized for marshalling and unmarshalling operations.
* @param jaxbContextProperties A Map relating property names (as Strings) to their respective values (as Objects).
* Valid key/value combinations are implementation-specific; please refer to the actual implementation documentation.
*
* @author [Lennart Jörelid](mailto:[email protected]), jGuru Europe AB
*
* @see EclipseLink
* @see Moxy
*/
open class ReferenceImplementationMarshallerAndUnmarshaller @JvmOverloads constructor(
typeInformation: MutableList> = mutableListOf()) : AbstractMarshallerAndUnmarshaller(
typeInformation,
listOf(MarshallingFormat.XML)) {
override fun performUnmarshalling(loader: ClassLoader,
format: MarshallingFormat,
resultType: Class,
toUnmarshal: String): T {
// Create the unmarshaller
val initialUnmarshaller = createUnmarshaller(getJaxbContext(setOf(resultType)))
val unmarshaller = when (format) {
MarshallingFormat.XML -> initialUnmarshaller
MarshallingFormat.JSON -> throw IllegalArgumentException("Cannot handle JSON MarshallingFormat.")
}
try {
return unmarshaller.unmarshal(StreamSource(StringReader(toUnmarshal)), resultType).value
} catch (e: JAXBException) {
throw IllegalArgumentException("Could not unmarshal ${format.name} into [${resultType.name}]", e)
}
}
override fun performMarshalling(loader: ClassLoader,
format: MarshallingFormat,
toMarshal: Array): String {
// Find the type information, by shallow extraction
val typesToMarshal = Introspection.getTypesFrom(loader, toMarshal)
// Get the Marshaller
val initialMarshaller = createMarshaller(getJaxbContext(typesToMarshal))
initialMarshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
NamespacePrefixMapperWrapper(namespacePrefixResolver))
// Decorate the Marshaller as required
val marshaller = when (format) {
MarshallingFormat.XML -> initialMarshaller
else -> throw IllegalArgumentException("Cannot handle JSON MarshallingFormat.")
}
// All Done.
return doMarshalling(marshaller, toMarshal)
}
/**
* Retrieves the EclipseLink JAXBContext.
*/
protected open fun getJaxbContext(classes: Set>): JAXBContext {
// Join with previously given types
val allClasses = mutableListOf>()
allClasses.addAll(typeInformation)
classes.stream()
.filter { it != null }
.filter { !it.isArray }
.filter { it != Object::class.java }
.forEach { allClasses.add(it) }
// All Done
return ContextFactory.createContext(allClasses.toTypedArray(), jaxbContextProperties)
}
companion object {
/**
* The [JAXBContextFactory] implementation class exposed by Metro.
*/
@JvmStatic
val METRO_JAXB_FACTORY_CLASS = "com.sun.xml.bind.v2.ContextFactory"
/**
* # Note
* Note that using this method is not normally required - instead the [getJaxbContext] method
* will return the correct type. Simply invoke getJaxbContext.
*
* ### This function
* Convenience function which assigns the [JAXBContext.JAXB_CONTEXT_FACTORY] system property
* to the value found in [METRO_JAXB_FACTORY_CLASS].
*
* @return the (pre-)existing value for the JAXBContextFactory system property.
*/
@JvmStatic
fun setupSystemPropertiesForMetro(): String? =
System.setProperty(JAXBContext.JAXB_CONTEXT_FACTORY, METRO_JAXB_FACTORY_CLASS)
}
}
/**
* [NamespacePrefixMapper] implementation which delegates all relevant calls to a [NamespacePrefixResolver].
* The wrapping is required for the JAXB ReferenceImplementation, which requires a [NamespacePrefixMapper] instance
* to relate XML Namespace URIs to their corresponding prefixes.
*
* @param resolver The [NamespacePrefixResolver] which will actually perform the NS resolving. Defaults to
*
* @author [Lennart Jörelid](mailto:[email protected]), jGuru Europe AB
*
* @see [NamespacePrefixResolver]
*/
class NamespacePrefixMapperWrapper(val resolver: NamespacePrefixResolver = SimpleNamespacePrefixResolver())
: NamespacePrefixMapper() {
override fun getPreferredPrefix(namespaceUri: String?, suggestion: String?, requirePrefix: Boolean): String? =
when (namespaceUri) {
null -> null
else -> resolver.getXmlPrefix(namespaceUri)
}
}