/* * Chrysalix * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * See the AUTHORS.txt file in the distribution for a full listing of * individual contributors. * * Chrysalix is free software. Unless otherwise indicated, all code in Chrysalix * is licensed to you under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Chrysalix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.chrysalix.operation; import java.util.List; import org.chrysalix.ChrysalixException; import org.chrysalix.ChrysalixI18n; import org.chrysalix.transformation.Operation; import org.chrysalix.transformation.OperationDescriptor; import org.chrysalix.transformation.Transformation; import org.chrysalix.transformation.TransformationFactory; import org.chrysalix.transformation.ValidationProblem; import org.chrysalix.transformation.ValidationProblems; import org.chrysalix.transformation.Value; import org.chrysalix.transformation.ValueDescriptor; import org.modelspace.ModelElement; import org.modelspace.ModelObject; import org.modelspace.ModelProperty; import org.modelspace.ModelspaceException; /** * Maps one {@link ModelObject model object's} property to another model object's property. */ public final class Map extends AbstractOperation< Void > { static final String DESCRIPTION = "Maps one model object's property to another model object's property"; private static final String ERROR_READING_MODEL_PROP = "Map operation could not read a model property descriptor in transformation '%s'"; private static final String ERROR_SETTING_TARGET = "Map operation could not set target property in transformation '%s'"; private static final String INVALID_VALUES_COUNT = "A map operation in transformation '%s' source model and target model property are not both single-valued or both multi-valued."; private static final String INVALID_SOURCE_PROP_COUNT = "A map operation in transformation '%s' does not have exactly one source model property"; private static final String INVALID_SOURCE_PROP_TYPE = "The source property of a map operation in transformation '%s' is not a model object property or operation"; private static final String INVALID_TARGET_PROP_COUNT = "A map operation in transformation '%s' does not have exactly one target model property name"; private static final String INVALID_TARGET_PROP_TYPE = "The target property of a map operation in transformation '%s' is not a model object property"; static final String NAME = "Map"; private static final String SOURCE_PROP_DESCRIPTION = "The name of the source model's property being mapped"; private static final String SOURCE_PROP_NAME = "Source Property"; private static final String TARGET_PROP_DESCRIPTION = "The name of the target model's property being mapped"; private static final String TARGET_PROP_NAME = "Target Property"; /** * The descriptor for the name of the source {@link ModelObject model object} property used in the mapping. */ public static final ValueDescriptor< ModelProperty > SOURCE_PROP_DESCRIPTOR = TransformationFactory.createValueDescriptor( TransformationFactory.createId( Map.class, "sourceProperty" ), ChrysalixI18n.localize( SOURCE_PROP_DESCRIPTION ), ChrysalixI18n.localize( SOURCE_PROP_NAME ), ModelProperty.class, true, 1, false ); /** * The descriptor for the name of the target {@link ModelObject model object} property used in the mapping. */ public static final ValueDescriptor< ModelProperty > TARGET_PROP_DESCRIPTOR = TransformationFactory.createValueDescriptor( TransformationFactory.createId( Map.class, "targetProperty" ), ChrysalixI18n.localize( TARGET_PROP_DESCRIPTION ), ChrysalixI18n.localize( TARGET_PROP_NAME ), ModelProperty.class, true, 1, false ); /** * The input descriptors. */ private static final ValueDescriptor< ? >[] INPUT_DESCRIPTORS = { SOURCE_PROP_DESCRIPTOR, TARGET_PROP_DESCRIPTOR }; /** * The output descriptor. */ public static final OperationDescriptor< Void > DESCRIPTOR = new AbstractOperationDescriptor< Void >( TransformationFactory.createId( Map.class ), ChrysalixI18n.localize( DESCRIPTION ), ChrysalixI18n.localize( NAME ), Void.class, INPUT_DESCRIPTORS ) { /** * {@inheritDoc} * * @see org.chrysalix.transformation.OperationDescriptor#newInstance(org.modelspace.ModelObject, * org.chrysalix.transformation.Transformation) */ @Override public Operation< Void > newInstance( final ModelObject operation, final Transformation transformation ) throws ModelspaceException, ChrysalixException { return new Map( operation, transformation ); } }; /** * @param opModelOperation * the operation model object (cannot be <code>null</code>) * @param transformation * the transformation containing this operation (cannot be <code>null</code>) * @throws ModelspaceException * if an error with the model object occurs * @throws ChrysalixException * if a non-model object error occurs * @throws IllegalArgumentException * if the input is <code>null</code> */ Map( final ModelObject opModelOperation, final Transformation transformation ) throws ModelspaceException, ChrysalixException { super( opModelOperation, transformation ); } /** * {@inheritDoc} * * @see org.chrysalix.operation.AbstractOperation#calculate() */ @Override protected Void calculate() throws ChrysalixException { assert !problems().isError(); final ModelElement source = inputs( SOURCE_PROP_DESCRIPTOR.name() ).get( 0 ); final ModelProperty targetProp = ( ModelProperty ) inputs( TARGET_PROP_DESCRIPTOR.name() ).get( 0 ); try { if ( source instanceof ModelProperty ) { final ModelProperty sourceProp = ( ModelProperty ) source; if ( sourceProp.descriptor().multiple() ) { final Object[] values = sourceProp.values(); targetProp.set( values ); } else { final Object value = sourceProp.value(); targetProp.set( value ); } } else if ( source instanceof Operation ) { final Object value = ( ( Operation< ? > ) source ).get(); targetProp.set( value ); } return null; } catch ( final ModelspaceException e ) { throw new ChrysalixException( e, ChrysalixI18n.localize( ERROR_SETTING_TARGET, this.transformationId() ) ); } } /** * {@inheritDoc} * * @see org.chrysalix.operation.AbstractOperation#problems() */ @Override public ValidationProblems problems() throws ChrysalixException { this.problems.clear(); // make sure all terms have been added if ( inputs().length != 2 ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( AbstractOperation.INVALID_TERM_COUNT, NAME, transformationId(), inputs().length ) ); problems().add( problem ); } else { boolean sourcePropIsMultiValued = false; boolean targetPropIsMultiValued = false; { // source model property final List< Value< ? >> sourceProps = inputs( SOURCE_PROP_DESCRIPTOR.id() ); if ( sourceProps.size() != 1 ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( INVALID_SOURCE_PROP_COUNT, transformationId() ) ); problems().add( problem ); } else { final Value< ? > term = sourceProps.get( 0 ); Object prop; try { prop = term.get(); if ( !( prop instanceof ModelProperty ) || !( prop instanceof Operation ) ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( INVALID_SOURCE_PROP_TYPE, transformationId() ) ); problems().add( problem ); } else { if ( prop instanceof ModelProperty ) { try { sourcePropIsMultiValued = ( ( ModelProperty ) prop ).descriptor().multiple(); } catch ( final ModelspaceException e ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( ERROR_READING_MODEL_PROP, NAME ) ); problems().add( problem ); } } } } catch ( final ChrysalixException e ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( AbstractOperation.OPERATION_VALIDATION_ERROR, NAME, transformationId() ) ); problems().add( problem ); } } } { // target model property final List< Value< ? >> props = inputs( TARGET_PROP_DESCRIPTOR.id() ); if ( props.size() != 1 ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( INVALID_TARGET_PROP_COUNT, transformationId() ) ); problems().add( problem ); } else { final Value< ? > term = props.get( 0 ); Object prop; try { prop = term.get(); if ( !( prop instanceof ModelProperty ) ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( INVALID_TARGET_PROP_TYPE, transformationId() ) ); problems().add( problem ); } else { try { targetPropIsMultiValued = ( ( ModelProperty ) prop ).descriptor().multiple(); } catch ( final ModelspaceException e ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( ERROR_READING_MODEL_PROP, NAME ) ); problems().add( problem ); } } } catch ( final ChrysalixException e ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( AbstractOperation.OPERATION_VALIDATION_ERROR, NAME, transformationId() ) ); problems().add( problem ); } } } if ( sourcePropIsMultiValued != targetPropIsMultiValued ) { final ValidationProblem problem = TransformationFactory.createError( transformationId(), ChrysalixI18n.localize( INVALID_VALUES_COUNT, transformationId() ) ); problems().add( problem ); } } return super.problems(); } }