001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.datatypes;
011:
012: import java.util.*;
013:
014: import org.mmbase.bridge.*;
015: import org.mmbase.util.logging.*;
016: import org.mmbase.util.LocalizedString;
017: import org.w3c.dom.Element;
018:
019: /**
020: * Comparable datatypes have values which are {@link java.lang.Comparable}, so can be ordered, and
021: * therefore can have a minimum and a maximum value.
022: *
023: * @author Michiel Meeuwissen
024: * @version $Id: ComparableDataType.java,v 1.34 2007/09/16 17:55:28 michiel Exp $
025: * @since MMBase-1.8
026: */
027: public abstract class ComparableDataType<E extends java.io.Serializable & Comparable<E>>
028: extends BasicDataType<E> {
029:
030: private static final Logger log = Logging
031: .getLoggerInstance(ComparableDataType.class);
032:
033: private static final long serialVersionUID = 1L;
034:
035: protected MinRestriction minRestriction = new MinRestriction(true);
036: protected MaxRestriction maxRestriction = new MaxRestriction(true);
037:
038: protected ComparableDataType(String name, Class<E> classType) {
039: super (name, classType);
040: }
041:
042: @Override
043: protected void inheritRestrictions(BasicDataType<E> origin) {
044: super .inheritRestrictions(origin);
045: if (origin instanceof ComparableDataType) {
046: ComparableDataType<E> compOrigin = (ComparableDataType<E>) origin;
047:
048: E currentMin = minRestriction.getValue();
049: // cast origin minimum type to new datatype type
050: E originMin = cast(compOrigin.minRestriction.getValue(),
051: null, null);
052: // Only apply the new min if it is higher
053: if (currentMin == null
054: || (originMin != null && (currentMin
055: .compareTo(originMin) < 0))) {
056: minRestriction.inherit(compOrigin.minRestriction, true);
057: }
058:
059: E currentMax = maxRestriction.getValue();
060: // cast origin maximum type to new datatype type
061: E originMax = cast(compOrigin.maxRestriction.getValue(),
062: null, null);
063: // Only apply the new max if it is lower
064: if (currentMax == null
065: || (originMax != null && (currentMax
066: .compareTo(originMax) > 0))) {
067: maxRestriction.inherit(compOrigin.maxRestriction, true);
068: }
069: }
070: }
071:
072: protected void cloneRestrictions(BasicDataType<E> origin) {
073: super .cloneRestrictions(origin);
074: if (origin instanceof ComparableDataType) {
075: ComparableDataType<E> dataType = (ComparableDataType<E>) origin;
076: minRestriction = new MinRestriction(dataType.minRestriction);
077: maxRestriction = new MaxRestriction(dataType.maxRestriction);
078: }
079: }
080:
081: /**
082: */
083: public DataType.Restriction<E> getMinRestriction() {
084: return minRestriction;
085: }
086:
087: /**
088: * Returns whether the minimum value for this data type is inclusive or not.
089: * @return <code>true</code> if the minimum value if inclusive, <code>false</code> if it is not, or if there is no minimum.
090: */
091: public boolean isMinInclusive() {
092: return minRestriction.isInclusive();
093: }
094:
095: /**
096: */
097: public DataType.Restriction<E> getMaxRestriction() {
098: return maxRestriction;
099: }
100:
101: /**
102: * Returns whether the maximum value for this data type is inclusive or not.
103: * @return <code>true</code> if the maximum value if inclusive, <code>false</code> if it is not, or if there is no minimum.
104: */
105: public boolean isMaxInclusive() {
106: return maxRestriction.isInclusive();
107: }
108:
109: /**
110: * @inheritDoc
111: *
112: * If the default value of comparable datatype is somewhy out the range, it will be truncated into it.
113: */
114: public final E getDefaultValue() {
115: E def = super .getDefaultValue();
116: if (!minRestriction.valid(def, null, null)) {
117: def = minRestriction.getValue();
118: } else if (!maxRestriction.valid(def, null, null)) {
119: def = maxRestriction.getValue();
120: }
121: return def;
122: }
123:
124: public void toXml(Element parent) {
125: super .toXml(parent);
126:
127: if (minRestriction.getValue() != null) {
128: if (minRestriction.isInclusive()) {
129: addRestriction(
130: parent,
131: "(minInclusive|minExclusive)",
132: "minInclusive",
133: "description,class,property,default,unique,required,(minInclusive|minExclusive)",
134: minRestriction);
135:
136: } else {
137: addRestriction(
138: parent,
139: "(minExclusive|minInclusive)",
140: "minExclusive",
141: "description,class,property,default,unique,required,(minInclusive|minExclusive)",
142: minRestriction);
143: }
144: }
145: if (maxRestriction.getValue() != null) {
146: if (maxRestriction.isInclusive()) {
147: addRestriction(
148: parent,
149: "(maxInclusive|maxExclusive)",
150: "maxInclusive",
151: "description,class,property,default,unique,required,(minInclusive|minExclusive),(maxInclusive|maxExclusive)",
152: maxRestriction);
153: } else {
154: addRestriction(
155: parent,
156: "(maxExclusive|maxInclusive)",
157: "maxInclusive",
158: "description,class,property,default,unique,required,(minInclusive|minExclusive),(maxInclusive|maxExclusive)",
159: maxRestriction);
160: }
161: }
162:
163: }
164:
165: /**
166: * Sets the minimum Date value for this data type.
167: * @param value the minimum as an <code>Comparable</code> (and <code>Serializable</code>), or <code>null</code> if there is no minimum.
168: * @param inclusive whether the minimum value is inclusive or not
169: * @throws Class Identifier: java.lang.UnsupportedOperationException if this data type is read-only (i.e. defined by MMBase)
170: */
171: public void setMin(E value, boolean inclusive) {
172: edit();
173: checkType(value);
174: if (inclusive != minRestriction.isInclusive())
175: minRestriction = new MinRestriction(inclusive);
176: minRestriction.setValue(value);
177: }
178:
179: /**
180: * Sets the maximum Date value for this data type.
181: * @param value the maximum as an <code>Comparable</code> (and <code>Serializable</code>), or <code>null</code> if there is no maximum.
182: * @param inclusive whether the maximum value is inclusive or not
183: * @throws Class Identifier: java.lang.UnsupportedOperationException if this data type is read-only (i.e. defined by MMBase)
184: */
185: public void setMax(E value, boolean inclusive) {
186: edit();
187: checkType(value);
188: if (inclusive != maxRestriction.isInclusive())
189: maxRestriction = new MaxRestriction(inclusive);
190: maxRestriction.setValue(value);
191: }
192:
193: public int getEnforceStrength() {
194: int enforceStrength = Math.max(super .getEnforceStrength(),
195: minRestriction.getEnforceStrength());
196: return Math.max(enforceStrength, maxRestriction
197: .getEnforceStrength());
198: }
199:
200: protected Collection<LocalizedString> validateCastValue(
201: Collection<LocalizedString> errors, Object castValue,
202: Object value, Node node, Field field) {
203: errors = super .validateCastValue(errors, castValue, value,
204: node, field);
205: errors = minRestriction
206: .validate(errors, castValue, node, field);
207: errors = maxRestriction
208: .validate(errors, castValue, node, field);
209: return errors;
210: }
211:
212: public DataType<E> clone(String name) {
213: ComparableDataType<E> clone = (ComparableDataType<E>) super
214: .clone(name);
215: return clone;
216: }
217:
218: protected StringBuilder toStringBuilder() {
219: StringBuilder buf = super .toStringBuilder();
220: Object minValue = minRestriction.getValue();
221: Object maxValue = maxRestriction.getValue();
222: if (minValue != null) {
223: buf.append(minRestriction.isInclusive() ? '[' : '<');
224: buf.append(minValue);
225: if (minValue instanceof Date) {
226: // tss, the toString of Date object doesn't have BC in it if needed!
227: Calendar cal = Calendar.getInstance();
228: cal.setTime((Date) minValue);
229: if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
230: buf.append(" BC");
231: }
232: }
233: buf
234: .append(minRestriction.enforceStrength == DataType.ENFORCE_NEVER ? "*"
235: : "");
236: }
237: if (minValue != null || maxValue != null) {
238: buf.append("...");
239: }
240: if (maxValue != null) {
241: buf.append(maxValue);
242: buf
243: .append(maxRestriction.enforceStrength == DataType.ENFORCE_NEVER ? "*"
244: : "");
245: buf.append(maxRestriction.isInclusive() ? ']' : '>');
246: }
247: return buf;
248: }
249:
250: protected class MinRestriction extends AbstractRestriction<E> {
251: private boolean inclusive;
252:
253: MinRestriction(MinRestriction source) {
254: super (source);
255: inclusive = source.inclusive;
256: }
257:
258: MinRestriction(boolean inc) {
259: super ("min" + (inc ? "Inclusive" : "Exclusive"), null);
260: inclusive = inc;
261: }
262:
263: protected boolean simpleValid(Object v, Node node, Field field) {
264: if ((v == null) || (getValue() == null))
265: return true;
266: Comparable comparable = (Comparable) v;
267: Comparable minimum;
268: try {
269: minimum = (Comparable) ComparableDataType.this
270: .castToValidate(getValue(), node, field);
271: } catch (CastException ce) {
272: log.error(ce); // probably config error.
273: // invalid value, but not because of min-restriction
274: return true;
275: }
276: if (inclusive && (comparable.equals(minimum)))
277: return true;
278: return comparable.compareTo(minimum) > 0;
279: }
280:
281: public boolean isInclusive() {
282: return inclusive;
283: }
284: }
285:
286: protected class MaxRestriction extends AbstractRestriction<E> {
287: private boolean inclusive;
288:
289: MaxRestriction(MaxRestriction source) {
290: super (source);
291: inclusive = source.inclusive;
292: }
293:
294: MaxRestriction(boolean inc) {
295: super ("max" + (inc ? "Inclusive" : "Exclusive"), null);
296: inclusive = inc;
297: }
298:
299: protected boolean simpleValid(Object v, Node node, Field field) {
300: if ((v == null) || (getValue() == null))
301: return true;
302: Comparable comparable = (Comparable) v;
303: Comparable maximum;
304: try {
305: maximum = (Comparable) ComparableDataType.this
306: .castToValidate(getValue(), node, field);
307: } catch (CastException ce) {
308: log.error(ce); // probably config error.
309: // invalid value, but not because of max-restriction
310: return true;
311: }
312: if (inclusive && (comparable.equals(maximum)))
313: return true;
314: boolean res = comparable.compareTo(maximum) < 0;
315: return res;
316: }
317:
318: public boolean isInclusive() {
319: return inclusive;
320: }
321: }
322:
323: }
|