View Javadoc
1   package eu.simuline.relana.model;
2   
3   import java.util.Map;
4   import java.util.HashMap;
5   import java.util.Set;
6   import java.util.HashSet;
7   import java.util.Iterator;
8   
9   /**
10   * Describes a map between <code>Deficiency</code>'s 
11   * of one type to another one. 
12   *
13   * Created: Thu Apr 21 14:47:33 2005
14   *
15   * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
16   * @version 1.0
17   */
18  public final class DeficiencyMap {
19  
20      /* -------------------------------------------------------------------- *
21       * constants.                                                           *
22       * -------------------------------------------------------------------- */
23  
24      /* -------------------------------------------------------------------- *
25       * attributes.                                                          *
26       * -------------------------------------------------------------------- */
27  
28      /**
29       * The set of deficiencies all mapped to the same deficiency. 
30       * The values are assumed to be pairwise different 
31       * and the keys to be pairwise disjoint. 
32       * The keys are subsets of the set represented by {@link #source} 
33       * whereas the values are elements 
34       * of the set represented by {@link #target}. 
35       */
36      private final Map<Set<Deficiency>, Deficiency> setOfNew2old;
37  
38      /**
39       * The source of this map. 
40       * @see #domain
41       */
42      private final SClass source;
43  
44      /**
45       * The target of this map. 
46       * @see #range
47       */
48      private final SClass target;
49  
50      /**
51       * The elements of {@link #source} 
52       * for which {@link #map} yields a non-<code>null</code>-result. 
53       * @see #source
54       */
55      private final Set<Deficiency> domain;
56  
57      /**
58       * The elements of {@link #target} 
59       * for which {@link #getInverseImage} yields a non-empty result. 
60       * @see #target
61       */
62      private final Set<Deficiency> range;
63  
64      /**
65       * The set of deficiencies mapped identically. 
66       * As a consequence, this is a subset of both 
67       * the set represented by {@link #source} 
68       * and the set represented by {@link #target}. 
69       */
70      private Set<Deficiency> idDom;
71  
72      /* -------------------------------------------------------------------- *
73       * constructors.                                                        *
74       * -------------------------------------------------------------------- */
75  
76      public static 
77      DeficiencyMap getSubclassMap(Map<Set<Deficiency>, Deficiency> setOfNew2old,
78  				 SClass source,
79  				 SClass target) {
80  
81  	DeficiencyMap result = 
82  	    new DeficiencyMap(setOfNew2old,
83  			      source,
84  			      target,
85  			      new HashSet<Deficiency>());
86  
87  	Set<Deficiency> sourceId = 
88  	    new HashSet<Deficiency>(source.getType().asSet());
89  	sourceId.removeAll(result.getDomain());
90  	Set<Deficiency> rangeId = 
91  	    new HashSet<Deficiency>(target.getType().asSet());
92  	rangeId.removeAll(result.getRange());
93  	if (!sourceId.equals(rangeId)) {
94  	    throw new IllegalArgumentException
95  		("No subclass map extending " + result + ". ");
96  	}
97  	result.idDom = rangeId;
98  	
99  	return result;
100     }
101 
102     public DeficiencyMap(Map<Set<Deficiency>, Deficiency> setOfNew2old,
103 			 SClass source,
104 			 SClass target,
105 			 Set<Deficiency> idDom) {
106 	this.setOfNew2old = setOfNew2old;
107 	this.source = source;
108 	this.target = target;
109 	this.idDom = idDom;
110 
111 	// initialize domain and range 
112 	this.domain = new HashSet<Deficiency>(this.idDom);
113 	for (Set<Deficiency> setDef : this.setOfNew2old.keySet()) {
114 	    this.domain.addAll(setDef);
115 	}
116 	this.range  = new HashSet<Deficiency>(this.idDom);
117 	this.range.addAll(this.setOfNew2old.values());
118 
119 	checkInvImg01();
120 
121 	// Check that the domain is within source. 
122 	Set<Deficiency> domain = getDomain();
123 	if (!this.source.getType().asSet().containsAll(domain)) {
124 	    throw new IllegalArgumentException
125 		("Domain " + domain + " not inside type range " + 
126 		 this.source.getType().asSet() + ". ");
127 	}
128 
129 	// check whether the key set of setOfNew2old and idDom 
130 	// are pairwise disjoint
131 	int num = this.idDom.size();
132 	for (Set<Deficiency> setDef : this.setOfNew2old.keySet()) {
133 	    num += setDef.size();
134 	}
135 	if (num != domain.size()) {
136 	    throw new IllegalArgumentException
137 		("Inverse images " + this.setOfNew2old.keySet() + 
138 		 " and identity domain " + this.idDom + 
139 		 " are not pairwise disjoint. ");
140 	}
141 
142 	// Check that the range is within target. 
143 	Set<Deficiency> range = getRange();
144 	if (!this.target.getType().asSet().containsAll(range)) {
145 	    throw new IllegalArgumentException
146 		("Range " + range + " not inside type range " + 
147 		 this.target.getType().asSet() + ". ");
148 	}
149 
150 	// check whether the key set of setOfNew2old and idDom 
151 	// are pairwise disjoint
152 	if (this.setOfNew2old.size() + this.idDom.size() != range.size()) {
153 	    throw new IllegalArgumentException
154 		("Images " + this.setOfNew2old.values() + 
155 		 " and identity domain " + this.idDom + 
156 		 " are not pairwise disjoint. ");
157 	}
158     } // DeficiencyMap constructor
159 
160 
161     void checkInvImg01() {
162 	// check inverse images of size 0 or 1. 
163 	//Iterator<Set<Deficiency>> iter = 
164 	//                           this.setOfNew2old.keySet().iterator();
165 	Iterator<Map.Entry<Set<Deficiency>, Deficiency>> iter = 
166 	    this.setOfNew2old.entrySet()
167 	    .iterator();
168 	Set<Deficiency> setDef;
169 	Map.Entry<Set<Deficiency>, Deficiency> mapEntry;
170 	Deficiency def;
171 	while (iter.hasNext()) {
172 	    mapEntry = iter.next();
173 	    //setDef = iter.next();
174 	    setDef = mapEntry.getKey();
175 	    switch (setDef.size()) { // NOPMD
176 		case 0:
177 		    // size == 0: this is superfluous. 
178 		    throw new IllegalArgumentException
179 			("Found empty inverse image for " + 
180 			 //this.setOfNew2old.get(setDef) 
181 			 mapEntry.getValue() + ". ");
182 		case 1:
183 		    // size == 1: if identity, remove and add to idDomain. 
184 		    def = this.setOfNew2old.get(setDef);
185 		    if (setDef.contains(def)) {
186 			// Here, inverse image and image coincide. 
187 			iter.remove();
188 			this.idDom.add(def);
189 		    }
190 		    continue;
191 		default:
192 		    // size is at least two: nothing to do. 
193 		    continue;
194 	    }
195 	}
196     }
197 
198 /*
199     public static DeficiencyMap getBooleanMap(SClass source,
200 					      Set<Deficiency> domain) {
201 	Map<Set<Deficiency>, Deficiency> setOfNew2old = 
202 	    new HashMap<Set<Deficiency>, Deficiency>();
203 	setOfNew2old.put(domain, Deficiency.UNDET);
204 	return new DeficiencyMap(setOfNew2old,
205 				 source,
206 				 SClass.BOOLEAN,
207 				 domain);
208     }
209 */
210 
211     /* -------------------------------------------------------------------- *
212      * methods.                                                             *
213      * -------------------------------------------------------------------- */
214 
215     /**
216      * Returns the identity map with the given source and target 
217      * with full domain and range. 
218      *
219      * @param sourceTarget 
220      *    an <code>SClass</code> which is both 
221      *    {@link #source} and {@link #target} 
222      *    and {@link #idDom} comprises all its deficiencies. 
223      * @return 
224      *    a <code>DeficiencyMap</code> which is the identity 
225      *    on the set of deficiencies of <code>sourceTarget</code>. 
226      */
227     public static DeficiencyMap identity(SClass sourceTarget) {
228 	System.out.println("sourceTarget: " + sourceTarget);
229 	DeficiencyMap map = sourceTarget.getDeficiencyMap();
230 	Set<Deficiency> idDom = (map == null) 
231 	    ? new HashSet<Deficiency>
232 	    (sourceTarget.getDeclaredDeficiency2ordering().keySet())
233 	    : map.getDomain();
234 
235 	return new DeficiencyMap(new HashMap<Set<Deficiency>, Deficiency>(),
236 				 sourceTarget,
237 				 sourceTarget,
238 				 idDom);
239     }
240 
241     /**
242      * Returns the inverse of this map provided it exists. 
243      *
244      * @return 
245      *    a <code>DeficiencyMap</code> which is the inverse of this one. 
246      * @throws UnsupportedOperationException
247      *    if this map is not invertible. 
248      */
249     public DeficiencyMap getInverse() {
250 	// check invertibility 
251 	if (getDomain().size() != getRange().size()) {
252 	    throw new UnsupportedOperationException
253 		("This map is not invertible: " + this + ". ");
254 	}
255 	
256 	Map<Set<Deficiency>, Deficiency> invSetOfNew2old = 
257 	    new HashMap<Set<Deficiency>, Deficiency>();
258 	Set<Deficiency> oneDefSet;
259 	Deficiency def;
260 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
261 		 : this.setOfNew2old.entrySet()) {
262 	    assert entry.getKey().size() == 1;
263 	    oneDefSet = new HashSet<Deficiency>();
264 	    oneDefSet.add(entry.getValue());
265 	    def = entry.getKey().iterator().next();
266 	    invSetOfNew2old.put(oneDefSet, def);
267 	}
268 	
269 	return new DeficiencyMap(invSetOfNew2old,
270 				 this.target, // exchanged. 
271 				 this.source,
272 				 this.idDom);
273     }
274 
275 
276     /**
277      * Returns the composition of this map and <code>second</code> 
278      * (in this order). 
279      *
280      * @param second 
281      *    another <code>DeficiencyMap</code>. 
282      * @return 
283      *    the composition of this <code>DeficiencyMap</code> 
284      *    and <code>second</code>. 
285      * @throws IllegalArgumentException
286      *    if <code>this.target != second.source</code>. 
287      */
288     public DeficiencyMap compose(DeficiencyMap second) {
289 	if (this.target != second.source) {
290 	    throw new IllegalArgumentException
291 		("Composition of maps is allowed only " + 
292 		 "if source and target coincide but found " + 
293 		 second.source.getName() + " and " + 
294 		 this.target  .getName() + ". ");
295 	}
296 	
297 	Map<Set<Deficiency>, Deficiency> setOfNew2old = 
298 	    new HashMap<Set<Deficiency>, Deficiency>();
299 	// handle all images in the value set of second.setOfNew2old 
300 	Set<Deficiency> invImg;
301 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
302 		 : second.setOfNew2old.entrySet()) {
303 
304 	    invImg = this.cont(entry.getKey());
305 	    setOfNew2old.put(invImg, entry.getValue());
306 	}
307 	// Handle all images in second.idDom 
308 	// which are in the image set of this.setOfNew2old 
309 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
310 		 : this.setOfNew2old.entrySet()) {
311 	    if (!second.idDom.contains(entry.getValue())) {
312 		continue;
313 	    }
314 	    // Here, entry.getValue() is in second.idDom
315 	    setOfNew2old.put(entry.getKey(), entry.getValue());
316 	}
317 	// handle all images this.idDom and in second.idDom 
318 	Set<Deficiency> idDom = new HashSet<Deficiency>(this.idDom);
319 	idDom.retainAll(second.idDom);
320 
321 	// move from setOfNew2old to idDom whatever possible 
322 	Deficiency cand;
323 	Iterator<Map.Entry<Set<Deficiency>, Deficiency>> iter = 
324 	    setOfNew2old.entrySet().iterator();
325 	Map.Entry<Set<Deficiency>, Deficiency> entry;
326 	while (iter.hasNext()) {
327 	    entry = iter.next();
328 	    if (entry.getKey().size() != 1) {
329 		continue;
330 	    }
331 	    assert entry.getKey().size() == 1;
332 	    cand = entry.getKey().iterator().next();
333 	    if (!cand.equals(entry.getValue())) {
334 		continue;
335 	    }
336 
337 	    idDom.add(cand);
338 	    iter.remove();
339 	}
340 
341 	return new DeficiencyMap(setOfNew2old,
342 				 this.source,
343 				 second.target,
344 				 idDom);
345     }
346 
347     // **** null if def is not in the domain. 
348     /**
349      * Performs the mapping. 
350      *
351      * @param def 
352      *    the <code>Deficiency</code> to be mapped. 
353      * @return 
354      *    the image of <code>def</code> with respect to this map. 
355      *    If and only if <code>def</code> is outside the domain, 
356      *    returns <code>null</code>. 
357      */
358     public Deficiency map(Deficiency def) {
359 	if (this.idDom.contains(def)) {
360 	    return def;
361 	}
362 	
363 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
364 		 : this.setOfNew2old.entrySet()) {
365 	    if (entry.getKey().contains(def)) {
366 		return entry.getValue();
367 	    }
368 	}
369 	// def was nowhere found. 
370 	return null;
371     }
372 
373     // 
374     /**
375      * Returns the inverse image of the given deficiency 
376      * with respect to this map. 
377      *
378      * @param def 
379      *    the <code>Deficiency</code> 
380      *    for which the inverse image is to be determined. 
381      * @return 
382      *    the inverse image of <code>def</code> with respect to this map. 
383      *    This may well be an empty set. 
384      */
385     Set<Deficiency> getInverseImage(Deficiency def) {
386 	Set<Deficiency> result = new HashSet<Deficiency>();
387 	if (this.idDom.contains(def)) {
388 	    result.add(def);
389 	    return result;
390 	}
391 
392 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
393 		 : this.setOfNew2old.entrySet()) {
394 	    if (entry.getValue().equals(def)) {
395 		return entry.getKey();
396 	    }
397 	}
398 	
399 	return  result;
400     }
401 
402     /**
403      * Returns the domain of this map. 
404      *
405      * @return 
406      *    the domain of this map. 
407      */
408     Set<Deficiency> getDomain() {
409 	return this.domain;
410     }
411 
412     /**
413      * Returns the range of this map. 
414      *
415      * @return 
416      *    the range of this map. 
417      */
418     Set<Deficiency> getRange() {
419 	return this.range;
420     }
421 
422     /**
423      * Returns the source of this map. 
424      * @see #source
425      */
426     public SClass getSource() {
427 	return this.source;
428     }
429 
430     /**
431      * Returns the target of this map. 
432      * @see #target
433      */
434     public SClass getTarget() {
435 	return this.target;
436     }
437 
438     /**
439      * Returns whether this map is isotone. 
440      *
441      * @return 
442      *    whether this map is isotone. 
443      */
444     public boolean isIsotone() {
445 	Set<Deficiency> cone;
446 	for (Deficiency def1 : getDomain()) {
447 	    cone = this.source.getType().getCone(def1);
448 	    if (!getDomain().containsAll(cone)) {
449 		return false;
450 	    }
451 	    for (Deficiency def2 : cone) {
452 		if (!this.target.getType().implies(map(def1), map(def2))) {
453 		    return false;
454 		}
455 	    }
456 	}
457 	return true;
458     }
459 
460     /**
461      * Returns whether this map is twist-isotone. 
462      *
463      * @return 
464      *    whether this map is twist-isotone. 
465      */
466     public boolean isTwistIsotone() {
467 	Set<Deficiency> cone, coneT;
468 	for (Deficiency def1 : getDomain()) {
469 	    cone  = this.source.getType().getCone(    def1);
470 	    coneT = this.target.getType().getCone(map(def1));
471 	    for (Deficiency defT2 : coneT) {
472 		Set<Deficiency> inter = 
473 		    new HashSet<Deficiency>(getInverseImage(defT2));
474 		inter.retainAll(cone);
475 		if (inter.isEmpty()) {
476 		    return false;
477 		}
478 	    }
479 	}
480 	return true;
481     }
482 
483     /**
484      * Returns the image of <code>defs</code> under this map. 
485      *
486      * @param defs
487      *    a set of <code>Deficiency</code>s to be mapped. 
488      * @return 
489      *    the image of <code>defs</code> under this map. 
490      */
491     public Set<Deficiency> cov(Set<Deficiency> defs) {
492 	assert this.source.getType().isValid(defs);
493 	Set<Deficiency> defsToMap = new HashSet<Deficiency>(defs);
494 	defsToMap.retainAll(this.domain);
495 	Set<Deficiency> result = new HashSet<Deficiency>();
496 
497 	Set<Deficiency> inter;
498 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
499 		 : this.setOfNew2old.entrySet()) {
500 	    inter = new HashSet<Deficiency>(entry.getKey());
501 	    inter.retainAll(defsToMap);
502 	    if (!inter.isEmpty()) {
503 		result.add(entry.getValue());
504 	    }
505 	}
506 
507 	defsToMap.retainAll(this.target.getType().asSet());
508 	result.addAll(defsToMap);
509 	assert this.target.getType().isValid(result);
510 	return result;
511     }
512 
513     /**
514      * Returns the inverse image of <code>defs</code> under this map. 
515      *
516      * @param defs
517      *    a set of <code>Deficiency</code>s to be "reverse mapped'. 
518      * @return 
519      *    the inverse image of <code>defs</code> under this map. 
520      */
521     public Set<Deficiency> cont(Set<Deficiency> defs) {
522 	assert this.target.getType().isValid(defs);
523 	Set<Deficiency> result = new HashSet<Deficiency>();
524 	for (Map.Entry<Set<Deficiency>, Deficiency> entry 
525 		 : this.setOfNew2old.entrySet()) {
526 	    if (defs.contains(entry.getValue())) {
527 		result.addAll(entry.getKey());
528 	    }
529 	}
530 
531 	Set<Deficiency> defsToMap = 
532 	    new HashSet<Deficiency>(this.source.getType().asSet());
533 	defsToMap.retainAll(defs);
534 	result.addAll(defsToMap);
535 	assert this.source.getType().isValid(result);
536 	return result;
537     }
538 
539 
540     public String toString() {
541 	StringBuffer res = new StringBuffer();
542 	res.append("<DeficiencyMap source=\"");
543 	res.append(source.getPathName());
544 	res.append("\" target=\"");
545 	res.append(target.getPathName());
546 	res.append("\">\n");
547 	res.append(this.setOfNew2old);
548 	res.append('\n');
549 	res.append(this.idDom);
550 	res.append("\n</DeficiencyMap>");
551 	return res.toString();
552     }
553 
554 } // DeficiencyMap