/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.xml.ws.server.marshal;
import gw.internal.schema.gw.xsd.w3c.xmlschema.ComplexType;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Schema;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Sequence;
import gw.internal.schema.gw.xsd.w3c.xmlschema.anonymous.elements.ExplicitGroup_Element;
import gw.internal.schema.gw.xsd.w3c.xmlschema.types.complex.LocalElement;
import gw.internal.xml.Marshaller;
import gw.internal.xml.config.XmlServices;
import gw.internal.xml.ws.WsiAdditions;
import gw.internal.xml.ws.server.WsiServiceInfo;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.resources.Res;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuExceptionUtil;
import gw.util.Pair;
import gw.util.concurrent.LocklessLazyVar;
import gw.xml.XmlElement;
import gw.xml.XmlSchemaAccess;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public abstract class ClassBasedMarshalInfo extends MarshalInfo implements AdditionalCheckType {
static final LocklessLazyVar<URI> GW_XSD_NAMESPACE = new LocklessLazyVar<URI>() {
@Override
protected URI init() {
try {
return new URI("http://guidewire.com/xsd");
} catch (URISyntaxException e) {
throw new RuntimeException("could not create a URI", e);
}
}
};
protected final IType _type;
protected boolean _isComponent;
public ClassBasedMarshalInfo(IType type, boolean isComponent) {
super(type);
_isComponent = isComponent;
_type = type;
}
static boolean isExportable(IType type) {
return RemotableMarshalInfo._gwRemotableType.get().isAssignableFrom(type);
}
@Override
public Map<String,Set<XmlSchemaAccess>> getAllSchemas() {
// TODO required when added xml elements and/or types
return new HashMap<String, Set<XmlSchemaAccess>>();
}
@Override
public void addType( LocalElement element, WsiServiceInfo createInfo ) throws Exception {
if ( _isComponent ) {
element.setNillable$( true );
}
else {
element.setMinOccurs$( BigInteger.ZERO );
}
ComplexType complexType = createInfo.getComplexTypeIfNeededFor(_type);
if (complexType != null) {
Sequence sequence = new Sequence();
complexType.setSequence$(sequence);
boolean addedGWXsd = false;
for (Map.Entry<String,IPropertyInfo> prop : getProperties(_type).entrySet()) {
ExplicitGroup_Element typeEl = new ExplicitGroup_Element();
String name = prop.getKey();
typeEl.setName$(name);
final IType featureType = prop.getValue().getFeatureType();
if (!addedGWXsd && JavaTypes.DATE().isAssignableFrom(featureType)) {
addGWNamespaceIfNotAlreadyPresent(createInfo.getSchemaFor(WsiAdditions.getInstance().getTargetNamespace(_type)));
addedGWXsd = true;
}
XmlMarshaller.addType(featureType, typeEl.getTypeInstance(), createInfo );
if (featureType.isPrimitive()) {
typeEl.setMinOccurs$( BigInteger.ZERO );
}
sequence.Element().add( typeEl );
}
}
element.setType$( createInfo.getQName(_type) );
}
protected abstract Map<String,IPropertyInfo> getProperties(IType type);
public static void addGWNamespaceIfNotAlreadyPresent(Schema schema) {
for (Pair<String, URI> entry : schema.getDeclaredNamespaces()) {
if (entry.getSecond().equals(GW_XSD_NAMESPACE.get())) {
return;
}
}
schema.declareNamespace(GW_XSD_NAMESPACE.get(), "gw");
}
@Override
public Object unmarshal( XmlElement componentElement, UnmarshalContext context ) {
URI nsURI;
try {
nsURI = new URI( WsiAdditions.getInstance().getTargetNamespace( _type ) );
}
catch ( URISyntaxException e ) {
throw GosuExceptionUtil.forceThrow(e);
}
Object obj = null;
if (componentElement != null) {
obj = _type.getTypeInfo().getConstructor().getConstructor().newInstance();
for (Map.Entry<String,IPropertyInfo> prop : getProperties(_type).entrySet()) {
XmlElement child = componentElement.getChild(new QName(nsURI.toString(), prop.getKey()));
if (child != null) {
Object childObj = XmlServices.unmarshal(prop.getValue().getFeatureType(), child, context);
if (childObj != null) {
prop.getValue().getAccessor().setValue(obj, childObj);
}
}
}
}
return obj;
}
@Override
public void marshal(XmlElement returnEl, IType type, Object obj, MarshalContext context) {
if (obj != null && context.getSeen().contains(obj)) {
throw new RuntimeException("Recursion on " + obj);
}
context.getSeen().add(obj);
URI nsURI;
try {
nsURI = new URI( WsiAdditions.getInstance().getTargetNamespace( type ) );
returnEl.declareNamespace(nsURI, "pogo");
} catch (URISyntaxException e) {
throw new RuntimeException("on " + returnEl, e);
}
for (Map.Entry<String,IPropertyInfo> prop : getProperties(_type).entrySet()) {
Object childObj = prop.getValue().getAccessor().getValue(obj);
if (childObj != null) {
XmlElement childEl = new XmlElement(new QName(nsURI.toString(), prop.getKey()));
returnEl.addChild(childEl);
XmlServices.marshal(childEl, prop.getValue().getFeatureType(), childObj, context);
}
}
context.getSeen().remove(obj);
}
@Override
public void checkType(Marshaller marshaller, IParsedElement parsedElement, String label, IType type, Map<String, Object> seenNamespaces) {
if (!type.isValid() && !type.equals(_type)) {
parsedElement.addParseException(Res.WS_ERR_Can_Not_Marshal, type.getDisplayName(), label );
}
else {
for (Map.Entry<String,IPropertyInfo> prop : getProperties(type).entrySet()) {
String newLabel = label + (label.equals("") ? "" : ".") + prop.getKey();
IType partType = prop.getValue().getFeatureType();
if (!_type.equals(partType) && isExportable(partType)) {
// do not recurse
}
else if (_type.equals(getComponentType(partType))) { // test this before recursing into checkType
parsedElement.addParseWarning(Res.WS_ERR_Export_Recursive, newLabel );
}
else {
MarshalInfo marshalInfo = marshaller.checkType(parsedElement, newLabel, prop.getValue().getFeatureType(), seenNamespaces);
checkMarshalInfoType(parsedElement, prop, newLabel, marshalInfo);
}
}
}
}
private IType getComponentType(IType partType) {
if ( partType.isArray()) {
return getComponentType(partType.getComponentType());
}
if ( JavaTypes.LIST().isAssignableFrom( partType )) {
IType[] parameters = partType.getTypeParameters();
return parameters.length != 1 ? partType : getComponentType(parameters[0]);
}
return partType;
}
private void checkMarshalInfoType(IParsedElement parsedElement, Map.Entry<String, IPropertyInfo> prop, String newLabel, MarshalInfo marshalInfo) {
if (marshalInfo instanceof SimpleValueMarshalInfo) {
}
else if (marshalInfo instanceof EnumMarshalInfo) {
}
else if (marshalInfo instanceof RemotableMarshalInfo) {
}
else if (marshalInfo instanceof ExportableMarshalInfo) {
}
else if (marshalInfo instanceof ArrayMarshalInfo) {
ArrayMarshalInfo ami = (ArrayMarshalInfo) marshalInfo;
MarshalInfo componentMI = ami.getComponentMarshalInfo();
checkMarshalInfoType(parsedElement, prop, newLabel + "[]", componentMI);
}
else if (marshalInfo instanceof ListMarshalInfo) {
ListMarshalInfo lmi = (ListMarshalInfo) marshalInfo;
MarshalInfo componentMI = lmi.getComponentMarshalInfo();
checkMarshalInfoType(parsedElement, prop, newLabel + "[]", componentMI);
}
else if (marshalInfo instanceof XmlElementMarshalInfo) {
// TODO when you change this you need to also deal with seen type names in getAllSchemas()
parsedElement.addParseException(Res.WS_ERR_Can_Not_Marshal, prop.getValue().getDisplayName(), newLabel );
}
else if (marshalInfo instanceof XmlTypeInstanceMarshalInfo) {
// TODO when you change this need to also deal with seen type names in getAllSchemas()
parsedElement.addParseException(Res.WS_ERR_Can_Not_Marshal, prop.getValue().getDisplayName(), newLabel );
}
else if (marshalInfo != null) {
// These other types should just be supported
parsedElement.addParseException(Res.WS_ERR_Can_Not_Marshal, prop.getValue().getDisplayName(), newLabel );
}
}
}