/*
* Copyright (c) 2010-2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.evolveum.midpoint.prism.marshaller;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.xnode.RootXNode;
import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.util.exception.SchemaException;
import org.jetbrains.annotations.NotNull;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
/**
* @author mederly
*/
public class PrismSerializerImpl<T> implements PrismSerializer<T> {
@NotNull private final PrismContextImpl prismContext;
@NotNull private final SerializerTarget<T> target;
private final QName itemName;
private final ItemDefinition itemDefinition;
private final SerializationContext context;
//region Setting up =============================================================================================
public PrismSerializerImpl(@NotNull SerializerTarget<T> target, QName itemName, ItemDefinition itemDefinition,
SerializationContext context, @NotNull PrismContextImpl prismContext) {
this.target = target;
this.itemName = itemName;
this.itemDefinition = itemDefinition;
this.context = context;
this.prismContext = prismContext;
}
@NotNull
@Override
public PrismSerializerImpl<T> context(SerializationContext context) {
return new PrismSerializerImpl<>(this.target, itemName, itemDefinition, context, prismContext);
}
@NotNull
@Override
public PrismSerializerImpl<T> root(QName elementName) {
return new PrismSerializerImpl<>(this.target, elementName, itemDefinition, this.context, prismContext);
}
@NotNull
@Override
public PrismSerializer<T> definition(ItemDefinition itemDefinition) {
return new PrismSerializerImpl<>(this.target, itemName, itemDefinition, this.context, prismContext);
}
@NotNull
@Override
public PrismSerializerImpl<T> options(SerializationOptions options) {
SerializationContext context;
if (this.context != null) {
context = this.context.clone();
context.setOptions(options);
} else {
context = new SerializationContext(options);
}
return new PrismSerializerImpl<>(target, itemName, itemDefinition, context, prismContext);
}
//endregion
//region Serialization =============================================================================================
@NotNull
@Override
public T serialize(@NotNull Item<?, ?> item) throws SchemaException {
RootXNode xroot = getMarshaller().marshalItemAsRoot(item, itemName, itemDefinition, context);
checkPostconditions(xroot); // TODO find better way
return target.write(xroot, context);
}
@NotNull
public T serialize(@NotNull Item<?, ?> item, QName itemName) throws SchemaException {
return root(itemName).serialize(item);
}
@NotNull
@Override
public T serialize(@NotNull PrismValue value) throws SchemaException {
QName nameToUse;
if (itemName != null) {
nameToUse = itemName;
} else if (itemDefinition != null) {
nameToUse = itemDefinition.getName();
} else if (value.getParent() != null) {
nameToUse = value.getParent().getElementName();
} else {
nameToUse = null;
}
// else {
// // TODO derive from the value type itself? Not worth the effort.
// throw new IllegalArgumentException("Item name nor definition is not known for " + value);
// }
RootXNode xroot = getMarshaller().marshalPrismValueAsRoot(value, nameToUse, itemDefinition, context);
checkPostconditions(xroot); // TODO find better way
return target.write(xroot, context);
}
@NotNull
@Override
public T serialize(@NotNull PrismValue value, QName itemName) throws SchemaException {
return root(itemName).serialize(value);
}
@NotNull
@Override
public T serialize(@NotNull RootXNode xnode) throws SchemaException {
return target.write(xnode, context);
}
@Override
public T serializeRealValue(Object realValue) throws SchemaException {
PrismValue prismValue;
if (realValue instanceof Objectable) {
return serialize(((Objectable) realValue).asPrismObject(), itemName); // to preserve OID and name
} else if (realValue instanceof Containerable) {
prismValue = ((Containerable) realValue).asPrismContainerValue();
} else {
prismValue = new PrismPropertyValue<>(realValue);
}
return serialize(prismValue, itemName);
}
@Override
public T serializeRealValue(Object realValue, QName itemName) throws SchemaException {
return root(itemName).serializeRealValue(realValue);
}
@Override
public T serialize(JAXBElement<?> value) throws SchemaException {
return serializeRealValue(value.getValue(), value.getName()); // TODO declared type?
}
@Override
public T serializeAnyData(Object value) throws SchemaException {
RootXNode xnode = getMarshaller().marshalAnyData(value, itemName, itemDefinition, context);
checkPostconditions(xnode); // TODO find better way
return target.write(xnode, context);
}
@Override
public T serializeAnyData(Object value, QName itemName) throws SchemaException {
return root(itemName).serializeAnyData(value);
}
@NotNull
private PrismMarshaller getMarshaller() {
return target.prismContext.getPrismMarshaller();
}
private void checkPostconditions(RootXNode root) {
if (itemName != null && !(root.getRootElementName().equals(itemName))) {
throw new IllegalStateException("Postcondition fail: marshaled root name (" + root.getRootElementName() +
" is different from preset one (" + itemName + ")");
}
if (PrismContextImpl.isExtraValidation()) {
checkTypeResolvable(root);
}
}
private void checkTypeResolvable(RootXNode root) {
root.accept(n -> {
QName type;
if (n instanceof XNode && (type = ((XNode) n).getTypeQName()) != null && ((XNode) n).isExplicitTypeDeclaration()) {
if (prismContext.getSchemaRegistry().determineClassForType(type) == null) {
// it could be sufficient to find a TD
if (prismContext.getSchemaRegistry().findTypeDefinitionByType(type) == null) {
throw new IllegalStateException(
"Postcondition fail: type " + type + " is not resolvable in:\n" + root.debugDump());
}
}
}
});
}
//endregion
}