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
13 import java.util.Iterator;
14
15
16
17
18
19
20
21
22
23
24
25
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
34
35
36
37
38
39 private final String sName;
40
41
42
43
44 private final Package pkg;
45
46
47
48
49
50
51 private final SClass superClass;
52
53 private final Map<Deficiency, SClass> oldDef2innerClasses;
54
55 private final DeficiencyMap map;
56
57
58
59
60
61
62
63
64 private final Map<Deficiency, DeficiencyNode> deficiency2ordering;
65
66 private final Type type;
67
68
69
70
71
72
73
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;
86 }
87
88 private SClass(String sName,
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 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
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
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
187 return new Type(Type.BOOLEAN);
188 }
189
190
191
192
193 Type result = isInner()
194 ? new Type()
195 : new Type(getSuperClass().getType());
196
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
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
214
215 }
216 result.replace(entry.getKey(),
217 innerCls.getMinDefic(),
218 innerCls.getMaxDefic(),
219 innerType);
220 }
221
222
223 result.addAll(getDeclaredDeficiency2ordering());
224 return result;
225 }
226
227
228 public SClass getSuperClass() {
229 return this.superClass;
230 }
231
232 public Map<Deficiency, SClass> getDeclaredInnerClasses() {
233 return this.oldDef2innerClasses;
234 }
235
236
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
252 Map<Deficiency, DeficiencyNode> getDeclaredDeficiency2ordering() {
253 return this.deficiency2ordering;
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268 private Deficiency minDef;
269
270 private Deficiency maxDef;
271
272
273 private void verifyMMDefics() {
274 Set<Deficiency> minDefs = getType().getMin();
275 Set<Deficiency> maxDefs = getType().getMax();
276
277 if (minDefs.size() != 1) {
278
279 throw new VerifyException
280 ("Expected unique minimal element in class \"" +
281 this.getPathName() + "\" but found " + minDefs + ". ");
282 }
283 if (maxDefs.size() != 1) {
284
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
295 public Deficiency getMinDefic() {
296 return this.minDef;
297 }
298
299
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 }
324
325
326
327
328
329 void verify() throws VerifyException {
330
331
332
333
334
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
364
365
366
367
368
369 private void verifyNoShortcut() throws VerifyException {
370
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
382
383 public void verifyInner() throws VerifyException {
384
385 if (this.deficiency2ordering.keySet().isEmpty()) {
386 throw new VerifyException
387 ("No deficiencies found. ");
388 }
389
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 }