001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.shared.value;
034:
035: import com.flexive.shared.FxFormatUtils;
036: import com.flexive.shared.exceptions.FxInvalidParameterException;
037:
038: import java.io.Serializable;
039: import java.util.Date;
040: import java.text.SimpleDateFormat;
041: import java.text.ParsePosition;
042:
043: /**
044: * A class to describe an immutable date range
045: * <p/>
046: * parts of the code are taken from JFreeChart (http://www.jfree.org/jfreechart/index.html) which is released
047: * under an LGPL license. Code is taken from org.jfree.data.Range (http://www.koders.com/java/fidBBD43A5DBC05830ABCF1267D7B17BF90BD2B6521.aspx)
048: *
049: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
050: */
051: public class DateRange implements Serializable {
052: private static final long serialVersionUID = -2528729942501241287L;
053:
054: private Date lower;
055: private Date upper;
056:
057: public DateRange(Date lower, Date upper) {
058: if (lower == null || upper == null) {
059: throw new FxInvalidParameterException("lower, upper",
060: "ex.daterange.empty").asRuntimeException();
061: }
062: if (upper.getTime() < lower.getTime()) {
063: throw new FxInvalidParameterException("lower",
064: "ex.daterange.order", lower, upper)
065: .asRuntimeException();
066: }
067: this .lower = (Date) lower.clone();
068: this .upper = (Date) upper.clone();
069: }
070:
071: public DateRange(String dateRange) {
072: final SimpleDateFormat sdf = FxFormatUtils.getDateTimeFormat();
073: final ParsePosition pos = new ParsePosition(0);
074: this .lower = sdf.parse(dateRange, pos);
075: pos.setIndex(pos.getIndex() + 2); // skip the " - "
076: this .upper = sdf.parse(dateRange, pos);
077: if (this .lower == null || this .upper == null) {
078: throw new FxInvalidParameterException("dateRange",
079: "ex.daterange.format", dateRange)
080: .asRuntimeException();
081: }
082: }
083:
084: public Date getLower() {
085: return (Date) lower.clone();
086: }
087:
088: public Date getUpper() {
089: return (Date) upper.clone();
090: }
091:
092: public long getDuration() {
093: return upper.getTime() - lower.getTime();
094: }
095:
096: /**
097: * Does <code>range</code> intersect with this DateRange?
098: *
099: * @param range the DateRange to test for intersection
100: * @return if the ranges intersect
101: */
102: public boolean intersects(DateRange range) {
103: if (range.lower.getTime() <= this .lower.getTime()) {
104: return (range.upper.getTime() > this .lower.getTime());
105: } else {
106: return (range.lower.getTime() < this .upper.getTime() && range.upper
107: .getTime() >= range.lower.getTime());
108: }
109: }
110:
111: /**
112: * Returns the central value for the range.
113: *
114: * @return The central value.
115: */
116: public Date getCentralValue() {
117: return new Date(
118: (long) ((double) this .lower.getTime() / 2.0 + (double) this .upper
119: .getTime() / 2.0));
120: }
121:
122: /**
123: * Returns <code>true</code> if the range contains the specified value and <code>false</code>
124: * otherwise.
125: *
126: * @param value the value to check
127: * @return <code>true</code> if the range contains the specified value.
128: */
129: public boolean contains(final Date value) {
130: return (value.getTime() >= this .lower.getTime() && value
131: .getTime() <= this .upper.getTime());
132: }
133:
134: /**
135: * Returns the value within the range that is closest to the specified value.
136: *
137: * @param value the value.
138: * @return The constrained value.
139: */
140: public Date constrain(final Date value) {
141: Date result = value;
142: if (!contains(value)) {
143: if (value.getTime() > this .upper.getTime()) {
144: result = new Date(this .upper.getTime());
145: } else if (value.getTime() < this .lower.getTime()) {
146: result = new Date(this .lower.getTime());
147: }
148: }
149: return result;
150: }
151:
152: /**
153: * Creates a new range by combining two existing ranges.
154: * <p/>
155: * Note that:
156: * <ul>
157: * <li>either range can be <code>null</code>, in which case the other range is returned;</li>
158: * <li>if both ranges are <code>null</code> the return value is <code>null</code>.</li>
159: * </ul>
160: *
161: * @param range1 the first range (<code>null</code> permitted).
162: * @param range2 the second range (<code>null</code> permitted).
163: * @return A new range (possibly <code>null</code>).
164: */
165: public static DateRange combine(final DateRange range1,
166: final DateRange range2) {
167: if (range1 == null) {
168: return range2;
169: } else {
170: if (range2 == null) {
171: return range1;
172: } else {
173: final Date l = new Date(Math.min(
174: range1.lower.getTime(), range2.lower.getTime()));
175: final Date u = new Date(Math.max(
176: range1.upper.getTime(), range2.upper.getTime()));
177: return new DateRange(l, u);
178: }
179: }
180: }
181:
182: /**
183: * Creates a new range by adding margins to an existing range.
184: *
185: * @param range the range (<code>null</code> not permitted).
186: * @param lowerMargin the lower margin (expressed as a percentage of the range length).
187: * @param upperMargin the upper margin (expressed as a percentage of the range length).
188: * @return The expanded range.
189: */
190: public static DateRange expand(DateRange range, double lowerMargin,
191: double upperMargin) {
192: if (range == null) {
193: throw new IllegalArgumentException("Null 'range' argument.");
194: }
195: double length = range.getDuration();
196: double lower = length * lowerMargin;
197: double upper = length * upperMargin;
198: return new DateRange(new Date(range.lower.getTime()
199: - (long) lower), new Date(range.upper.getTime()
200: + (long) upper));
201: }
202:
203: /**
204: * Indicates whether some other object is "equal to" this one.
205: *
206: * @param obj the reference object with which to compare.
207: * @return <code>true</code> if this object is the same as the obj
208: * argument; <code>false</code> otherwise.
209: * @see #hashCode()
210: * @see java.util.Hashtable
211: */
212: @Override
213: public boolean equals(Object obj) {
214: if (!(obj instanceof DateRange))
215: return false;
216: DateRange o = (DateRange) obj;
217: return o.lower.getTime() == this .lower.getTime()
218: && o.upper.getTime() == this .upper.getTime();
219: }
220:
221: /**
222: * Returns a hash code value for the object. This method is
223: * supported for the benefit of hashtables such as those provided by
224: * <code>java.util.Hashtable</code>.
225: *
226: * @return a hash code value for this object.
227: * @see Object#equals(Object)
228: * @see java.util.Hashtable
229: */
230: @Override
231: public int hashCode() {
232: return lower.hashCode() + 31 * upper.hashCode();
233: }
234:
235: @Override
236: public String toString() {
237: final SimpleDateFormat sdf = FxFormatUtils.getDateTimeFormat();
238: return sdf.format(lower) + " - " + sdf.format(upper);
239: }
240:
241: }
|