com.evolveum.midpoint.prism.parser.PrismBeanConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.prism.parser.PrismBeanConverter.java

Source

/*
 * Copyright (c) 2014 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.parser;

import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.Raw;
import com.evolveum.midpoint.prism.Revivable;
import com.evolveum.midpoint.prism.SerializationContext;
import com.evolveum.midpoint.prism.parser.util.XNodeProcessorUtil;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.prism.xnode.ListXNode;
import com.evolveum.midpoint.prism.xnode.MapXNode;
import com.evolveum.midpoint.prism.xnode.PrimitiveXNode;
import com.evolveum.midpoint.prism.xnode.RootXNode;
import com.evolveum.midpoint.prism.xnode.SchemaXNode;
import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.Handler;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.exception.TunnelException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedByteArrayType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import com.evolveum.prism.xml.ns._public.types_3.RawType;
import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType;
import com.evolveum.prism.xml.ns._public.types_3.XmlAsStringType;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Element;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class PrismBeanConverter {

    private static final Trace LOGGER = TraceManager.getTrace(PrismBeanConverter.class);

    public static final String DEFAULT_PLACEHOLDER = "##default";

    private PrismBeanInspector inspector;

    private PrismContext prismContext;

    // Not fully implemented yet.
    private XNodeProcessorEvaluationMode mode;

    public PrismBeanConverter(PrismContext prismContext, PrismBeanInspector inspector) {
        this(prismContext, inspector, XNodeProcessorEvaluationMode.STRICT);
    }

    public PrismBeanConverter(PrismContext prismContext, PrismBeanInspector inspector,
            XNodeProcessorEvaluationMode mode) {
        this.prismContext = prismContext;
        this.inspector = inspector;
        this.mode = mode;
    }

    public PrismContext getPrismContext() {
        return prismContext;
    }

    //   public void setPrismContext(PrismContext prismContext) {
    //      this.prismContext = prismContext;
    //   }

    private SchemaRegistry getSchemaRegistry() {
        if (prismContext == null) {
            return null;
        }
        return prismContext.getSchemaRegistry();
    }

    public boolean canProcess(QName typeName) {
        return getSchemaRegistry().determineCompileTimeClass(typeName) != null;
    }

    public boolean canProcess(Class<?> clazz) {
        return RawType.class.equals(clazz) || clazz.getAnnotation(XmlType.class) != null;
    }

    public QName determineTypeForClass(Class<?> clazz) {
        return inspector.determineTypeForClass(clazz);
    }

    public <T> T unmarshall(MapXNode xnode, QName typeQName) throws SchemaException {
        Class<T> classType = getSchemaRegistry().determineCompileTimeClass(typeQName);
        return unmarshall(xnode, classType);
    }

    public <T> T unmarshall(XNode xnode, Class<T> beanClass) throws SchemaException {
        if (xnode instanceof PrimitiveXNode) {
            return unmarshallPrimitive((PrimitiveXNode<T>) xnode, beanClass);
        } else if (xnode instanceof MapXNode) {
            return unmarshall((MapXNode) xnode, beanClass);
        } else if (xnode instanceof RootXNode) {
            return unmarshall(((RootXNode) xnode).getSubnode(), beanClass);
        } else
            throw new IllegalStateException("Unexpected xnode " + xnode + ". Could not unmarshall value");
    }

    public <T> T unmarshall(MapXNode xnode, Class<T> beanClass) throws SchemaException {

        if (PolyStringType.class.equals(beanClass)) {
            PolyString polyString = unmarshalPolyString(xnode);
            return (T) polyString; // violates the method interface but ... TODO fix it
        } else if (ProtectedStringType.class.equals(beanClass)) {
            ProtectedStringType protectedType = new ProtectedStringType();
            XNodeProcessorUtil.parseProtectedType(protectedType, xnode, prismContext);
            return (T) protectedType;
        } else if (ProtectedByteArrayType.class.equals(beanClass)) {
            ProtectedByteArrayType protectedType = new ProtectedByteArrayType();
            XNodeProcessorUtil.parseProtectedType(protectedType, xnode, prismContext);
            return (T) protectedType;
        } else if (SchemaDefinitionType.class.equals(beanClass)) {
            SchemaDefinitionType schemaDefType = unmarshalSchemaDefinitionType(xnode);
            return (T) schemaDefType;
        } else if (prismContext.getSchemaRegistry().determineDefinitionFromClass(beanClass) != null) {
            return (T) prismContext.getXnodeProcessor().parseObject(xnode).asObjectable();
        } else if (XmlAsStringType.class.equals(beanClass)) {
            // reading a string represented a XML-style content
            // used e.g. when reading report templates (embedded XML)
            // A necessary condition: there may be only one map entry.
            if (xnode.size() > 1) {
                throw new SchemaException("Map with more than one item cannot be parsed as a string: " + xnode);
            } else if (xnode.isEmpty()) {
                return (T) new XmlAsStringType();
            } else {
                Map.Entry<QName, XNode> entry = xnode.entrySet().iterator().next();
                DomParser domParser = prismContext.getParserDom();
                String value = domParser.serializeToString(entry.getValue(), entry.getKey());
                return (T) new XmlAsStringType(value);
            }
        }
        T bean;
        Set<String> keysToParse; // only these keys will be parsed (null if all)
        if (SearchFilterType.class.isAssignableFrom(beanClass)) {
            keysToParse = Collections.singleton("condition"); // TODO fix this BRUTAL HACK - it is here because of c:ConditionalSearchFilterType
            bean = (T) unmarshalSearchFilterType(xnode, (Class<? extends SearchFilterType>) beanClass);
        } else {
            keysToParse = null;
            try {
                bean = beanClass.newInstance();
            } catch (InstantiationException e) {
                throw new SystemException("Cannot instantiate bean of type " + beanClass + ": " + e.getMessage(),
                        e);
            } catch (IllegalAccessException e) {
                throw new SystemException("Cannot instantiate bean of type " + beanClass + ": " + e.getMessage(),
                        e);
            }
        }

        if (ProtectedDataType.class.isAssignableFrom(beanClass)) {
            ProtectedDataType protectedDataType = null;
            if (bean instanceof ProtectedStringType) {
                protectedDataType = new ProtectedStringType();
            } else if (bean instanceof ProtectedByteArrayType) {
                protectedDataType = new ProtectedByteArrayType();
            } else {
                throw new SchemaException("Unexpected subtype of protected data type: " + bean.getClass());
            }
            XNodeProcessorUtil.parseProtectedType(protectedDataType, xnode, prismContext);
            return (T) protectedDataType;
        }

        for (Entry<QName, XNode> entry : xnode.entrySet()) {
            QName key = entry.getKey();
            if (keysToParse != null && !keysToParse.contains(key.getLocalPart())) {
                continue;
            }
            XNode xsubnode = entry.getValue();
            String propName = key.getLocalPart();
            Field field = inspector.findPropertyField(beanClass, propName);
            Method propertyGetter = null;
            if (field == null) {
                propertyGetter = inspector.findPropertyGetter(beanClass, propName);
            }

            Method elementMethod = null;
            Object objectFactory = null;
            if (field == null && propertyGetter == null) {
                // We have to try to find a more generic field, such as xsd:any or substitution element
                // check for global element definition first
                Class objectFactoryClass = inspector.getObjectFactoryClass(beanClass.getPackage());
                objectFactory = instantiateObjectFactory(objectFactoryClass);
                elementMethod = inspector.findElementMethodInObjectFactory(objectFactoryClass, propName);
                if (elementMethod == null) {
                    // Check for "any" method
                    elementMethod = inspector.findAnyMethod(beanClass);
                    if (elementMethod == null) {
                        String m = "No field " + propName + " in class " + beanClass
                                + " (and no element method in object factory too)";
                        if (mode == XNodeProcessorEvaluationMode.COMPAT) {
                            LOGGER.warn("{}", m);
                            continue;
                        } else {
                            throw new SchemaException(m);
                        }
                    }
                    unmarshallToAny(bean, elementMethod, key, xsubnode);
                    continue;

                }
                field = inspector.lookupSubstitution(beanClass, elementMethod);
                if (field == null) {
                    // Check for "any" field
                    field = inspector.findAnyField(beanClass);
                    if (field == null) {
                        elementMethod = inspector.findAnyMethod(beanClass);
                        if (elementMethod == null) {
                            String m = "No field " + propName + " in class " + beanClass
                                    + " (and no element method in object factory too)";
                            if (mode == XNodeProcessorEvaluationMode.COMPAT) {
                                LOGGER.warn("{}", m);
                                continue;
                            } else {
                                throw new SchemaException(m);
                            }
                        }
                        unmarshallToAny(bean, elementMethod, key, xsubnode);
                        continue;
                        //                  throw new SchemaException("No field "+propName+" in class "+beanClass+" (no suitable substitution and no 'any' field)");
                    }
                    unmarshallToAny(bean, field, key, xsubnode);
                    continue;
                }
            }

            boolean storeAsRawType;
            if (elementMethod != null) {
                storeAsRawType = elementMethod.getAnnotation(Raw.class) != null;
            } else if (propertyGetter != null) {
                storeAsRawType = propertyGetter.getAnnotation(Raw.class) != null;
            } else {
                storeAsRawType = field.getAnnotation(Raw.class) != null;
            }

            String fieldName;
            if (field != null) {
                fieldName = field.getName();
            } else {
                fieldName = propName;
            }

            Method setter = inspector.findSetter(beanClass, fieldName);
            Method getter = null;
            boolean wrapInJaxbElement = false;
            Class<?> paramType = null;
            if (setter == null) {
                // No setter. But if the property is multi-value we need to look
                // for a getter that returns a collection (Collection<Whatever>)
                getter = inspector.findPropertyGetter(beanClass, fieldName);
                if (getter == null) {
                    String m = "Cannot find setter or getter for field " + fieldName + " in " + beanClass;
                    if (mode == XNodeProcessorEvaluationMode.COMPAT) {
                        LOGGER.warn("{}", m);
                        continue;
                    } else {
                        throw new SchemaException(m);
                    }
                }
                Class<?> getterReturnType = getter.getReturnType();
                if (!Collection.class.isAssignableFrom(getterReturnType)) {
                    throw new SchemaException("Cannot find getter for field " + fieldName + " in " + beanClass
                            + " does not return collection, cannot use it to set value");
                }
                Type genericReturnType = getter.getGenericReturnType();
                Type typeArgument = getTypeArgument(genericReturnType,
                        "for field " + fieldName + " in " + beanClass + ", cannot determine collection type");
                //         System.out.println("type argument " + typeArgument);
                if (typeArgument instanceof Class) {
                    paramType = (Class<?>) typeArgument;
                } else if (typeArgument instanceof ParameterizedType) {
                    ParameterizedType paramTypeArgument = (ParameterizedType) typeArgument;
                    Type rawTypeArgument = paramTypeArgument.getRawType();
                    if (rawTypeArgument.equals(JAXBElement.class)) {
                        // This is the case of Collection<JAXBElement<....>>
                        wrapInJaxbElement = true;
                        Type innerTypeArgument = getTypeArgument(typeArgument, "for field " + fieldName + " in "
                                + beanClass + ", cannot determine collection type (inner type argument)");
                        if (innerTypeArgument instanceof Class) {
                            // This is the case of Collection<JAXBElement<Whatever>>
                            paramType = (Class<?>) innerTypeArgument;
                        } else if (innerTypeArgument instanceof WildcardType) {
                            // This is the case of Collection<JAXBElement<?>>
                            // we need to exctract the specific type from the factory method
                            if (elementMethod == null) {
                                // TODO: TEMPORARY CODE!!!!!!!!!! fix in 3.1 [med]
                                Class objectFactoryClass = inspector.getObjectFactoryClass(beanClass.getPackage());
                                objectFactory = instantiateObjectFactory(objectFactoryClass);
                                elementMethod = inspector.findElementMethodInObjectFactory(objectFactoryClass,
                                        propName);
                                if (elementMethod == null) {
                                    throw new IllegalArgumentException(
                                            "Wildcard type in JAXBElement field specification and no factory method found for field "
                                                    + fieldName + " in " + beanClass
                                                    + ", cannot determine collection type (inner type argument)");
                                }
                            }
                            Type factoryMethodGenericReturnType = elementMethod.getGenericReturnType();
                            Type factoryMethodTypeArgument = getTypeArgument(factoryMethodGenericReturnType,
                                    "in factory method " + elementMethod + " return type for field " + fieldName
                                            + " in " + beanClass + ", cannot determine collection type");
                            if (factoryMethodTypeArgument instanceof Class) {
                                // This is the case of JAXBElement<Whatever>
                                paramType = (Class<?>) factoryMethodTypeArgument;
                                if (Object.class.equals(paramType) && !storeAsRawType) {
                                    throw new IllegalArgumentException("Factory method " + elementMethod
                                            + " type argument is Object (and not @Raw) for field " + fieldName
                                            + " in " + beanClass + ", property " + propName);
                                }
                            } else {
                                throw new IllegalArgumentException(
                                        "Cannot determine factory method return type, got "
                                                + factoryMethodTypeArgument + " - for field " + fieldName + " in "
                                                + beanClass
                                                + ", cannot determine collection type (inner type argument)");
                            }
                        } else {
                            throw new IllegalArgumentException("Ejha! " + innerTypeArgument + " "
                                    + innerTypeArgument.getClass() + " from " + getterReturnType + " from "
                                    + fieldName + " in " + propName + " " + beanClass);
                        }
                    } else {
                        // The case of Collection<Whatever<Something>>
                        if (rawTypeArgument instanceof Class) {
                            paramType = (Class<?>) rawTypeArgument;
                        } else {
                            throw new IllegalArgumentException("EH? Eh!? " + typeArgument + " "
                                    + typeArgument.getClass() + " from " + getterReturnType + " from " + fieldName
                                    + " in " + propName + " " + beanClass);
                        }
                    }
                } else {
                    throw new IllegalArgumentException(
                            "EH? " + typeArgument + " " + typeArgument.getClass() + " from " + getterReturnType
                                    + " from " + fieldName + " in " + propName + " " + beanClass);
                }
            } else {
                Class<?> setterType = setter.getParameterTypes()[0];
                if (JAXBElement.class.equals(setterType)) {
                    //               TODO some handling for the returned generic parameter types
                    Type[] genericTypes = setter.getGenericParameterTypes();
                    if (genericTypes.length != 1) {
                        throw new IllegalArgumentException("Too lazy to handle this.");
                    }
                    Type genericType = genericTypes[0];
                    if (genericType instanceof ParameterizedType) {
                        Type actualType = getTypeArgument(genericType, "add some description");
                        if (actualType instanceof WildcardType) {
                            if (elementMethod == null) {
                                Class objectFactoryClass = inspector.getObjectFactoryClass(beanClass.getPackage());
                                objectFactory = instantiateObjectFactory(objectFactoryClass);
                                elementMethod = inspector.findElementMethodInObjectFactory(objectFactoryClass,
                                        propName);
                            }
                            // This is the case of Collection<JAXBElement<?>>
                            // we need to exctract the specific type from the factory method
                            if (elementMethod == null) {
                                throw new IllegalArgumentException(
                                        "Wildcard type in JAXBElement field specification and no facotry method found for field "
                                                + fieldName + " in " + beanClass
                                                + ", cannot determine collection type (inner type argument)");
                            }
                            Type factoryMethodGenericReturnType = elementMethod.getGenericReturnType();
                            Type factoryMethodTypeArgument = getTypeArgument(factoryMethodGenericReturnType,
                                    "in factory method " + elementMethod + " return type for field " + fieldName
                                            + " in " + beanClass + ", cannot determine collection type");
                            if (factoryMethodTypeArgument instanceof Class) {
                                // This is the case of JAXBElement<Whatever>
                                paramType = (Class<?>) factoryMethodTypeArgument;
                                if (Object.class.equals(paramType) && !storeAsRawType) {
                                    throw new IllegalArgumentException("Factory method " + elementMethod
                                            + " type argument is Object (without @Raw) for field " + fieldName
                                            + " in " + beanClass + ", property " + propName);
                                }
                            } else {
                                throw new IllegalArgumentException(
                                        "Cannot determine factory method return type, got "
                                                + factoryMethodTypeArgument + " - for field " + fieldName + " in "
                                                + beanClass
                                                + ", cannot determine collection type (inner type argument)");
                            }
                        }
                    }
                    //               Class enclosing = paramType.getEnclosingClass();
                    //               Class clazz = paramType.getClass();
                    //               Class declaring = paramType.getDeclaringClass();
                    wrapInJaxbElement = true;
                } else {
                    paramType = setterType;
                }
            }

            if (Element.class.isAssignableFrom(paramType)) {
                // DOM!
                throw new IllegalArgumentException("DOM not supported in field " + fieldName + " in " + beanClass);
            }

            //check for subclasses???
            if (!storeAsRawType && xsubnode.getTypeQName() != null) {
                Class explicitParamType = getSchemaRegistry().determineCompileTimeClass(xsubnode.getTypeQName());
                if (explicitParamType == null) {
                    explicitParamType = XsdTypeMapper.toJavaTypeIfKnown(xsubnode.getTypeQName());
                }

                if (explicitParamType != null) {
                    paramType = explicitParamType;
                }
            }

            if (!(xsubnode instanceof ListXNode) && Object.class.equals(paramType) && !storeAsRawType) {
                throw new IllegalArgumentException(
                        "Object property (without @Raw) not supported in field " + fieldName + " in " + beanClass);
            }

            String paramNamespace = inspector.determineNamespace(paramType);

            boolean problem = false;
            Object propValue = null;
            Collection<Object> propValues = null;
            if (xsubnode instanceof ListXNode) {
                ListXNode xlist = (ListXNode) xsubnode;
                if (setter != null) {
                    try {
                        propValue = convertSinglePropValue(xsubnode, fieldName, paramType, storeAsRawType,
                                beanClass, paramNamespace);
                    } catch (SchemaException e) {
                        problem = processSchemaException(e, xsubnode);
                    }
                } else {
                    // No setter, we have to use collection getter
                    propValues = new ArrayList<>(xlist.size());
                    for (XNode xsubsubnode : xlist) {
                        try {
                            propValues.add(convertSinglePropValue(xsubsubnode, fieldName, paramType, storeAsRawType,
                                    beanClass, paramNamespace));
                        } catch (SchemaException e) {
                            problem = processSchemaException(e, xsubsubnode);
                        }
                    }
                }
            } else {
                try {
                    propValue = convertSinglePropValue(xsubnode, fieldName, paramType, storeAsRawType, beanClass,
                            paramNamespace);
                } catch (SchemaException e) {
                    problem = processSchemaException(e, xsubnode);
                }
            }

            if (setter != null) {
                try {
                    setter.invoke(bean, prepareValueToBeStored(propValue, wrapInJaxbElement, objectFactory,
                            elementMethod, propName, beanClass));
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    throw new SystemException("Cannot invoke setter " + setter + " on bean of type " + beanClass
                            + ": " + e.getMessage(), e);
                }
            } else if (getter != null) {
                Object getterReturn;
                Collection<Object> col;
                try {
                    getterReturn = getter.invoke(bean);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    throw new SystemException("Cannot invoke getter " + getter + " on bean of type " + beanClass
                            + ": " + e.getMessage(), e);
                }
                try {
                    col = (Collection<Object>) getterReturn;
                } catch (ClassCastException e) {
                    throw new SystemException("Getter " + getter + " on bean of type " + beanClass + " returned "
                            + getterReturn + " instead of collection");
                }
                if (propValue != null) {
                    col.add(prepareValueToBeStored(propValue, wrapInJaxbElement, objectFactory, elementMethod,
                            propName, beanClass));
                } else if (propValues != null) {
                    for (Object propVal : propValues) {
                        col.add(prepareValueToBeStored(propVal, wrapInJaxbElement, objectFactory, elementMethod,
                                propName, beanClass));
                    }
                } else if (!problem) {
                    throw new IllegalStateException("Strange. Multival property " + propName + " in " + beanClass
                            + " produced null values list, parsed from " + xnode);
                }
                checkJaxbElementConsistence(col);
            } else {
                throw new IllegalStateException("Uh? No setter nor getter.");
            }
        }

        if (prismContext != null && bean instanceof Revivable) {
            ((Revivable) bean).revive(prismContext);
        }

        return bean;
    }

    /*
     *  We want to avoid this:
     *    <expression>
      *      <script>
      *        <code>'up'</code>
      *      </script>
      *      <value>up</value>
      *    </expression>
      *
      *  Because it cannot be reasonably serialized in XNode (<value> gets changed to <script>).
     */
    private void checkJaxbElementConsistence(Collection<Object> collection) throws SchemaException {
        QName elementName = null;
        for (Object object : collection) {
            if (!(object instanceof JAXBElement)) {
                continue;
            }
            JAXBElement element = (JAXBElement) object;
            if (elementName == null) {
                elementName = element.getName();
            } else {
                if (!QNameUtil.match(elementName, element.getName())) {
                    String m = "Mixing incompatible element names in one property: " + elementName + " and "
                            + element.getName();
                    if (mode != XNodeProcessorEvaluationMode.COMPAT) {
                        throw new SchemaException(m);
                    } else {
                        LOGGER.warn("{}", m);
                    }
                }
            }
        }
    }

    protected boolean processSchemaException(SchemaException e, XNode xsubnode) throws SchemaException {
        boolean problem;
        if (mode != XNodeProcessorEvaluationMode.COMPAT) {
            throw e;
        } else {
            LoggingUtils.logException(LOGGER,
                    "Couldn't parse part of the document. It will be ignored. Document part:\n{}", e, xsubnode);
            problem = true;
        }
        return problem;
    }

    private <T, S> void unmarshallToAny(T bean, Method getter, QName elementName, XNode xsubnode)
            throws SchemaException {
        Class<T> beanClass = (Class<T>) bean.getClass();

        Class objectFactoryClass = inspector.getObjectFactoryClass(elementName.getNamespaceURI());
        Object objectFactory = instantiateObjectFactory(objectFactoryClass);
        Method elementFactoryMethod = inspector.findElementMethodInObjectFactory(objectFactoryClass,
                elementName.getLocalPart());
        Class<S> subBeanClass = (Class<S>) elementFactoryMethod.getParameterTypes()[0];

        if (xsubnode instanceof ListXNode) {
            for (XNode xsubSubNode : ((ListXNode) xsubnode)) {
                S subBean = unmarshall(xsubSubNode, subBeanClass);
                unmarshallToAnyValue(bean, beanClass, subBean, objectFactoryClass, objectFactory,
                        elementFactoryMethod, getter);
            }
        } else {
            S subBean = unmarshall(xsubnode, subBeanClass);
            unmarshallToAnyValue(bean, beanClass, subBean, objectFactoryClass, objectFactory, elementFactoryMethod,
                    getter);
        }

    }

    private <T, S> void unmarshallToAnyValue(T bean, Class beanClass, S subBean, Class objectFactoryClass,
            Object objectFactory, Method elementFactoryMethod, Method getter) {

        JAXBElement<S> subBeanElement;
        try {
            subBeanElement = (JAXBElement<S>) elementFactoryMethod.invoke(objectFactory, subBean);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
            throw new IllegalArgumentException("Cannot invoke factory method " + elementFactoryMethod + " on "
                    + objectFactoryClass + " with " + subBean + ": " + e1, e1);
        }

        Collection<Object> col;
        Object getterReturn;
        try {
            getterReturn = getter.invoke(bean);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new SystemException(
                    "Cannot invoke getter " + getter + " on bean of type " + beanClass + ": " + e.getMessage(), e);
        }
        try {
            col = (Collection<Object>) getterReturn;
        } catch (ClassCastException e) {
            throw new SystemException("Getter " + getter + " on bean of type " + beanClass + " returned "
                    + getterReturn + " instead of collection");
        }
        col.add(subBeanElement != null ? subBeanElement.getValue() : subBeanElement);
    }

    private <T, S> void unmarshallToAny(T bean, Field anyField, QName elementName, XNode xsubnode)
            throws SchemaException {
        Method getter = inspector.findPropertyGetter(bean.getClass(), anyField.getName());
        unmarshallToAny(bean, getter, elementName, xsubnode);
    }

    private Object instantiateObjectFactory(Class objectFactoryClass) {
        try {
            return objectFactoryClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot instantiate object factory class "
                    + objectFactoryClass.getName() + ": " + e.getMessage(), e);
        }
    }

    // parses any subtype of SearchFilterType
    private <T extends SearchFilterType> T unmarshalSearchFilterType(MapXNode xmap, Class<T> beanClass)
            throws SchemaException {
        if (xmap == null) {
            return null;
        }
        T filterType;
        try {
            filterType = beanClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new SystemException("Cannot instantiate " + beanClass + ": " + e.getMessage(), e);
        }
        filterType.parseFromXNode(xmap, prismContext);
        return filterType;
    }

    private MapXNode marshalSearchFilterType(SearchFilterType value) throws SchemaException {
        if (value == null) {
            return null;
        }
        return value.serializeToXNode();
    }

    private Type getTypeArgument(Type origType, String desc) {
        if (!(origType instanceof ParameterizedType)) {
            throw new IllegalArgumentException("No a parametrized type " + desc);
        }
        ParameterizedType parametrizedType = (ParameterizedType) origType;
        Type[] actualTypeArguments = parametrizedType.getActualTypeArguments();
        if (actualTypeArguments == null || actualTypeArguments.length == 0) {
            throw new IllegalArgumentException("No type arguments for getter " + desc);
        }
        if (actualTypeArguments.length > 1) {
            throw new IllegalArgumentException("Too many type arguments for getter for " + desc);
        }
        return actualTypeArguments[0];
    }

    // Prepares value to be stored into the bean - e.g. converts PolyString->PolyStringType, wraps a value to JAXB if specified, ...
    private <T> Object prepareValueToBeStored(Object propVal, boolean wrapInJaxbElement, Object objectFactory,
            Method factoryMehtod, String propName, Class beanClass) {

        if (propVal instanceof PolyString) {
            propVal = new PolyStringType((PolyString) propVal);
        }

        if (wrapInJaxbElement) {
            if (factoryMehtod == null) {
                throw new IllegalArgumentException(
                        "Param type is JAXB element but no factory method found for it, property " + propName
                                + " in " + beanClass);
            }
            try {
                return factoryMehtod.invoke(objectFactory, propVal);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                throw new SystemException("Unable to invoke factory method " + factoryMehtod + " on "
                        + objectFactory.getClass() + " for property " + propName + " in " + beanClass);
            }
        } else {
            return propVal;
        }
    }

    private Object convertSinglePropValue(XNode xsubnode, String fieldName, Class paramType, boolean storeAsRawType,
            Class classType, String schemaNamespace) throws SchemaException {
        Object propValue;
        if (paramType.equals(XNode.class)) {
            propValue = xsubnode;
        } else if (storeAsRawType || paramType.equals(RawType.class)) {
            propValue = new RawType(xsubnode, prismContext);
        } else {
            // paramType is what we expect e.g. based on parent definition
            // but actual type (given by xsi:type/@typeDef) may be different, e.g. more specific
            if (xsubnode.getTypeQName() != null) {
                Class explicitParamType = getSchemaRegistry().determineCompileTimeClass(xsubnode.getTypeQName());
                if (explicitParamType != null) {
                    paramType = explicitParamType;
                } else {
                    // TODO or throw an exception?
                    LOGGER.warn("Unknown type name: " + xsubnode.getTypeQName() + ", ignoring it.");
                }
            }
            if (xsubnode instanceof PrimitiveXNode<?>) {
                propValue = unmarshallPrimitive(((PrimitiveXNode<?>) xsubnode), paramType);
            } else if (xsubnode instanceof MapXNode) {
                propValue = unmarshall((MapXNode) xsubnode, paramType);
            } else if (xsubnode instanceof ListXNode) {
                ListXNode xlist = (ListXNode) xsubnode;
                if (xlist.size() > 1) {
                    throw new SchemaException("Cannot set multi-value value to a single valued property "
                            + fieldName + " of " + classType);
                } else {
                    if (xlist.isEmpty()) {
                        propValue = null;
                    } else {
                        propValue = xlist.get(0);
                    }
                }
            } else {
                throw new IllegalArgumentException("Cannot parse " + xsubnode + " to a bean " + classType);
            }
        }
        return propValue;
    }

    public <T> T unmarshallPrimitive(PrimitiveXNode<?> xprim, QName typeQName) throws SchemaException {
        Class<T> classType = getSchemaRegistry().determineCompileTimeClass(typeQName);
        return unmarshallPrimitive(xprim, classType);
    }

    private <T> T unmarshallPrimitive(PrimitiveXNode<?> xprim, Class<T> classType) throws SchemaException {
        if (XmlAsStringType.class.equals(classType)) {
            return (T) new XmlAsStringType((String) xprim.getParsedValue(DOMUtil.XSD_STRING));
        }
        if (XmlTypeConverter.canConvert(classType)) {
            // Trivial case, direct conversion
            QName xsdType = XsdTypeMapper.toXsdType(classType);
            T primValue = postConvertUnmarshall(xprim.getParsedValue(xsdType));
            return primValue;
        }

        if (RawType.class.isAssignableFrom(classType)) {
            RawType rawType = new RawType(xprim, prismContext);
            return (T) rawType;
        }

        if (PolyStringType.class.isAssignableFrom(classType)) {
            String value = (String) xprim.getParsedValue(DOMUtil.XSD_STRING);
            PolyString polyString = new PolyString(value);
            if (value != null) {
                if (prismContext != null) { // actually this should be always so [med]
                    // TODO should we always use default normalizer?
                    polyString.recompute(prismContext.getDefaultPolyStringNormalizer());
                }
            }
            return (T) new PolyStringType(polyString);
        }

        if (ItemPathType.class.isAssignableFrom(classType)) {
            Object parsedValue = xprim.getParsedValue(ItemPathType.COMPLEX_TYPE);
            T primValue = postConvertUnmarshall(parsedValue);
            return (T) primValue;
        }

        if (SearchFilterType.class.isAssignableFrom(classType)) {
            throw new SchemaException("Cannot unmarshall search filter from " + xprim);
        }

        if (xprim.isEmpty() && !classType.isEnum()) {
            // Special case. Just return empty object
            try {
                return classType.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new SystemException("Cannot instantiate " + classType + ": " + e.getMessage(), e);
            }
        }

        if (!classType.isEnum()) {
            throw new SchemaException("Cannot convert primitive value to non-enum bean of type " + classType);
        }
        // Assume string, maybe TODO extend later
        String primValue = (String) xprim.getParsedValue(DOMUtil.XSD_STRING);
        if (StringUtils.isBlank(primValue)) {
            return null;
        }
        primValue = StringUtils.trim(primValue);

        String javaEnumString = inspector.findEnumFieldName(classType, primValue);
        //      for (Field field: classType.getDeclaredFields()) {
        //         XmlEnumValue xmlEnumValue = field.getAnnotation(XmlEnumValue.class);
        //         if (xmlEnumValue != null && xmlEnumValue.value() != null && xmlEnumValue.value().equals(primValue)) {
        //            javaEnumString = field.getName();
        //            break;
        //         }
        //      }

        if (javaEnumString == null) {
            for (Field field : classType.getDeclaredFields()) {
                if (field.getName().equals(primValue)) {
                    javaEnumString = field.getName();
                    break;
                }
            }
        }

        if (javaEnumString == null) {
            throw new SchemaException("Cannot find enum value for string '" + primValue + "' in " + classType);
        }

        T bean = (T) Enum.valueOf((Class<Enum>) classType, javaEnumString);

        return bean;
    }

    // TODO hacked, for now
    //    private <T> String findEnumFieldValue(Class classType, Object bean){
    //        String name = bean.toString();
    //        for (Field field: classType.getDeclaredFields()) {
    //            XmlEnumValue xmlEnumValue = field.getAnnotation(XmlEnumValue.class);
    //            if (xmlEnumValue != null && field.getName().equals(name)) {
    //                return xmlEnumValue.value();
    //            }
    //        }
    //        return null;
    //    }

    private <T> T postConvertUnmarshall(Object parsedPrimValue) {
        if (parsedPrimValue == null) {
            return null;
        }
        if (parsedPrimValue instanceof ItemPath) {
            return (T) new ItemPathType((ItemPath) parsedPrimValue);
        } else {
            return (T) parsedPrimValue;
        }
    }

    public <T> XNode marshall(T bean) throws SchemaException {
        return marshall(bean, null);
    }

    public <T> XNode marshall(T bean, SerializationContext ctx) throws SchemaException {
        if (bean == null) {
            return null;
        }
        if (bean instanceof SchemaDefinitionType) {
            return marshalSchemaDefinition((SchemaDefinitionType) bean);
        } else if (bean instanceof ProtectedDataType<?>) {
            MapXNode xProtected = marshalProtectedDataType((ProtectedDataType<?>) bean);
            return xProtected;
        } else if (bean instanceof ItemPathType) {
            return marshalItemPathType((ItemPathType) bean);
        } else if (bean instanceof RawType) {
            return marshalRawValue((RawType) bean);
        } else if (bean instanceof XmlAsStringType) {
            return marshalXmlAsStringType((XmlAsStringType) bean);
        } else if (prismContext != null
                && prismContext.getSchemaRegistry().determineDefinitionFromClass(bean.getClass()) != null) {
            return prismContext.getXnodeProcessor().serializeObject(((Objectable) bean).asPrismObject(), false, ctx)
                    .getSubnode();
        }
        // Note: SearchFilterType is treated below

        Class<? extends Object> beanClass = bean.getClass();

        if (beanClass == String.class) {
            return createPrimitiveXNode((String) bean, DOMUtil.XSD_STRING, false);
        }

        //check for enums
        if (beanClass.isEnum()) {
            String enumValue = inspector.findEnumFieldValue(beanClass, bean.toString());
            if (StringUtils.isEmpty(enumValue)) {
                enumValue = bean.toString();
            }
            QName fieldTypeName = inspector.findFieldTypeName(null, beanClass, DEFAULT_PLACEHOLDER);
            return createPrimitiveXNode(enumValue, fieldTypeName, false);
        }

        MapXNode xmap;
        if (bean instanceof SearchFilterType) {
            // this hack is here because of c:ConditionalSearchFilterType - it is analogous to situation when unmarshalling this type (TODO: rework this in a nicer way)
            xmap = marshalSearchFilterType((SearchFilterType) bean);
            if (SearchFilterType.class.equals(bean.getClass())) {
                return xmap; // nothing more to serialize; otherwise we continue, because in that case we deal with a subclass of SearchFilterType
            }
        } else {
            xmap = new MapXNode();
        }

        XmlType xmlType = beanClass.getAnnotation(XmlType.class);
        if (xmlType == null) {
            throw new IllegalArgumentException(
                    "Cannot marshall " + beanClass + " it does not have @XmlType annotation");
        }

        String namespace = inspector.determineNamespace(beanClass);
        if (namespace == null) {
            throw new IllegalArgumentException("Cannot determine namespace of " + beanClass);
        }

        List<String> propOrder = inspector.getPropOrder(beanClass);
        for (String fieldName : propOrder) {
            QName elementName = inspector.findFieldElementQName(fieldName, beanClass, namespace);
            Method getter = inspector.findPropertyGetter(beanClass, fieldName);
            if (getter == null) {
                throw new IllegalStateException("No getter for field " + fieldName + " in " + beanClass);
            }
            Object getterResult;
            try {
                getterResult = getter.invoke(bean);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                throw new SystemException(
                        "Cannot invoke method for field " + fieldName + " in " + beanClass + ": " + e.getMessage(),
                        e);
            }

            if (getterResult == null) {
                continue;
            }

            Field field = inspector.findPropertyField(beanClass, fieldName);
            boolean isAttribute = inspector.isAttribute(field, getter);

            if (getterResult instanceof Collection<?>) {
                Collection col = (Collection) getterResult;
                if (col.isEmpty()) {
                    continue;
                }
                Iterator i = col.iterator();
                if (i == null) {
                    // huh?!? .. but it really happens
                    throw new IllegalArgumentException(
                            "Iterator of collection returned from " + getter + " is null");
                }
                Object getterResultValue = i.next();
                if (getterResultValue == null) {
                    continue;
                }

                ListXNode xlist = new ListXNode();

                // elementName will be determined from the first item on the list
                // TODO make sure it will be correct with respect to other items as well!
                if (getterResultValue instanceof JAXBElement
                        && ((JAXBElement) getterResultValue).getName() != null) {
                    elementName = ((JAXBElement) getterResultValue).getName();
                }

                for (Object element : col) {
                    if (element == null) {
                        continue;
                    }
                    QName fieldTypeName = inspector.findFieldTypeName(field, element.getClass(), namespace);
                    Object elementToMarshall = element;
                    if (element instanceof JAXBElement) {
                        elementToMarshall = ((JAXBElement) element).getValue();
                    }
                    XNode marshalled = marshallValue(elementToMarshall, fieldTypeName, isAttribute, ctx);

                    // Brutal hack - made here just to make scripts (bulk actions) functional while not breaking anything else
                    // Fix it in 3.1. [med]
                    if (fieldTypeName == null && element instanceof JAXBElement && marshalled != null) {
                        QName typeName = inspector.determineTypeForClass(elementToMarshall.getClass());
                        if (typeName != null
                                && !getSchemaRegistry().hasImplicitTypeDefinition(elementName, typeName)) {
                            marshalled.setExplicitTypeDeclaration(true);
                            marshalled.setTypeQName(typeName);
                        }
                    } else {
                        // end of hack

                        setExplicitTypeDeclarationIfNeeded(getter, getterResultValue, marshalled, fieldTypeName);
                    }
                    xlist.add(marshalled);
                }
                xmap.put(elementName, xlist);
            } else {
                QName fieldTypeName = inspector.findFieldTypeName(field, getterResult.getClass(), namespace);
                Object valueToMarshall = null;
                if (getterResult instanceof JAXBElement) {
                    valueToMarshall = ((JAXBElement) getterResult).getValue();
                    elementName = ((JAXBElement) getterResult).getName();
                } else {
                    valueToMarshall = getterResult;
                }
                XNode marshelled = marshallValue(valueToMarshall, fieldTypeName, isAttribute, ctx);
                if (!getter.getReturnType().equals(valueToMarshall.getClass())
                        && getter.getReturnType().isAssignableFrom(valueToMarshall.getClass())) {
                    if (prismContext != null) {
                        PrismObjectDefinition def = prismContext.getSchemaRegistry()
                                .determineDefinitionFromClass(valueToMarshall.getClass());
                        if (def != null) {
                            QName type = def.getTypeName();
                            marshelled.setTypeQName(type);
                            marshelled.setExplicitTypeDeclaration(true);
                        }
                    }
                }
                xmap.put(elementName, marshelled);

                //            setExplicitTypeDeclarationIfNeeded(getter, valueToMarshall, xmap, fieldTypeName);
            }
        }

        return xmap;
    }

    private XNode marshalXmlAsStringType(XmlAsStringType bean) {
        PrimitiveXNode xprim = new PrimitiveXNode<>();
        xprim.setValue(bean.getContentAsString(), DOMUtil.XSD_STRING);
        return xprim;
    }

    public void revive(Object bean, final PrismContext prismContext) throws SchemaException {
        Handler<Object> visitor = new Handler<Object>() {
            @Override
            public boolean handle(Object o) {
                if (o instanceof Revivable) {
                    try {
                        ((Revivable) o).revive(prismContext);
                    } catch (SchemaException e) {
                        throw new TunnelException(e);
                    }
                }
                return true;
            }
        };
        try {
            visit(bean, visitor);
        } catch (TunnelException te) {
            SchemaException e = (SchemaException) te.getCause();
            throw e;
        }
    }

    public void visit(Object bean, Handler<Object> handler) {
        if (bean == null) {
            return;
        }

        Class<? extends Object> beanClass = bean.getClass();

        handler.handle(bean);

        if (beanClass.isEnum() || beanClass.isPrimitive()) {
            //nothing more to do
            return;
        }

        // TODO: implement special handling for RawType, if necessary (it has no XmlType annotation any more)

        XmlType xmlType = beanClass.getAnnotation(XmlType.class);
        if (xmlType == null) {
            // no @XmlType annotation, we are not interested to go any deeper
            return;
        }

        List<String> propOrder = inspector.getPropOrder(beanClass);
        for (String fieldName : propOrder) {
            Method getter = inspector.findPropertyGetter(beanClass, fieldName);
            if (getter == null) {
                throw new IllegalStateException("No getter for field " + fieldName + " in " + beanClass);
            }
            Object getterResult;
            try {
                getterResult = getter.invoke(bean);
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                throw new SystemException(
                        "Cannot invoke method for field " + fieldName + " in " + beanClass + ": " + e.getMessage(),
                        e);
            }

            if (getterResult == null) {
                continue;
            }

            if (getterResult instanceof Collection<?>) {
                Collection col = (Collection) getterResult;
                if (col.isEmpty()) {
                    continue;
                }

                for (Object element : col) {
                    visitValue(element, handler);

                }
            } else {
                visitValue(getterResult, handler);
            }
        }
    }

    private void visitValue(Object element, Handler<Object> handler) {
        Object elementToMarshall = element;
        if (element instanceof JAXBElement) {
            elementToMarshall = ((JAXBElement) element).getValue();
        }
        visit(elementToMarshall, handler);
    }

    private void setExplicitTypeDeclarationIfNeeded(Method getter, Object getterResult, XNode xmap,
            QName fieldTypeName) {
        Class getterReturnType = getter.getReturnType();
        Class getterType = null;
        if (Collection.class.isAssignableFrom(getterReturnType)) {
            Type genericReturnType = getter.getGenericReturnType();
            if (genericReturnType instanceof ParameterizedType) {
                Type actualType = getTypeArgument(genericReturnType, "explicit type declaration");

                if (actualType instanceof Class) {
                    getterType = (Class) actualType;
                }
            }
        }
        if (getterType == null) {
            getterType = getterReturnType;
        }
        Class getterResultReturnType = getterResult.getClass();
        if (getterType != getterResultReturnType && getterType.isAssignableFrom(getterResultReturnType)) {
            xmap.setExplicitTypeDeclaration(true);
            xmap.setTypeQName(fieldTypeName);
        }
    }

    private <T> XNode marshallValue(T value, QName fieldTypeName, boolean isAttribute, SerializationContext ctx)
            throws SchemaException {
        if (value == null) {
            return null;
        }
        if (canProcess(value.getClass())) {
            // This must be a bean
            return marshall(value, ctx);
        } else {
            // primitive value
            return createPrimitiveXNode(value, fieldTypeName, isAttribute);
        }
    }

    private <T> PrimitiveXNode<T> createPrimitiveXNode(T value, QName fieldTypeName, boolean isAttribute) {
        PrimitiveXNode<T> xprim = new PrimitiveXNode<T>();
        xprim.setValue(value, fieldTypeName);
        xprim.setAttribute(isAttribute);
        return xprim;
    }

    private <T> PrimitiveXNode<T> createPrimitiveXNode(T val, QName type) {
        return createPrimitiveXNode(val, type, false);
    }

    private XNode marshalRawValue(RawType value) throws SchemaException {
        return value.serializeToXNode();
    }

    private XNode marshalItemPathType(ItemPathType itemPath) {
        PrimitiveXNode<ItemPath> xprim = new PrimitiveXNode<ItemPath>();
        if (itemPath != null) {
            ItemPath path = itemPath.getItemPath();
            xprim.setValue(path, ItemPathType.COMPLEX_TYPE);
        }
        return xprim;
    }

    private XNode marshalSchemaDefinition(SchemaDefinitionType schemaDefinitionType) {
        SchemaXNode xschema = new SchemaXNode();
        xschema.setSchemaElement(schemaDefinitionType.getSchema());
        MapXNode xmap = new MapXNode();
        xmap.put(DOMUtil.XSD_SCHEMA_ELEMENT, xschema);
        return xmap;
    }

    // TODO create more appropriate interface to be able to simply serialize ProtectedStringType instances
    public <T> MapXNode marshalProtectedDataType(ProtectedDataType<T> protectedType) throws SchemaException {
        MapXNode xmap = new MapXNode();
        if (protectedType.getEncryptedDataType() != null) {
            EncryptedDataType encryptedDataType = protectedType.getEncryptedDataType();
            MapXNode xEncryptedDataType = (MapXNode) marshall(encryptedDataType);
            xmap.put(ProtectedDataType.F_ENCRYPTED_DATA, xEncryptedDataType);
        } else if (protectedType.getClearValue() != null) {
            QName type = XsdTypeMapper.toXsdType(protectedType.getClearValue().getClass());
            PrimitiveXNode xClearValue = createPrimitiveXNode(protectedType.getClearValue(), type);
            xmap.put(ProtectedDataType.F_CLEAR_VALUE, xClearValue);
        }
        // TODO: clearValue
        return xmap;
    }

    private PolyString unmarshalPolyString(MapXNode xmap) throws SchemaException {
        String orig = xmap.getParsedPrimitiveValue(QNameUtil.nullNamespace(PolyString.F_ORIG), DOMUtil.XSD_STRING);
        if (orig == null) {
            throw new SchemaException("Null polystring orig in " + xmap);
        }
        String norm = xmap.getParsedPrimitiveValue(QNameUtil.nullNamespace(PolyString.F_NORM), DOMUtil.XSD_STRING);
        return new PolyString(orig, norm);
    }

    private SchemaDefinitionType unmarshalSchemaDefinitionType(MapXNode xmap) throws SchemaException {
        Entry<QName, XNode> subEntry = xmap.getSingleSubEntry("schema element");
        if (subEntry == null) {
            return null;
        }
        XNode xsub = subEntry.getValue();
        if (xsub == null) {
            return null;
        }
        if (!(xsub instanceof SchemaXNode)) {
            throw new SchemaException("Cannot parse schema from " + xsub);
        }
        //      Element schemaElement = ((SchemaXNode)xsub).getSchemaElement();
        //      if (schemaElement == null) {
        //         throw new SchemaException("Empty schema in "+xsub);
        //      }
        SchemaDefinitionType schemaDefType = unmarshalSchemaDefinitionType((SchemaXNode) xsub);
        //      new SchemaDefinitionType();
        //      schemaDefType.setSchema(schemaElement);
        return schemaDefType;
    }

    public SchemaDefinitionType unmarshalSchemaDefinitionType(SchemaXNode xsub) throws SchemaException {
        Element schemaElement = ((SchemaXNode) xsub).getSchemaElement();
        if (schemaElement == null) {
            throw new SchemaException("Empty schema in " + xsub);
        }
        SchemaDefinitionType schemaDefType = new SchemaDefinitionType();
        schemaDefType.setSchema(schemaElement);
        return schemaDefType;
    }

}