blob: 49308f7614042a2d1c7909fa3ae60c957b888492 [file] [log] [blame]
/*
* Copyright 2010-2012 Niclas Hedhman.
* Copyright 2011 Rickard Öberg.
* Copyright 2013-2015 Paul Merlin.
*
* 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.apache.zest.library.conversion.values;
import java.lang.reflect.AccessibleObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.zest.api.association.Association;
import org.apache.zest.api.association.AssociationDescriptor;
import org.apache.zest.api.association.AssociationStateDescriptor;
import org.apache.zest.api.association.AssociationStateHolder;
import org.apache.zest.api.association.ManyAssociation;
import org.apache.zest.api.association.NamedAssociation;
import org.apache.zest.api.entity.EntityComposite;
import org.apache.zest.api.entity.EntityDescriptor;
import org.apache.zest.api.entity.EntityReference;
import org.apache.zest.api.entity.Identity;
import org.apache.zest.api.injection.scope.Structure;
import org.apache.zest.api.mixin.Mixins;
import org.apache.zest.api.property.PropertyDescriptor;
import org.apache.zest.api.structure.Module;
import org.apache.zest.api.value.NoSuchValueException;
import org.apache.zest.api.value.ValueBuilder;
import org.apache.zest.api.value.ValueDescriptor;
import org.apache.zest.functional.Function;
import org.apache.zest.functional.Iterables;
import org.apache.zest.spi.Qi4jSPI;
import static org.apache.zest.library.conversion.values.Shared.STRING_COLLECTION_TYPE_SPEC;
import static org.apache.zest.library.conversion.values.Shared.STRING_MAP_TYPE_SPEC;
import static org.apache.zest.library.conversion.values.Shared.STRING_TYPE_SPEC;
/**
* @deprecated Please use {@link org.apache.zest.api.unitofwork.UnitOfWork#toValue(Class, Identity)} instead.
*/
@Mixins( EntityToValue.EntityToValueMixin.class )
public interface EntityToValue
{
/**
* Convert an entity to a value.
*
* @param <T> parametrized type of the value
* @param valueType type of the value
* @param entity the entity to convert to a value
* @return the resulting value
*/
<T> T convert( Class<T> valueType, Object entity );
/**
* Convert an entity to a value with an opportunity to customize its prototype.
*
* @param <T> parametrized type of the value
* @param valueType type of the value
* @param entity the entity to convert to a value
* @param prototypeOpportunity a Function that will be mapped on the value prototype before instanciantion
* @return the resulting value
*/
<T> T convert( Class<T> valueType, Object entity, Function<T, T> prototypeOpportunity );
/**
* Convert an iterable of entities to an iterable of values.
*
* @param <T> parametrized type of the value
* @param valueType type of the value
* @param entities the entities to convert to values
* @return the resulting values
*/
<T> Iterable<T> convert( Class<T> valueType, Iterable<Object> entities );
/**
* Convert an iterable of entities to an iterable of values with an opportunity to customize their prototypes.
*
* @param <T> parametrized type of the value
* @param valueType type of the value
* @param entities the entities to convert to values
* @param prototypeOpportunity a Function that will be mapped on each of the value prototypes before instanciation.
* @return the resulting values
*/
<T> Iterable<T> convert( Class<T> valueType, Iterable<Object> entities, Function<T, T> prototypeOpportunity );
static class EntityToValueMixin
implements EntityToValue
{
@Structure
private Qi4jSPI spi;
@Structure
private Module module;
@Override
public <T> T convert( final Class<T> valueType, Object entity )
{
return createInstance( doConversion( valueType, entity ) );
}
@Override
@SuppressWarnings( "unchecked" )
public <T> T convert( final Class<T> valueType, Object entity, Function<T, T> prototypeOpportunity )
{
ValueBuilder<?> builder = doConversion( valueType, entity );
prototypeOpportunity.map( (T) builder.prototype() );
return createInstance( builder );
}
@Override
public <T> Iterable<T> convert( final Class<T> valueType, Iterable<Object> entities )
{
return Iterables.map(
new Function<Object, T>()
{
@Override
public T map( Object entity )
{
return convert( valueType, entity );
}
}, entities );
}
@Override
public <T> Iterable<T> convert( final Class<T> valueType, Iterable<Object> entities, final Function<T, T> prototypeOpportunity )
{
return Iterables.map(
new Function<Object, T>()
{
@Override
public T map( Object entity )
{
return convert( valueType, entity, prototypeOpportunity );
}
}, entities );
}
private <T> ValueBuilder<?> doConversion( final Class<T> valueType, Object entity )
{
ValueDescriptor valueDescriptor = module.valueDescriptor( valueType.getName() );
if( valueDescriptor == null )
{
throw new NoSuchValueException( valueType.getName(), module.name() );
}
Unqualified unqualified = valueDescriptor.metaInfo( Unqualified.class );
// Iterable<? extends PropertyDescriptor> properties = valueDescriptor.state().properties();
final EntityComposite composite = (EntityComposite) entity;
final EntityDescriptor entityDescriptor = spi.entityDescriptorFor( composite );
final AssociationStateHolder associationState = spi.stateOf( composite );
ValueBuilder<?> builder;
if( unqualified == null || !unqualified.value() )
{
// Copy state using qualified names
builder = module.newValueBuilderWithState(
valueType,
new Function<PropertyDescriptor, Object>()
{
@Override
public Object map( PropertyDescriptor descriptor )
{
try
{
return associationState.propertyFor( descriptor.accessor() ).get();
}
catch( IllegalArgumentException e )
{
AssociationStateDescriptor entityState = entityDescriptor.state();
String associationName = descriptor.qualifiedName().name();
if( STRING_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
// Find Association and convert to string
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getAssociationByName( associationName );
}
catch( IllegalArgumentException e1 )
{
return null;
}
AccessibleObject associationMethod = associationDescriptor.accessor();
Object entity = associationState.associationFor( associationMethod ).get();
if( entity != null )
{
return ( (Identity) entity ).identity().get();
}
else
{
return null;
}
}
else if( STRING_COLLECTION_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getManyAssociationByName( associationName );
}
catch( IllegalArgumentException e1 )
{
return Collections.emptyList();
}
ManyAssociation<?> state = associationState.manyAssociationFor( associationDescriptor.accessor() );
List<String> entities = new ArrayList<>( state.count() );
for( Object entity : state )
{
entities.add( ( (Identity) entity ).identity().get() );
}
return entities;
}
else if( STRING_MAP_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getNamedAssociationByName( associationName );
}
catch( IllegalArgumentException e1 )
{
return Collections.emptyMap();
}
NamedAssociation<?> state = associationState.namedAssociationFor( associationDescriptor.accessor() );
Map<String, String> entities = new LinkedHashMap<>( state.count() );
for( String name : state )
{
entities.put( name, ( (Identity) state.get( name ) ).identity().get() );
}
return entities;
}
return null;
}
}
},
new Function<AssociationDescriptor, EntityReference>()
{
@Override
public EntityReference map( AssociationDescriptor associationDescriptor )
{
return EntityReference.entityReferenceFor(
associationState.associationFor( associationDescriptor.accessor() ).get() );
}
},
new Function<AssociationDescriptor, Iterable<EntityReference>>()
{
@Override
public Iterable<EntityReference> map( AssociationDescriptor associationDescriptor )
{
ManyAssociation<?> state = associationState.manyAssociationFor( associationDescriptor.accessor() );
List<EntityReference> refs = new ArrayList<>( state.count() );
for( Object entity : state )
{
refs.add( EntityReference.entityReferenceFor( entity ) );
}
return refs;
}
},
new Function<AssociationDescriptor, Map<String, EntityReference>>()
{
@Override
public Map<String, EntityReference> map( AssociationDescriptor associationDescriptor )
{
NamedAssociation<?> assoc = associationState.namedAssociationFor( associationDescriptor.accessor() );
Map<String, EntityReference> refs = new LinkedHashMap<>( assoc.count() );
for( String name : assoc )
{
refs.put( name, EntityReference.entityReferenceFor( assoc.get( name ) ) );
}
return refs;
}
} );
}
else
{
builder = module.newValueBuilderWithState(valueType,
new Function<PropertyDescriptor, Object>()
{
@Override
public Object map( final PropertyDescriptor descriptor )
{
AssociationStateDescriptor entityState = entityDescriptor.state();
String propertyName = descriptor.qualifiedName().name();
try
{
PropertyDescriptor propertyDescriptor = entityState.findPropertyModelByName( propertyName );
return associationState.propertyFor( propertyDescriptor.accessor() ).get();
}
catch( IllegalArgumentException e )
{
if( STRING_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
// Find Association and convert to string
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getAssociationByName( propertyName );
}
catch( IllegalArgumentException e1 )
{
return null;
}
AccessibleObject associationMethod = associationDescriptor.accessor();
Object entity = associationState.associationFor( associationMethod ).get();
if( entity != null )
{
return ( (Identity) entity ).identity().get();
}
else
{
return null;
}
}
else if( STRING_COLLECTION_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getManyAssociationByName( propertyName );
}
catch( IllegalArgumentException e1 )
{
return null;
}
AccessibleObject associationMethod = associationDescriptor.accessor();
ManyAssociation<?> state = associationState.manyAssociationFor( associationMethod );
List<String> entities = new ArrayList<>( state.count() );
for( Object entity : state )
{
entities.add( ( (Identity) entity ).identity().get() );
}
return entities;
}
else if( STRING_MAP_TYPE_SPEC.satisfiedBy( descriptor.valueType() ) )
{
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityState.getNamedAssociationByName( propertyName );
}
catch( IllegalArgumentException e1 )
{
return null;
}
AccessibleObject associationMethod = associationDescriptor.accessor();
NamedAssociation<?> state = associationState.namedAssociationFor( associationMethod );
Map<String, String> entities = new LinkedHashMap<>( state.count() );
for( String name : state )
{
entities.put( name, ( (Identity) state.get( name ) ).identity().get() );
}
return entities;
}
return null;
}
}
},
new Function<AssociationDescriptor, EntityReference>()
{
@Override
public EntityReference map( AssociationDescriptor descriptor )
{
AssociationDescriptor associationDescriptor;
try
{
associationDescriptor = entityDescriptor.state()
.getAssociationByName( descriptor.qualifiedName().name() );
}
catch( IllegalArgumentException e )
{
return null;
}
AccessibleObject associationMethod = associationDescriptor.accessor();
Association<Object> association = associationState.associationFor( associationMethod );
return EntityReference.entityReferenceFor( association.get() );
}
},
new Function<AssociationDescriptor, Iterable<EntityReference>>()
{
@Override
public Iterable<EntityReference> map( final AssociationDescriptor descriptor )
{
AssociationDescriptor associationDescriptor;
try
{
String associationName = descriptor.qualifiedName().name();
AssociationStateDescriptor entityState = entityDescriptor.state();
associationDescriptor = entityState.getManyAssociationByName( associationName );
}
catch( IllegalArgumentException e )
{
return Iterables.empty();
}
ManyAssociation<?> state = associationState.manyAssociationFor( associationDescriptor.accessor() );
List<EntityReference> refs = new ArrayList<>( state.count() );
for( Object entity : state )
{
refs.add( EntityReference.entityReferenceFor( entity ) );
}
return refs;
}
},
new Function<AssociationDescriptor, Map<String, EntityReference>>()
{
@Override
public Map<String, EntityReference> map( AssociationDescriptor descriptor )
{
AssociationDescriptor associationDescriptor;
try
{
String associationName = descriptor.qualifiedName().name();
AssociationStateDescriptor entityState = entityDescriptor.state();
associationDescriptor = entityState.getNamedAssociationByName( associationName );
}
catch( IllegalArgumentException e )
{
return Collections.emptyMap();
}
AccessibleObject associationMethod = associationDescriptor.accessor();
NamedAssociation<Object> assoc = associationState.namedAssociationFor( associationMethod );
Map<String, EntityReference> refs = new LinkedHashMap<>( assoc.count() );
for( String name : assoc )
{
refs.put( name, EntityReference.entityReferenceFor( assoc.get( name ) ) );
}
return refs;
}
} );
}
return builder;
}
@SuppressWarnings( "unchecked" )
private <T> T createInstance( ValueBuilder<?> builder )
{
return (T) builder.newInstance();
}
}
}