# Qi4j.java

    /*
     * Copyright (c) 2007, Rickard Öberg. 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.api;

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import org.qi4j.api.association.AbstractAssociation;
    import org.qi4j.api.association.AssociationDescriptor;
    import org.qi4j.api.composite.Composite;
    import org.qi4j.api.composite.CompositeDescriptor;
    import org.qi4j.api.composite.CompositeInstance;
    import org.qi4j.api.composite.InvalidCompositeException;
    import org.qi4j.api.composite.ModelDescriptor;
    import org.qi4j.api.composite.TransientDescriptor;
    import org.qi4j.api.entity.EntityDescriptor;
    import org.qi4j.api.entity.Identity;
    import org.qi4j.api.property.Property;
    import org.qi4j.api.property.PropertyDescriptor;
    import org.qi4j.api.service.ServiceDescriptor;
    import org.qi4j.api.structure.Module;
    import org.qi4j.api.value.ValueDescriptor;
    import org.qi4j.functional.Function;

    /**
     * Encapsulation of the Zest API.
     */
    public interface Qi4j
    {
        /**
         * If a Modifier gets a reference to the Composite using @This,
         * then that reference must be dereferenced using this method
         * before handing it out for others to use.
         *
         * @param <T>       Parameterized type of the Composite
         * @param composite instance reference injected in Modified using @This
         *
         * @return the dereferenced Composite
         */
        <T> T dereference( T composite );

        /**
         * Returns the Module or UnitOfWork where the Composite belongs.
         *
         * @param compositeOrUow The Composite (Service, Value, Entity or Transient) or UnitOfWork to lookup the Module it
         *                       belongs to.
         *
         * @return The Module instance where the Composite or UnitOfWork belongs to.
         */
        Module moduleOf( Object compositeOrUow );

        /**
         * Returns the ModelDescriptor of the Composite.
         *
         * @param compositeOrServiceReference The Composite (Service, Value, Entity or Transient) for which to lookup the
         *                                    ModelDescriptor
         *
         * @return The ModelDescriptor of the Composite
         */
        ModelDescriptor modelDescriptorFor( Object compositeOrServiceReference );

        /**
         * Returns the CompositeDescriptor of the Composite.
         *
         * @param compositeOrServiceReference The Composite (Service, Value, Entity or Transient) for which to lookup the
         *                                    CompositeDescriptor
         *
         * @return The CompositeDescriptor of the Composite
         */
        CompositeDescriptor compositeDescriptorFor( Object compositeOrServiceReference );

        /**
         * Returns the TransientDescriptor of the TransientComposite.
         *
         * @param transsient The TransientComposite for which to lookup the TransientDescriptor
         *
         * @return The TransientDescriptor of the TransientComposite
         */
        TransientDescriptor transientDescriptorFor( Object transsient );

        /**
         * Returns the EntityDescriptor of the EntityComposite.
         *
         * @param entity The EntityComposite for which to lookup the EntityDescriptor
         *
         * @return The EntityDescriptor of the EntityComposite
         */
        EntityDescriptor entityDescriptorFor( Object entity );

        /**
         * Returns the ValueDescriptor of the ValueComposite.
         *
         * @param value The ValueComposite for which to lookup the ValueDescriptor
         *
         * @return The ValueDescriptor of the ValueComposite
         */
        ValueDescriptor valueDescriptorFor( Object value );

        /**
         * Returns the ServiceDescriptor of the ServiceComposite.
         *
         * @param service The ServiceComposite for which to lookup the ServiceDescriptor
         *
         * @return The ServiceDescriptor of the ServiceComposite
         */
        ServiceDescriptor serviceDescriptorFor( Object service );

        /**
         * Returns the PropertyDescriptor of the Property.
         *
         * @param property The Property for which to lookup the PropertyDescriptor
         *
         * @return The PropertyDescriptor of the Property
         */
        PropertyDescriptor propertyDescriptorFor( Property<?> property );

        /**
         * Returns the AssociationDescriptor of the Association.
         *
         * @param association The Association for which to lookup the AssociationDescriptor
         *
         * @return The AssociationDescriptor of the Association
         */
        AssociationDescriptor associationDescriptorFor( AbstractAssociation association );

        /**
         * Function that returns the CompositeDescriptor of a Composite.
         */
        Function<Composite, CompositeDescriptor> FUNCTION_DESCRIPTOR_FOR = new Function<Composite, CompositeDescriptor>()
        {
            @Override
            public CompositeDescriptor map( Composite composite )
            {
                if( composite instanceof Proxy )
                {
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler( composite );
                    return ( (CompositeInstance) invocationHandler ).descriptor();
                }
                try
                {
                    Class<? extends Composite> compositeClass = composite.getClass();
                    Field instanceField = compositeClass.getField( "_instance" );
                    Object instance = instanceField.get( composite );
                    return ( (CompositeInstance) instance ).descriptor();
                }
                catch( Exception e )
                {
                    InvalidCompositeException exception = new InvalidCompositeException( "Could not get _instance field" );
                    exception.initCause( e );
                    throw exception;
                }
            }
        };

        /**
         * Function that returns the CompositeInstance of a Composite.
         */
        Function<Composite, CompositeInstance> FUNCTION_COMPOSITE_INSTANCE_OF = new Function<Composite, CompositeInstance>()
        {
            @Override
            public CompositeInstance map( Composite composite )
            {
                if( composite instanceof Proxy )
                {
                    return ( (CompositeInstance) Proxy.getInvocationHandler( composite ) );
                }
                try
                {
                    Class<? extends Composite> compositeClass = composite.getClass();
                    Field instanceField = compositeClass.getField( "_instance" );
                    Object instance = instanceField.get( composite );
                    return (CompositeInstance) instance;
                }
                catch( Exception e )
                {
                    InvalidCompositeException exception = new InvalidCompositeException( "Could not get _instance field" );
                    exception.initCause( e );
                    throw exception;
                }
            }
        };
    }