View Javadoc

1   /*	
2   	Copyright 2007-2014 Fraunhofer IGD, http://www.igd.fraunhofer.de
3   	Fraunhofer-Gesellschaft - Institute for Computer Graphics Research
4   	
5   	See the NOTICE file distributed with this work for additional 
6   	information regarding copyright ownership
7   	
8   	Licensed under the Apache License, Version 2.0 (the "License");
9   	you may not use this file except in compliance with the License.
10  	You may obtain a copy of the License at
11  	
12  	  http://www.apache.org/licenses/LICENSE-2.0
13  	
14  	Unless required by applicable law or agreed to in writing, software
15  	distributed under the License is distributed on an "AS IS" BASIS,
16  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  	See the License for the specific language governing permissions and
18  	limitations under the License.
19   */
20  package org.universAAL.middleware.owl;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  
28  import org.universAAL.middleware.container.utils.LogUtils;
29  import org.universAAL.middleware.datarep.SharedResources;
30  import org.universAAL.middleware.rdf.Resource;
31  import org.universAAL.middleware.rdf.TypeMapper;
32  
33  /**
34   * <p>
35   * Helper class to handle multiple {@link PropertyRestriction}s of the same
36   * property.
37   * </p>
38   * <p>
39   * There is often more than one restriction for a specific property, e.g. a
40   * minimum cardinality restriction and a maximum cardinality restriction. All
41   * these restrictions must be specified separately by an instance of
42   * {@link PropertyRestriction}. This class helps to manage multiple restrictions
43   * for the same property.
44   * </p>
45   * <p>
46   * NOTE: If you add this class to another Resource with
47   * {@link #setProperty(String, Object)}, it will behave like an
48   * {@link Intersection} of the restrictions contained in this MergedRestriction.
49   * </p>
50   * 
51   * @author Carsten Stockloew
52   */
53  public final class MergedRestriction extends Intersection {
54  
55      public static final String MY_URI = uAAL_VOCABULARY_NAMESPACE
56  	    + "MergedRestriction";
57  
58      /** A safe iterator that does not allow to remove elements. */
59      private class SafeIterator implements Iterator {
60  	private Iterator it;
61  
62  	SafeIterator(Iterator it) {
63  	    this.it = it;
64  	}
65  
66  	public boolean hasNext() {
67  	    return it.hasNext();
68  	}
69  
70  	public Object next() {
71  	    return it.next();
72  	}
73  
74  	public void remove() {
75  	}
76      }
77  
78      /** The value of minimum cardinality if not defined. */
79      public static final int MIN_UNDEFINED = 0;
80  
81      /** The value of maximum cardinality if not defined. */
82      public static final int MAX_UNDEFINED = -1;
83  
84      /**
85       * ID of the {@link AllValuesFromRestriction}. Only used in the context of
86       * this class.
87       */
88      public static final int allValuesFromID = 0;
89  
90      /**
91       * ID of the {@link SomeValuesFromRestriction}. Only used in the context of
92       * this class.
93       */
94      public static final int someValuesFromID = 1;
95  
96      /**
97       * ID of the {@link HasValueRestriction}. Only used in the context of this
98       * class.
99       */
100     public static final int hasValueID = 2;
101 
102     /**
103      * ID of the {@link MinCardinalityRestriction}. Only used in the context of
104      * this class.
105      */
106     public static final int minCardinalityID = 3;
107 
108     /**
109      * ID of the {@link MaxCardinalityRestriction}. Only used in the context of
110      * this class.
111      */
112     public static final int maxCardinalityID = 4;
113 
114     /**
115      * ID of the {@link ExactCardinalityRestriction}. Only used in the context
116      * of this class.
117      */
118     public static final int exactCardinalityID = 5;
119 
120     /**
121      * Index of all {@link PropertyRestriction}s that are part of this
122      * {@link MergedRestriction}. All restrictions are stored in an array (
123      * <code>types</code> from the superclass). <code>index</code> points to the
124      * correct Restriction in this array, e.g.
125      * <code>index[allValuesFromID]</code> is the index in <code>types</code>
126      * where the AllValuesFromRestriction is stored.
127      */
128     private int index[] = new int[6];
129 
130     /** The property for which this restriction is defined. */
131     private String onProperty = null;
132 
133     /** Constructor for deserializers. */
134     public MergedRestriction() {
135 	super(new String[] { MY_URI });
136 	for (int i = 0; i < 6; i++)
137 	    index[i] = -1;
138     }
139 
140     /**
141      * Constructor for an empty set of restrictions. Restrictions can be added
142      * by calling {@link #addRestriction(PropertyRestriction)} or
143      * {@link #addRestriction(MergedRestriction)}.
144      * 
145      * @param onProperty
146      *            The property for which this restriction is defined.
147      */
148     public MergedRestriction(String onProperty) {
149 	this();
150 	this.onProperty = onProperty;
151     }
152 
153     /**
154      * Constructor with a given initial set of restrictions.
155      * 
156      * @param onProperty
157      *            The property for which this restriction is defined.
158      * @param restrictions
159      *            The initial set of restrictions. The array must contain only
160      *            instances of {@link PropertyRestriction}.
161      */
162     public MergedRestriction(String onProperty, ArrayList restrictions) {
163 	this();
164 	setRestrictions(onProperty, restrictions);
165     }
166 
167     /**
168      * <p>
169      * Create a new restriction to state that no individual of a certain class
170      * has the given property, i.e. the maximum cardinality of the given
171      * property is zero.
172      * </p>
173      * <p>
174      * The new {@link MergedRestriction} contains
175      * <ul>
176      * <li>a {@link MaxCardinalityRestriction} with value <code>0</code>.
177      * </ul>
178      * </p>
179      * 
180      * @param propURI
181      *            The property for which this restriction is defined.
182      * @return the restriction, or <code>null</code> if the parameters are
183      *         invalid.
184      */
185     public static final MergedRestriction getPropertyBanningRestriction(
186 	    String propURI) {
187 	if (propURI == null)
188 	    return null;
189 	MergedRestriction m = new MergedRestriction(propURI);
190 	m.addRestriction(new MaxCardinalityRestriction(propURI, 0));
191 	return m;
192     }
193 
194     /**
195      * <p>
196      * Create a new restriction to state that all individuals of a certain class
197      * must have the given value for the given property.
198      * </p>
199      * <p>
200      * The new {@link MergedRestriction} contains
201      * <ul>
202      * <li>a {@link HasValueRestriction} with value <code>value</code>.
203      * </ul>
204      * </p>
205      * 
206      * @param propURI
207      *            The property for which this restriction is defined.
208      * @param value
209      *            The value that the given property must have. If this value is
210      *            a {@link java.lang.String} that contains a valid URI, then the
211      *            value must be a {@link Resource} with this URI.
212      * @return the restriction, or <code>null</code> if the parameters are
213      *         invalid.
214      */
215     public static final MergedRestriction getFixedValueRestriction(
216 	    String propURI, Object value) {
217 	if (propURI == null || value == null)
218 	    return null;
219 
220 	if (value instanceof String && isQualifiedName((String) value))
221 	    value = new Resource((String) value);
222 
223 	MergedRestriction m = new MergedRestriction(propURI);
224 	m.addRestriction(new HasValueRestriction(propURI, value));
225 	return m;
226     }
227 
228     /**
229      * <p>
230      * Create a new restriction to state that for all individuals of a certain
231      * class the cardinality of the given property is at least <code>min</code>
232      * and at most <code>max</code>.
233      * </p>
234      * <p>
235      * The new {@link MergedRestriction} contains
236      * <ul>
237      * <li>a {@link MinCardinalityRestriction} if <code>min</code> is valid and
238      * <code>min != max</code>.
239      * <li>a {@link MaxCardinalityRestriction} if <code>max</code> is valid and
240      * <code>min != max</code>.
241      * <li>an {@link ExactCardinalityRestriction} if <code>min</code> and
242      * <code>max</code> are valid and <code>min == max</code>.
243      * </ul>
244      * </p>
245      * 
246      * @param propURI
247      *            The property for which this restriction is defined.
248      * @param min
249      *            The minimum cardinality, or <code>0</code> if undefined.
250      * @param max
251      *            The maximum cardinality, or <code>-1</code> if undefined.
252      * @return the restriction, or <code>null</code> if the parameters are
253      *         invalid.
254      */
255     public static final MergedRestriction getCardinalityRestriction(
256 	    String propURI, int min, int max) {
257 	if (propURI == null)
258 	    return null;
259 	if (max > -1 && max < min) { // invalid
260 	    LogUtils.logDebug(
261 		    SharedResources.moduleContext,
262 		    MergedRestriction.class,
263 		    "getCardinalityRestriction",
264 		    new String[] { "Can not create the restriction because of invalid input parameters: "
265 			    + "the maximum cardinality is smaller than the minimum cardinality; this does not make sense." },
266 		    null);
267 	    return null;
268 	}
269 	if (max < 0 && min < 1) { // everything
270 	    LogUtils.logDebug(
271 		    SharedResources.moduleContext,
272 		    MergedRestriction.class,
273 		    "getCardinalityRestriction",
274 		    new String[] { "Can not create the restriction because of invalid input parameters: "
275 			    + "both maximum cardinality and minimum cardinality are undefined. This is not a restriction because everything is allowed." },
276 		    null);
277 	    return null;
278 	}
279 
280 	MergedRestriction ret = new MergedRestriction(propURI);
281 
282 	if (min > 0 && min == max) {
283 	    ret.addRestriction(new ExactCardinalityRestriction(propURI, min));
284 	} else {
285 	    if (min > 0)
286 		ret.addRestriction(new MinCardinalityRestriction(propURI, min));
287 	    if (max >= 0)
288 		ret.addRestriction(new MaxCardinalityRestriction(propURI, max));
289 	}
290 
291 	return ret;
292     }
293 
294     /**
295      * <p>
296      * Create a new restriction to state that for all individuals of a certain
297      * class the cardinality of the given property is at least <code>min</code>
298      * and at most <code>max</code> and the value of the given property is of
299      * type <code>typeURI</code>.
300      * </p>
301      * <p>
302      * The new {@link MergedRestriction} contains
303      * <ul>
304      * <li>an {@link AllValuesFromRestriction}
305      * <li>a {@link MinCardinalityRestriction} if <code>min</code> is valid and
306      * <code>min != max</code>.
307      * <li>a {@link MaxCardinalityRestriction} if <code>max</code> is valid and
308      * <code>min != max</code>.
309      * <li>an {@link ExactCardinalityRestriction} if <code>min</code> and
310      * <code>max</code> are valid and <code>min == max</code>.
311      * </ul>
312      * </p>
313      * 
314      * @param propURI
315      *            The property for which this restriction is defined.
316      * @param min
317      *            The minimum cardinality, or <code>0</code> if undefined.
318      * @param max
319      *            The maximum cardinality, or <code>-1</code> if undefined.
320      * @param typeURI
321      *            URI of the type of values for this property.
322      * @return the restriction, or <code>null</code> if the parameters are
323      *         invalid.
324      */
325     public static final MergedRestriction getAllValuesRestrictionWithCardinality(
326 	    String propURI, String typeURI, int min, int max) {
327 	if (typeURI == null)
328 	    return null;
329 
330 	TypeURI type = null;
331 
332 	if (TypeMapper.isRegisteredDatatypeURI(typeURI))
333 	    type = new TypeURI(typeURI, true);
334 	else if (OntologyManagement.getInstance().isRegisteredClass(typeURI,
335 		true))
336 	    type = new TypeURI(typeURI, false);
337 
338 	if (type == null) {
339 	    LogUtils.logDebug(
340 		    SharedResources.moduleContext,
341 		    MergedRestriction.class,
342 		    "getAllValuesRestrictionWithCardinality",
343 		    new String[] { "Can not create the restriction because of invalid input parameters: "
344 			    + "the specified type URI ("
345 			    + typeURI
346 			    + ") is not registered. It is neither a data type nor a registered ontology class." },
347 		    null);
348 	    return null;
349 	}
350 
351 	return getAllValuesRestrictionWithCardinality(propURI, type, min, max);
352     }
353 
354     /**
355      * <p>
356      * Create a new restriction to state that for all individuals of a certain
357      * class the cardinality of the given property is at least <code>min</code>
358      * and at most <code>max</code> and the value of the given property is of
359      * type <code>expr</code>.
360      * </p>
361      * <p>
362      * The new {@link MergedRestriction} contains
363      * <ul>
364      * <li>an {@link AllValuesFromRestriction}
365      * <li>a {@link MinCardinalityRestriction} if <code>min</code> is valid and
366      * <code>min != max</code>.
367      * <li>a {@link MaxCardinalityRestriction} if <code>max</code> is valid and
368      * <code>min != max</code>.
369      * <li>an {@link ExactCardinalityRestriction} if <code>min</code> and
370      * <code>max</code> are valid and <code>min == max</code>.
371      * </ul>
372      * </p>
373      * 
374      * @param propURI
375      *            The property for which this restriction is defined.
376      * @param min
377      *            The minimum cardinality, or <code>0</code> if undefined.
378      * @param max
379      *            The maximum cardinality, or <code>-1</code> if undefined.
380      * @param expr
381      *            The type of values for this property.
382      * @return the restriction, or <code>null</code> if the parameters are
383      *         invalid.
384      */
385     public static final MergedRestriction getAllValuesRestrictionWithCardinality(
386 	    String propURI, TypeExpression expr, int min, int max) {
387 	if (expr == null)
388 	    return null;
389 
390 	MergedRestriction ret = new MergedRestriction(propURI);
391 	if (min > 0 || max >= 0)
392 	    ret.addRestriction(MergedRestriction.getCardinalityRestriction(
393 		    propURI, min, max));
394 	ret.addRestriction(new AllValuesFromRestriction(propURI, expr));
395 
396 	return ret;
397     }
398 
399     /**
400      * <p>
401      * Create a new restriction to state that for all individuals of a certain
402      * class the value of the given property is of type <code>typeURI</code>.
403      * </p>
404      * <p>
405      * The new {@link MergedRestriction} contains
406      * <ul>
407      * <li>an {@link AllValuesFromRestriction}
408      * </ul>
409      * </p>
410      * 
411      * @param propURI
412      *            The property for which this restriction is defined.
413      * @param typeURI
414      *            The type of values of the property.
415      * @return the restriction, or <code>null</code> if the parameters are
416      *         invalid.
417      */
418     public static final MergedRestriction getAllValuesRestriction(
419 	    String propURI, String typeURI) {
420 	if (typeURI == null || propURI == null)
421 	    return null;
422 
423 	TypeURI type = null;
424 
425 	if (TypeMapper.isRegisteredDatatypeURI(typeURI))
426 	    type = new TypeURI(typeURI, true);
427 	else if (OntologyManagement.getInstance().isRegisteredClass(typeURI,
428 		true))
429 	    type = new TypeURI(typeURI, false);
430 
431 	if (type == null) {
432 	    LogUtils.logDebug(
433 		    SharedResources.moduleContext,
434 		    MergedRestriction.class,
435 		    "getAllValuesRestriction",
436 		    new String[] { "Can not create the restriction because of invalid input parameters: "
437 			    + "the specified type URI ("
438 			    + typeURI
439 			    + ") is not registered. It is neither a data type nor a registered ontology class." },
440 		    null);
441 	    return null;
442 	}
443 
444 	MergedRestriction ret = new MergedRestriction(propURI);
445 	ret.addRestriction(new AllValuesFromRestriction(propURI, type));
446 	return ret;
447     }
448 
449     /**
450      * <p>
451      * Create a new restriction to state that for all individuals of a certain
452      * class the value of the given property is of type <code>expr</code>.
453      * </p>
454      * <p>
455      * The new {@link MergedRestriction} contains
456      * <ul>
457      * <li>an {@link AllValuesFromRestriction}
458      * </ul>
459      * </p>
460      * 
461      * @param propURI
462      *            The property for which this restriction is defined.
463      * @param expr
464      *            The type of values of the property.
465      * @return the restriction, or <code>null</code> if the parameters are
466      *         invalid.
467      */
468     public static final MergedRestriction getAllValuesRestriction(
469 	    String propURI, TypeExpression expr) {
470 	if (expr == null || propURI == null)
471 	    return null;
472 
473 	MergedRestriction ret = new MergedRestriction(propURI);
474 	ret.addRestriction(new AllValuesFromRestriction(propURI, expr));
475 	return ret;
476     }
477 
478     /**
479      * Get a list of {@link MergedRestriction}s from the specified list which
480      * may contain restrictions for <i>different</i> properties. For each
481      * property, there exists exactly one {@link MergedRestriction} in the
482      * returned list.
483      * 
484      * @param o
485      *            The list of {@link PropertyRestriction}s. Elements of this
486      *            list that are not instances of {@link PropertyRestriction} are
487      *            ignored. Restrictions can be defined for different properties.
488      * @return The list of {@link MergedRestriction}.
489      */
490     public static ArrayList getFromList(List o) {
491 	// multiple restrictions for (possibly multiple) properties
492 	// -> build array of MergedRestrictions
493 	// temporary map:
494 	// ___key: property URI (String)
495 	// ___val: ArrayList of PropertyRestrictions
496 	// -> each ArrayList will be a MergedRestriction
497 	HashMap map = new HashMap();
498 	Object tmp;
499 
500 	// build temporary map
501 	for (int i = 0; i < o.size(); i++) {
502 	    tmp = o.get(i);
503 	    if (tmp instanceof PropertyRestriction) {
504 		PropertyRestriction res = (PropertyRestriction) tmp;
505 		ArrayList a = (ArrayList) map.get(res.getOnProperty());
506 		if (a == null)
507 		    map.put(res.getOnProperty(), a = new ArrayList());
508 		a.add(res);
509 	    }
510 	}
511 
512 	// create MergedRestrictions
513 	ArrayList ret = new ArrayList();
514 	Iterator it = map.keySet().iterator();
515 	while (it.hasNext()) {
516 	    String prop = (String) it.next();
517 	    ArrayList a = (ArrayList) map.get(prop);
518 	    MergedRestriction m = new MergedRestriction(prop, a);
519 	    ret.add(m);
520 	}
521 	return ret;
522     }
523 
524     /**
525      * Reset this object. The object is then in the same state like a newly
526      * created object with {@link #MergedRestriction()}, i.e. the
527      * {@link #onProperty} is not specified and it contains no
528      * {@link PropertyRestriction}.
529      */
530     private void reset() {
531 	types.clear();
532 	onProperty = null;
533 	for (int i = 0; i < index.length; i++)
534 	    index[i] = -1;
535     }
536 
537     /**
538      * Get the minimum cardinality, if this object specifies one. The minimum
539      * cardinality can be specified be either a
540      * {@link MinCardinalityRestriction} or an
541      * {@link ExactCardinalityRestriction}.
542      * 
543      * @return the minimum cardinality, or <code>0</code> if no minimum
544      *         cardinality is specified.
545      */
546     public int getMinCardinality() {
547 	if (index[minCardinalityID] != -1)
548 	    return ((MinCardinalityRestriction) (types
549 		    .get(index[minCardinalityID]))).getValue();
550 	if (index[exactCardinalityID] != -1)
551 	    return ((ExactCardinalityRestriction) (types
552 		    .get(index[exactCardinalityID]))).getValue();
553 	return MIN_UNDEFINED;
554     }
555 
556     /**
557      * Get the maximum cardinality, if this object specifies one. The maximum
558      * cardinality can be specified be either a
559      * {@link MaxCardinalityRestriction} or an
560      * {@link ExactCardinalityRestriction}.
561      * 
562      * @return the maximum cardinality, or <code>-1</code> if no maximum
563      *         cardinality is specified.
564      */
565     public int getMaxCardinality() {
566 	if (index[maxCardinalityID] != -1)
567 	    return ((MaxCardinalityRestriction) (types
568 		    .get(index[maxCardinalityID]))).getValue();
569 	if (index[exactCardinalityID] != -1)
570 	    return ((ExactCardinalityRestriction) (types
571 		    .get(index[exactCardinalityID]))).getValue();
572 	return MAX_UNDEFINED;
573     }
574 
575     /**
576      * Get the constraint of a specific restriction, e.g. the minimum
577      * cardinality for {@link MinCardinalityRestriction}. This method calls
578      * {@link PropertyRestriction#getConstraint()}; the return value depends on
579      * the individual restriction.
580      * 
581      * @param id
582      *            ID of the restriction.
583      * @return The constraint of the restriction.
584      */
585     public Object getConstraint(int id) {
586 	if (index[id] != -1)
587 	    return ((PropertyRestriction) types.get(index[id])).getConstraint();
588 	return null;
589     }
590 
591     /**
592      * Set a list of restrictions. The object is first reset before new
593      * restrictions are set. This method is similar to calling the constructor
594      * {@link #MergedRestriction(String, ArrayList)} with the only difference
595      * that no new object is created.
596      * 
597      * @param onProperty
598      *            The property for which this restriction is defined.
599      * @param restrictions
600      *            The list of restrictions. The array must contain only
601      *            instances of {@link PropertyRestriction}.
602      */
603     private void setRestrictions(String onProperty, ArrayList restrictions) {
604 	if (restrictions == null || onProperty == null)
605 	    throw new NullPointerException();
606 	reset();
607 	types.addAll(restrictions);
608 	this.onProperty = onProperty;
609 	try {
610 	    analyze();
611 	} catch (IllegalArgumentException e) {
612 	    reset();
613 	    throw e;
614 	}
615     }
616 
617     /**
618      * Get all {@link PropertyRestriction}s. The list is backed by the
619      * {@link MergedRestriction}, so changes to the {@link MergedRestriction}
620      * are reflected in the list.
621      * 
622      * @return an unmodifiable list of restrictions.
623      */
624     public List getRestrictions() {
625 	return Collections.unmodifiableList(types);
626     }
627 
628     /**
629      * Remove a restriction.
630      * 
631      * @param id
632      *            ID of the restriction to remove.
633      */
634     private void removeRestriction(int id) {
635 	int index = this.index[id];
636 	if (index < 0 || index >= types.size())
637 	    return;
638 
639 	// remove element
640 	types.remove(index);
641 
642 	// adapt indices
643 	for (int i = 0; i < this.index.length; i++)
644 	    if (this.index[i] > index)
645 		this.index[i]--;
646     }
647 
648     /**
649      * Get a specific {@link PropertyRestriction}.
650      * 
651      * @param id
652      *            ID of the property restriction.
653      * @return the property restriction.
654      */
655     public PropertyRestriction getRestriction(int id) {
656 	if (index[id] == -1)
657 	    return null;
658 	return (PropertyRestriction) types.get(index[id]);
659     }
660 
661     /**
662      * Add a new Restriction, performing a sanity check. It is not guaranteed
663      * that the given restriction will really be added, e.g. when this
664      * MergedRestriction already has a {@link MinCardinalityRestriction} and a
665      * {@link MaxCardinalityRestriction} with the same value is added, then the
666      * {@link MinCardinalityRestriction} is removed and an
667      * {@link ExactCardinalityRestriction} is added instead.
668      * 
669      * @param res
670      *            The Restriction to add.
671      * @return this restriction. This object is returned to allow for multiple
672      *         calls of this method.
673      * @throws IllegalArgumentException
674      *             If the given restriction is defined for a different property
675      *             than this merged restriction.
676      */
677     public MergedRestriction addRestriction(PropertyRestriction res) {
678 	addRestrictionCheck(res);
679 	return this;
680     }
681 
682     /**
683      * Add a new Restriction, performing a sanity check. It is not guaranteed
684      * that the given restriction will really be added, e.g. when this
685      * MergedRestriction already has a {@link MinCardinalityRestriction} and a
686      * {@link MaxCardinalityRestriction} with the same value is added, then the
687      * {@link MinCardinalityRestriction} is removed and an
688      * {@link ExactCardinalityRestriction} is added instead.
689      * 
690      * @param res
691      *            The Restriction to add.
692      * @return true, if the restriction could be added, i.e. the underlying list
693      *         of restrictions has changed.
694      * @throws IllegalArgumentException
695      *             If the given restriction is defined for a different property
696      *             than this merged restriction.
697      */
698     public boolean addRestrictionCheck(PropertyRestriction res) {
699 	// if (types.size()==1) {
700 	// addType(res);
701 	// onProperty = res.getOnProperty();
702 	// return this;
703 	// }
704 	if (res == null)
705 	    return false;
706 
707 	if (onProperty == null) {
708 	    onProperty = res.getOnProperty();
709 	} else {
710 	    if (!onProperty.equals(res.getOnProperty()))
711 		throw new IllegalArgumentException(
712 			"Trying to add a restriction for a different property. All restrictions of a MergedRestriction must be defined for the same property.");
713 	}
714 
715 	int max = getMaxCardinality();
716 	if (max == 0) {
717 	    LogUtils.logDebug(
718 		    SharedResources.moduleContext,
719 		    MergedRestriction.class,
720 		    "addRestriction",
721 		    new String[] { "Can not add the PropertyRestriction ("
722 			    + res.getType()
723 			    + ") because the maximum cardinality is 0 (no additional restriction is allowed)." },
724 		    null);
725 	    return false;
726 	}
727 
728 	// id points to the appropriate element in 'index' of the given
729 	// Restriction
730 	int id = getID(res);
731 
732 	PropertyRestriction all = getRestriction(allValuesFromID);
733 	PropertyRestriction some = getRestriction(someValuesFromID);
734 
735 	switch (id) {
736 	case allValuesFromID:
737 	    if (all == null
738 		    && (some == null || (max != 1 && res.matches(some, null,
739 			    getDefaultMatchmakingTTL(), null)))) {
740 		index[allValuesFromID] = types.size();
741 		types.add(res);
742 		return true;
743 	    } else {
744 		if (all != null)
745 		    LogUtils.logDebug(
746 			    SharedResources.moduleContext,
747 			    MergedRestriction.class,
748 			    "addRestriction",
749 			    new String[] { "Can not add the AllValuesFromRestriction because such a restriction is already set." },
750 			    null);
751 		else
752 		    LogUtils.logDebug(
753 			    SharedResources.moduleContext,
754 			    MergedRestriction.class,
755 			    "addRestriction",
756 			    new String[] { "Can not add the AllValuesFromRestriction, please check with your SomeValuesFromRestriction." },
757 			    null);
758 	    }
759 	    break;
760 	case someValuesFromID:
761 	    if (some == null
762 		    && (all == null || ((TypeExpression) all)
763 			    .matches(res))) {
764 		if (all != null && max == 1)
765 		    removeRestriction(allValuesFromID);
766 		index[someValuesFromID] = types.size();
767 		types.add(res);
768 		return true;
769 	    }
770 	    break;
771 	case hasValueID:
772 	    PropertyRestriction has = getRestriction(hasValueID);
773 	    if (has == null) {
774 		if (max != 0) {
775 		    index[hasValueID] = types.size();
776 		    types.add(res);
777 		    return true;
778 		} else {
779 		    LogUtils.logDebug(
780 			    SharedResources.moduleContext,
781 			    MergedRestriction.class,
782 			    "addRestriction",
783 			    new String[] { "Can not add the HasValueRestriction because the maximum cardinality is set to zero, so no instances are allowed." },
784 			    null);
785 		}
786 	    } else {
787 		LogUtils.logDebug(
788 			SharedResources.moduleContext,
789 			MergedRestriction.class,
790 			"addRestriction",
791 			new String[] { "Can not add the HasValueRestriction because such a restriction is already set." },
792 			null);
793 	    }
794 	    break;
795 	case minCardinalityID:
796 	    int newMin = ((MinCardinalityRestriction) res).getValue();
797 	    if (getMinCardinality() == MIN_UNDEFINED)
798 		if (max == MAX_UNDEFINED || max > newMin) {
799 		    if (index[minCardinalityID] == -1) {
800 			index[minCardinalityID] = types.size();
801 			types.add(res);
802 			return true;
803 		    }
804 		} else if (max == newMin) {
805 		    removeRestriction(maxCardinalityID);
806 		    res = new ExactCardinalityRestriction(onProperty, newMin);
807 		    if (index[exactCardinalityID] == -1) {
808 			index[exactCardinalityID] = types.size();
809 			types.add(res);
810 			return true;
811 		    } else {
812 			types.set(index[exactCardinalityID], res);
813 			return true;
814 		    }
815 		}
816 	    break;
817 	case maxCardinalityID:
818 	    int newMax = ((MaxCardinalityRestriction) res).getValue();
819 	    int min = getMinCardinality();
820 	    if (max == MAX_UNDEFINED
821 		    && (newMax > 1
822 			    || (newMax == 1 && (some == null || all == null)) || (newMax == 0
823 			    && all == null && some == null)))
824 		if (min < newMax) {
825 		    index[maxCardinalityID] = types.size();
826 		    types.add(res);
827 		    return true;
828 		} else if (min == newMax) {
829 		    removeRestriction(minCardinalityID);
830 		    res = new ExactCardinalityRestriction(onProperty, newMax);
831 		    index[exactCardinalityID] = types.size();
832 		    types.add(res);
833 		    return true;
834 		}
835 	    break;
836 	case exactCardinalityID:
837 	    int newExact = ((ExactCardinalityRestriction) res).getValue();
838 	    if (max == MAX_UNDEFINED
839 		    && getRestriction(minCardinalityID) == null
840 		    && (newExact > 1
841 			    || (newExact == 1 && (some == null || all == null)) || (newExact == 0
842 			    && all == null && some == null)))
843 		if (index[exactCardinalityID] == -1) {
844 		    index[exactCardinalityID] = types.size();
845 		    types.add(res);
846 		    return true;
847 		} else {
848 		    types.set(index[exactCardinalityID], res);
849 		    return true;
850 		}
851 	    break;
852 	}
853 	return false;
854     }
855 
856     /**
857      * Add all restrictions of the given {@link MergedRestriction}, performing a
858      * sanity check. It is not guaranteed that the given restriction will really
859      * be added, e.g. when this {@link MergedRestriction} already has a
860      * {@link MinCardinalityRestriction} and a {@link MaxCardinalityRestriction}
861      * with the same value is added, then the {@link MinCardinalityRestriction}
862      * is removed and an {@link ExactCardinalityRestriction} is added instead.
863      * 
864      * @param r
865      *            The restriction to add.
866      * @return this restriction. This object is returned to allow for multiple
867      *         calls of this method.
868      */
869     public MergedRestriction addRestriction(MergedRestriction r) {
870 	ArrayList resList = (ArrayList) r.types;
871 	for (int i = 0; i < resList.size(); i++)
872 	    addRestriction((PropertyRestriction) ((TypeExpression) resList
873 		    .get(i)).copy());
874 	return this;
875     }
876 
877     /**
878      * Get the ID of the specified restriction.
879      * 
880      * @param res
881      *            Restriction for which to return the ID.
882      * @return ID of the specified restriction.
883      */
884     private int getID(PropertyRestriction res) {
885 	int idx = -1;
886 	if (res instanceof AllValuesFromRestriction)
887 	    idx = allValuesFromID;
888 	else if (res instanceof SomeValuesFromRestriction)
889 	    idx = someValuesFromID;
890 	else if (res instanceof HasValueRestriction)
891 	    idx = hasValueID;
892 	else if (res instanceof MinCardinalityRestriction)
893 	    idx = minCardinalityID;
894 	else if (res instanceof MaxCardinalityRestriction)
895 	    idx = maxCardinalityID;
896 	else if (res instanceof ExactCardinalityRestriction)
897 	    idx = exactCardinalityID;
898 	else
899 	    throw new IllegalArgumentException("Unknown Restriction type: "
900 		    + res + " (Class URI: " + res.getClassURI() + ")");
901 	return idx;
902     }
903 
904     /**
905      * Analyze the list of restrictions to check for invalid data. The
906      * restrictions are stored in {@link Intersection#types} and are checked for
907      * duplicate restrictions and restriction that are defined for a different
908      * property. Additionally, the {@link #index} is checked. This analysis is
909      * necessary after setting an unknown list of restriction, e.g. after
910      * calling {@link #setRestrictions(String, ArrayList)}.
911      */
912     private void analyze() {
913 	for (int i = 0; i < types.size(); i++) {
914 	    if (!(types.get(i) instanceof PropertyRestriction))
915 		throw new IllegalArgumentException(
916 			"Non-restriction found at index: " + i);
917 
918 	    PropertyRestriction res = (PropertyRestriction) types.get(i);
919 	    if (!onProperty.equals(res.getOnProperty()))
920 		throw new IllegalArgumentException(
921 			"Restriction defined for wrong property: "
922 				+ res.getClassURI() + " " + res.getOnProperty());
923 
924 	    int idx = getID(res);
925 	    if (idx != -1 && index[idx] != -1)
926 		throw new IllegalArgumentException("Duplicate Restriction: "
927 			+ res.getClassURI());
928 	    index[idx] = i;
929 	}
930     }
931 
932     /** @see org.universAAL.middleware.owl.TypeExpression#copy() */
933     public TypeExpression copy() {
934 	ArrayList newList = new ArrayList();
935 	for (int i = 0; i < types.size(); i++)
936 	    newList.add(((PropertyRestriction) types.get(i)).copy());
937 	return new MergedRestriction(onProperty, newList);
938     }
939 
940     /**
941      * Create a new {@link MergedRestriction} with modified cardinality
942      * restrictions.
943      * 
944      * @param min
945      *            The new value for {@link MinCardinalityRestriction}.
946      * @param max
947      *            The new value for {@link MaxCardinalityRestriction}.
948      * @return The new {@link MergedRestriction}.
949      */
950     public MergedRestriction copyWithNewCardinality(int min, int max) {
951 	if (max > -1 && max < min)
952 	    return null;
953 	MergedRestriction r = (MergedRestriction) copy();
954 	r.removeRestriction(minCardinalityID);
955 	r.removeRestriction(maxCardinalityID);
956 	r.removeRestriction(exactCardinalityID);
957 	r.addRestriction(new MinCardinalityRestriction(getOnProperty(), min));
958 	r.addRestriction(new MaxCardinalityRestriction(getOnProperty(), max));
959 	return r;
960     }
961 
962     /**
963      * Create a new {@link MergedRestriction} with modified onProperty value.
964      * 
965      * @param onProp
966      *            The new URI of the property this restriction is defined for.
967      * @return the new {@link MergedRestriction} which contains all simple
968      *         restrictions of this class, but is defined for a different
969      *         property.
970      */
971     public MergedRestriction copyOnNewProperty(String onProp) {
972 	MergedRestriction r = (MergedRestriction) copy();
973 	for (Iterator i = types.iterator(); i.hasNext();)
974 	    ((Resource) i.next()).setProperty(
975 		    PropertyRestriction.PROP_OWL_ON_PROPERTY, onProp);
976 	onProperty = onProp;
977 	return r;
978     }
979 
980     /** @see org.universAAL.middleware.owl.TypeExpression#isWellFormed() */
981     public boolean isWellFormed() {
982 	for (int i = 0; i < types.size(); i++)
983 	    if (!((TypeExpression) types.get(i)).isWellFormed())
984 		return false;
985 	return true;
986     }
987 
988     /** @see org.universAAL.middleware.rdf.Resource#setProperty(String, Object) */
989     public boolean setProperty(String propURI, Object value) {
990 	if (Intersection.PROP_OWL_INTERSECTION_OF.equals(propURI)) {
991 	    List lst = null;
992 	    if (value instanceof List) {
993 		lst = (List) value;
994 	    } else if (value instanceof PropertyRestriction) {
995 		// this should not happen because an intersection should have at
996 		// least two elements. However, we add the property and assume
997 		// that there will be a further call to addRestriction
998 		return addRestrictionCheck((PropertyRestriction) value);
999 	    } else if (value instanceof MergedRestriction) {
1000 		lst = ((MergedRestriction) value).getRestrictions();
1001 	    }
1002 
1003 	    if (lst != null) {
1004 		boolean res = true;
1005 		boolean tmp;
1006 		for (Object el : lst) {
1007 		    if (el instanceof PropertyRestriction) {
1008 			tmp = addRestrictionCheck((PropertyRestriction) el);
1009 			res = res && tmp;
1010 		    }
1011 		}
1012 		return res;
1013 	    }
1014 	}
1015 	return false;
1016     }
1017 
1018     /** Get an iterator for the added child class expressions. */
1019     public Iterator types() {
1020 	// safe iterator, so that elements cannot be removed (this would destroy
1021 	// our index)
1022 	return new SafeIterator(types.iterator());
1023     }
1024 
1025     /**
1026      * If this MergedRestriction restricts the values of a property to be
1027      * individuals of a specific class (i.e. an {@link AllValuesFromRestriction}
1028      * of a either a {@link TypeURI} or a {@link BoundedValueRestriction}), then
1029      * return the URI of this class.
1030      * 
1031      * @return The URI of the class the property must be an individual of.
1032      */
1033     public String getPropTypeURI() {
1034 	TypeExpression all = null;
1035 
1036 	if (index[allValuesFromID] == -1)
1037 	    return null;
1038 
1039 	all = (TypeExpression) ((AllValuesFromRestriction) types
1040 		.get(index[allValuesFromID])).getConstraint();
1041 	
1042 	if (all instanceof TypeURI) {
1043 	    return ((TypeURI) all).getURI();
1044 	}
1045 
1046 	if (all instanceof TypeRestriction) {
1047 	    return ((TypeRestriction)all).getTypeURI();
1048 	}
1049 	
1050 	return null;
1051     }
1052 
1053     public String getOnProperty() {
1054 	return onProperty;
1055     }
1056 
1057     /**
1058      * Appends this restriction to the given root restriction on the given
1059      * property path.
1060      * 
1061      * @param root
1062      *            The root restriction for the first element of the property
1063      *            path. Can be null.
1064      * @param path
1065      *            The property path.
1066      * @return <b>null</b> if the parameters are invalid because of either<br>
1067      *         <ul>
1068      *         <li><code>path</code> is <i>null</i></li>
1069      *         <li><code>path</code> is empty</li>
1070      *         <li>the <code>onProperty</code> of this object is not set</li>
1071      *         <li>the <code>onProperty</code> does not match the last element
1072      *         of the <code>path</code></li>
1073      *         <li>the <code>onProperty</code> of <code>root</code> does not
1074      *         match the first element of the <code>path</code></li>
1075      *         </ul>
1076      *         a <b>{@link #MergedRestriction()}</b>that is either<br>
1077      *         <ul>
1078      *         <li><b><code>this</code></b> if <code>root</code> is <i>null</i>
1079      *         and the length of <code>path</code> is one</li>
1080      *         <li>a modified <b><code>root</code></b> (or a new
1081      *         {@link #MergedRestriction()} if <code>root</code> is <i>null</i>)
1082      *         otherwise</li>
1083      *         </ul>
1084      */
1085     public MergedRestriction appendTo(MergedRestriction root, String[] path) {
1086 	// System.out.println("appending \n" + this.toStringRecursive());
1087 	// System.out.println("\n\nto\n"
1088 	// + (root == null ? "null" : root.toStringRecursive()));
1089 	if (path == null || path.length == 0) {
1090 	    LogUtils.logDebug(
1091 		    SharedResources.moduleContext,
1092 		    MergedRestriction.class,
1093 		    "appendTo",
1094 		    new String[] {
1095 			    "Not possible to append a restriction because the specified path is invalid: ",
1096 			    path == null ? "path is null." : "path is empty." },
1097 		    null);
1098 	    return null;
1099 	}
1100 	if (getOnProperty() == null) {
1101 	    LogUtils.logDebug(
1102 		    SharedResources.moduleContext,
1103 		    MergedRestriction.class,
1104 		    "appendTo",
1105 		    new String[] { "Not possible to append a restriction because it does not define a property for which this restriction applies: the onProperty must be set." },
1106 		    null);
1107 	    return null;
1108 	}
1109 	if (!getOnProperty().equals(path[path.length - 1])) {
1110 	    LogUtils.logDebug(
1111 		    SharedResources.moduleContext,
1112 		    MergedRestriction.class,
1113 		    "appendTo",
1114 		    new String[] { "Not possible to append a restriction because "
1115 			    + "the restriction is not defined for the property at the end of the property path: the onProperty value of the restriction must correspond to the last element of the property path." },
1116 		    null);
1117 	    return null;
1118 	}
1119 	if (path.length == 1)
1120 	    if (root == null) {
1121 		return this;
1122 	    } else {
1123 		// add this restriction to the given list of restrictions, if it
1124 		// is not yet available
1125 		root.addRestriction(this);
1126 		return root;
1127 	    }
1128 	if (root == null) {
1129 	    root = new MergedRestriction(path[0]);
1130 	    PropertyRestriction r = new AllValuesFromRestriction();
1131 	    r.setProperty(PropertyRestriction.PROP_OWL_ON_PROPERTY, path[0]);
1132 	    root.addRestriction(r);
1133 	} else {
1134 	    // just a test: are all restrictions in root defined for the correct
1135 	    // property?
1136 	    if (!root.getOnProperty().equals(path[0])) {
1137 		LogUtils.logDebug(
1138 			SharedResources.moduleContext,
1139 			MergedRestriction.class,
1140 			"appendTo",
1141 			new String[] { "Not possible to append a new restriction to an existing restriction because "
1142 				+ "the existing restriction is defined for a different property than what is specified in the property path:\n"
1143 				+ "the existing restriction restriction is defined for property "
1144 				+ root.getOnProperty()
1145 				+ "\nand the property path starts with "
1146 				+ path[0] }, null);
1147 		return null;
1148 	    }
1149 	}
1150 
1151 	// get the AllValuesFromRestriction in root
1152 	PropertyRestriction tmp = root.getRestriction(allValuesFromID);
1153 	if (tmp == null) {
1154 	    // we couldn't find the AllValuesFromRestriction in the root array
1155 	    // -> create an empty one here
1156 	    tmp = new AllValuesFromRestriction();
1157 	    tmp.setOnProperty(path[0]);
1158 	    root.addRestriction(tmp);
1159 	}
1160 	tmp = root.getRestriction(allValuesFromID);
1161 	if (tmp == null) {
1162 	    LogUtils.logDebug(
1163 		    SharedResources.moduleContext,
1164 		    MergedRestriction.class,
1165 		    "appendTo",
1166 		    new String[] { "The root object does not contain an AllValuesFromRestriction and it is not possible to add a new AllValuesFromRestriction. Maybe root already contains a different restriction that prevents the AllValuesFromRestriction from being added (e.g. a SomeValuesFromRestriction or a HasValueRestriction)." },
1167 		    null);
1168 	    return null;
1169 	}
1170 
1171 	// tmp is now the root AllValuesFromRestriction
1172 	// -> follow the AllValuesFromRestriction along the path, create
1173 	// appropriate Restrictions and Intersections, if necessary
1174 	for (int i = 1; i < path.length - 1; i++)
1175 	    tmp = tmp.getRestrictionOnProperty(path[i]);
1176 	// System.out.println("TEMP ROOT\n" + root.toStringRecursive());
1177 	// System.out.println("TMP (last AllValues)\n" +
1178 	// tmp.toStringRecursive());
1179 
1180 	// tmp now points to the AllValuesFromRestriction of the path element
1181 	// before the last path element
1182 	TypeExpression all = (TypeExpression) tmp.getConstraint();
1183 	if (all == null && types.size() == 1) {
1184 	    // there is only one property restriction at the end of the path, so
1185 	    // we don't need an intersection
1186 	    // just add the new restriction from ~this~
1187 	    tmp.changeProperty(
1188 		    AllValuesFromRestriction.PROP_OWL_ALL_VALUES_FROM,
1189 		    types.get(0));
1190 	} else {
1191 	    // there are multiple property restrictions at the end of the path,
1192 	    // they are all in an intersection
1193 
1194 	    if (!(all instanceof Intersection)) {
1195 		Intersection i = new Intersection();
1196 		if (all != null)
1197 		    i.addType(all);
1198 		tmp.changeProperty(
1199 			AllValuesFromRestriction.PROP_OWL_ALL_VALUES_FROM, i);
1200 		all = i;
1201 	    }
1202 
1203 	    // add all restrictions of ~this~ MergedRestriction (which is an
1204 	    // Intersection and can't be set directly)
1205 	    Intersection isec = (Intersection) all;
1206 	    for (int i = 0; i < types.size(); i++)
1207 		isec.addType((TypeExpression) types.get(i));
1208 	}
1209 	return root;
1210     }
1211 
1212     /**
1213      * Get a {@link MergedRestriction} of a property path previously set by
1214      * {@link #appendTo(MergedRestriction, String[])}
1215      * 
1216      * @param path
1217      *            The property path.
1218      * @return The {@link MergedRestriction} that is defined for the last
1219      *         element of the property path
1220      */
1221     public MergedRestriction getRestrictionOnPath(String[] path) {
1222 	if (path == null || path.length == 0
1223 		|| !getOnProperty().equals(path[0]))
1224 	    return null;
1225 
1226 	MergedRestriction tmp = this;
1227 	for (int i = 1; i < path.length && tmp != null; i++)
1228 	    tmp = tmp.getRestrictionOnPathElement(path[i]);
1229 	return tmp;
1230     }
1231 
1232     private MergedRestriction getRestrictionOnPathElement(String pathElement) {
1233 	TypeExpression all = (TypeExpression) getConstraint(allValuesFromID);
1234 	if (all instanceof Intersection) {
1235 	    // create a new MergedRestriction and collect all simple
1236 	    // Restrictions
1237 	    MergedRestriction m = new MergedRestriction(pathElement);
1238 	    PropertyRestriction tmpRes;
1239 	    for (Iterator i = ((Intersection) all).types(); i.hasNext();) {
1240 		TypeExpression tmp = (TypeExpression) i.next();
1241 		if (tmp instanceof PropertyRestriction) {
1242 		    tmpRes = (PropertyRestriction) tmp;
1243 		    if (tmpRes.getOnProperty().equals(pathElement))
1244 			m.addRestriction(tmpRes);
1245 		}
1246 	    }
1247 	    return m;
1248 	} else if (all instanceof TypeURI)
1249 	    return ManagedIndividual.getClassRestrictionsOnProperty(
1250 		    all.getURI(), pathElement);
1251 
1252 	if (all instanceof PropertyRestriction) {
1253 	    PropertyRestriction res = (PropertyRestriction) all;
1254 	    if (res.getOnProperty().equals(pathElement)) {
1255 		MergedRestriction m = new MergedRestriction(pathElement);
1256 		m.addRestriction(res);
1257 		return m;
1258 	    }
1259 	}
1260 
1261 	return null;
1262     }
1263 
1264     /**
1265      * Get the set of instances.
1266      * 
1267      * @return an array of all known instances.
1268      */
1269     public Object[] getEnumeratedValues() {
1270 	int idx;
1271 
1272 	idx = index[allValuesFromID];
1273 	if (idx < 0)
1274 	    return null;
1275 	AllValuesFromRestriction allres = (AllValuesFromRestriction) types
1276 		.get(idx);
1277 	if (allres != null) {
1278 	    TypeExpression all = (TypeExpression) allres.getConstraint();
1279 	    if (all instanceof Enumeration)
1280 		return ((Enumeration) all).getUpperEnumeration();
1281 	    else if (all instanceof TypeURI) {
1282 		OntClassInfo info = OntologyManagement.getInstance()
1283 			.getOntClassInfo(all.getURI());
1284 		return info == null ? null : info.getInstances();
1285 	    }
1286 	}
1287 
1288 	idx = index[hasValueID];
1289 	if (idx < 0)
1290 	    return null;
1291 	HasValueRestriction hasres = (HasValueRestriction) types.get(idx);
1292 	if (hasres != null) {
1293 	    TypeExpression has = (TypeExpression) hasres.getConstraint();
1294 	    return (has == null) ? null : new Object[] { has };
1295 	}
1296 
1297 	return null;
1298     }
1299 
1300     /**
1301      * Create a new {@link MergedRestriction} that is a combination of this
1302      * restriction and the given restriction. If some parts are defined in both
1303      * restrictions, then the parts from <code>other</code> will be preferred.
1304      * 
1305      * @param other
1306      *            The restriction to merge with
1307      * @return <p>
1308      *         null if this restriction is not fully defined (has no
1309      *         onProperty).
1310      *         </p>
1311      *         <p>
1312      *         this if the onProperty of this object does not match the
1313      *         onProperty of the given object.
1314      *         </p>
1315      *         <p>
1316      *         A new MergedRestriction that combines all
1317      *         {@link PropertyRestriction}s of this object and the given object.
1318      *         </p>
1319      */
1320     public MergedRestriction merge(MergedRestriction other) {
1321 	String onThis = getOnProperty();
1322 	if (onThis == null)
1323 	    return null;
1324 
1325 	if (other == null)
1326 	    return this;
1327 
1328 	String onOther = other.getOnProperty();
1329 	if (onOther == null || !onThis.equals(onOther))
1330 	    return this;
1331 
1332 	MergedRestriction res = (MergedRestriction) other.copy();
1333 	res.addRestriction(this);
1334 	return res;
1335     }
1336 
1337     /**
1338      * Returns true if the given object is a member of the class represented by
1339      * this class expression, otherwise false; cardinality restrictions are
1340      * ignored.
1341      * 
1342      * @param o
1343      *            The object to test for membership.
1344      * @return true, if the given object is a member of this class expression.
1345      */
1346     public boolean hasMemberIgnoreCardinality(Object o) {
1347 	int j = 0;
1348 	for (Iterator i = types.iterator(); i.hasNext();) {
1349 	    if (j != minCardinalityID && j != maxCardinalityID
1350 		    && j != exactCardinalityID)
1351 		if (!((TypeExpression) i.next()).hasMember(o))
1352 		    return false;
1353 	    j++;
1354 	}
1355 	return true;
1356     }
1357 }