View Javadoc
1   package eu.simuline.relana.model;
2   
3   import eu.simuline.relana.expressions.Type;
4   
5   import static eu.simuline.relana.model.DeficiencyNode.unwrap;
6   
7   import java.util.List;
8   import java.util.Map;
9   import java.util.HashMap;
10  import java.util.Set;
11  import java.util.HashSet;
12  //import java.util.Collections;
13  import java.util.Iterator;
14  
15  /**
16   * Describes classes of Effects ('services'). 
17   * Like a java-class this has a name {@link #sName}, 
18   * a package {@link #pkg} and a superclass {@link #superClass} 
19   * which is again an {@link SClass}. 
20   *
21   *
22   * Created: Thu Apr 14 19:35:08 2005
23   *
24   * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
25   * @version 1.0
26   */
27  public final class SClass {
28  
29      public static final String BOOL_S_CLASS_NAME = "B";
30      public static final SClass BOOLEAN = new SClass();
31  
32      /* -------------------------------------------------------------------- *
33       * attributes.                                                          *
34       * -------------------------------------------------------------------- */
35  
36      /**
37       * The name of this <code>SClass</code>. 
38       */
39      private final String sName;
40  
41      /**
42       * The package of this <code>SClass</code>. 
43       */
44      private final Package pkg;
45  
46      /**
47       * The superclass of this <code>SClass</code>. 
48       * This is <code>null</code> if and only if 
49       * <code>this=={@link #BOOLEAN}</code>. 
50       */
51      private final SClass superClass;
52  
53      private final Map<Deficiency, SClass> oldDef2innerClasses;
54  
55      private final DeficiencyMap map;
56  
57      /**
58       * Maps declared <code>Deficiency</code>s 
59       * (see {@link #getDeclaredDeficiency2ordering}) to their nodes 
60       * which determine their predecessors and their successors. 
61       * It is required that this relation extends that 
62       * given by {@link #superClass}. 
63       */
64      private final Map<Deficiency, DeficiencyNode> deficiency2ordering;
65  
66      private final Type type;
67  
68  
69      /* -------------------------------------------------------------------- *
70       * constructors.                                                        *
71       * -------------------------------------------------------------------- */
72  
73      // yields the Boolean Class
74      private SClass() {
75  	this.sName = BOOL_S_CLASS_NAME;
76  	this.pkg = Package.BUILD_IN;
77  	this.superClass = null;
78  	this.oldDef2innerClasses = new HashMap<Deficiency, SClass>();
79  	this.deficiency2ordering = new HashMap<Deficiency, DeficiencyNode>();
80  	DeficiencyNode node = new DeficiencyNode(Deficiency.UNDET);
81  	this.deficiency2ordering.put(Deficiency.UNDET, node);
82  
83  	this.type = Type.BOOLEAN;
84  	verifyMMDefics();
85  	this.map = null; // DeficiencyMap.ID_BOOL;
86      }
87  
88      private SClass(String sName, //**** common superclass with CClass 
89  		   Package pkg,
90  		   SClass superClass,
91  		   Map<Deficiency, SClass> oldDef2innerClasses,
92  		   Map<Deficiency, DeficiencyNode> deficiency2ordering) {
93  	this.sName = sName;
94  	this.pkg = pkg;
95  	this.superClass = superClass;
96  	this.oldDef2innerClasses = oldDef2innerClasses;
97  	this.deficiency2ordering = deficiency2ordering;
98  	
99  	this.type = createType();
100 	verifyMMDefics();
101 	if (isInner()) {
102 	    Map<Set<Deficiency>, Deficiency> mapAll2Undet = 
103 		new HashMap<Set<Deficiency>, Deficiency>();
104 	    mapAll2Undet.put(deficiency2ordering.keySet(),
105 			     Deficiency.UNDET);
106 	    this.map = DeficiencyMap
107 		.getSubclassMap(mapAll2Undet,
108 				this,
109 				getSuperClass());
110 
111 	} else {
112 	    this.map = DeficiencyMap
113 		.getSubclassMap(getMap(oldDef2innerClasses),
114 				this,
115 				getSuperClass());
116 
117 	}
118 
119     } // SClass constructor 
120 
121     // **** maybe better in class DeficiencyMap ?
122     /**
123      * Converts a map from the overwritten deficiencies 
124      * to the overwriting classes, 
125      * to a map from the set of deficiencies in the class 
126      * back to the original deficiency. 
127      *
128      * @param oldDef2intCls
129      *    maps a deficiency in outer class 
130      *    to the inner class overwriting this deficiency. 
131      * @return
132      *    a map from the set of deficiencies of a class 
133      *    back to the overwritten deficiency in the enclosing class. 
134      */
135     private static Map<Set<Deficiency>, Deficiency> 
136 	getMap(Map<Deficiency, SClass> oldDef2intCls) {
137 
138 	Map<Set<Deficiency>, Deficiency> setOfNew2old = 
139 	    new HashMap<Set<Deficiency>, Deficiency>();
140 	for (Map.Entry<Deficiency, SClass> entry : 
141 		 oldDef2intCls.entrySet()) {
142 	    Deficiency oldDef = entry.getKey();
143 	    Set<Deficiency> newDefs = 
144 		entry.getValue().getDeclaredDeficiency2ordering().keySet();
145 	    setOfNew2old.put(newDefs, oldDef);
146 
147 	}
148 	return setOfNew2old;
149     }
150 
151     /* -------------------------------------------------------------------- *
152      * methods.                                                             *
153      * -------------------------------------------------------------------- */
154 
155     public String getName() {
156 	return this.sName;
157     }
158 
159     public Package getPackage() {
160 	return this.pkg;
161     }
162 
163     public Package asPackage() {
164 	List<String> path = getPackage().getPath();
165 	path.add(getName());
166 	return Package.getPackage(path);
167     }
168 
169     public String getPathName() {
170 	String res = getPackage().getPathName();
171 	return res + getName();
172     }
173 
174     // **** This means it is an inner class itself. 
175     public boolean isInner() {
176 	return (getSuperClass().equals(SClass.BOOLEAN) && 
177 		getDeclaredInnerClasses().keySet().isEmpty());
178     }
179 
180     public Type getType() {
181 	return this.type;
182     }
183 	
184     private Type createType() {
185 	if (getSuperClass() == null) {
186 	    // Here, the class is the Boolean one. 
187 	    return new Type(Type.BOOLEAN);
188 	}
189 	// For inner classes, 
190 	// the superclass is Boolean but UNDET is replaced implicitly 
191 	// otherwise we have to start with a copy 
192 	// of the type of the superclass. 
193 	Type result = isInner() 
194 	    ? new Type() 
195 	    : new Type(getSuperClass().getType());
196 	// Here, we replace the properties overwritten by inner classes. 
197 	SClass innerCls;
198 	Type innerType;
199 	Set<Deficiency> inter;
200 	for (Map.Entry<Deficiency, SClass> entry 
201 		 : getDeclaredInnerClasses().entrySet()) {
202 	    innerCls = entry.getValue();
203 
204 	    // namespace check 
205 	    innerType = new Type(innerCls.getType());
206 	    inter = new HashSet<Deficiency>(result.asSet());
207 	    inter.retainAll(innerType.asSet());
208 	    if (!inter.isEmpty()) {
209 		throw new IllegalArgumentException
210 		    ("Found duplicate property \"" + inter + 
211 		     "\" in enclosing class \"" + getPathName() +
212 		     "\" overwritten by inner class. ");
213 		// **** should be fixed by introducing separate namespaces 
214 		// for each SClass. 
215 	    }
216 	    result.replace(entry.getKey(),
217 			   innerCls.getMinDefic(),
218 			   innerCls.getMaxDefic(),
219 			   innerType);
220 	} // for all inner classes 
221 
222 	// add the relations explicitly added in the relations section. 
223 	result.addAll(getDeclaredDeficiency2ordering());
224 	return result;
225     }
226 
227     // is null exactly for {@link #BOOL_S_CLASS}. 
228     public SClass getSuperClass() {
229 	return this.superClass;
230     }
231 
232     public Map<Deficiency, SClass> getDeclaredInnerClasses() {
233 	return this.oldDef2innerClasses;
234     }
235 
236     // may not return null ****
237     public SClass getDeclaredInnerClass(Deficiency def) {
238 	SClass res = this.oldDef2innerClasses.get(def);
239 	if (res == null) {
240 	    throw new IllegalArgumentException
241 		("Found no inner class for key \"" + def.getName() + "\". ");
242 	}
243 	
244 	return res;
245     }
246 
247     public DeficiencyMap getDeficiencyMap() {
248 	return this.map;
249     }
250 
251     // **** used internally and by ProbDistr. 
252     Map<Deficiency, DeficiencyNode> getDeclaredDeficiency2ordering() {
253 	return this.deficiency2ordering;
254     }
255 
256 
257 
258 
259 
260 
261 
262 
263 
264 
265 
266 
267     // **** valid after verification only {@link #verifyMMDefics} 
268     private Deficiency minDef;
269     // **** valid after verification only {@link #verifyMMDefics} 
270     private Deficiency maxDef;
271 
272     // **** also initializes fields minDefs and maxDefs
273     private void verifyMMDefics() {
274 	Set<Deficiency> minDefs = getType().getMin();
275 	Set<Deficiency> maxDefs = getType().getMax();
276 
277 	if (minDefs.size() != 1) {
278 	    // exactly one minimal deficiency expected. 
279 	    throw new VerifyException
280 		("Expected unique minimal element in class \"" + 
281 		 this.getPathName() + "\" but found " + minDefs + ". ");
282 	}
283 	if (maxDefs.size() != 1) {
284 	    // exactly one minimal deficiency expected. 
285 	    throw new VerifyException
286 		("Expected unique maximal element in class \"" + 
287 		 this.getPathName() + "\" but found " + maxDefs + ". ");
288 	}
289 
290 	this.minDef = minDefs.toArray(new Deficiency[0])[0];
291 	this.maxDef = maxDefs.toArray(new Deficiency[0])[0];
292     }
293 
294     // **** valid after verification only 
295     public Deficiency getMinDefic() {
296 	return this.minDef;
297     }
298 
299     // **** valid after verification only 
300     public Deficiency getMaxDefic() {
301 	return this.maxDef;
302     }
303 
304     Set<DeficiencyNode> getMinDeficN() {
305 	Set<DeficiencyNode> minDefs = new HashSet<DeficiencyNode>();
306 	for (DeficiencyNode node : this.deficiency2ordering.values()) {
307 	    if (node.getPredecessors().isEmpty()) {
308 		minDefs.add(node);
309 	    }
310 	}
311 	
312 	return minDefs;
313     }
314 
315     public static SClass getSClass(String sName,
316 				   Package pkg,
317 				   SClass superClass,
318 				   Map<Deficiency, SClass> oldDef2innerClasses,
319 				   Map<Deficiency, DeficiencyNode> def2ord) {
320 	return new SClass(sName, pkg, superClass,
321 			  oldDef2innerClasses,
322 			  def2ord);	
323     } // SClass constructor
324 
325  
326     // checks whether the additional relations 
327     // reflect relations in the superclass 
328     // should thus not be invoked with BOOL_S_CLASS. 
329     void verify() throws VerifyException {
330 
331 	// inner classes need not be verified: 
332 	// they are either loaded or verified by the parser. 
333 	// the latter is the case if and only if they occur as inner classes 
334 	// with explicitly given Deficiency's and relations. 
335 
336 	Deficiency def1, def2;
337 	Deficiency def1super, def2super;
338 	for (DeficiencyNode node1 : this.deficiency2ordering.values()) {
339 	    def1 = node1.getDeficiency();
340 	    for (DeficiencyNode node2 : node1.getSuccessors()) {
341 		def2 = node2.getDeficiency();
342 		def1super = getDeficiencyMap().map(def1);
343 		def2super = getDeficiencyMap().map(def2);
344 		if (def1super.equals(def2super)) {
345 		    throw new VerifyException
346 			("Relation \"" + def1 + "==>" + def2 + 
347 			 "\" should be specified in inner class because " + 
348 			 getSuperClass() + 
349 			 " maps both to " + def1super + ". ");
350 		}
351 
352 		if (!getSuperClass().getType().implies(def1super, def2super)) {
353 		    throw new VerifyException
354 			("Relation \"" + def1 + "==>" + def2 + 
355 			 "\" not reflected by superclass " + getSuperClass() + 
356 			 ": " + def1super + "=/=>" + def2super + ". ");
357 		}
358 	    }
359 	}
360     }
361 
362     /**
363      * Throws an exception if the transitive hull of the relation 
364      * contains a cycle. 
365      *
366      * @exception VerifyException 
367      *    if the transitive hull of the relation contains a cycle. 
368      */
369     private void verifyNoShortcut() throws VerifyException {
370 	// **** not so good: instead of unwrap use new Collection.
371 	for (DeficiencyNode node : this.deficiency2ordering.values()) {
372 	    if (unwrap(node.getPredecessors()).contains(node.getDeficiency()) ||
373 		unwrap(node.getSuccessors  ()).contains(node.getDeficiency())) {
374 		throw new VerifyException
375 		("Deficiency " + node.getDeficiency() + 
376 		 " is cyclically related with itself. ");
377 	    }
378 	}
379     }
380 
381     // **** invoked by the parser for inner classes only. 
382     // checks absence of deficiencies, cycles 
383     public void verifyInner() throws VerifyException {
384 
385 	if (this.deficiency2ordering.keySet().isEmpty()) {
386 	    throw new VerifyException
387 		("No deficiencies found. ");
388 	}
389 	// Here, there is at least one deficiency. 
390 
391 	verifyNoShortcut();
392 
393 	Set<DeficiencyNode> minDeficN = getMinDeficN();
394 	if (minDeficN.isEmpty()) {
395 	    throw new VerifyException
396 		("Relation has no minimal elements and is hence cyclic. ");
397 	}
398 
399 	Set<DeficiencyNode> occured = new HashSet<DeficiencyNode>(minDeficN);
400 	Set<DeficiencyNode> maximal = new HashSet<DeficiencyNode>(minDeficN);
401 	Set<DeficiencyNode> newMaximal = new HashSet<DeficiencyNode>();
402 	Set<DeficiencyNode> intersection;
403 	Iterator<DeficiencyNode> iter = maximal.iterator();
404 	DeficiencyNode node;
405 	do {
406 	    while (iter.hasNext()) {
407 		node = iter.next();
408 		newMaximal.addAll(node.getSuccessors());
409 	    }
410 	    intersection = new HashSet<DeficiencyNode>(occured);
411 	    intersection.retainAll(newMaximal);
412 	    if (!intersection.isEmpty()) {
413 		throw new VerifyException
414 		    ("Relation is cyclic: consider " + intersection + ". ");
415 	    }
416 	    occured.addAll(newMaximal);
417 	    maximal = newMaximal;
418 	    newMaximal = new HashSet<DeficiencyNode>();
419 	} while (!maximal.isEmpty());
420 
421 	verifyMMDefics();
422     }
423 
424     public String toString() {
425 	StringBuffer res = new StringBuffer();
426 	res.append("<SClass name=\"");
427 	res.append(getName());
428 	res.append("\" \npackage=\"");
429 	res.append(getPackage().getPathName());
430 	if (getSuperClass() != null) {
431 	    res.append("\" \nsuperClass=\"");
432 	    res.append(getSuperClass().getPathName());
433 	}
434 	res.append("\">\n<Deficiencies>\n");
435 	res.append(this.deficiency2ordering.keySet().toString());
436 	res.append("\n</Deficiencies>\n\n<InnerClasses>\n");
437 	for (Map.Entry<Deficiency, SClass> entry 
438 		 : this.oldDef2innerClasses.entrySet()) {
439 	    res.append("<InnerClass name=\"");
440 	    res.append(entry.getKey().getName());
441 	    res.append("\">");
442 	    res.append(entry.getValue());
443 	    res.append("</InnerClass>\n");
444 	}
445 	
446 	res.append("</InnerClasses>\n\n<Map>\n");
447 	res.append(this.map);
448 	res.append("</Map>\n\n<Relations>");
449 	Iterator<DeficiencyNode> iterN = 
450 	    this.deficiency2ordering.values().iterator();
451 	DeficiencyNode node;
452 	Deficiency def;
453 	while (iterN.hasNext()) {
454 	    node = iterN.next();
455 	    def = node.getDeficiency();
456 	    Iterator<DeficiencyNode> iterD = node.getSuccessors().iterator();
457 	    while (iterD.hasNext()) {
458 		res.append(def);
459 		res.append("\n==>");
460 		res.append(iterD.next().getDeficiency());
461 		res.append('\n');
462 	    }
463 	}
464 	
465 	res.append("</Relations>\n</SClass>\n");
466 	return res.toString();
467     }
468 
469     public boolean equals(Object obj) {
470 	if (!(obj instanceof SClass)) {
471 	    return false;
472 	}
473 	return getPathName().equals(((SClass) obj).getPathName());
474     }
475 
476     public int hashCode() {
477 	return getPathName().hashCode();
478     }
479 } // SClass