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 static net.mdatools.modelant.core.name.NameImpl.NO_MAP_NAME; 011 012import java.util.HashMap; 013import java.util.Map; 014import java.util.logging.Level; 015import java.util.logging.Logger; 016 017import javax.jmi.reflect.RefAssociationLink; 018import javax.jmi.reflect.RefObject; 019import javax.jmi.reflect.RefPackage; 020import javax.jmi.reflect.RefStruct; 021 022import net.mdatools.modelant.core.api.Operation; 023import net.mdatools.modelant.core.api.Procedure; 024import net.mdatools.modelant.core.api.model.ConstructOperation; 025import net.mdatools.modelant.core.api.model.ConstructProcedure; 026import net.mdatools.modelant.core.api.model.NameMapping; 027import net.mdatools.modelant.core.api.name.AssociationName; 028import net.mdatools.modelant.core.api.name.ClassName; 029import net.mdatools.modelant.core.api.name.EnumValueName; 030import net.mdatools.modelant.core.api.name.FieldName; 031import net.mdatools.modelant.core.api.name.Name; 032import net.mdatools.modelant.core.api.name.StructName; 033 034/** 035 * Define correspondence between the source and target models either as:<ul> 036 * <li> direct name-to-name mapping 037 * <li> direct parent package name-to-name mapping and deriving the specific name-to-name mapping for nested elements 038 * <li> explicit name-to-transformation mapping 039 * </ul> 040 * @author Rusi Popov (popovr@mdatools.net) 041 */ 042public class RenamingMapping implements NameMapping { 043 044 private static final Logger LOGGER = Logger.getLogger( RenamingMapping.class.getName() ); 045 046 /** 047 * Maps source to target names for transformation 048 */ 049 private final Map<Name<?>, Name<?>> renaming = new HashMap<>(); 050 051 /** 052 * Maps source model name to a constructor of procedures to update the target model elements 053 */ 054 private final Map<Name<?>, ConstructProcedure<?>> nameToMethodMap = new HashMap<>(); 055 056 /** 057 * Maps source model name to a constructor operations to produce the target model elements 058 * Used only for operations on RefStruct 059 */ 060 private final Map<StructName, ConstructOperation<RefStruct>> nameToOperationMap = new HashMap<>(); 061 062 /* 063 * INVARIANT: 064 * 1. renaming.keySet() intersect nameToMethodMap.keySet() = EMPTY 065 * 2. nameToMethodMap maps different types considering the key: 066 * key instanceof ClassName - mapped is produce Procedure<RefObject> 067 * key instanceof AssociationName - mapped is produce Procedure<RefAssociationLink> 068 * key instanceof FieldName - mapped is produce Procedure<RefObejct> 069 */ 070 071 /** 072 * Implement in the subclass to initialize itself by calling the map() methods 073 */ 074 protected RenamingMapping() { 075 } 076 077 /** 078 * Register a name-to-name mapping 079 * @param key not null qualified name, not a struct name 080 * @param name not null qualified name 081 */ 082 protected final <T extends Name<?>> void set(T key, T name) { 083 assert key != null : "Expected a non-null key provided"; 084 assert !(key instanceof StructName) : "Expected "+key+" is not a struct name"; 085 086 assert name != null : "Expected a non-null name provided"; 087 088 renaming.put(key, name); 089 nameToMethodMap.remove(key); 090 } 091 092 /** 093 * Mark a class,association, field, enum or struct as not mapped 094 * @param key not null 095 */ 096 protected final <T extends Name<?>> void unset(T key) { 097 set(key, NO_MAP_NAME); 098 } 099 100 /** 101 * Register a field name transfer method 102 * 103 * As of the requirements of {@link NameMapping#mapMetaFieldName(FieldName, RefPackage, RefPackage, Map)}, the 104 * procedure should NOT write into target attributes, that are NOT CHANGEABLE. 105 * 106 * @param key not null 107 * @param translate not null 108 */ 109 protected final void set(FieldName key, 110 ConstructProcedure<RefObject> translate) { 111 112 assert key != null : "Expected a non-null key provided"; 113 assert translate != null : "Expected a non-null translate operation provided"; 114 115 nameToMethodMap.put( key, translate ); 116 } 117 118 /** 119 * Map an association to a transfer procedure. 120 * 121 * As of the requirements of {@link NameMapping#mapMetaAssociation(AssociationName, RefPackage, RefPackage, Map)}, the 122 * procedure should NOT copy links into target associations, that are DERIVED. 123 * 124 * @param key 125 * @param translate 126 */ 127 protected final void set(AssociationName key, 128 ConstructProcedure<RefAssociationLink> translate) { 129 assert key != null : "Expected a non-null key provided"; 130 assert translate != null : "Expected a non-null translate operation provided"; 131 132 nameToMethodMap.put( key, translate ); 133 } 134 135 /** 136 * Mark an association as mapped to the target one in the same direction of the links 137 * @param key not null qualified name of the original association 138 * @param target not null qualified name 139 */ 140 protected final void setForward(AssociationName key, AssociationName target) { 141 assert key != null : "Expected a non-null key provided"; 142 assert target != null : "Expected a non-null target provided"; 143 144 set( key, 145 target.newForwardLinkProduction()); 146 } 147 148 /** 149 * Mark an association as mapped to the target one in the opposite direction of the links 150 * @param key not null qualified name of the original association 151 * @param target not null qualified name 152 */ 153 protected final void setBackward(AssociationName key, AssociationName target) { 154 assert key != null : "Expected a non-null key provided"; 155 assert target != null : "Expected a non-null target provided"; 156 157 set( key, 158 target.newBackwardLinkProduction()); 159 } 160 161 /** 162 * Map a class to a conversion procedure 163 * @param key 164 * @param translate 165 */ 166 protected final void set(ClassName key, 167 ConstructProcedure<RefObject> translate) { 168 assert key != null : "Expected a non-null key provided"; 169 assert translate != null : "Expected a non-null translate operation provided"; 170 171 nameToMethodMap.put( key, translate ); 172 } 173 174 /** 175 * Map a struct type to a conversion operation 176 * @param key 177 * @param translate 178 */ 179 protected final void set(StructName key, 180 ConstructOperation<RefStruct> translate) { 181 assert key != null : "Expected a non-null key provided"; 182 assert translate != null : "Expected a non-null translate operation provided"; 183 184 nameToOperationMap.put( key, translate ); 185 } 186 187 /** 188 * @see NameMapping#mapMetaClass(ClassName, RefPackage, RefPackage, Map) 189 */ 190 public final Procedure<RefObject> mapMetaClass(ClassName className, 191 RefPackage sourceExtent, 192 RefPackage targetExtent, 193 Map<RefObject, RefObject> objectsMap) { 194 ConstructProcedure<RefObject> mapped; 195 196 mapped = (ConstructProcedure<RefObject>) lookupName(className); 197 198 return mapped.construct( sourceExtent, targetExtent, objectsMap, this ); 199 } 200 201 /** 202 * @see NameMapping#mapMetaAssociation(AssociationName, RefPackage, RefPackage, Map) 203 */ 204 public final Procedure<RefAssociationLink> mapMetaAssociation(AssociationName associationName, 205 RefPackage sourceExtent, 206 RefPackage targetExtent, 207 Map<RefObject, RefObject> objectsMap) { 208 ConstructProcedure<RefAssociationLink> mapped; 209 210 mapped = (ConstructProcedure<RefAssociationLink>) lookupName(associationName); 211 212 return mapped.construct( sourceExtent, targetExtent, objectsMap, this ); 213 } 214 215 /** 216 * @see NameMapping#mapMetaFieldName(FieldName, RefPackage, RefPackage, Map) 217 */ 218 public final Procedure<RefObject> mapMetaFieldName(FieldName fieldName, 219 RefPackage sourceExtent, 220 RefPackage targetExtent, Map<RefObject, RefObject> objectsMap) { 221 ConstructProcedure<RefObject> mapped; 222 223 mapped = (ConstructProcedure<RefObject>) lookupName(fieldName); 224 225 return mapped.construct(sourceExtent, targetExtent, objectsMap, this); 226 } 227 228 229 230 /** 231 * @param source not null source metamodel name 232 * @return possibly null target metamodel name mapped to the source name. 233 * NOTE: It might happen that not all names, that are mapped to procedures through map*(name) 234 * are mapped to names. Use this method for testing purposes only 235 */ 236 public final <T extends Name<?>> Name<T> getName(Name<T> source) { 237 return constructMappedName( source ); 238 } 239 240 /** 241 * @param name not null 242 * @return the non-null method to produce the transformation for the name into the corresponding object into the target extent 243 */ 244 private ConstructProcedure<?> lookupName(Name<?> name) { 245 ConstructProcedure<?> result; 246 Name<?> mappedName; 247 248 result = nameToMethodMap.get(name); 249 250 if ( result==null ) { // no explicit method mapped 251 mappedName = constructMappedName(name); 252 253 if ( mappedName != null ) { 254 result = mappedName.constructTransfromation(); 255 } else { 256 result = name.constructNoTransfromation(); 257 } 258 } else { // log the explicit procedure mapping to complete the name mapping log in constructMappedName 259 260 LOGGER.log( Level.FINE, "{0} mapped to {1}", new Object[] {name, result}); 261 } 262 return result; 263 } 264 265 /** 266 * @param name not null 267 * @return the non-null method to produce the transformation for the name into the corresponding object into the target extent 268 */ 269 private ConstructOperation<RefStruct> lookupNameOperation(StructName name) { 270 ConstructOperation<RefStruct> result; 271 StructName mappedName; 272 273 result = nameToOperationMap.get(name); 274 275 if ( result==null ) { // no explicit method mapped 276 mappedName = constructMappedName(name); 277 278 if ( mappedName != null ) { 279 result = mappedName.constructCopyOperation(); 280 } else { 281 result = new ConstructOperation<RefStruct>() { 282 public Operation<RefStruct> construct(RefPackage targetExtent, Map<RefObject, RefObject> objectsMap, 283 NameMapping valueMapping) { 284 return new Operation<RefStruct>() { 285 public RefStruct execute(RefStruct argument) throws RuntimeException, IllegalArgumentException { 286 return null; 287 } 288 }; 289 } 290 }; 291 } 292 } else { // log the explicit procedure mapping to complete the name mapping log in constructMappedName 293 294 LOGGER.log( Level.FINE, "{0} mapped to {1}", new Object[] {name, result}); 295 } 296 return result; 297 } 298 299 /** 300 * Use the defaults and existing mappings to construct the name that corresponds to the provided one in the target mode 301 * @param name not null 302 * @return null when no mapping defined 303 */ 304 private <P extends Name<?>, T extends Name<P>> T constructMappedName(T name) { 305 T result; 306 Name<?> constructedParent; 307 308 result = (T) renaming.get(name); 309 if ( result == NO_MAP_NAME ) { // stop any mapping 310 result = null; 311 312 } else if ( result == null 313 && name.getOwner() != null ) { 314 constructedParent = constructMappedName((Name<?>) name.getOwner()); 315 316 if ( constructedParent != null ) { 317 result = (T) name.constructName((P) constructedParent, name.getName()); 318 } 319 } 320 321 LOGGER.log( Level.FINE, "{0} mapped to {1}", new Object[] {name, result}); 322 323 return result; 324 } 325 326 /** 327 * @see net.mdatools.modelant.core.api.model.NameMapping#mapEnum(net.mdatools.modelant.core.api.name.EnumValueName) 328 */ 329 public final EnumValueName mapEnum(EnumValueName value) { 330 return constructMappedName(value); 331 } 332 333 /** 334 * @see net.mdatools.modelant.core.api.model.NameMapping#mapEnum(net.mdatools.modelant.core.api.name.EnumValueName) 335 */ 336 public final Operation<RefStruct> mapStruct(StructName structName, RefPackage targetExtent, Map<RefObject, RefObject> objectsMap) { 337 return lookupNameOperation( structName ).construct( targetExtent, objectsMap, this ); 338 } 339}