View Javadoc

1   /*
2    * CommittedOrderHandler.java Created @ Jul 12, 2004
3    * 
4    * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
5    * Delft, the Netherlands. All rights reserved.
6    * 
7    * See for project information <a href="http://www.simulation.tudelft.nl/">
8    * www.simulation.tudelft.nl </a>.
9    * 
10   * The source code and binary code of this software is proprietary information
11   * of Delft University of Technology.
12   */
13  
14  package org.gscg.singleuser.handlers;
15  
16  import java.rmi.RemoteException;
17  import java.util.Calendar;
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.Map;
22  
23  import nl.tudelft.simulation.dsol.experiment.TimeUnit;
24  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
25  import nl.tudelft.simulation.event.Event;
26  import nl.tudelft.simulation.event.EventInterface;
27  import nl.tudelft.simulation.event.EventListenerInterface;
28  import nl.tudelft.simulation.event.EventProducer;
29  import nl.tudelft.simulation.event.EventType;
30  import nl.tudelft.simulation.logger.Logger;
31  
32  import org.gscg.common.gui.exceptions.ReceivedUnknownEventException;
33  import org.gscg.common.interactionlayer.AnnounceInterface;
34  import org.gscg.common.interactionlayer.timecontrol.GlobalRowOrColumnNumber;
35  import org.gscg.game.GameActorContentStore;
36  import org.gscg.gameactors.GameInteractiveActorRoleInterface;
37  import org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface;
38  import org.gscg.singleuser.interactionlayer.dataobjects.CommittedOrderDayData;
39  import org.gscg.singleuser.interactionlayer.dataobjects.CommittedOrderMonthData;
40  import org.gscg.singleuser.interactionlayer.dataobjects.CommittedOrderWeekData;
41  import org.gscg.singleuser.interactionlayer.dataobjects.content.OrderConfirmationData;
42  
43  /***
44   * Updates the number of orders based on the day in the interactive simulation.
45   * Whenever an order is committed to a customer the number is updated at the
46   * client side gui. This handler is used by the SALES PANEL of a client-side
47   * gui.
48   * <p>
49   * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
50   * Delft, the Netherlands. All rights reserved.
51   * 
52   * See for project information <a href="http://www.simulation.tudelft.nl/">
53   * www.simulation.tudelft.nl </a>.
54   * 
55   * The source code and binary code of this software is proprietary information
56   * of Delft University of Technology.
57   * 
58   * @author <a
59   *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
60   *         van Houten </a>
61   * @version $Revision: 1.3 $ $Date: 2005/08/09 15:43:41 $
62   * @since 1.0.0
63   */
64  public class CommittedOrderHandler extends EventProducer implements
65  		EventListenerInterface, AnnounceInterface
66  {
67  	/*** the serial version uid */
68  	private static final long serialVersionUID = 11L;
69  
70  	/*** the event type for the amount of orders per product per day */
71  	public static final EventType EVENT_NUMBER_ORDERCOMMIT_SENT_DAY = new EventType(
72  			"EVENT_NUMBER_ORDERCOMMIT_SENT_DAY");
73  
74  	/*** the event type for the amount of orders per product per week */
75  	public static final EventType EVENT_NUMBER_ORDERCOMMIT_SENT_WEEK = new EventType(
76  			"EVENT_NUMBER_ORDERCOMMIT_SENT_WEEK");
77  
78  	/*** the event type for the amount of orders per product per month */
79  	public static final EventType EVENT_NUMBER_ORDERCOMMIT_SENT_MONTH = new EventType(
80  			"EVENT_NUMBER_ORDERCOMMIT_SENT_MONTH");
81  
82  	/*** the event type to update all the order commitments */
83  	public static final EventType EVENT_NUMBER_ORDERCOMMIT_SENT_UPDATE = new EventType(
84  			"EVENT_NUMBER_ORDERCOMMIT_SENT_UPDATE");
85  
86  	/*** the owner */
87  	private SingleUserInteractionLayerInterface owner = null;
88  
89  	/*** the content store */
90  	private GameActorContentStore contentStore = null;
91  
92  	/*** the product map per day */
93  	private Map productDayMap = new HashMap();
94  
95  	/*** the product map per week */
96  	private Map productWeekMap = new HashMap();
97  
98  	/*** the product map per month */
99  	private Map productMonthMap = new HashMap();
100 
101 	/*** indicates the current day */
102 	private int currentDay = 0;
103 
104 	/*** indicates the current week */
105 	private int currentWeek = 0;
106 
107 	/*** indicates the current month */
108 	private int currentMonth = 0;
109 
110 	/*** used when the runlength of a simulation run spans two calendar years */
111 	private int additionalDays = 0;
112 
113 	/*** used when the runlength of a simulation run spans two calendar years */
114 	private int additionalWeeks = 0;
115 
116 	/*** used when the runlength of a simulation run spans two calendar years */
117 	private int additionalMonths = 0;
118 
119 	/*** the events to subscribe the owner to */
120 	private EventType[] eventsToSubscribeOwnerTo = {
121 			CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_DAY,
122 			CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_WEEK,
123 			CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_MONTH};
124 
125 	/*** the history of the fired events */
126 	private Map history = Collections.synchronizedMap(new HashMap());
127 
128 	/***
129 	 * constructs a new CommittedOrderHandler
130 	 * 
131 	 * @param owner the owner
132 	 */
133 	public CommittedOrderHandler(final SingleUserInteractionLayerInterface owner)
134 	{
135 		super();
136 		this.owner = owner;
137 		try
138 		{
139 			this.contentStore = ((GameInteractiveActorRoleInterface) this.owner
140 					.getOwner()).getGameContentStore();
141 
142 			// set the week and year
143 			Calendar calendar = Calendar.getInstance();
144 			double simTime = this.owner.getSimulator().getSimulatorTime();
145 
146 			// convert the simtime
147 			double convertedTime = TimeUnit.convert(simTime, this.owner
148 					.getSimulator().getReplication().getRunControl()
149 					.getTreatment().getTimeUnit(),
150 					TimeUnitInterface.MILLISECOND);
151 
152 			double startTime = this.owner.getSimulator().getReplication()
153 					.getRunControl().getTreatment().getStartTime();
154 			calendar.setTimeInMillis((long) (convertedTime + startTime));
155 		} catch (RemoteException remoteException)
156 		{
157 			Logger.severe(this, "CommittedOrderHandler", remoteException);
158 		}
159 
160 		// add requestForQuotes
161 		this.contentStore.addListener(this,
162 				GameActorContentStore.EVENT_ORDERCOMMIT_SENT, true);
163 
164 		try
165 		{
166 			// subscribe the owner to events fired from this object
167 			// also add the necessary announce events
168 			for (int i = 0; i < this.eventsToSubscribeOwnerTo.length; i++)
169 			{
170 				this.addListener(this.owner, this.eventsToSubscribeOwnerTo[i]);
171 				this.owner.addEventType(this.eventsToSubscribeOwnerTo[i]);
172 			}
173 			this.owner.addEventTypeToAnnounceList(
174 					CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_UPDATE,
175 					this);
176 		} catch (RemoteException remoteException)
177 		{
178 			Logger.severe(this, "<init>", remoteException);
179 		}
180 
181 		// initialize the history
182 		this.history.put(new String("day"), new HashMap());
183 		this.history.put(new String("week"), new HashMap());
184 		this.history.put(new String("month"), new HashMap());
185 	}
186 
187 	/***
188 	 * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
189 	 */
190 	public void notify(final EventInterface event) throws RemoteException
191 	{
192 		if (event.getType()
193 				.equals(GameActorContentStore.EVENT_ORDERCOMMIT_SENT))
194 		{
195 			OrderConfirmationData data = (OrderConfirmationData) event
196 					.getContent();
197 			Calendar calendar = Calendar.getInstance();
198 			calendar.set(data.getProposedShippingDate().getYear(), data
199 					.getProposedShippingDate().getMonth(), data
200 					.getProposedShippingDate().getDay());
201 
202 			Calendar startCalendar = Calendar.getInstance();
203 			startCalendar.setTimeInMillis(GlobalRowOrColumnNumber
204 					.getStartTime());
205 
206 			// calculcate difference between startCalendar and order date
207 			int yearDiffence = calendar.get(Calendar.YEAR)
208 					- startCalendar.get(Calendar.YEAR);
209 			// reset old additional values
210 			this.additionalDays = 0;
211 			this.additionalWeeks = 0;
212 			this.additionalMonths = 0;
213 
214 			while (yearDiffence > 0)
215 			{
216 				startCalendar.set(Calendar.YEAR, startCalendar
217 						.get(Calendar.YEAR) + 1);
218 				this.additionalDays = startCalendar
219 						.getActualMaximum(Calendar.DAY_OF_YEAR);
220 				this.additionalWeeks += 52;
221 				this.additionalMonths += 12;
222 
223 				yearDiffence--;
224 			}
225 
226 			// handle per day
227 			this.handlePerDay(data, calendar);
228 
229 			// handle per week
230 			this.handlePerWeek(data, calendar);
231 
232 			// handle per month
233 			this.handlePerMonth(data, calendar);
234 
235 			return;
236 		}
237 		new ReceivedUnknownEventException(this, "notify", event.getType());
238 	}
239 
240 	/***
241 	 * handles an orderconfirmation per day
242 	 * 
243 	 * @param data the data
244 	 * @param calendar the calendar
245 	 */
246 	private void handlePerDay(final OrderConfirmationData data,
247 			final Calendar calendar)
248 	{
249 		int day = calendar.get(Calendar.DAY_OF_YEAR);
250 
251 		// we compensate extra years
252 		day += this.additionalDays;
253 
254 		if (this.currentDay != day)
255 		{
256 			this.currentDay = day;
257 		}
258 
259 		double value = Double.NaN;
260 		if (!this.productDayMap.containsKey(data.getProductName()))
261 		{
262 			Map map = new HashMap();
263 			map.put("" + this.currentDay, new Double(data.getAmount()));
264 			this.productDayMap.put(data.getProductName(), map);
265 			value = data.getAmount();
266 		} else
267 		{
268 			Map map = (Map) this.productDayMap.get(data.getProductName());
269 
270 			if (map.containsKey("" + this.currentDay))
271 			{
272 				value = ((Double) map.get("" + this.currentDay)).doubleValue();
273 				value += data.getAmount();
274 				map.put("" + this.currentDay, new Double(value));
275 			} else
276 			{
277 				value = data.getAmount();
278 				map.put("" + this.currentDay, new Double(value));
279 			}
280 		}
281 
282 		// lookup table row
283 		try
284 		{
285 			int rowNumber = 0;
286 			try
287 			{
288 				rowNumber = ((Integer) GlobalRowOrColumnNumber
289 						.getDayNumberTableRowNumber().get(new Integer(day)))
290 						.intValue();
291 			} catch (NullPointerException nullPointerException)
292 			{
293 				if (this.dateWithinRange(data))
294 				{
295 					Logger.severe(this, "handlePerDay",
296 							"Date should be in range, but no row found.");
297 				} else
298 				{
299 					// set the scope outside the game
300 					rowNumber = GlobalRowOrColumnNumber.getDayHeaderValues()
301 							.size() + 10;
302 				}
303 			}
304 
305 			Event event = new Event(
306 					CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_DAY,
307 					this, new CommittedOrderDayData(data.getProductName(),
308 							rowNumber, value));
309 			this.fireEvent(event);
310 			this.addToHistory("day", day, data.getProductName(), event);
311 		} catch (Exception exception)
312 		{
313 			Logger.severe(this, "handlePerDay", exception);
314 		}
315 	}
316 
317 	/***
318 	 * handles an orderconfirmation per week
319 	 * 
320 	 * @param data the data
321 	 * @param calendar the calendar
322 	 */
323 	private void handlePerWeek(final OrderConfirmationData data,
324 			final Calendar calendar)
325 	{
326 		int week = (calendar.get(Calendar.WEEK_OF_YEAR));
327 
328 		// we compensate extra years
329 		week += this.additionalWeeks;
330 
331 		if (this.currentWeek != week)
332 		{
333 			this.currentWeek = week;
334 		}
335 
336 		double value = Double.NaN;
337 		if (!this.productWeekMap.containsKey(data.getProductName()))
338 		{
339 			Map map = new HashMap();
340 			map.put("" + this.currentWeek, new Double(data.getAmount()));
341 			this.productWeekMap.put(data.getProductName(), map);
342 			value = data.getAmount();
343 		} else
344 		{
345 			Map map = (Map) this.productWeekMap.get(data.getProductName());
346 
347 			if (map.containsKey("" + this.currentWeek))
348 			{
349 				value = ((Double) map.get("" + this.currentWeek)).doubleValue();
350 				value += data.getAmount();
351 				map.put("" + this.currentWeek, new Double(value));
352 			} else
353 			{
354 				value = data.getAmount();
355 				map.put("" + this.currentWeek, new Double(value));
356 			}
357 		}
358 
359 		// lookup table row
360 		try
361 		{
362 			int rowNumber = 0;
363 			try
364 			{
365 				rowNumber = ((Integer) GlobalRowOrColumnNumber
366 						.getWeekNumberTableRowNumber().get(new Integer(week)))
367 						.intValue();
368 			} catch (NullPointerException nullPointerException)
369 			{
370 				if (this.dateWithinRange(data))
371 				{
372 					Logger.severe(this, "handlePerWeek",
373 							"Date should be in range, but no row found.");
374 				} else
375 				{
376 					// set the scope outside the game
377 					rowNumber = GlobalRowOrColumnNumber.getWeekHeaderValues()
378 							.size() + 10;
379 				}
380 			}
381 			if (value >= 0)
382 			{
383 				Event event = new Event(
384 						CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_WEEK,
385 						this, new CommittedOrderWeekData(data.getProductName(),
386 								rowNumber, value));
387 				this.fireEvent(event);
388 				this.addToHistory("week", week, data.getProductName(), event);
389 			} else
390 			{
391 				Logger.warning(this, "handlePerWeek", "Value equals: " + value
392 						+ ", where it should be >= 0.");
393 			}
394 		} catch (Exception exception)
395 		{
396 			Logger.severe(this, "handlePerWeek", exception);
397 		}
398 	}
399 
400 	/***
401 	 * handles an orderconfirmation per month
402 	 * 
403 	 * @param data the data
404 	 * @param calendar the calendar
405 	 */
406 	private void handlePerMonth(final OrderConfirmationData data,
407 			final Calendar calendar)
408 	{
409 		int month = calendar.get(Calendar.MONTH);
410 
411 		// we compensate extra years
412 		month += this.additionalMonths;
413 
414 		if (this.currentMonth != month)
415 		{
416 			this.currentMonth = month;
417 		}
418 
419 		double value = Double.NaN;
420 		if (!this.productMonthMap.containsKey(data.getProductName()))
421 		{
422 			Map map = new HashMap();
423 			map.put("" + this.currentMonth, new Double(data.getAmount()));
424 			this.productMonthMap.put(data.getProductName(), map);
425 			value = data.getAmount();
426 		} else
427 		{
428 			Map map = (Map) this.productMonthMap.get(data.getProductName());
429 
430 			if (map.containsKey("" + this.currentMonth))
431 			{
432 				value = ((Double) map.get("" + this.currentMonth))
433 						.doubleValue();
434 				value += data.getAmount();
435 				map.put("" + this.currentMonth, new Double(value));
436 			} else
437 			{
438 				value = data.getAmount();
439 				map.put("" + this.currentMonth, new Double(value));
440 			}
441 		}
442 
443 		// lookup table row
444 		try
445 		{
446 			int rowNumber = 0;
447 			try
448 			{
449 				rowNumber = ((Integer) GlobalRowOrColumnNumber
450 						.getMonthNumberTableRowNumber().get(new Integer(month)))
451 						.intValue();
452 			} catch (NullPointerException nullPointerException)
453 			{
454 				if (this.dateWithinRange(data))
455 				{
456 					Logger.severe(this, "handlePerMonth",
457 							"Date should be in range, but no row found.");
458 				} else
459 				{
460 					// set the scope outside the game
461 					rowNumber = GlobalRowOrColumnNumber.getMonthHeaderValues()
462 							.size() + 10;
463 				}
464 			}
465 			Event event = new Event(
466 					CommittedOrderHandler.EVENT_NUMBER_ORDERCOMMIT_SENT_MONTH,
467 					this, new CommittedOrderMonthData(data.getProductName(),
468 							rowNumber, value));
469 			this.fireEvent(event);
470 			this.addToHistory("month", month, data.getProductName(), event);
471 		} catch (Exception exception)
472 		{
473 			Logger.severe(this, "handlePerMonth", exception);
474 		}
475 	}
476 
477 	/***
478 	 * @see org.gscg.common.interactionlayer.AnnounceInterface#announce(nl.tudelft.simulation.event.EventType,
479 	 *      boolean)
480 	 */
481 	public void announce(final EventType eventType, final boolean announce)
482 	{
483 		if (announce)
484 		{
485 			for (Iterator i = this.history.keySet().iterator(); i.hasNext();)
486 			{
487 				HashMap dateMap = (HashMap) this.history.get(i.next());
488 				for (Iterator ii = dateMap.keySet().iterator(); ii.hasNext();)
489 				{
490 					HashMap productMap = (HashMap) dateMap.get(ii.next());
491 					for (Iterator iii = productMap.keySet().iterator(); iii
492 							.hasNext();)
493 					{
494 						try
495 						{
496 							this.owner
497 									.notifyAnnounced((EventInterface) productMap
498 											.get(iii.next()));
499 						} catch (RemoteException remoteException)
500 						{
501 							Logger.severe(this, "announce", remoteException);
502 						}
503 					}
504 				}
505 			}
506 		}
507 	}
508 
509 	/***
510 	 * Method addToHistory.
511 	 * 
512 	 * @param dateType a string reflecting the date type, e.g. day, week or
513 	 *        month
514 	 * @param key the key, i.e. the number of the day, week or month
515 	 * @param productName the name of the product the confirmed order is
516 	 *        received for
517 	 * @param event the event to store in the history
518 	 */
519 	private void addToHistory(final String dateType, final int key,
520 			final String productName, final Event event)
521 	{
522 		HashMap dateMap = (HashMap) this.history.get(dateType);
523 		if (dateMap == null)
524 		{
525 			dateMap = new HashMap();
526 			this.history.put(dateType, dateMap);
527 		}
528 
529 		HashMap productMap = (HashMap) dateMap.get(productName);
530 		if (productMap == null)
531 		{
532 			productMap = new HashMap();
533 			dateMap.put(productName, productMap);
534 		}
535 		productMap.put(new Integer(key), event);
536 	}
537 
538 	/***
539 	 * calculates whether a date is within range
540 	 * 
541 	 * @param data the data
542 	 * @return returns false if the date is not within range, true otherwise
543 	 */
544 	private boolean dateWithinRange(final OrderConfirmationData data)
545 	{
546 		Calendar startCalendar = Calendar.getInstance();
547 		startCalendar.setTimeInMillis(GlobalRowOrColumnNumber.getStartTime());
548 
549 		Calendar endCalendar = Calendar.getInstance();
550 		endCalendar.set(data.getProposedDelivery().getYear(), data
551 				.getProposedDelivery().getMonth(), data.getProposedDelivery()
552 				.getDay());
553 
554 		long runLength = GlobalRowOrColumnNumber.getNumberOfDays()
555 				* TimeUnitInterface.DAY.getValue();
556 		if ((endCalendar.getTimeInMillis() - startCalendar.getTimeInMillis()) > runLength)
557 		{
558 			return false;
559 		}
560 		return true;
561 	}
562 }