001/*
002 * Copyright c 2018 Rusi Popov, MDA Tools.net All rights reserved.
003 *
004 * This program and the accompanying materials are made available under the terms of the
005 * Eclipse Public License v2.0 which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v20.html
007 */
008package net.mdatools.modelant.core.operation.model.transform;
009
010import java.util.Collection;
011import java.util.Map;
012import java.util.logging.Level;
013import java.util.logging.Logger;
014
015import javax.jmi.reflect.RefEnum;
016import javax.jmi.reflect.RefObject;
017import javax.jmi.reflect.RefPackage;
018import javax.jmi.reflect.RefStruct;
019
020import net.mdatools.modelant.core.api.Operation;
021import net.mdatools.modelant.core.api.Procedure;
022import net.mdatools.modelant.core.api.model.NameMapping;
023import net.mdatools.modelant.core.api.name.EnumValueName;
024import net.mdatools.modelant.core.api.name.StructName;
025import net.mdatools.modelant.core.name.EnumValueNameImpl;
026import net.mdatools.modelant.core.name.StructNameImpl;
027import net.mdatools.modelant.core.operation.element.PrintModelElement;
028
029/**
030 * Copy a single attribute operation from the source attribute in the source object
031 * to the target attribute in the target object.
032 */
033public class CopyAttributeImpl implements Procedure<RefObject> {
034
035  private static final Logger LOGGER = Logger.getLogger( CopyAttributeImpl.class.getName() );
036
037  private static final PrintModelElement PRINT_MODEL_ELEMENT = new PrintModelElement();
038
039  private final String sourceName;
040  private final String targetName;
041  private final Map<RefObject, RefObject> objectsMap;
042  private final RefPackage sourceExtent;
043  private final RefPackage targetExtent;
044  private final NameMapping valueMapping;
045
046  /**
047   * @param sourceName not null source attribute name
048   * @param targetName not null target attribute name
049   * @param objectsMap not null mapping of source model elements to target model elements
050   * @param targetExtent not null target model extent
051   * @param valueMapping not null
052   */
053  public CopyAttributeImpl(String sourceName,
054                           String targetName,
055                           Map<RefObject, RefObject> objectsMap,
056                           RefPackage sourceExtent,
057                           RefPackage targetExtent,
058                           NameMapping valueMapping) {
059    this.sourceName = sourceName;
060    this.targetName = targetName;
061    this.objectsMap = objectsMap;
062    this.sourceExtent = sourceExtent;
063    this.targetExtent = targetExtent;
064    this.valueMapping = valueMapping;
065  }
066
067  /**
068   * Copy the value of the source filed as a value of the target field with some
069   * type transformation.
070   *
071   * Copied are single value attributes, assuming that the multiple-valued attributes
072   * are associations.
073   *
074   * @param source not null source object
075   */
076  public void execute(RefObject source) throws IllegalArgumentException {
077    Object sourceValue;
078    RefObject target;
079
080    target = objectsMap.get( source );
081    if ( target != null ) {
082      try {
083        sourceValue = source.refGetValue( sourceName );
084
085      } catch (Exception ex) {
086        throw new IllegalArgumentException( "Getting '"+sourceName+"' attribute of object "
087                                            +PRINT_MODEL_ELEMENT.execute( source )
088                                            + " instance of "
089                                            +PRINT_MODEL_ELEMENT.execute( source.refMetaObject() )+" failed with: ",  ex );
090      }
091
092      if ( sourceValue instanceof RefStruct ) {
093        assignStructValue((RefStruct) sourceValue, target);
094
095      } else if ( sourceValue instanceof RefEnum ) {
096        assignEnumValue((RefEnum) sourceValue, target);
097
098      } else if ( sourceValue instanceof RefObject ) { // this object should have been mapped
099        assignObjectVlaue((RefObject) sourceValue, target );
100
101      } else if (sourceValue != null) { // any other non-JMI/non-MOF object
102        setValue(sourceValue, target);
103      }
104    }
105  }
106
107  /**
108   * Convert and assign a struct value to the target model element.
109   * @param sourceStruct not null source struct value
110   * @param target not null target model value to update
111   */
112  private void assignStructValue(RefStruct sourceStruct, RefObject target) {
113    RefStruct targetStruct;
114    Operation<RefStruct> operation;
115
116    operation = valueMapping.mapStruct(new StructNameImpl(sourceExtent, sourceStruct),
117                                       targetExtent,
118                                       objectsMap);
119    try {
120      targetStruct = operation.execute( sourceStruct );
121      setValue(targetStruct, target);
122    } catch (Exception ex) {
123      LOGGER.log( Level.INFO,
124                  "Mapping source struct "
125                  +PRINT_MODEL_ELEMENT.execute( sourceStruct )
126                  +" caused:", ex);
127    }
128  }
129
130  /**
131   * Convert and assign an enum value to the target model element.
132   * @param sourceValue not null enum value
133   * @param target not null target model value to update
134   */
135  private void assignEnumValue(RefEnum sourceValue, RefObject target) {
136    EnumValueName targetEnumName;
137
138    targetEnumName = valueMapping.mapEnum( new EnumValueNameImpl(sourceExtent, sourceValue));
139
140    if ( targetEnumName != null ) {
141      setValue( targetEnumName.lookupValue( targetExtent ), target );
142
143    } else {
144      LOGGER.log( Level.INFO,
145                  "The source enum value of "
146                  + PRINT_MODEL_ELEMENT.execute( sourceValue )
147                  + " is not mapped. Skipped ");
148    }
149  }
150
151  /**
152   * @param sourceValue not null object value to convert and assign to the targetField of the target object
153   * @param target not null target model object to update
154   */
155  private void assignObjectVlaue(RefObject sourceValue, RefObject target) {
156    Object convertedValue;
157
158    convertedValue = objectsMap.get( sourceValue );
159    setValue(convertedValue, target);
160
161    LOGGER.log( Level.FINE, "{0} mapped to {1}",
162                new Object[] {PRINT_MODEL_ELEMENT.toPrint( sourceValue ),
163                              PRINT_MODEL_ELEMENT.toPrint( convertedValue )});
164  }
165
166  /**
167   * @param convertedValue not null
168   * @param target not null
169   */
170  private void setValue(Object convertedValue, RefObject target) {
171    Object targetValue;
172
173    try {
174      targetValue = target.refGetValue(targetName);
175
176      if ( targetValue instanceof Collection ) {
177        if ( convertedValue instanceof Collection ) {
178          ((Collection) targetValue).addAll((Collection)  convertedValue);
179        } else {
180          ((Collection) targetValue).add( convertedValue );
181        }
182      } else {
183        target.refSetValue( targetName, convertedValue );
184      }
185    } catch (Exception ex) {
186      throw new IllegalArgumentException( "Setting '"+targetName+"' to value: '"
187                                          +PRINT_MODEL_ELEMENT.execute( convertedValue )
188                                          +"' on target object "
189                                          +PRINT_MODEL_ELEMENT.execute( target )
190                                          + " instance of "
191                                          +PRINT_MODEL_ELEMENT.execute( target.refMetaObject() )+" failed with: ", ex);
192    }
193  }
194
195  /**
196   * @see java.lang.Object#toString()
197   */
198  public String toString() {
199    return "Copy from "+sourceName+" to "+targetName;
200  }
201}