/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.core.metamodel;
import java.io.Serializable;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.HashCodeUtil;
import org.teiid.designer.core.ModelerCore;
/**
* Multiplicity is the specification of the range of allowable cardinality values that a set may assume. Essentially, multiplicity
* is a (possibly infinite) subset of the nonnegative integers. The Multiplicity class represents an interface for handling and
* manipulating multiplicity specifications.
* <p>
* In practice, it is usually a finite set of integer intervals, most often a single interval with a minimum and maximum value.
* Any set must be finite, but the upper bound can be finite or unbounded (the latter is called "many"); the upper bound must be
* greater than zero (multiplicity of zero is not useful). Note that although the set may be unbounded, any particular cardinality
* is always finite.
* <p>
* The multiplicity is usually defined simply as a text expression consisting of a comman-separated list of integer intervals,
* each in the form "<i>minimum</i>..<i>maximum</i>", where <i>minimum</i> and <i>maximum</i> are integers, or <i>maximum</i> can
* be a "*" which indicates an unbounded interval. An interval may also have the form <i>number</i>, where <i>number</i> is an
* integer representing an interval of a single integer. The multiplicity defined as a single star ("*") is equivalent to the
* expression "0..*", and indicates that the cardinality is unrestricted (i.e., "zero or more", or "many"). Examples of
* well-formed multiplicity text expressions include:
* <ul>
* 0..1
* </ul>
* <ul>
* 1
* </ul>
* <ul>
* 0..*
* </ul>
* <ul>
* *
* </ul>
* <ul>
* 1..*
* </ul>
* <ul>
* 1..5
* </ul>
* <ul>
* 1..5,10,13..18,20..*
* </ul>
* Generally, a well-formed multiplicity text expression will have intervals that monotonically increase (e.g., <i>1..5,10,13</i>
* rather than <i>10,1..5,13</i>), and will have two continguous intervals combined into a single interval (e.g., <i>1..8</i>
* rather than <i>1..5,6..8</i>, and <i>0..1</i> rather than <i>0,1</i>).
* <p>
* The Multiplicity class is an abstract class that is intended to hide all implementation details from the user. In this manner,
* the user only sees and uses the Multiplicity interface definition and its static <code>getInstance</code> and
* <code>getUnboundedInstance</code> methods, but the best possible implementation class with the most efficient representation is
* used for each instance. Various implementation classes are provided to efficiently handle the unbounded case and the single
* interval case, both of which are by far the most commonly used. However, any well-formed multiplicity text expression will be
* handled as well.
* <p>
* The Multiplicity class (and subclasses) are also immutable, and therefore may be referenced by multiple users without the
* chance of the instance being modified. This also simplifies the interface (since no modification methods need be provided) and
* makes possible the use of different underlying classes to handle different situations without exposing the specific
* implementation classes to the user; modification methods would make this very difficult to successfully implement.
*
* @since 8.0
*/
public abstract class Multiplicity implements Serializable, Comparable {
/**
*/
private static final long serialVersionUID = 1L;
/**
* The String definition of completely unbounded of either the maximum or both minimum and maximum if the multiplicity is
* considered unlimited.
*/
public static final String UNBOUNDED_DEFINITION = "*"; //$NON-NLS-1$
protected static final char UNBOUNDED_CHAR = '*';
/**
* The delimiter string ".." used between the minimum and maximum values.
*/
public static final String RANGE_DELIMITER = ".."; //$NON-NLS-1$
protected static final char RANGE_DELIMITER_CHAR = '.';
protected static final int RANGE_DELIMITER_LENGTH = RANGE_DELIMITER.length();
/**
* The delimiter string "," used between two intervals.
*/
public static final String INTERVAL_DELIMITER = ","; //$NON-NLS-1$
protected static final char INTERVAL_DELIMITER_CHAR = ',';
protected static final int INTERVAL_DELIMITER_LENGTH = INTERVAL_DELIMITER.length();
/**
* The value of either the maximum or both minimum and maximum if the multiplicity is considered unlimited.
*/
public static final int UNBOUNDED_VALUE = Integer.MAX_VALUE;
/**
* The minimum single multiplicity value allowed. A multiplicity of 0 is undefined.
*/
protected static final int MINIMUM_SINGLE_VALUE = 1;
/**
* The default single multiplicity value.
*/
protected static final int DEFAULT_SINGLE_VALUE = 1;
/**
* The default value for <code>isOrdered</code>.
*/
public static final boolean DEFAULT_ORDERING = false;
/**
* The default value for <code>isUnique</code>.
*/
public static final boolean DEFAULT_UNIQUENESS = false;
/**
* @label UNBOUNDED
* @supplierCardinality 1
*/
public static final Multiplicity UNBOUNDED = new UnlimitedMultiplicity();
public static final Multiplicity ZERO_OR_ONE = Multiplicity.getInstance(0, 1);
public static final Multiplicity ONLY_ONE = Multiplicity.getInstance(1);
public static final Multiplicity ONE_OR_MORE = Multiplicity.getInstance(1, Multiplicity.UNBOUNDED_VALUE);
private static final Map InstancePool = Collections.synchronizedMap(new HashMap());
public static boolean DEBUG_MULTIPLICITY_CREATION = false;
public static boolean DEBUG_MULTIPLICITY_LOCATION = false;
/**
* @label DEFAULT
* @supplierCardinality 1
*/
protected static final Multiplicity DEFAULT = new IntervalMultiplicity(DEFAULT_SINGLE_VALUE);
public static Multiplicity get( String multiplicityValue,
boolean isOrdered,
boolean isUnique ) throws MultiplicityExpressionException {
if (multiplicityValue == null) {
CoreArgCheck.isNotNull(multiplicityValue,
ModelerCore.Util.getString("Multiplicity.The_multiplicity_string_may_not_be_null")); //$NON-NLS-1$
}
MultiplicityHolder key = new MultiplicityHolder(multiplicityValue, isOrdered, isUnique);
Multiplicity result = (Multiplicity)InstancePool.get(key);
if (result == null) {
result = Multiplicity.getInstance(multiplicityValue, isOrdered, isUnique);
InstancePool.put(key, result);
if (DEBUG_MULTIPLICITY_CREATION) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("Multiplicity.DEBUG.Adding_Multiplicity") + result + "\""); //$NON-NLS-1$ //$NON-NLS-2$
}
} else {
if (DEBUG_MULTIPLICITY_LOCATION) {
ModelerCore.Util.log(IStatus.INFO,
ModelerCore.Util.getString("Multiplicity.DEBUG.Found_Multiplicity") + result + "\""); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return result;
}
public static Multiplicity get( String multiplicityValue ) throws MultiplicityExpressionException {
return get(multiplicityValue, Multiplicity.DEFAULT_ORDERING, Multiplicity.DEFAULT_UNIQUENESS);
}
/**
* Utility method that clears the pool.
*/
public static void clearPool() {
InstancePool.clear();
}
private boolean isOrdered = false;
private boolean isUnique = false;
/**
* Parse the specified text expression and return the list of Multiplicity objects that capture the expression.
*
* @param expression the well-formed text expression for the Multiplicity
* @param isOrdered the ordering constraint for the multiplicity
* @param isUnique the uniqueness constraint for the multiplicity
* @return the list of intervals defined by the expression
* @throws MultiplicityExpressionException if the specified expression cannot be parsed without errors or is not well-formed
*/
static List parseExpression( String defn,
boolean isOrdered,
boolean isUnique ) throws MultiplicityExpressionException {
List results = new ArrayList();
if (defn == null || defn.trim().length() == 0) {
results.add(DEFAULT);
return results;
}
// If it is "*" then return ...
if (defn == UNBOUNDED_DEFINITION || defn.equals(UNBOUNDED_DEFINITION)) {
results.add(UNBOUNDED);
return results;
}
// Parse the expression by iterating through it and finding all intervals ...
int minimum = -1;
int maximum = -1;
int startIndex = 0;
int currentIndex = 0;
CharacterIterator iter = new StringCharacterIterator(defn);
for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
currentIndex = iter.getIndex();
// If a range delimiter is found ...
if (c == RANGE_DELIMITER_CHAR) {
// If minimum was not yet found, parse it ...
if (minimum == -1) {
try {
minimum = Integer.parseInt(defn.substring(startIndex, currentIndex));
} catch (NumberFormatException e) {
final Object[] params = new Object[] {RANGE_DELIMITER, new Integer(currentIndex), defn};
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_text_before_the_range_delimiter_at_index_is_not_an_integer", params)); //$NON-NLS-1$
}
}
startIndex = currentIndex + RANGE_DELIMITER_LENGTH;
iter.setIndex(startIndex);
}
// If an interval delimiter is found ...
if (c == INTERVAL_DELIMITER_CHAR) {
if (defn.charAt(startIndex) == UNBOUNDED_CHAR) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_unbounded_interval_maximum_must_appear_at_the_end_of_the_expression", new Integer(startIndex), defn)); //$NON-NLS-1$
}
try {
maximum = Integer.parseInt(defn.substring(startIndex, currentIndex));
// If maximum was not yet found, set it to the minimum ...
if (minimum == -1) {
minimum = maximum;
}
} catch (NumberFormatException e) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_text_at_indexes_is_not_an_integer", new Integer(startIndex), new Integer(currentIndex), defn)); //$NON-NLS-1$
}
try {
results.add(new IntervalMultiplicity(minimum, maximum, isOrdered, isUnique));
} catch (IllegalArgumentException e) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_minimum_must_be_equal_to_or_less_than_the_maximum_interval", new Integer(minimum), new Integer(maximum), defn)); //$NON-NLS-1$
}
minimum = -1;
maximum = -1;
startIndex = currentIndex + INTERVAL_DELIMITER_LENGTH;
iter.setIndex(startIndex);
}
}
if (defn.length() <= startIndex) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_expression_may_not_end_with_a_delimiter", new Integer((defn.length() - 1)), defn)); //$NON-NLS-1$
}
// Check if the last value is a "*"
if (defn.charAt(startIndex) == UNBOUNDED_CHAR) {
maximum = UNBOUNDED_VALUE;
}
// Parse the last value ...
else {
try {
maximum = Integer.parseInt(defn.substring(startIndex));
} catch (NumberFormatException e) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_text_at_indexes_is_not_an_integer", new Integer(startIndex), new Integer((defn.length() - 1)), defn)); //$NON-NLS-1$
}
}
// If maximum was not yet found, set it to the minimum ...
if (minimum == -1) {
minimum = maximum;
}
try {
results.add(new IntervalMultiplicity(minimum, maximum, isOrdered, isUnique));
} catch (IllegalArgumentException e) {
throw new MultiplicityExpressionException(
ModelerCore.Util.getString("Multiplicity.Malformed_minimum_must_be_equal_to_or_less_than_the_maximum", new Integer(minimum), new Integer(maximum), defn)); //$NON-NLS-1$
}
// Sort the intervals ...
Collections.sort(results);
// Merge adjacent intervals ...
if (results.size() > 1) {
int index = 0;
Multiplicity first = (Multiplicity)results.get(index);
Multiplicity next = (Multiplicity)results.get(++index);
while (next != null) {
if (first.getMaximum() >= next.getMinimum() || first.getMaximum() == (next.getMinimum() - 1)) {
int min = Math.min(first.getMinimum(), next.getMinimum());
int max = Math.max(first.getMaximum(), next.getMaximum());
first = new IntervalMultiplicity(min, max, isOrdered, isUnique);
results.set(index - 1, first);
results.remove(index);
} else {
++index;
first = next;
}
if (index < results.size()) {
next = (Multiplicity)results.get(index);
} else {
next = null;
}
}
}
// Return the intervals ...
return results;
}
protected Multiplicity( boolean isOrdered,
boolean isUnique ) {
this.isOrdered = isOrdered;
this.isUnique = isUnique;
}
protected Multiplicity() {
this(DEFAULT_ORDERING, DEFAULT_UNIQUENESS);
}
/**
* Obtain an instance by parsing the specified multiplicity text expression. The text expression must be well-formed.
*
* @param expression the string definition of the multiplicity specification; if null, then the default Multiplicity instance
* of "1" is returned.
* @param isOrdered the ordering constraint for the multiplicity
* @param isUnique the uniqueness constraint for the multiplicity
* @return the MultiplicityInstance that best captures the specified definition.
* @throws MultiplicityExpressionException if the expression is not well-formed and cannot be parsed.
*/
public static Multiplicity getInstance( String defn,
boolean isOrdered,
boolean isUnique ) throws MultiplicityExpressionException {
if ((UNBOUNDED_DEFINITION.equals(defn) || "0..*".equals(defn)) && //$NON-NLS-1$
UNBOUNDED.isOrdered() == isOrdered && UNBOUNDED.isUnique() == isUnique) {
return UNBOUNDED;
}
if ("0..1".equals(defn) && ZERO_OR_ONE.isOrdered() == isOrdered && ZERO_OR_ONE.isUnique() == isUnique) { //$NON-NLS-1$
return ZERO_OR_ONE;
}
if ("1".equals(defn) && ONLY_ONE.isOrdered() == isOrdered && ONLY_ONE.isUnique() == isUnique) { //$NON-NLS-1$
return ONLY_ONE;
}
if ("1..*".equals(defn) && ONE_OR_MORE.isOrdered() == isOrdered && ONE_OR_MORE.isUnique() == isUnique) { //$NON-NLS-1$
return ONE_OR_MORE;
}
List intervals = parseExpression(defn, isOrdered, isUnique);
if (intervals.size() == 1) {
return (Multiplicity)intervals.get(0);
}
return new RangeMultiplicity(intervals, isOrdered, isUnique);
}
/**
* Obtain an instance by parsing the specified multiplicity text expression, that is unordered and not unique. The text
* expression must be well-formed.
*
* @param expression the string definition of the multiplicity specification; if null, then the default Multiplicity instance
* of "1" is returned.
* @return the MultiplicityInstance that best captures the specified definition.
* @throws MultiplicityExpressionException if the expression is not well-formed and cannot be parsed.
*/
public static Multiplicity getInstance( String defn ) throws MultiplicityExpressionException {
List intervals = parseExpression(defn, DEFAULT_ORDERING, DEFAULT_UNIQUENESS);
if (intervals.size() == 1) {
return (Multiplicity)intervals.get(0);
}
return new RangeMultiplicity(intervals, DEFAULT_ORDERING, DEFAULT_UNIQUENESS);
}
/**
* Obtain an instance that represents an unbounded multiplicity, which is equivalent to the text expression "*", that is
* unordered and not unique.
*
* @return the MultiplicityInstance representing "*".
*/
public static Multiplicity getUnboundedInstance() {
return UNBOUNDED;
}
/**
* Obtain an instance that represents an unbounded multiplicity, which is equivalent to the text expression "*".
*
* @param isOrdered the ordering constraint for the multiplicity
* @param isUnique the uniqueness constraint for the multiplicity
* @return the MultiplicityInstance representing "*".
*/
public static Multiplicity getUnboundedInstance( boolean isOrdered,
boolean isUnique ) {
return new UnlimitedMultiplicity(isOrdered, isUnique);
}
/**
* Obtain an instance that represents a singular multiplicity of "1" that is unordered and not unique. This is considered the
* default multiplicity, since many situations will require a cardinality of 1.
*
* @return the MultiplicityInstance representing "1".
*/
public static Multiplicity getInstance() {
return DEFAULT;
}
/**
* Obtain an instance for a single-interval multiplicity of the specified value. The corresponding text expression is
* "<i>number</i>".
*
* @param number the size of the single interval; must be a positive integer value.
* @param isOrdered the ordering constraint for the multiplicity
* @param isUnique the uniqueness constraint for the multiplicity
* @return the MultiplicityInstance that best captures the specified definition.
* @throws IllegalArgumentException if the value is negative or zero.
*/
public static Multiplicity getInstance( int number,
boolean isOrdered,
boolean isUnique ) {
return new IntervalMultiplicity(number, number, isOrdered, isUnique);
}
/**
* Obtain an instance for a single-interval multiplicity over the specified range. The corresponding text expression is
* "<i>minimum</i>..<i>maximum</i>".
*
* @param minimum the minimum value for the interval; must be positive, and may be zero only if <code>maximum</code> is
* non-zero.
* @param maximum the maximum value for the interval; must be equal to or greater than <code>minimum</code>, and may be
* Multiplicity.UNBOUNDED_VALUE for an unbounded maximum value.
* @param isOrdered the ordering constraint for the multiplicity
* @param isUnique the uniqueness constraint for the multiplicity
* @return the MultiplicityInstance that best captures the specified definition.
* @throws IllegalArgumentException if the two values are not compatible.
*/
public static Multiplicity getInstance( int minimum,
int maximum,
boolean isOrdered,
boolean isUnique ) {
return new IntervalMultiplicity(minimum, maximum, isOrdered, isUnique);
}
/**
* Obtain an instance for a single-interval multiplicity of the specified value that is unordered and not unique. The
* corresponding text expression is "<i>number</i>".
*
* @param number the size of the single interval; must be a positive integer value.
* @return the MultiplicityInstance that best captures the specified definition.
* @throws IllegalArgumentException if the value is negative or zero.
*/
public static Multiplicity getInstance( int number ) {
return new IntervalMultiplicity(number);
}
/**
* Obtain an instance for a single-interval multiplicity over the specified range that is unordered and not unique. The
* corresponding text expression is "<i>minimum</i>..<i>maximum</i>".
*
* @param minimum the minimum value for the interval; must be positive, and may be zero only if <code>maximum</code> is
* non-zero.
* @param maximum the maximum value for the interval; must be equal to or greater than <code>minimum</code>, and may be
* Multiplicity.UNBOUNDED_VALUE for an unbounded maximum value.
* @return the MultiplicityInstance that best captures the specified definition.
* @throws IllegalArgumentException if the two values are not compatible.
*/
public static Multiplicity getInstance( int minimum,
int maximum ) {
return new IntervalMultiplicity(minimum, maximum, DEFAULT_ORDERING, DEFAULT_UNIQUENESS);
}
/**
* Get the maximum number of values required for this property. The result from this method will be equal to or greater than
* zero.
*
* @return the maximum number of property values required.
*/
public abstract int getMaximum();
/**
* Return whether the multiplicity is defined as requiring an order.
*
* @return true if the instances order is important.
*/
public boolean isOrdered() {
return this.isOrdered;
}
/**
* Return whether the multiplicity is defined as requiring uniqueness.
*
* @return true if the instances uniqueness is important.
*/
public boolean isUnique() {
return this.isUnique;
}
/**
* Get the minimum number of values required for this property. The result from this method will be equal to or greater than
* zero.
*
* @return the minimum number of property values required.
*/
public abstract int getMinimum();
/**
* Determine whether the specified cardinality is included in this multiplicity expression.
*
* @return true if the cardinality is included in the range of allowable values for this multiplicity.
*/
public abstract boolean isIncluded( int cardinality );
/**
* Obtain whether the multiplicity has a maximum value that is unlimited.
*
* @return true if the maximum value of this multiplicity is unlimited.
*/
public abstract boolean isUnlimited();
/**
* Returns a string representing the current state of the object.
*
* @return the string representation of this instance.
*/
@Override
public abstract String toString();
/**
* Compares this object to another. If the specified object is an instance of the MetaMatrixSessionID class, then this method
* compares the contents; otherwise, it throws a ClassCastException (as instances are comparable only to instances of the same
* class).
* <p>
* Note: this method <i>is</i> consistent with <code>equals()</code>, meaning that
* <code>(compare(x, y)==0) == (x.equals(y))</code>.
* <p>
*
* @param obj the object that this instance is to be compared to.
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the
* specified object, respectively.
* @throws IllegalArgumentException if the specified object reference is null
* @throws ClassCastException if the specified object's type prevents it from being compared to this instance.
*/
@Override
public abstract int compareTo( Object obj );
protected int compareFlags( Multiplicity that ) {
// Assumed to not be null ...
if (this.isOrdered()) {
if (!that.isOrdered()) {
return -1;
}
} else {
if (that.isOrdered()) {
return 1;
}
}
if (this.isUnique()) {
if (!that.isUnique()) {
return -1;
}
} else {
if (that.isUnique()) {
return 1;
}
}
return 0; // otherwise these values are the same ...
}
static int compare( Multiplicity obj1,
Multiplicity obj2 ) {
if (obj1 instanceof UnlimitedMultiplicity) {
return obj1.compareTo(obj2);
}
if (obj2 instanceof UnlimitedMultiplicity) {
return -obj2.compareTo(obj1);
}
if (obj1 instanceof IntervalMultiplicity) {
int diffInMinimum = obj1.getMinimum() - obj2.getMinimum();
int diffInMaximum = obj1.getMaximum() - obj2.getMaximum();
if (obj2 instanceof IntervalMultiplicity) {
if (diffInMinimum != 0) {
return diffInMinimum;
}
return obj1.compareFlags(obj2);
}
if (diffInMinimum != 0) {
return diffInMinimum;
}
if (diffInMaximum != 0) {
return diffInMaximum;
}
return 1; // Interval is a complete interval, so is greater
}
if (obj1 instanceof RangeMultiplicity) {
if (obj2 instanceof IntervalMultiplicity) {
return -compare(obj2, obj1);
}
int diffInMinimum = obj1.getMinimum() - obj2.getMinimum();
int diffInMaximum = obj1.getMaximum() - obj2.getMaximum();
if (diffInMinimum != 0) {
return diffInMinimum;
}
if (diffInMaximum != 0) {
return diffInMaximum;
}
RangeMultiplicity r1 = (RangeMultiplicity)obj1;
RangeMultiplicity r2 = (RangeMultiplicity)obj2;
Iterator r1Iter = r1.getIntervals().iterator();
Iterator r2Iter = r2.getIntervals().iterator();
while (r1Iter.hasNext()) {
if (!r2Iter.hasNext()) {
return 1;
}
IntervalMultiplicity r1Interval = (IntervalMultiplicity)r1Iter.next();
IntervalMultiplicity r2Interval = (IntervalMultiplicity)r2Iter.next();
int diff = r1Interval.compareTo(r2Interval);
if (diff != 0) {
return diff;
}
}
return obj1.compareFlags(obj2);
}
throw new IllegalArgumentException(
ModelerCore.Util.getString("Multiplicity.The_multiplicity_object_is_not_a_known_subclass", obj1)); //$NON-NLS-1$
}
/*
public static void main(String[] args) {
try {
Multiplicity.getInstance("5..3");
System.out.println("Attempting to create \"5..3\" DID NOT failed as it should have");
} catch ( Exception e ) {
System.out.println("Attempting to create \"5..3\" successfully failed");
}
try {
Multiplicity star = Multiplicity.getUnboundedInstance();
Multiplicity m1_3 = Multiplicity.getInstance(1, 3);
Multiplicity m0_3 = Multiplicity.getInstance(0, 3);
Multiplicity m1_1 = Multiplicity.getInstance(1);
Multiplicity m1_7 = Multiplicity.getInstance(1, 7);
Multiplicity m1_star = Multiplicity.getInstance(1, Multiplicity.UNBOUNDED_VALUE);
Multiplicity m1_3c7 = Multiplicity.getInstance("1..3,7");
Multiplicity m1c3c7 = Multiplicity.getInstance("1,3,7");
Multiplicity m1_7c3 = Multiplicity.getInstance("1..7,3");
Multiplicity m1c2 = Multiplicity.getInstance("1,2");
Multiplicity m1c1 = Multiplicity.getInstance("1,1");
Multiplicity m1c3 = Multiplicity.getInstance("1,3");
Multiplicity m1c3_6c7_9c10_star = Multiplicity.getInstance("1,3..6,7..9,10..*");
Multiplicity m1c7_9c3_6c10_star = Multiplicity.getInstance("1,7..9,3..6,10..*");
System.out.println("star = \"" + star + "\" (" + getType(star) + ")");
System.out.println("m1_3 = \"" + m1_3 + "\" (" + getType(m1_3) + ")");
System.out.println("m0_3 = \"" + m0_3 + "\" (" + getType(m0_3) + ")");
System.out.println("m1_1 = \"" + m1_1 + "\" (" + getType(m1_1) + ")");
System.out.println("m1_7 = \"" + m1_7 + "\" (" + getType(m1_7) + ")");
System.out.println("m1_star = \"" + m1_star + "\" (" + getType(m1_star) + ")");
System.out.println("m1_3c7 = \"" + m1_3c7 + "\" (" + getType(m1_3c7) + ")");
System.out.println("m1c3c7 = \"" + m1c3c7 + "\" (" + getType(m1c3c7) + ")");
System.out.println("m1_7c3 = \"" + m1_7c3 + "\" (" + getType(m1_7c3) + ")");
System.out.println("m1c2 = \"" + m1c2 + "\" (" + getType(m1c2) + ")");
System.out.println("m1c1 = \"" + m1c1 + "\" (" + getType(m1c1) + ")");
System.out.println("m1c3 = \"" + m1c3 + "\" (" + getType(m1c3) + ")");
System.out.println("m1c3_6c7_9c10_star = \"" + m1c3_6c7_9c10_star + "\" (" + getType(m1c3_6c7_9c10_star) + ")");
System.out.println("m1c7_9c3_6c10_star = \"" + m1c7_9c3_6c10_star + "\" (" + getType(m1c7_9c3_6c10_star) + ")");
System.out.println();
Multiplicity star_STR = Multiplicity.getInstance(star.toString());
Multiplicity m1_3_STR = Multiplicity.getInstance(m1_3.toString());
Multiplicity m0_3_STR = Multiplicity.getInstance(m0_3.toString());
Multiplicity m1_1_STR = Multiplicity.getInstance(m1_1.toString());
Multiplicity m1_7_STR = Multiplicity.getInstance(m1_7.toString());
Multiplicity m1_3c7_STR = Multiplicity.getInstance(m1_3c7.toString());
Multiplicity m1c3c7_STR = Multiplicity.getInstance(m1c3c7.toString());
Multiplicity m1_star_STR = Multiplicity.getInstance(m1_star.toString());
Multiplicity m1c2_STR = Multiplicity.getInstance(m1c2.toString());
Multiplicity m1c1_STR = Multiplicity.getInstance(m1c1.toString());
Multiplicity m1c3_STR = Multiplicity.getInstance(m1c3.toString());
System.out.println("star_STR = \"" + star_STR + "\" (" + getType(star_STR) + ")");
System.out.println("m1_3_STR = \"" + m1_3_STR + "\" (" + getType(m1_3_STR) + ")");
System.out.println("m0_3_STR = \"" + m0_3_STR + "\" (" + getType(m0_3_STR) + ")");
System.out.println("m1_1_STR = \"" + m1_1_STR + "\" (" + getType(m1_1_STR) + ")");
System.out.println("m1_7_STR = \"" + m1_7_STR + "\" (" + getType(m1_7_STR) + ")");
System.out.println("m1_3c7_STR = \"" + m1_3c7_STR + "\" (" + getType(m1_3c7_STR) + ")");
System.out.println("m1c3c7_STR = \"" + m1c3c7_STR + "\" (" + getType(m1c3c7_STR) + ")");
System.out.println("m1_star_STR = \"" + m1_star_STR + "\" (" + getType(m1_star_STR) + ")");
System.out.println("m1c2_STR = \"" + m1c2_STR + "\" (" + getType(m1c2_STR) + ")");
System.out.println("m1c1_STR = \"" + m1c1_STR + "\" (" + getType(m1c1_STR) + ")");
System.out.println("m1c3_STR = \"" + m1c3_STR + "\" (" + getType(m1c3_STR) + ")");
System.out.println("\nTest equals ...");
testEquality(star, star_STR, true);
testEquality(m1_3, m1_3_STR, true);
testEquality(m0_3, m0_3_STR, true);
testEquality(m1_1, m1_1_STR, true);
testEquality(m1_star, m1_star_STR, true);
testEquality(m1_3c7, m1_3c7, true);
testEquality(m0_3, m1_3_STR, false);
testEquality(m0_3, m1_star, false);
testEquality(m0_3, m1_1_STR, false);
System.out.println("\nTest compareTo between same types ...");
testCompare(star, star_STR, 0);
testCompare(m1_3, m1_3_STR, 0);
testCompare(m0_3, m0_3_STR, 0);
testCompare(m1_1, m1_1_STR, 0);
testCompare(m1_star, m1_star_STR, 0);
testCompare(m1_3c7, m1_3c7, 0);
System.out.println("\nTest compareTo between Unlimited and Unlimited ...");
testCompare(star, star_STR, 0);
System.out.println("\nTest compareTo between Unlimited and Interval ...");
testCompare(star, m1_3, 1);
testCompare(m1_3, star, -1);
testCompare(m1_3, m1_3, 0);
System.out.println("\nTest compareTo between Interval and Interval ...");
testCompare(m1_3, m1_star, -1);
testCompare(m1_star, m1_3, 1);
testCompare(m1_3, m0_3, 1);
testCompare(m0_3, m1_3, -1);
testCompare(m0_3, m0_3, 0);
System.out.println("\nTest compareTo between Explicit and Explicit ...");
testCompare(m1_3c7, m1c3c7, 1); //??
testCompare(m1c3c7, m1_3c7, -1); //??
System.out.println("\nTest compareTo between Unlimited and Explicit ...");
testCompare(star, m1_3c7, 1);
testCompare(m1_3c7, star, -1);
System.out.println("\nTest compareTo between Interval and Explicit ...");
testCompare(m1_7, m1_3c7, 1);
testCompare(m1_3c7, m1_7, -1);
} catch ( Exception e ) {
e.printStackTrace();
}
}
private static void testEquality(Multiplicity m1, Multiplicity m2, boolean equal) {
if (equal != m1.equals(m2)) {
System.out.println("\"" + m1 + " should" + (equal ? " " : " not ") + "be equal to \"" + m2 + "\"");
} else {
System.out.println("\"" + m1 + " is correctly" + (equal ? " " : " not ") + "equal to \"" + m2 + "\"");
}
}
private static void testCompare(Multiplicity m1, Multiplicity m2, int lessOrGreater) {
int result = m1.compareTo(m2);
int result2 = m2.compareTo(m1);
if (result < 0 && lessOrGreater >= 0 ||
result > 0 && lessOrGreater <= 0 ||
result == 0 && lessOrGreater != 0) {
System.out.println("\"" + m1 + " compares to \"" + m2 + "\" as " + result + " (should be " + lessOrGreater + ")");
} else {
System.out.println("\"" + m1 + " compares to \"" + m2 + "\" as " + result + " (correct)");
}
}
private static String getType(Multiplicity m) {
if (m instanceof UnlimitedMultiplicity) {
return "Unlimited";
}
if (m instanceof RangeMultiplicity) {
return "Range";
}
if (m instanceof IntervalMultiplicity) {
return "Interval";
}
return "Unknown";
}
*/
}
final class UnlimitedMultiplicity extends Multiplicity {
/**
*/
private static final long serialVersionUID = 1L;
UnlimitedMultiplicity() {
super();
}
UnlimitedMultiplicity( boolean isOrdered,
boolean isUnique ) {
super(isOrdered, isUnique);
}
@Override
public int getMaximum() {
return Multiplicity.UNBOUNDED_VALUE;
}
@Override
public int getMinimum() {
return 0;
}
@Override
public boolean isUnlimited() {
return true;
}
@Override
public boolean isIncluded( int cardinality ) {
return cardinality >= 0;
}
@Override
public String toString() {
return Multiplicity.UNBOUNDED_DEFINITION;
}
@Override
public int compareTo( Object obj ) {
CoreArgCheck.isNotNull(obj);
Multiplicity that = (Multiplicity)obj; // May throw ClassCastException
if (that instanceof UnlimitedMultiplicity) {
return super.compareFlags(that);
}
return 1; // this is always greater than that
}
@Override
public boolean equals( Object obj ) {
// Check if instances are identical ...
if (this == obj) {
return true;
}
// Check if object can be compared to this one
// (this includes checking for null ) ...
if (obj instanceof UnlimitedMultiplicity) {
// equal so far ...
return super.compareFlags((Multiplicity)obj) == 0;
}
// Otherwise not comparable ...
return false;
}
}
final class IntervalMultiplicity extends Multiplicity {
/**
*/
private static final long serialVersionUID = 1L;
private int minimum = 0;
private int maximum = Multiplicity.UNBOUNDED_VALUE;
IntervalMultiplicity( int number ) {
super();
if (number < 0) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("Multiplicity.The_multiplicity_must_be_a_positive_number_")); //$NON-NLS-1$
}
if (number < Multiplicity.MINIMUM_SINGLE_VALUE) {
throw new IllegalArgumentException(ModelerCore.Util.getString("Multiplicity.The_multiplicity_may_not_be_0")); //$NON-NLS-1$
}
this.maximum = number;
this.minimum = number;
}
IntervalMultiplicity( int minimum,
int maximum,
boolean isOrdered,
boolean isUnique ) {
super(isOrdered, isUnique);
if (maximum < 0) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("Multiplicity.The_maximum_multiplicity_must_be_between_0_and_UNBOUNDED_VALUE", maximum)); //$NON-NLS-1$
}
if (minimum > maximum) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("Multiplicity.The_maximum_multiplicity_must_be_equal_to_or_greater_than_the_minimum_multiplicity", new Integer(maximum), new Integer(minimum))); //$NON-NLS-1$
}
this.maximum = maximum;
this.minimum = minimum;
}
/**
* Constructs an instance that represents the default multiplicity of "1..1"
*/
IntervalMultiplicity() {
this(MINIMUM_SINGLE_VALUE);
}
@Override
public int getMaximum() {
return maximum;
}
@Override
public int getMinimum() {
return minimum;
}
@Override
public boolean isUnlimited() {
return this.maximum == UNBOUNDED_VALUE;
}
@Override
public boolean isIncluded( int cardinality ) {
return (cardinality >= minimum && cardinality <= maximum);
}
@Override
public String toString() {
String result = null;
if (this.minimum != this.maximum) {
if (this.maximum != UNBOUNDED_VALUE) {
result = Integer.toString(this.minimum) + RANGE_DELIMITER + Integer.toString(this.maximum);
} else {
result = Integer.toString(this.minimum) + RANGE_DELIMITER + UNBOUNDED_CHAR;
}
} else {
result = Integer.toString(this.minimum);
}
return result;
}
@Override
public int compareTo( Object obj ) {
CoreArgCheck.isNotNull(obj);
Multiplicity that = (Multiplicity)obj; // May throw ClassCastException
return Multiplicity.compare(this, that);
}
@Override
public boolean equals( Object obj ) {
// Check if instances are identical ...
if (this == obj) {
return true;
}
// Check if object can be compared to this one
// (this includes checking for null ) ...
// if ( this.getClass().isInstance(obj) ) {
if (obj instanceof IntervalMultiplicity) {
IntervalMultiplicity that = (IntervalMultiplicity)obj;
if (that.minimum == this.minimum && that.maximum == this.maximum) {
// equal so far ...
return super.compareFlags(that) == 0;
}
return false;
}
// Otherwise not comparable ...
return false;
}
}
final class RangeMultiplicity extends Multiplicity {
/**
*/
private static final long serialVersionUID = 1L;
private List intervals = new ArrayList();
private Multiplicity first;
private Multiplicity last;
RangeMultiplicity( List intervals,
boolean isOrdered,
boolean isUnique ) {
super(isOrdered, isUnique);
this.intervals = intervals;
if (this.intervals == null || this.intervals.size() == 0) {
throw new IllegalArgumentException(
ModelerCore.Util.getString("Multiplicity.Unable_to_create_a_RangeMultiplicity_with_zero-length_or_null_list_of_intervals")); //$NON-NLS-1$
}
this.first = (Multiplicity)this.intervals.get(0);
this.last = (Multiplicity)this.intervals.get(this.intervals.size() - 1);
}
public List getIntervals() {
return intervals;
}
@Override
public int getMaximum() {
return this.last.getMaximum();
}
@Override
public int getMinimum() {
return this.first.getMinimum();
}
@Override
public boolean isUnlimited() {
return this.last.isUnlimited();
}
@Override
public boolean isIncluded( int cardinality ) {
boolean result = false;
Iterator iter = intervals.iterator();
while (iter.hasNext()) {
Multiplicity m = (Multiplicity)iter.next();
if (m.isIncluded(cardinality)) {
result = true;
break;
}
}
return result;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
Iterator iter = intervals.iterator();
if (iter.hasNext()) {
sb.append(iter.next().toString());
}
while (iter.hasNext()) {
sb.append(INTERVAL_DELIMITER_CHAR);
sb.append(iter.next().toString());
}
return sb.toString();
}
@Override
public int compareTo( Object obj ) {
CoreArgCheck.isNotNull(obj);
Multiplicity that = (Multiplicity)obj; // May throw ClassCastException
return Multiplicity.compare(this, that);
}
@Override
public boolean equals( Object obj ) {
// Check if instances are identical ...
if (this == obj) {
return true;
}
// Check if object can be compared to this one
if (obj instanceof Multiplicity) {
Multiplicity that = (Multiplicity)obj;
return Multiplicity.compare(this, that) == 0;
}
// Otherwise not comparable ...
return false;
}
}
class MultiplicityHolder {
String multiplicity = null;
boolean isOrdered = true;
boolean isUnique = true;
public MultiplicityHolder( String m,
boolean isOrdered,
boolean isUnique ) {
this.multiplicity = m;
this.isOrdered = isOrdered;
this.isUnique = isUnique;
}
@Override
public int hashCode() {
int seed = 0;
seed = HashCodeUtil.hashCode(seed, multiplicity);
seed = HashCodeUtil.hashCode(seed, isOrdered);
seed = HashCodeUtil.hashCode(seed, isUnique);
return seed;
}
@Override
public boolean equals( Object obj ) {
// Check if instances are identical ...
if (this == obj) {
return true;
}
// Check if object can be compared to this one
// (this includes checking for null ) ...
if (this.getClass().isInstance(obj)) {
MultiplicityHolder that = (MultiplicityHolder)obj;
if (that.isOrdered != this.isOrdered) {
return false;
}
if (that.isUnique != this.isUnique) {
return false;
}
if (!this.multiplicity.equals(that.multiplicity)) {
return false;
}
return true;
}
// Otherwise not comparable ...
return false;
}
}