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.Collection;
012import java.util.IdentityHashMap;
013import java.util.Iterator;
014import java.util.List;
015import java.util.Map;
016
017/**
018 * @param <V> is the class of the mapped elements
019 * @author Rusi Popov (popovr@mdatools.net)
020 */
021public class EquivalenceClassesMapImpl<V> implements EquivalenceClassesMap<V> {
022
023  /**
024   * Maps each element to the representative of the set it pertains to
025   */
026  private final Map<V,V> elementToKey = new IdentityHashMap<V,V>();
027
028  /**
029   * Maps the key (set representative) to the representative of the other set
030   */
031  private final Map<V,V> keyToKey = new IdentityHashMap<V,V>();
032
033  /**
034   * The unique keys of X elements added here
035   */
036  private final List<V> xKeys = new ArrayList<>();
037
038  /**
039   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#add(java.util.Collection, java.util.Collection)
040   */
041  public void add(Collection<V> xClass, Collection<V> yClass) {
042    Iterator<V> xIterator;
043    Iterator<V> yIterator;
044    V x;
045    V y;
046
047    assert xClass != null : "Expected not null xClass";
048    assert !xClass.isEmpty() : "Expected not empty xClass";
049
050    assert yClass != null : "Expected not null yClass";
051    assert !yClass.isEmpty() : "Expected not empty yClass";
052
053    xIterator = xClass.iterator();
054    yIterator = yClass.iterator();
055
056    x = xIterator.next();
057    y = yIterator.next();
058
059    add(x,y);
060    while ( xIterator.hasNext() ) {
061      add( xIterator.next(), y );
062    }
063    while ( yIterator.hasNext() ) {
064      add( x, yIterator.next() );
065    }
066  }
067
068  /**
069   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#add(java.lang.Object, java.lang.Object)
070   */
071  public final void add(V x, V y) throws IllegalArgumentException {
072    V xRep;
073    V yRep;
074
075    xRep = getRepresentative( x );
076    yRep = getRepresentative( y );
077    if ( xRep == null ) {
078      if ( yRep == null ) { // x, y still not mapped
079        // bind x,y as representatives == create a new equivalence set
080        elementToKey.put( x, x );
081        elementToKey.put( y, y );
082
083        // make the mapping symmetric
084        keyToKey.put(x, y);
085        keyToKey.put(y, x);
086
087        xKeys.add( x );
088      } else { // y mapped to yRep, x not mapped
089        elementToKey.put( x, keyToKey.get( yRep ));
090      }
091    } else { // x mapped to xRep
092      if ( yRep == null ) { // y still not mapped
093        elementToKey.put( y, keyToKey.get( xRep ));
094
095      } else { // y mapped to yRep & x mapped
096        if ( yRep != keyToKey.get( xRep ) ) { // xRep != keyToKey.get( yRep )
097          throw new IllegalArgumentException("Expected "+x+ " with its representative "+xRep
098                                             +" is mapped to be mapped to the representative of "+yRep+" of "+y);
099        }
100      }
101    }
102  }
103
104  /**
105   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#getXKeys()
106   */
107  public final Collection<V> getXKeys() {
108    return xKeys;
109  }
110
111  /**
112   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#getEquivalents(java.lang.Object)
113   */
114  public final Collection<V> getEquivalents(V element) {
115    Collection<V> result;
116    V representative;
117
118    result = new ArrayList<V>();
119    representative = elementToKey.get( element );
120    for (Map.Entry<V, V> entry: elementToKey.entrySet()) {
121      if ( entry.getValue() == representative ) {
122        result.add( entry.getKey() );
123      }
124    }
125    return result;
126  }
127
128  /**
129   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#getRepresentative(java.lang.Object)
130   */
131  public final V getRepresentative(V element) {
132    return elementToKey.get( element );
133  }
134
135  /**
136   * @see net.mdatools.modelant.core.operation.model.topology.EquivalenceClassesMap#map(java.lang.Object)
137   */
138  public final V map(V element) {
139    V result;
140
141    result = keyToKey.get( getRepresentative( element ) );
142
143    return result;
144  }
145}