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.name;
009
010import java.util.Map;
011import java.util.logging.Level;
012import java.util.logging.Logger;
013
014import javax.jmi.model.Association;
015import javax.jmi.reflect.JmiException;
016import javax.jmi.reflect.RefAssociation;
017import javax.jmi.reflect.RefAssociationLink;
018import javax.jmi.reflect.RefObject;
019import javax.jmi.reflect.RefPackage;
020
021import net.mdatools.modelant.core.api.Procedure;
022import net.mdatools.modelant.core.api.model.ConstructProcedure;
023import net.mdatools.modelant.core.api.model.NameMapping;
024import net.mdatools.modelant.core.api.name.AssociationName;
025import net.mdatools.modelant.core.api.name.Name;
026import net.mdatools.modelant.core.api.name.PackageName;
027
028/**
029 * A mechanism to locate a RefAssociation by its qualified name in the metamodel
030 * @author Rusi Popov (popovr@mdatools.net)
031 */
032public class AssociationNameImpl extends NameImpl<PackageName> implements AssociationName {
033
034  private static final Logger LOGGER = Logger.getLogger( AssociationNameImpl.class.getName() );
035
036  public AssociationNameImpl(String packageName) {
037    super(packageName);
038  }
039
040  public AssociationNameImpl(PackageName parent, String name) {
041    super(parent, name);
042  }
043
044  public AssociationNameImpl(Association association) {
045    super(new PackageNameImpl(association.getContainer()), association.getName());
046  }
047
048  /**
049   * @param qualifiedName not null
050   * @return the Association Name that represents the qualified name provided,
051   * @throws IllegalArgumentException when qualifiedName is empty
052   */
053  public static AssociationName parseQualifiedClassName(String qualifiedName) throws IllegalArgumentException {
054    AssociationName result;
055    PackageName pack;
056    String[] names;
057
058    pack = null;
059    names = qualifiedName.split( METAMODEL_PATH_SEPARATOR_PARSE );
060    for (int i=0; i<names.length-1; i++) {
061      pack = new PackageNameImpl( pack, names[i] );
062    }
063    if (names.length > 0) {
064      result = new AssociationNameImpl(pack, names[names.length-1]);
065    } else {
066      result = null;
067    }
068    return result;
069  }
070
071  /**
072   * @param rootPackage not null extent
073   */
074  public RefAssociation getMetaAssociation(RefPackage rootPackage) throws JmiException {
075    RefAssociation result;
076    RefPackage ownerPackage;
077
078    assert rootPackage != null : "Expected a non-null package";
079
080    if ( getOwner() == null ) {
081      ownerPackage = rootPackage;
082    } else {
083      ownerPackage = getOwner().getMetaPackage( rootPackage );
084    }
085
086    try {
087      result = ownerPackage.refAssociation( getName() );
088    } catch (JmiException ex) {
089      throw new IllegalArgumentException("Looking up the association "+ this
090                                       + " reached " + PRINT_MODEL_ELEMENT.execute( ownerPackage )
091                                       + " for which retrieving the nested class '"+getName()+"'"
092                                       + " caused ",ex);
093    }
094    return result;
095  }
096
097  /**
098   * @see Name#constructName(Name, String)
099   */
100  public Name<PackageName> constructName(PackageName parent, String name) {
101    return new AssociationNameImpl(parent, name);
102  }
103
104  /**
105   * @return non-null transformation of source model Links into target model links
106   * @see net.mdatools.modelant.core.api.name.Name#constructTransfromation()
107   */
108  public ConstructProcedure<RefAssociationLink> constructTransfromation() {
109    return newForwardLinkProduction();
110  }
111
112  /**
113   * This represents a target model association
114   * @return non-null producer of transformations of source model links to target model links in the same direction, e.g. the source of the produced
115   *         link is the object that corresponds to the source of the original link, the same is for link targets
116   */
117  public ConstructProcedure<RefAssociationLink> newForwardLinkProduction() {
118    return new ConstructProcedure<RefAssociationLink>() {
119
120        public Procedure<RefAssociationLink> construct(RefPackage sourceExtent,
121                                                       RefPackage targetExtent, Map<RefObject, RefObject> objectsMap, NameMapping valueMapping) throws RuntimeException, IllegalArgumentException {
122          Procedure<RefAssociationLink> result;
123          RefAssociation associationClass;
124          Association association;
125
126          associationClass = AssociationNameImpl.this.getMetaAssociation(targetExtent);
127          association = (Association) associationClass.refMetaObject();
128
129          if ( association.isDerived() ) { // the derived target associations are NOT copied
130            result = Procedure.EMPTY;
131
132          } else {
133            result = new Procedure<RefAssociationLink>() {
134              public void execute(RefAssociationLink link) throws RuntimeException, IllegalArgumentException {
135                RefObject target;
136                RefObject source;
137
138                source = objectsMap.get(link.refFirstEnd());
139                target = objectsMap.get(link.refSecondEnd());
140
141                if ( source != null ) {
142                  if ( target != null ) {
143                    associationClass.refAddLink(source, target);
144                  } else {
145                    LOGGER.log( Level.INFO, "{0} mapped to null", PRINT_MODEL_ELEMENT.execute( link.refSecondEnd() ));
146                  }
147                } else {
148                  LOGGER.log( Level.INFO, "{0} mapped to null", PRINT_MODEL_ELEMENT.execute( link.refFirstEnd() ));
149                }
150              }
151            };
152          }
153          return result;
154        }
155   };
156  }
157
158  /**
159   * This represents a target model association
160   * @return non-null producer of transformations of source model links to target model links in the opposite direction, e.g. the source of the produced
161   *         link is the object that corresponds to the target of the original link, the same is for link targets
162   */
163  public ConstructProcedure<RefAssociationLink> newBackwardLinkProduction() {
164    return new ConstructProcedure<RefAssociationLink>() {
165
166        public Procedure<RefAssociationLink> construct(RefPackage sourceExtent,
167                                                       RefPackage targetExtent, Map<RefObject, RefObject> objectsMap, NameMapping valueMapping) throws RuntimeException, IllegalArgumentException {
168          Procedure<RefAssociationLink> result;
169          RefAssociation associationClass;
170          Association association;
171
172          associationClass = AssociationNameImpl.this.getMetaAssociation(targetExtent);
173          association = (Association) associationClass.refMetaObject();
174
175          if ( association.isDerived() ) { // the derived target associations are NOT copied
176            result = Procedure.EMPTY;
177
178          } else {
179            result = new Procedure<RefAssociationLink>() {
180              public void execute(RefAssociationLink link) throws RuntimeException, IllegalArgumentException {
181                RefObject target;
182                RefObject source;
183
184                source = objectsMap.get(link.refFirstEnd());
185                target = objectsMap.get(link.refSecondEnd());
186
187                if ( source != null ) {
188                  if ( target != null ) {
189                    associationClass.refAddLink(target, source);
190                  } else {
191                    LOGGER.log( Level.INFO, "{0} mapped to null", PRINT_MODEL_ELEMENT.execute( link.refSecondEnd() ));
192                  }
193                } else {
194                  LOGGER.log( Level.INFO, "{0} mapped to null", PRINT_MODEL_ELEMENT.execute( link.refFirstEnd() ));
195                }
196              }
197            };
198          }
199          return result;
200        }
201   };
202  }
203}