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.topology;
009
010import java.util.ArrayList;
011import java.util.HashSet;
012import java.util.List;
013import java.util.Set;
014
015import javax.jmi.model.GeneralizableElement;
016import javax.jmi.reflect.RefObject;
017
018import net.mdatools.modelant.core.api.match.MatchingCriteria;
019
020/**
021 * This class represents criteria based on the target object's class, i.e. if the target object/model element
022 * is not of the class this criteria represents, then it just returns no associations.
023 * This is a composite criteria
024 * @author Rusi Popov (popovr@mdatools.net)
025 */
026public class ClassCriteria implements MatchingCriteria {
027
028  private final MatchingCriteria nested;
029  private final Set<String> knownNotCovered = new HashSet<String>(11);
030  private final Set<String> knownSubclasses = new HashSet<String>(11);
031
032  /**
033   * @param className
034   * @param nested
035   */
036  public ClassCriteria(String className, MatchingCriteria nested) {
037    this.nested = nested;
038
039    knownSubclasses.add( className );
040  }
041
042  /**
043   * Returns the wrapped list of attributes only if the model element is of the metaclass with class name provided
044   * @see net.mdatools.modelant.core.operation.model.topology.SimpleCriteria#getAttributes(javax.jmi.reflect.RefObject)
045   */
046  public List<String> getAttributes(RefObject forObject) {
047    List<String> result;
048
049    if ( isOfClass(forObject) ) {
050      result = nested.getAttributes( forObject );
051    } else {
052      result = new ArrayList<String>();
053    }
054    return result;
055  }
056
057  /**
058   * Returns the wrapped list of associations only if the model element is of the metaclass with class name provided
059   * @see net.mdatools.modelant.core.operation.model.topology.SimpleCriteria#getAssociations(RefObject)
060   */
061  public List<String> getAssociations(RefObject forObject) {
062    List<String> result;
063
064    if ( isOfClass(forObject) ) {
065      result = nested.getAssociations( forObject );
066    } else {
067      result = new ArrayList<String>();
068    }
069    return result;
070  }
071
072  /**
073   * @param forObject
074   * @return true if forObject is of class with the simple name provided.
075   */
076  private boolean isOfClass(RefObject forObject) {
077    boolean result;
078    List<GeneralizableElement> superclasses;
079    int i;
080    boolean decided;
081    GeneralizableElement superClass;
082    String forClassName;
083    String thisClassName;
084
085    superClass = (GeneralizableElement) forObject.refMetaObject();
086    thisClassName = superClass.getName();
087
088    // examine all superclasses
089    superclasses = new ArrayList<GeneralizableElement>();
090    superclasses.add( superClass );
091
092    i = 0;
093    do {
094      superClass = superclasses.get( i++ );
095
096      forClassName = superClass.getName();
097
098      result = knownSubclasses.contains( forClassName );
099      decided = result
100                || knownNotCovered.contains( forClassName );
101      if ( !decided ) {
102        superclasses.addAll( superClass.getSupertypes() );
103      }
104    } while ( !decided && i < superclasses.size() );
105
106    if ( result ) {
107      knownSubclasses.add( thisClassName );
108    } else if ( !decided ) { // decision not found in the cache
109      knownNotCovered.add( thisClassName );
110    }
111    return result;
112  }
113
114  /**
115   * @see java.lang.Object#toString()
116   */
117  public String toString() {
118    StringBuilder builder;
119
120    builder = new StringBuilder();
121    builder.append( "ClassCriteria [" );
122    builder.append( "nested=" ).append( nested ).append( ", " );
123
124    builder.append( "knownNotCovered=" ).append( knownNotCovered ).append( ", " );
125    builder.append( "knownSubclasses=" ).append( knownSubclasses );
126    builder.append( "]" );
127
128    return builder.toString();
129  }
130}