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.Collection;
011import java.util.HashSet;
012import java.util.Set;
013
014/**
015 * A mechanism to map sets <b> 1:1 </b> to other sets.
016 * <b>All sets do not intersect.</b>
017 * The mapping is symmetric, allowing <pre>
018 *   map(getRepresentative(a))=getRepresentative(b)
019 *   map(getRepresentative(b))=getRepresentative(a)
020 * </pre>
021 * @param <V> the class of the elements in the mapped sets.
022 * @author Rusi Popov (popovr@mdatools.net)
023 */
024public interface EquivalenceClassesMap<V> {
025
026  /**
027   * Register the X equivalence class mapped  to Y equivalence class,
028   * Any existing mappings of the equivalence classes' elements are overridden.
029   * @param xClass not null, not empty equivalence class at x side
030   * @param yClass not null, not empty equivalence class at y side
031   */
032  void add(Collection<V> xClass, Collection<V> yClass);
033
034  /**
035   * This method guarantees that each x or y objects pertain to no more than a single set and there is
036   * one-to-one mapping between these sets
037   * If there is a set containing y and there is no set containing x, then {x} is put as the set that is mapped to y;
038   * If there is a set containing x and there is no set containing y, then {y} is put as the set that is mapped to x;
039   * If there is no set containing x and there is no set containing y, then a new set {x} is created and mapped to the new set {y}
040   * If there is a set containing x A.K.A {x} and there is a set containing y A.K.A {y}, then if {x} not mapped to {y}, this method fails.
041   * @param x not null, conditionally named key
042   * @param y not null, conditionally named value
043   * @throws IllegalArgumentException when mapping x,y that have already mapped representatives that do not match
044   */
045  void add(V x, V y) throws IllegalArgumentException;
046
047
048  /**
049   * @return a non-null collection the representatives for elements X add(X,y) was called with.
050   */
051  Collection<V> getXKeys();
052
053  /**
054   * @param element is non-null
055   * @return a non-null collection of objects of the same equivalence class the element pertains to
056   */
057  Collection<V> getEquivalents(V element);
058
059  /**
060   * @param element
061   * @return the element that represents the equivalence class this element pertains to
062   */
063  V getRepresentative(V element);
064
065  /**
066   * @param element
067   * @return the element this element (any element of its equivalence class) is mapped to
068   */
069  V map(V element);
070
071
072  /**
073   * @param elements non null collection
074   * @return a collection of their representatives or the objects themselves, if they have not been matched in
075   *         the target model and therefore have no representatives
076   */
077  default Set<V> getRepresentativesOrSelf(Collection<V> elements) {
078    Set<V> result;
079    V mapped;
080
081    result = new HashSet<>(elements.size()<<2);
082
083    for(V element: elements) {
084      mapped = getRepresentative( element );
085
086      if ( mapped == null ) { // no representative - use the element itself
087        result.add( element );
088      } else {
089        result.add( mapped );
090      }
091    }
092    return result;
093  }
094
095  /**
096   * @param elements not null
097   * @return a non-null set of the elements are mapped on. Mapped nulls are skipped.
098   */
099  default Set<V> getMappedRepresentatives(Collection<V> elements) {
100    Set<V> result;
101    V mapped;
102
103    result = new HashSet<V>(elements.size()<<1);
104
105    // collect the matched model elements for these nodes
106    for(V element :  elements ) {
107      mapped = map( element );
108
109      if ( mapped != null ) {
110        result.add( mapped );
111      }
112    }
113    return result;
114  }
115
116  /**
117   * @param elements non null collection
118   * @return a collection of their representatives. null representatives are skipped
119   */
120  default Set<V> getRepresentatives(Collection<V> elements) {
121    Set<V> result;
122    V mapped;
123
124    result = new HashSet<>(elements.size()<<2);
125
126    for(V element: elements) {
127      mapped = getRepresentative( element );
128
129      if ( mapped != null ) { // no representative - use the element itself
130        result.add( mapped );
131      }
132    }
133    return result;
134  }
135}