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.ArrayList;
011import java.util.List;
012import java.util.Map;
013
014import javax.jmi.reflect.InvalidNameException;
015import javax.jmi.reflect.JmiException;
016import javax.jmi.reflect.RefBaseObject;
017import javax.jmi.reflect.RefClass;
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.model.ConstructOperation;
024import net.mdatools.modelant.core.api.model.ConstructProcedure;
025import net.mdatools.modelant.core.api.model.NameMapping;
026import net.mdatools.modelant.core.api.name.Name;
027import net.mdatools.modelant.core.api.name.PackageName;
028import net.mdatools.modelant.core.api.name.StructName;
029
030/**
031 * @author Rusi Popov (popovr@mdatools.net)
032 */
033public class StructNameImpl extends NameImpl<Name<?>> implements StructName {
034  public StructNameImpl(String packageName) {
035    super(packageName);
036  }
037
038  public StructNameImpl(Name<?> parent, String name) {
039    super(parent, name);
040  }
041
042  public StructNameImpl(RefPackage extent, RefStruct struct) {
043    this(constructOwnerClassName(struct.refTypeName(), extent),
044          getStructName(struct));
045  }
046
047  /**
048   * @param struct not null
049   * @return the name of the Struct's class from its qualified name
050   */
051  private static String getStructName(RefStruct struct) {
052    String result;
053    List<String> names;
054
055    names = struct.refTypeName();
056    result = names.get(names.size()-1);
057
058    return result;
059  }
060
061  /**
062   * @param qualifiedName not null, not empty qualified name of the Enum Class
063   * @param targetExtent not null
064   * @return the class name of that Enum
065   */
066  private static Name<?> constructOwnerClassName(List<String> qualifiedName, RefPackage targetExtent) {
067    Name<?> result;
068    String name;
069    RefBaseObject namespace;
070
071    namespace = targetExtent;
072    result = null;
073    for (int i=0; i<qualifiedName.size()-1; i++) {
074      name = qualifiedName.get( i );
075
076      if ( namespace instanceof RefPackage ) { // the current name is a name of a package or class, packageName is a name of a package so far
077        try {
078          namespace = ((RefPackage) namespace).refPackage( name );
079          result = new PackageNameImpl((PackageName) result, name);
080
081        } catch (InvalidNameException ex) { // name is not a package, it could be only a class
082          namespace = ((RefPackage) namespace).refClass( name );
083          result = new ClassNameImpl((PackageName) result, name);
084        }
085      } else { // namespace is a Class owning at most an enumeration
086        throw new IllegalArgumentException("Resolving "+qualifiedName
087                                           +" struct name, reached "+PRINT_MODEL_ELEMENT.execute( namespace )
088                                           +" which cannot contain nested "+name);
089      }
090    }
091    return result;
092  }
093
094  /**
095   * @see net.mdatools.modelant.core.name.NameImpl#constructName(net.mdatools.modelant.core.api.name.Name, java.lang.String)
096   */
097  public Name<Name<?>> constructName(Name<?> parent, String name) {
098    return new StructNameImpl(parent, name);
099  }
100
101  /**
102   * @see net.mdatools.modelant.core.api.name.Name#constructTransfromation()
103   */
104  public ConstructProcedure<?> constructTransfromation() {
105    throw new IllegalStateException("This method should not be called by design");
106  }
107
108  /**
109   * The Structs are instantiated through the model package in contrast to the classes,
110   * and there is no
111   * so locate the package in the target model that contains the target struct type and
112   * instantiate that struct.
113   * @param rootPackage not null extent
114   * @param fieldValues TODO
115   * @return non-null structure instance
116   * @throws JmiException when the struct does not exist
117   */
118  private RefStruct construct(RefPackage targetExtent, List<?> fieldValues) throws JmiException {
119    RefStruct result;
120    Name<?> structClassNamespace;
121    PackageName structClassNamespaceNamespace;
122    RefPackage structClassNamespacePackage;
123    RefPackage structClassPackage;
124    RefClass structClassClass;
125
126    if ( getOwner() == null ) {
127      throw new InvalidNameException( toString(), "Expected a parent class or package name provided");
128    }
129
130    structClassNamespace = getOwner();
131    structClassNamespaceNamespace = (PackageName) structClassNamespace.getOwner();
132
133    if ( structClassNamespaceNamespace != null ) {
134      structClassNamespacePackage = structClassNamespaceNamespace.getMetaPackage( targetExtent );
135    } else {
136      structClassNamespacePackage = targetExtent;
137    }
138
139    // in enumClassNamespaceNamespace, enumClassNamespace could be a class or package
140    // try both options, which one will work
141    try {
142      structClassPackage = structClassNamespacePackage.refPackage( structClassNamespace.getName() );
143      result = structClassPackage.refCreateStruct(getName(), fieldValues );
144
145    } catch (InvalidNameException ex) {
146      structClassClass = structClassNamespacePackage.refClass( structClassNamespace.getName() );
147      result = structClassClass.refCreateStruct(getName(), fieldValues );
148    }
149    return result;
150  }
151
152
153  /**
154   * @see net.mdatools.modelant.core.api.name.StructName#constructCopyOperation()
155   */
156  public ConstructOperation<RefStruct> constructCopyOperation() {
157    return new ConstructOperation<RefStruct>() {
158      public Operation<RefStruct> construct(RefPackage targetExtent,
159                                            Map<RefObject, RefObject> objectsMap,
160                                            NameMapping valueMapping) {
161        return new Operation<RefStruct>() {
162          public RefStruct execute(RefStruct struct) throws RuntimeException, IllegalArgumentException {
163            List<Object> values;
164
165            values = new ArrayList<>();
166            for (String name : (List<String>) struct.refFieldNames()) {
167              values.add( struct.refGetValue( name ) );
168            }
169            return StructNameImpl.this.construct( targetExtent, values );
170          }
171        };
172      }
173    };
174  }
175}