Package org.qi4j.library.cxf

Source Code of org.qi4j.library.cxf.ValueCompositeCxfType

/*
* Copyright (c) 2010, Niclas Hehdman. All Rights Reserved.
*
* 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 org.qi4j.library.cxf;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.cxf.aegis.Context;
import org.apache.cxf.aegis.DatabindingException;
import org.apache.cxf.aegis.type.AegisType;
import org.apache.cxf.aegis.type.TypeMapping;
import org.apache.cxf.aegis.type.collection.CollectionType;
import org.apache.cxf.aegis.type.collection.MapType;
import org.apache.cxf.aegis.xml.MessageReader;
import org.apache.cxf.aegis.xml.MessageWriter;
import org.apache.cxf.common.xmlschema.XmlSchemaUtils;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.qi4j.api.Qi4j;
import org.qi4j.api.association.Association;
import org.qi4j.api.association.AssociationDescriptor;
import org.qi4j.api.association.AssociationStateHolder;
import org.qi4j.api.association.ManyAssociation;
import org.qi4j.api.association.NamedAssociation;
import org.qi4j.api.common.Optional;
import org.qi4j.api.common.QualifiedName;
import org.qi4j.api.composite.StateDescriptor;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.injection.scope.Uses;
import org.qi4j.api.property.Property;
import org.qi4j.api.property.PropertyDescriptor;
import org.qi4j.api.structure.Module;
import org.qi4j.api.util.Classes;
import org.qi4j.api.value.NoSuchValueException;
import org.qi4j.api.value.ValueBuilder;
import org.qi4j.api.value.ValueComposite;
import org.qi4j.api.value.ValueDescriptor;
import org.qi4j.functional.Function;
import org.qi4j.functional.Iterables;
import org.qi4j.spi.Qi4jSPI;

import static org.qi4j.functional.Iterables.first;

public class ValueCompositeCxfType
    extends AegisType
{
    @Structure
    private Module module;

    @Structure
    Qi4jSPI spi;

    public ValueCompositeCxfType( @Uses Type type, @Uses TypeMapping typeMapping )
    {
        setTypeMapping( typeMapping );
        setTypeClass( type );
        setSchemaType( NamespaceUtil.convertJavaTypeToQName( type ) );
    }

    @Override
    public Object readObject( final MessageReader reader, final Context context )
        throws DatabindingException
    {
        QName qname = getSchemaType();
        final String className = ( qname.getNamespaceURI() + "." + qname.getLocalPart() ).substring( 20 );

        // Read attributes
        ValueDescriptor descriptor = module.valueDescriptor( className );
        StateDescriptor stateDescriptor = descriptor.state();
        final Map<QualifiedName, Object> values = new HashMap<>();
        while( reader.hasMoreElementReaders() )
        {
            MessageReader childReader = reader.getNextElementReader();
            QName childName = childReader.getName();
            QualifiedName childQualifiedName = QualifiedName.fromClass( (Class) typeClass,
                                                                        childName.getLocalPart() );
            PropertyDescriptor propertyDescriptor = stateDescriptor.findPropertyModelByQualifiedName(
                childQualifiedName );
            Type propertyType = propertyDescriptor.type();
            AegisType type = getTypeMapping().getType( propertyType );
            Object value = type.readObject( childReader, context );
            values.put( childQualifiedName, value );
        }

        ValueBuilder<?> builder = module.newValueBuilderWithState(
            (Class<?>) typeClass,
            new Function<PropertyDescriptor, Object>()
        {
            @Override
            public Object map( PropertyDescriptor descriptor1 )
            {
                return values.get( descriptor1.qualifiedName() );
            }
            },
            new Function<AssociationDescriptor, EntityReference>()
            {
                @Override
                public EntityReference map( AssociationDescriptor descriptor )
                {
                    Object value = values.get( descriptor.qualifiedName() );
                    if( value == null )
                    {
                        return null;
                    }
                    return EntityReference.parseEntityReference( value.toString() );
                }
            },
            new Function<AssociationDescriptor, Iterable<EntityReference>>()
            {
                @Override
                public Iterable<EntityReference> map( AssociationDescriptor descriptor )
                {
                    Object value = values.get( descriptor.qualifiedName() );
                    if( value == null )
                    {
                        return Iterables.empty();
                    }
                    String[] ids = value.toString().split( "," );
                    List<EntityReference> references = new ArrayList<>( ids.length );
                    for( String id : ids )
                    {
                        references.add( EntityReference.parseEntityReference( id ) );
                    }
                    return references;
                }
            },
            new Function<AssociationDescriptor, Map<String, EntityReference>>()
            {
                @Override
                public Map<String, EntityReference> map( AssociationDescriptor descriptor )
                {
                    Object value = values.get( descriptor.qualifiedName() );
                    if( value == null )
                    {
                        return Collections.emptyMap();
                    }
                    String[] namedRefs = value.toString().split( "," );
                    Map<String, EntityReference> references = new HashMap<>( namedRefs.length );
                    for( String namedRef : namedRefs )
                    {
                        String[] splitted = namedRef.split( ":" );
                        references.put( splitted[0], EntityReference.parseEntityReference( splitted[1] ) );
                    }
                    return references;
                }
            } );

        return builder.newInstance();
    }

    @Override
    public void writeObject( Object object, final MessageWriter writer, final Context context )
        throws DatabindingException
    {
        ValueComposite composite = (ValueComposite) object;
        writer.writeXsiType( NamespaceUtil.convertJavaTypeToQName(
            first( Qi4j.FUNCTION_DESCRIPTOR_FOR.map( composite ).types() ) ) );
        AssociationStateHolder state = spi.stateOf( composite );
        for( Property<?> property : state.properties() )
        {
            Object value = property.get();
            AegisType type = null;
            if( value instanceof ValueComposite )
            {
                ValueComposite compositeValue = (ValueComposite) value;
                type = getTypeMapping().getType( NamespaceUtil.convertJavaTypeToQName(
                    first( Qi4j.FUNCTION_DESCRIPTOR_FOR.map( compositeValue ).types() ) ) );
            }
            else if( value != null )
            {
                type = getOrCreateNonQi4jType( value );
            }

            QName childName = new QName( "", spi.propertyDescriptorFor( property ).qualifiedName().name() );
            MessageWriter cwriter = writer.getElementWriter( childName );
            if( type != null )
            {
                type.writeObject( value, cwriter, context );
            }
            else
            {
//                    cwriter.writeXsiNil();
            }
            cwriter.close();
        }

        AegisType type = getTypeMapping().getType( NamespaceUtil.convertJavaTypeToQName( String.class ) );
        for( Association<?> association : state.allAssociations() )
        {
            QName childName = new QName( "", spi.associationDescriptorFor( association ).qualifiedName().name() );
            MessageWriter cwriter = writer.getElementWriter( childName );

            if( association.get() != null )
            {
                type.writeObject( ( (Identity) association.get() ).identity().get(), cwriter, context );
            }
            cwriter.close();
        }

        for( ManyAssociation<?> association : state.allManyAssociations() )
        {
            QName childName = new QName( "", spi.associationDescriptorFor( association ).qualifiedName().name() );
            MessageWriter cwriter = writer.getElementWriter( childName );

            String ids = null;
            for( Object entity : association )
            {
                String id = EntityReference.entityReferenceFor( entity ).identity();
                if( ids != null )
                {
                    ids += ",";
                }
                ids += id;
            }
            if( ids == null )
            {
                ids = "";
            }
            type.writeObject( ids, cwriter, context );
            cwriter.close();
        }

        for( NamedAssociation<?> association : state.allNamedAssociations() )
        {
            QName childName = new QName( "", spi.associationDescriptorFor( association ).qualifiedName().name() );
            MessageWriter cwriter = writer.getElementWriter( childName );

            String ids = null;
            for( String name : association )
            {
                String id = EntityReference.entityReferenceFor( association.get( name ) ).identity();
                if( ids != null )
                {
                    ids += ",";
                }
                ids += name + ":" + id;
            }
            if( ids == null )
            {
                ids = "";
            }
            type.writeObject( ids, cwriter, context );
            cwriter.close();
        }
    }

    private AegisType getOrCreateNonQi4jType( Object value )
    {
        AegisType type;
        TypeMapping mapping = getTypeMapping();
        Class<?> javaType = value.getClass();
        type = mapping.getType( javaType );
        if( type == null )
        {
            // This might be wrong and instead the ultimate top parent should be used. This works, since
            // we know that we are the top parent.
            type = getTypeMapping().getTypeCreator().createType( javaType );
            mapping.register( type );
        }
        return type;
    }

    @Override
    public void writeSchema( XmlSchema root )
    {
        XmlSchemaComplexType complex = new XmlSchemaComplexType( root, true );
        complex.setName( getSchemaType().getLocalPart() );
        root.getItems().add( complex );

        XmlSchemaSequence sequence = new XmlSchemaSequence(); // No clue why this?
        complex.setParticle( sequence )// No idea what this is for

        ValueDescriptor descriptor = module.valueDescriptor( getTypeClass().getName() );

        for( PropertyDescriptor p : descriptor.state().properties() )
        {
            if( isValueComposite( p.type() ) )
            {
                XmlSchemaElement element = new XmlSchemaElement( root, false );
                element.setName( p.qualifiedName().name() );
                element.setNillable( p.metaInfo( Optional.class ) != null ); // see below
                sequence.getItems().add( element );
                AegisType nested = getOrCreateAegisType( p.type(), root );
                element.setSchemaTypeName( nested.getSchemaType() );
            }
            else if( isCollectionOrMap( p ) )
            {
                XmlSchemaElement element = new XmlSchemaElement( root, false );
                element.setName( p.qualifiedName().name() );
                element.setNillable( p.metaInfo( Optional.class ) != null ); // see below
                sequence.getItems().add( element );
                AegisType nested = getOrCreateAegisType( p.type(), root );
                element.setSchemaTypeName( nested.getSchemaType() );
            }
            else
            {
                XmlSchemaAttribute attribute = new XmlSchemaAttribute( root, false );
                complex.getAttributes().add( attribute );
                attribute.setName( p.qualifiedName().name() );
                AegisType nested = getTypeMapping().getType( p.type() );
                attribute.setSchemaTypeName( nested.getSchemaType() );
            }
            QName name = NamespaceUtil.convertJavaTypeToQName( p.type() );
            String ns = name.getNamespaceURI();
            if( !ns.equals( root.getTargetNamespace() ) )
            {
                XmlSchemaUtils.addImportIfNeeded( root, ns );
            }
        }
    }

    private AegisType getOrCreateAegisType( Type type, XmlSchema root )
    {
        AegisType nested = getTypeMapping().getType( type );
        if( nested == null )
        {
            nested = createType( type, root );
            nested.writeSchema( root );
        }
        return nested;
    }

    private AegisType createType( Type type, XmlSchema root )
    {
        if( isCollection( type ) )
        {
            AegisType componentType = getOrCreateAegisType( getCollectionComponentType( type ), root );
            CollectionType resultType = new CollectionType( componentType );
            QName schemaType = new QName( "http://www.w3.org/2001/XMLSchema", "list" );
            resultType.setSchemaType( schemaType );
            return resultType;
        }
        else if( isMap( type ) )
        {
            AegisType keyType = getOrCreateAegisType( getMapKeyComponentType( type ), root );
            AegisType valueType = getOrCreateAegisType( getMapValueComponentType( type ), root );
            QName schemaType = new QName( Classes.toURI( Classes.RAW_CLASS.map( type ) ), "map" );
            return new MapType( schemaType, keyType, valueType );
        }
        else if( isValueComposite( type ) )
        {
            ValueCompositeCxfType aegisType = module.newObject( ValueCompositeCxfType.class, getTypeMapping(), type );
            getTypeMapping().register( aegisType );
            return aegisType;
        }
        else
        {
            throw new NoSuchValueException( type.toString(), module.name() );
        }
    }

    private boolean isCollectionOrMap( final PropertyDescriptor p )
    {
        Type type = p.type();
        return isCollectionOrMap( type );
    }

    private boolean isCollection( Type type )
    {
        if( isCollectionClass( type ) )
        {
            return true;
        }
        if( type instanceof ParameterizedType )
        {
            ParameterizedType param = (ParameterizedType) type;
            Type rawType = param.getRawType();
            if( isCollectionClass( rawType ) )
            {
                return true;
            }
        }
        return false;
    }

    private boolean isMap( Type type )
    {
        if( isMapClass( type ) )
        {
            return true;
        }
        if( type instanceof ParameterizedType )
        {
            ParameterizedType param = (ParameterizedType) type;
            Type rawType = param.getRawType();
            if( isCollectionClass( rawType ) )
            {
                return true;
            }
        }
        return false;
    }

    private boolean isCollectionOrMap( Type type )
    {
        return isMap( type ) || isCollection( type );
    }

    @SuppressWarnings( "raw" )
    private boolean isCollectionClass( Type type )
    {
        if( type instanceof Class )
        {
            Class clazz = (Class) type;
            return Collection.class.isAssignableFrom( clazz );
        }
        return false;
    }

    @SuppressWarnings( "raw" )
    private boolean isMapClass( Type type )
    {
        if( type instanceof Class )
        {
            Class clazz = (Class) type;
            return Map.class.isAssignableFrom( clazz );
        }
        return false;
    }

    private Type getCollectionComponentType( Type p )
    {
        return getActualTypeArgument( p, 0 );
    }

    private Type getMapKeyComponentType( Type p )
    {
        return getActualTypeArgument( p, 0 );
    }

    private Type getMapValueComponentType( Type p )
    {
        return getActualTypeArgument( p, 1 );
    }

    private Type getActualTypeArgument( Type p, int index )
    {
        if( p instanceof ParameterizedType )
        {
            ParameterizedType type = (ParameterizedType) p;
            return type.getActualTypeArguments()[index];
        }
        return null;
    }

    @SuppressWarnings( "raw" )
    private boolean isValueComposite( Type type )
    {
        Class clazz = Classes.RAW_CLASS.map( type );
        ValueDescriptor descriptor = module.valueDescriptor( clazz.getName() );
        return descriptor != null;
    }

    @Override
    public boolean isComplex()
    {
        return true;
    }
}
TOP

Related Classes of org.qi4j.library.cxf.ValueCompositeCxfType

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.