001package org.clafer.ast; 002 003import java.io.Serializable; 004 005/** 006 * Low and high cardinality. Immutable. 007 * 008 * @author jimmy 009 */ 010public class Card implements Serializable { 011 012 private static final int UNBOUNDED_HIGH = Integer.MAX_VALUE; 013 private final int low; 014 private final int high; 015 016 /** 017 * Construct an unbounded cardinality. 018 */ 019 public Card() { 020 this(0, UNBOUNDED_HIGH); 021 } 022 023 /** 024 * Construct a cardinality bounded from below, unbounded above. 025 * 026 * @param low the low cardinality 027 */ 028 public Card(int low) { 029 this(low, UNBOUNDED_HIGH); 030 } 031 032 /** 033 * Construct a cardinality bounded from below and above. 034 * 035 * @param low the low cardinality 036 * @param high the high cardinality 037 */ 038 public Card(int low, int high) { 039 if (low < 0) { 040 throw new IllegalArgumentException("low(" + low + ") < 0"); 041 } 042 if (high < low) { 043 throw new IllegalArgumentException("high(" + high + ") > low(" + low + ")"); 044 } 045 this.low = low; 046 this.high = high; 047 } 048 049 /** 050 * Detects if this cardinality is so restrictive that only one possible 051 * value is allowed. 052 * 053 * @return {@code true} if and only if low and high cardinality is the same, 054 * {@code false} otherwise 055 */ 056 public boolean isExact() { 057 return low == high; 058 } 059 060 /** 061 * Detects if this cardinality is either bounded below and/or above. 062 * 063 * @return {@code true} if and only if this cardinality is not unbounded, 064 * {@code false} otherwise 065 */ 066 public boolean isBounded() { 067 return hasLow() || hasHigh(); 068 } 069 070 /** 071 * Detects if this cardinality is bounded below. 072 * 073 * @return {@code true} if and only if this cardinality is bounded below, 074 * {@code false} otherwise 075 */ 076 public boolean hasLow() { 077 return low != 0; 078 } 079 080 /** 081 * Returns the low cardinality or 0 if unbounded from below. 082 * 083 * @return the low cardinality 084 */ 085 public int getLow() { 086 return low; 087 } 088 089 /** 090 * Detects if this cardinality is bounded above. 091 * 092 * @return {@code true} if and only if this cardinality is bounded above, 093 * {@code false} otherwise 094 */ 095 public boolean hasHigh() { 096 return high != UNBOUNDED_HIGH; 097 } 098 099 /** 100 * Returns the high cardinality or Integer.MAX_VALUE if unbounded form 101 * above. 102 * 103 * @return the high cardinality 104 */ 105 public int getHigh() { 106 return high; 107 } 108 109 /** 110 * Add two cardinalities together. 111 * 112 * @param addend the other cardinality 113 * @return the sum of this and the other cardinality 114 */ 115 public Card add(Card addend) { 116 if (hasHigh() && addend.hasHigh()) { 117 return new Card(low + addend.low, high + addend.high); 118 } 119 return new Card(low + addend.low); 120 } 121 122 /** 123 * Multiply two cardinalities together. 124 * 125 * @param factor the other cardinality 126 * @return the product of this and the other cardinality 127 */ 128 public Card mult(Card factor) { 129 if (hasHigh() && factor.hasHigh()) { 130 return new Card(low * factor.low, high * factor.high); 131 } 132 return new Card(low * factor.low); 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public boolean equals(Object obj) { 140 if (obj instanceof Card) { 141 Card other = (Card) obj; 142 return low == other.low && high == other.high; 143 } 144 return false; 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public int hashCode() { 152 return low ^ high; 153 } 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override 159 public String toString() { 160 if (!hasHigh()) { 161 return hasLow() ? low + "..*" : "*"; 162 } 163 return isExact() ? Integer.toString(low) : low + ".." + high; 164 } 165}