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 017import javax.jmi.reflect.RefObject; 018 019import net.mdatools.modelant.core.api.match.MatchingCriteria; 020import net.mdatools.modelant.core.util.map.MapToCollection; 021import net.mdatools.modelant.core.util.map.MapToSet; 022 023/** 024 * This class allows structurally comparing models, independently of their actual meta-models. It is 025 * intended to serve the model change tracking. 026 * 027 * <pre> 028 * Usage: 029 * new ModelTopology() 030 * load() 031 * findEquals() / findEquals(EquivalenceMap) returns the element matches 032 * getNotProcessed() returns not matched elements 033 * </pre> 034 * 035 * NOTE: The instances should not be reused 036 * 037 * @author Rusi Popov (popovr@mdatools.net) 038 */ 039public class ModelTopology { 040 041 /** 042 * Maps node.level to set of nodes with that value 043 * 044 * @see #add(Node) 045 * @see #remove(Node) 046 */ 047 private final MapToCollection<Key, Node<RefObject>> nodes = new MapToSet<>(); 048 049 /** 050 * Contains only nodes pertaining to <b>nodes</b> and having level()==0 == isReady() 051 * Used for controlled iteration in topological order over the nodes. 052 */ 053 private final List<Node<RefObject>> readyNodes = new ArrayList<>(); 054 055 /** 056 * The correspondence from model elements to their internal representation as nodes 057 */ 058 private final Map<RefObject, Node<RefObject>> elementToNodeMap = new IdentityHashMap<>( 101 ); 059 060 /** 061 * @return true iff getNodes() is empty 062 */ 063 public boolean isEmpty() { 064 return nodes.isEmpty(); 065 } 066 067 /** 068 * Load into this model topology the listed associations and attributes, so that model processing 069 * would treat as equal any model elements with same values of the listed attributes and equal 070 * objects bound in the listed associations. 071 * 072 * @param criteria is a not null criteria defining the attribute names to evaluate for all model 073 * elements 074 * @param elements is a non-null list of model elements to order 075 */ 076 public void load(MatchingCriteria criteria, Collection<RefObject> elements) { 077 Iterator<RefObject> elementsIterator; 078 Iterator<Node<RefObject>> nodesIterator; 079 RefObject element; 080 Node<RefObject> node; 081 082 assert criteria != null : "Expected non-null criteria"; 083 084 // enlist the model elements to order 085 elementsIterator = elements.iterator(); 086 while ( elementsIterator.hasNext() ) { 087 element = elementsIterator.next(); 088 089 node = new Node<>( element, criteria ); 090 elementToNodeMap.put( element, node ); 091 } 092 093 // arrange the nodes in this topology according to its level (relations to other nodes it refers) 094 nodesIterator = elementToNodeMap.values().iterator(); 095 while ( nodesIterator.hasNext() ) { 096 node = nodesIterator.next(); 097 098 node.assignAssociatedNodes( elementToNodeMap ); 099 add( node ); 100 } 101 } 102 103 104 /** 105 * (Re)calculate the key and bind the node and identify the ready nodes 106 * @param node 107 */ 108 private void add(Node<RefObject> node) { 109 nodes.put( node.getKey(), node ); 110 111 if ( node.isReady() ) { 112 readyNodes.add( node ); 113 } 114 } 115 116 /** 117 * @return a copy of the current ready nodes, this way forming a "generation of ready nodes", 118 * that could be manipulated independently of the current set of ready nodes 119 */ 120 public final ArrayList<Node<RefObject>> getGenerationOfReady() { 121 return new ArrayList<>(readyNodes); 122 } 123 124 125 /** 126 * Removes from this topology all nodes from the equivalence class the representative is of 127 * @param equivalents not null 128 */ 129 public final void remove(Collection<RefObject> equivalents) { 130 for (RefObject element : equivalents) { 131 removeFromTopology( elementToNodeMap.get( element ) ); 132 } 133 } 134 135 136 /** 137 * This method excludes the nodes provided from the owner topology, decreases the level of all 138 * nodes that refer this and rearranges the topology to reflect level of the changed nodes. 139 * 140 * @param nodes is a collection of ready nodes 141 */ 142 public void removeFromTopology(Collection<Node<RefObject>> nodes) { 143 for (Node<RefObject> node : new ArrayList<>( nodes )) { 144 removeFromTopology( node ); 145 } 146 } 147 148 /** 149 * Exclude the node from the owner topology, decreasing the level of all nodes that refer this and 150 * rearranges the topology to reflect level of the changed nodes. Because of the explicitly 151 * provided mappings of nodes that to be treated as equal, but they are not, we cannot expect 152 * anymore that 153 * <ul> 154 * <li>the nodes to remove are in ready state and 155 * <li>when removing an object its referers are in the topology (because they might have been 156 * removed) 157 * </ul> 158 * 159 * @param node is a ready node 160 */ 161 private void removeFromTopology(Node<RefObject> node) { 162 Iterator<Node<RefObject>> referencingIterator; 163 Node<RefObject> referer; 164 boolean actuallyRemoved; 165 166 actuallyRemoved = remove( node ); 167 168 assert actuallyRemoved : "Expected a node that existed in this topology " + node; 169 170 // for each of the referencing node nodes - decrease number of referenced and rearrange 171 referencingIterator = node.getReferers().iterator(); 172 while ( referencingIterator.hasNext() ) { 173 referer = referencingIterator.next(); 174 175 actuallyRemoved = remove( referer ); 176 177 // assert actuallyRemoved : "Expected the referrer node exists in this topology "+referer; 178 if ( actuallyRemoved ) { 179 referer.decreaseLevel(); 180 add( referer ); 181 } 182 } 183 } 184 185 186 /** 187 * @return true if actually removed the node 188 */ 189 private boolean remove(Node<RefObject> node) { 190 if ( node.isReady() ) { 191 readyNodes.remove( node ); 192 } 193 return nodes.remove( node.getKey(), node ); 194 } 195 196 /** 197 * Exclude the listed nodes from the READY list, but keep them in the topology itself 198 * @param targetGenerationReady not null 199 */ 200 public void removeFromReadyNodes(List<Node<RefObject>> targetGenerationReady) { 201 readyNodes.removeAll( targetGenerationReady ); 202 } 203 204 /** 205 * @return a non-null list of model elements that where left in the topology. 206 */ 207 public List<RefObject> getContents() { 208 List<RefObject> result; 209 210 result = new ArrayList<RefObject>(); 211 212 // left are circular dependencies only (if any) 213 for (Node<RefObject> node : nodes.values()) { 214 result.add( node.getWrapped() ); 215 } 216 return result; 217 } 218 219 220 /** 221 * Leave this topology empy and ready to load another model 222 */ 223 public void clear() { 224 readyNodes.clear(); 225 nodes.clear(); 226 elementToNodeMap.clear(); 227 } 228 229 230 public String toString() { 231 return getClass().getSimpleName() + "{" + nodes + "}"; 232 } 233}