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;
011
012import javax.jmi.model.Association;
013import javax.jmi.model.Classifier;
014import javax.jmi.model.Feature;
015import javax.jmi.model.ModelElement;
016import javax.jmi.model.MofPackage;
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.ClassName;
025import net.mdatools.modelant.core.api.name.Name;
026import net.mdatools.modelant.core.operation.element.PrintModelElement;
027import net.mdatools.modelant.core.util.key.Hash;
028
029/**
030 * A key in package/type/value mapping
031 * @param <P> the type of the parent/owner name
032 * @author Rusi Popov (popovr@mdatools.net)
033 */
034public class NameImpl<P extends Name<?>> implements Name<P> {
035
036  /**
037   * A common constant of "no name" and "stop mapping", as null is not name acceptable value
038   */
039  public static final Name<?> NO_MAP_NAME = new NameImpl<>("$NO_MAP_NAME$");
040
041  protected static final PrintModelElement PRINT_MODEL_ELEMENT = new PrintModelElement();
042
043  private final P owner;
044  private final String name;
045  private final int hash;
046
047  /**
048   * Construct root name
049   * @param name not null, not empty
050   */
051  protected NameImpl(String name) {
052    this(name, null);
053  }
054
055  /**
056   * Construct a name in the context of the owner name
057   * @param owner may be null, when null provided this is equal to new Name(name)
058   * @param name not null, not empty
059   */
060  protected NameImpl(P owner, String name) {
061    this(name, owner);
062  }
063
064  /**
065   * Construct a name in the context of the owner name
066   * @param owner not null
067   * @param name not null, not empty
068   */
069  private NameImpl(String name, P owner) {
070    int hash;
071
072    assert name != null : "Expected non-null name";
073
074    this.name = name;
075    this.owner = owner;
076
077    hash = getClass().hashCode();
078    if ( owner != null ) {
079      hash = (hash << 2) + owner.hashCode();
080    }
081    hash = (hash << 2) + Hash.hash( name );
082    this.hash = hash;
083  }
084
085  /**
086   * @param modelElement non-null model element
087   * @return the qualified name of the meta object (MOF element (class) of the metamodel), that describes
088   *         the provided model element
089   */
090  public static ClassName constructQualifiedMetaObjectName(RefObject modelElement) {
091    return (ClassName) constructQualifiedElementName( (ModelElement) modelElement.refMetaObject() );
092  }
093
094
095  /**
096   * @param metaObject non-null MOF object
097   * @return the qualified name of the MOF element, calculated down the containment relation
098   */
099  public static Name constructQualifiedElementName(ModelElement metaObject) {
100    Name result;
101
102    if ( metaObject instanceof MofPackage ) {
103      result = new PackageNameImpl( (PackageNameImpl) constructQualifiedElementName( metaObject.getContainer() ),
104                                    metaObject.getName());
105    } else if ( metaObject instanceof Feature ) {
106      result = new FieldNameImpl( (ClassNameImpl) constructQualifiedElementName( metaObject.getContainer() ),
107                                  metaObject.getName());
108    } else if ( metaObject instanceof Association ) {
109      result = new AssociationNameImpl( (PackageNameImpl) constructQualifiedElementName( metaObject.getContainer() ),
110                                        metaObject.getName());
111    } else if ( metaObject instanceof Classifier ) {
112      result = new ClassNameImpl( (PackageNameImpl) constructQualifiedElementName( metaObject.getContainer() ),
113                                  metaObject.getName());
114    } else {
115      result = null;
116    }
117    return result;
118  }
119
120
121  public final boolean isEmpty() {
122    boolean result;
123    result = name.isEmpty();
124    return result;
125  }
126
127  /**
128   * @see java.lang.Object#hashCode()
129   */
130  public final int hashCode() {
131    return hash;
132  }
133
134  /**
135   * @see java.lang.Object#equals(java.lang.Object)
136   */
137  public final boolean equals(Object obj) {
138    boolean result;
139    NameImpl other;
140
141    result = obj == this;
142
143    if ( !result && obj != null && obj.getClass() == getClass() ) {
144      other = (NameImpl) obj;
145
146      result = (owner == null && other.owner == null
147                || owner != null && owner.equals( other.owner ))
148               && name.equals( other.name );
149    }
150    return result;
151  }
152
153  /**
154   * @return the qualified name this represents
155   */
156  public String toString() {
157    String result;
158
159    if ( owner == null ) {
160      result = name;
161    } else {
162      result = owner.toString()+METAMODEL_PATH_SEPARATOR+name;
163    }
164    return result;
165  }
166
167  /**
168   * @return
169   * @see net.mdatools.modelant.core.api.name.Name#getOwner()
170   */
171  public final P getOwner() {
172    return owner;
173  }
174
175  /**
176   * @return
177   * @see net.mdatools.modelant.core.api.name.Name#getName()
178   */
179  public final String getName() {
180    return name;
181  }
182
183  /**
184   * @param qualifiedName not null
185   * @return the Name that represents the qualified name provided,
186   */
187  public static Name parseQualifiedName(String qualifiedName) {
188    NameImpl result;
189    String[] names;
190
191    result = null;
192    names = qualifiedName.split( METAMODEL_PATH_SEPARATOR_PARSE );
193    for (String name:names) {
194      result = new NameImpl( result, name );
195    }
196    return result;
197  }
198
199  /**
200   * @see net.mdatools.modelant.core.api.name.Name#constructName(net.mdatools.modelant.core.api.name.Name, java.lang.String)
201   */
202  public Name<P> constructName(P parent, String name) {
203    return new NameImpl<P>(parent, name);
204  }
205
206  /**
207   * @see net.mdatools.modelant.core.api.name.Name#constructNoTransfromation()
208   */
209  public final ConstructProcedure<RefAssociationLink> constructNoTransfromation() {
210    return new ConstructProcedure<RefAssociationLink>() {
211      public Procedure<RefAssociationLink> construct(RefPackage sourceExtent, RefPackage targetExtent, Map<RefObject, RefObject> objectsMap, NameMapping valueMapping) {
212        return Procedure.EMPTY;
213      }
214    };
215  }
216
217  /**
218   * Override in subclasses
219   * @see net.mdatools.modelant.core.api.name.Name#constructTransfromation()
220   */
221  public ConstructProcedure<?> constructTransfromation() {
222    return new ConstructProcedure<RefAssociationLink>() {
223      public Procedure<RefAssociationLink> construct(RefPackage sourceExtent, RefPackage targetExtent, Map<RefObject, RefObject> objectsMap, NameMapping valueMapping) {
224        return Procedure.EMPTY;
225      }
226    };
227  }
228}