001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.interval;
051:
052: import java.io.IOException;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.io.Serializable;
056: import java.util.ArrayList;
057: import java.util.Collections;
058: import java.util.GregorianCalendar;
059: import java.util.Iterator;
060: import java.util.List;
061:
062: import com.projity.grouping.core.model.NodeModel;
063: import com.projity.grouping.core.model.NodeModelDataFactory;
064: import com.projity.strings.Messages;
065: import com.projity.util.DateTime;
066:
067: /**
068: *
069: */
070: public abstract class ValueObjectForIntervalTable implements
071: NodeModelDataFactory, Serializable, Cloneable {
072: static final long serialVersionUID = 7728399282882L;
073: protected ArrayList valueObjects = new ArrayList();
074: protected String name;
075:
076: public List getList() {
077: return Collections.unmodifiableList(valueObjects);
078: }
079:
080: public ArrayList getValueObjects() { //serialization
081: return valueObjects;
082: }
083:
084: public ValueObjectForIntervalTable() {
085:
086: }
087:
088: public ValueObjectForIntervalTable(String name,
089: ArrayList valueObjects) { //serialization
090: this .name = name;
091: this .valueObjects = valueObjects;
092: }
093:
094: public ValueObjectForIntervalTable(String name) {
095: this .name = name;
096: valueObjects
097: .add(createValueObject(ValueObjectForInterval.NA_TIME)); // put in default one
098: }
099:
100: protected abstract ValueObjectForInterval createValueObject(
101: long date);
102:
103: /**
104: * A factory method returning a new value at a given date
105: * @param start
106: * @return
107: * @throws InvalidValueObjectForIntervalException
108: */
109: public ValueObjectForInterval newValueObject(long start)
110: throws InvalidValueObjectForIntervalException {
111: ValueObjectForInterval newOne = createValueObject(start);
112:
113: int index = Collections.binarySearch(valueObjects, newOne,
114: newOne); // find where to insert
115: if (index < 0) { // if doesn't already exist
116: ValueObjectForInterval previous = (ValueObjectForInterval) valueObjects
117: .get(-index - 2); // get previous element
118: valueObjects.add(-index - 1, newOne); // add new in place
119: newOne.setEnd(previous.getEnd()); //set new one's end to prevous end
120: previous.setEnd(start); // set previous end to this start
121: } else { // not allowed to make duplicate, so send back error
122: throw new InvalidValueObjectForIntervalException(
123: Messages
124: .getString("ValueObjectForIntervalTable.ThatEffectiveDateIsAlreadyInTheTable")); //$NON-NLS-1$
125: }
126: return newOne;
127: }
128:
129: public long getEnd() {
130: long end = 0;
131: Iterator i = valueObjects.iterator();
132: while (i.hasNext()) {
133: end = Math.max(end, ((ValueObjectForInterval) i.next())
134: .getEnd());
135: }
136: return end;
137: }
138:
139: /**
140: * Adjust the start date of a value object. Assure that it is in valid range, and adjust previous element's end as well as this one's start
141: * @param newStart
142: * @param valueObject
143: * @throws InvalidValueObjectForIntervalException
144: */
145: public void adjustStart(long newStart,
146: ValueObjectForInterval valueObject)
147: throws InvalidValueObjectForIntervalException {
148: int index = valueObjects.indexOf(valueObject);
149: if (index == 0)
150: return;
151: ValueObjectForInterval previous = (ValueObjectForInterval) valueObjects
152: .get(index - 1);
153: if (newStart <= previous.getStart())
154: throw new InvalidValueObjectForIntervalException(
155: Messages
156: .getString("ValueObjectForIntervalTable.ThisDateMustBeAfter")); //$NON-NLS-1$
157: if (newStart >= valueObject.getEnd()) // see if this would disappear
158: throw new InvalidValueObjectForIntervalException(
159: Messages
160: .getString("ValueObjectForIntervalTable.ThisDateMustBeBefore")); //$NON-NLS-1$
161:
162: previous.setEnd(newStart);
163: valueObject.setStart(newStart);
164: }
165:
166: public long getStart() {
167: long start = DateTime.getMaxDate().getTime();
168: Iterator i = valueObjects.iterator();
169: while (i.hasNext()) {
170: start = Math.min(start, ((ValueObjectForInterval) i.next())
171: .getStart());
172: }
173: return start;
174: }
175:
176: /**
177: * Remove an entry from the table
178: * @param interval object
179: * @throws InvalidValueObjectForIntervalException if it's the first element
180: */
181: public void remove(ValueObjectForInterval removeMe)
182: throws InvalidValueObjectForIntervalException {
183: if (removeMe.isFirst()) // don't allow removal of first value
184: throw new InvalidValueObjectForIntervalException(
185: Messages
186: .getString("ValueObjectForIntervalTable.YouCannotRemoveTheFirst")); //$NON-NLS-1$
187: int index = valueObjects.indexOf(removeMe);
188: ValueObjectForInterval previous = (ValueObjectForInterval) valueObjects
189: .get(index - 1); // set previous end to this end
190: previous.setEnd(removeMe.getEnd());
191: valueObjects.remove(removeMe);
192: }
193:
194: private int findActiveIndex(long date) {
195: ValueObjectForInterval find = createValueObject(date);
196: int index = Collections.binarySearch(valueObjects, find, find); // find it
197: if (index < 0) // binary search is weird. The element before is -index - 2
198: index = -index - 2; // gets index of element before
199: return index;
200: }
201:
202: /**
203: * Finds the Rate/Availability which is on or before a date
204: * @param date
205: * @return
206: */
207: public ValueObjectForInterval findActive(long date) {
208: return (ValueObjectForInterval) valueObjects
209: .get(findActiveIndex(date));
210: }
211:
212: public ValueObjectForInterval findCurrent() {
213: return findActive(System.currentTimeMillis());
214: }
215:
216: public String getName() {
217: return name;
218: }
219:
220: /**
221: * Create a new entry one year later
222: */
223: public Object createUnvalidatedObject(NodeModel nodeModel,
224: Object parent) {
225: long baseDate = DateTime.midnightToday();
226: ValueObjectForInterval last = (ValueObjectForInterval) valueObjects
227: .get(valueObjects.size() - 1); // get last one
228: baseDate = Math.max(baseDate, last.getStart()); // latest of today or last entry
229: GregorianCalendar cal = DateTime.calendarInstance();
230: cal.setTimeInMillis(baseDate);
231: cal.roll(GregorianCalendar.YEAR, true); // one year later than last one's start or today
232: long date = cal.getTimeInMillis();
233: try {
234: return newValueObject(date);
235: } catch (InvalidValueObjectForIntervalException e) {
236: // TODO Auto-generated catch block
237: e.printStackTrace(); // should not ever happen
238: return null;
239: }
240: }
241:
242: public void addUnvalidatedObject(Object object,
243: NodeModel nodeModel, Object parent) {
244:
245: }
246:
247: public NodeModelDataFactory getFactoryToUseForChildOfParent(
248: Object impl) {
249: return this ;
250: }
251:
252: public void rollbackUnvalidated(NodeModel nodeModel, Object object) {
253: // try {
254: remove(object, nodeModel, false, true, true);
255: // } catch (NodeException e) {
256: // // TODO Auto-generated catch block
257: // e.printStackTrace();
258: // }
259: }
260:
261: public void remove(Object toRemove, NodeModel nodeModel,
262: boolean deep, boolean undo, boolean removeDependencies) {
263: try {
264: remove((ValueObjectForInterval) toRemove);
265: } catch (InvalidValueObjectForIntervalException e) {
266: return;
267: // Alert.error(e.getMessage());
268: // throw new NodeException(e);
269: }
270:
271: }
272:
273: public void validateObject(Object newlyCreated,
274: NodeModel nodeModel, Object eventSource,
275: Object hierarchyInfo, boolean isNew) {
276: }
277:
278: // public void fireCreated(Object newlyCreated){}
279:
280: public void serialize(ObjectOutputStream s) throws IOException {
281: s.writeObject(name);
282: s.writeObject(valueObjects);
283: }
284:
285: protected static ValueObjectForIntervalTable deserialize(
286: ObjectInputStream s, ValueObjectForIntervalTable v)
287: throws IOException, ClassNotFoundException {
288: v.name = (String) s.readObject();
289: v.valueObjects = (ArrayList) s.readObject();
290: return v;
291: }
292:
293: public Object clone() {
294: try {
295: ValueObjectForIntervalTable v = (ValueObjectForIntervalTable) super
296: .clone();
297: v.name = (name == null) ? null : new String(name);
298: ArrayList newList = new ArrayList();
299: for (Iterator i = valueObjects.iterator(); i.hasNext();) {
300: newList
301: .add(((ValueObjectForInterval) i.next())
302: .clone());
303: }
304: v.valueObjects = newList;
305: return v;
306: } catch (CloneNotSupportedException e) {
307: throw new InternalError();
308: }
309: }
310:
311: public void initAfterCloning() {
312: for (Iterator i = valueObjects.iterator(); i.hasNext();) {
313: ((ValueObjectForInterval) i.next()).setTable(this);
314: }
315:
316: }
317:
318: }
|