View Javadoc

1   /*
2    * @(#) IntrospectionUtil.java Jan 24, 2005
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  package org.gscg.gameleader.interactionlayer.util;
14  
15  import java.io.Serializable;
16  import java.lang.reflect.Field;
17  import java.text.DateFormat;
18  import java.util.ArrayList;
19  import java.util.Calendar;
20  import java.util.List;
21  
22  import nl.tudelft.simulation.actor.ActorInterface;
23  import nl.tudelft.simulation.dsol.experiment.TimeUnit;
24  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
25  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
26  import nl.tudelft.simulation.language.reflection.ClassUtil;
27  import nl.tudelft.simulation.logger.Logger;
28  import nl.tudelft.simulation.supplychain.actor.SupplyChainActor;
29  import nl.tudelft.simulation.supplychain.actor.Trader;
30  import nl.tudelft.simulation.supplychain.banking.BankAccount;
31  import nl.tudelft.simulation.supplychain.content.Bill;
32  import nl.tudelft.simulation.supplychain.content.Content;
33  import nl.tudelft.simulation.supplychain.content.Order;
34  import nl.tudelft.simulation.supplychain.content.OrderBasedOnQuote;
35  import nl.tudelft.simulation.supplychain.content.OrderConfirmation;
36  import nl.tudelft.simulation.supplychain.content.Payment;
37  import nl.tudelft.simulation.supplychain.content.Quote;
38  import nl.tudelft.simulation.supplychain.content.RequestForQuote;
39  import nl.tudelft.simulation.supplychain.content.Shipment;
40  import nl.tudelft.simulation.supplychain.product.Product;
41  //import nl.tudelft.simulation.supplychain.reference.Manufacturer;
42  import org.gscg.gameactors.GameDistributorInteractive;
43  import org.gscg.gameleader.animation2D.mouse.IntrospectedFieldData;
44  import org.gscg.gameleader.animation2D.mouse.IntrospectionData;
45  
46  /***
47   * Utility class for introspection. This class contains methods which either
48   * introspect an object and return date for the fields, or updates the values of
49   * atributes based on changes made to an object using the introspection diaglog
50   * of an animation panel.
51   * <p>
52   * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
53   * Delft, the Netherlands. All rights reserved.
54   * 
55   * See for project information <a href="http://www.simulation.tudelft.nl/">
56   * www.simulation.tudelft.nl </a>.
57   * 
58   * The source code and binary code of this software is proprietary information
59   * of Delft University of Technology.
60   * 
61   * @author <a
62   *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
63   *         van Houten </a>
64   * @version $Revision: 1.3 $ $Date: 2005/08/10 11:23:30 $
65   * @since 1.1.2
66   */
67  public final class IntrospectionUtil implements Serializable
68  {
69  	/*** the serial version uid */
70  	private static final long serialVersionUID = 11L;
71  
72  	/*** the balance field */
73  	private static Field balanceField = null;
74  
75  	/*** the calendar */
76  	private static Calendar calendar = Calendar.getInstance();
77  
78  	/*** the date formatter */
79  	private static DateFormat dateFormat = DateFormat.getDateInstance();
80  
81  	/*** the start time of the simulator */
82  	private static long startTime = 0L;
83  
84  	/*** the time unit of the simulator */
85  	private static TimeUnitInterface timeUnit = null;
86  
87  	/*** initialize the field */
88  	static
89  	{
90  		try
91  		{
92  			IntrospectionUtil.balanceField = ClassUtil.resolveField(
93  					BankAccount.class, "balance");
94  			balanceField.setAccessible(true);
95  		} catch (Exception exception)
96  		{
97  			Logger.severe(IntrospectionUtil.class, "<class>", exception);
98  		}
99  	}
100 
101 	/***
102 	 * constructs a new IntrospectionUtil
103 	 */
104 	private IntrospectionUtil()
105 	{
106 		// Utility classes should not be instantiated
107 		super();
108 	}
109 
110 	/***
111 	 * Introspects the given object
112 	 * 
113 	 * @param object the object to get the data from
114 	 * @param key the key
115 	 * @return Returns an array of introspection data or null if there is no
116 	 *         logic to process the object
117 	 */
118 	public static IntrospectionData introspectObject(final Object object,
119 			final Serializable key)
120 	{
121 		if (object != null)
122 		{
123 			List fields = new ArrayList();
124 			String name = null;
125 			// determine the class of the object
126 			if (object instanceof ActorInterface)
127 			{
128 				name = "Dialog for actor: "
129 						+ ((ActorInterface) object).getName();
130 				if (GameDistributorInteractive.class.isAssignableFrom(object
131 								.getClass())
132 						|| Trader.class.isAssignableFrom(object.getClass()))
133 				{
134 					IntrospectionUtil.introspectSupplyChainActor(
135 							(SupplyChainActor) object, fields);
136 					// add some stock info
137 
138 					// add info field
139 					// the ## are used to indicate that we are dealing with an
140 					// info field
141 					fields.add(new IntrospectedFieldData("Stock Info", "##",
142 							false));
143 					List products = ((Trader) object).getProductsOnStock();
144 					for (int i = 0; i < products.size(); i++)
145 					{
146 						fields.add(new IntrospectedFieldData("Product: "
147 								+ ((Product) products.get(i)).getName(),
148 								new Double(((Trader) object).getStock()
149 										.getActualAmount(
150 												(Product) products.get(i))),
151 								true));
152 					}
153 				} else
154 				{
155 					IntrospectionUtil.introspectSupplyChainActor(
156 							(SupplyChainActor) object, fields);
157 				}
158 			} else if (object instanceof Content)
159 			{
160 				IntrospectionUtil.introspectContent((Content) object, fields);
161 				if (RequestForQuote.class.isAssignableFrom(object.getClass()))
162 				{
163 					name = "Dialog for Request for Quote";
164 					RequestForQuote rfq = (RequestForQuote) object;
165 					IntrospectionUtil.introspectRFQ(rfq, fields);
166 				} else if (Quote.class.isAssignableFrom(object.getClass()))
167 				{
168 					name = "Dialog for Quote";
169 					Quote quote = (Quote) object;
170 					IntrospectionUtil.introspectRFQ(quote.getRequestForQuote(),
171 							fields);
172 					IntrospectionUtil.introspectQuote(quote, fields);
173 				} else if (OrderBasedOnQuote.class.isAssignableFrom(object
174 						.getClass()))
175 				{
176 					name = "Dialog for Order";
177 					OrderBasedOnQuote order = (OrderBasedOnQuote) object;
178 					IntrospectionUtil.introspectRFQ(order.getQuote()
179 							.getRequestForQuote(), fields);
180 					IntrospectionUtil.introspectQuote(order.getQuote(), fields);
181 					IntrospectionUtil.introspectOrder(order, fields);
182 				} else if (OrderConfirmation.class.isAssignableFrom(object
183 						.getClass()))
184 				{
185 					name = "Dialog for Order Confirmation";
186 					OrderConfirmation confirmation = (OrderConfirmation) object;
187 					IntrospectionUtil.introspectRFQ(
188 							((OrderBasedOnQuote) confirmation.getOrder())
189 									.getQuote().getRequestForQuote(), fields);
190 					IntrospectionUtil.introspectQuote(
191 							((OrderBasedOnQuote) confirmation.getOrder())
192 									.getQuote(), fields);
193 					IntrospectionUtil.introspectOrder(
194 							(confirmation.getOrder()), fields);
195 					IntrospectionUtil.introspectOrderConfirmation(
196 							(confirmation), fields);
197 				} else if (Shipment.class.isAssignableFrom(object.getClass()))
198 				{
199 					name = "Dialog for Shipment";
200 					Shipment shipment = (Shipment) object;
201 					IntrospectionUtil.introspectRFQ(
202 							((OrderBasedOnQuote) shipment.getOrder())
203 									.getQuote().getRequestForQuote(), fields);
204 					IntrospectionUtil.introspectQuote(
205 							((OrderBasedOnQuote) shipment.getOrder())
206 									.getQuote(), fields);
207 					IntrospectionUtil.introspectOrder((shipment.getOrder()),
208 							fields);
209 				} else if (Bill.class.isAssignableFrom(object.getClass()))
210 				{
211 					name = "Dialog for Bill";
212 					Bill bill = (Bill) object;
213 					IntrospectionUtil.introspectRFQ(((OrderBasedOnQuote) bill
214 							.getOrder()).getQuote().getRequestForQuote(),
215 							fields);
216 					IntrospectionUtil.introspectQuote(((OrderBasedOnQuote) bill
217 							.getOrder()).getQuote(), fields);
218 					IntrospectionUtil
219 							.introspectOrder((bill.getOrder()), fields);
220 					IntrospectionUtil.introspectBill(bill, fields);
221 				} else if (Payment.class.isAssignableFrom(object.getClass()))
222 				{
223 					name = "Dialog for Payment";
224 					Payment payment = (Payment) object;
225 					IntrospectionUtil.introspectRFQ(
226 							((OrderBasedOnQuote) payment.getBill().getOrder())
227 									.getQuote().getRequestForQuote(), fields);
228 					IntrospectionUtil.introspectQuote(
229 							((OrderBasedOnQuote) payment.getBill().getOrder())
230 									.getQuote(), fields);
231 					IntrospectionUtil.introspectOrder((payment.getBill()
232 							.getOrder()), fields);
233 					IntrospectionUtil.introspectBill(payment.getBill(), fields);
234 					IntrospectionUtil.introspectPayment(payment, fields);
235 				}
236 			} else
237 			{
238 				Logger.severe(IntrospectionUtil.class, "introspectObject",
239 						"No logic to introspect object of class:  "
240 								+ object.getClass());
241 				return null;
242 			}
243 
244 			return new IntrospectionData(key, (IntrospectedFieldData[]) fields
245 					.toArray(new IntrospectedFieldData[fields.size()]), name);
246 		}
247 		return null;
248 	}
249 
250 	/***
251 	 * Method updateIntrospectedObject updates an introspected object based on
252 	 * the given data.
253 	 * 
254 	 * @param object the object to update
255 	 * @param data the data
256 	 */
257 	public static void updateIntrospectedObject(final Object object,
258 			final IntrospectionData data)
259 	{
260 		if (object != null)
261 		{
262 			IntrospectedFieldData[] fieldData = data.getData();
263 			for (int i = 0; i < fieldData.length; i++)
264 			{
265 				if (fieldData[i].getName().equalsIgnoreCase("Balance"))
266 				{
267 					// resolve bank account balance field
268 					BankAccount account = ((SupplyChainActor) object)
269 							.getBankAccount();
270 					try
271 					{
272 						double value = ((Number) (fieldData[i].getValue()))
273 								.doubleValue();
274 						value = 0.01 * Math.round(100.0 * value);
275 						IntrospectionUtil.balanceField.set(account, new Double(
276 								value));
277 						account.sendBalanceUpdateEvent();
278 					} catch (Exception exception)
279 					{
280 						exception.printStackTrace();
281 						Logger.severe(IntrospectionUtil.class,
282 								"updateIntrospectedObject", exception);
283 					}
284 				} else if (fieldData[i].getName().startsWith("Product: "))
285 				{
286 					String[] split = fieldData[i].getName().split(": ");
287 					// the second part of the split contains the product name
288 					String productName = split[1];
289 
290 					// look for the product
291 					List products = ((Trader) object).getProductsOnStock();
292 					for (int ii = 0; ii < products.size(); ii++)
293 					{
294 						Product product = (Product) products.get(ii);
295 						if (product.getName().equalsIgnoreCase(productName))
296 						{
297 							double amount = ((Number) fieldData[i].getValue())
298 									.doubleValue();
299 							double currentAmount = ((Trader) object)
300 									.getStock().getActualAmount(product);
301 
302 							// only change if there is a change
303 							// otherwise a divide by 0 occurs
304 							if (amount != currentAmount && amount >= 0)
305 							{
306 								((Trader) object).getStock().addStock(
307 										product,
308 										amount - currentAmount,
309 										product.getUnitMarketPrice()
310 												* (amount - currentAmount));
311 								// send an update event
312 								((Trader) object).getStock()
313 										.sendStockUpdateEvent(product);
314 							}
315 							break;
316 						}
317 					}
318 				} else
319 				{
320 					Logger.warning(IntrospectionUtil.class,
321 							"updateIntrospectedObject",
322 							"IntrospectionUtil: unknown field"
323 									+ fieldData[i].getName());
324 				}
325 			}
326 		} else
327 		{
328 			Logger.severe(IntrospectionUtil.class, "updateIntrospectedObject",
329 					"Introspected object to update is null");
330 		}
331 	}
332 
333 	//
334 	// private methods
335 	//
336 
337 	/***
338 	 * Introspects a supply chain actor object and adds fields to the list
339 	 * 
340 	 * @param actor the actor
341 	 * @param fields the list with fields to edit
342 	 */
343 	private static void introspectSupplyChainActor(
344 			final SupplyChainActor actor, final List fields)
345 	{
346 		// the ## are used to indicate that we are dealing with an info field
347 		fields.add(new IntrospectedFieldData("General Info", "##", false));
348 		fields.add(new IntrospectedFieldData("Name", actor.getName(), false));
349 		fields.add(new IntrospectedFieldData("Location", "lat: "
350 				+ actor.getLocation().y + " lon: " + actor.getLocation().x,
351 				false));
352 		fields.add(new IntrospectedFieldData("Place", actor
353 				.getLocationDescription(), false));
354 		// the ## are used to indicate that we are dealing with an info field
355 		fields.add(new IntrospectedFieldData("Balance Info", "##", false));
356 		fields.add(new IntrospectedFieldData("Balance", new Double(actor
357 				.getBankAccount().getBalance()), true));
358 	}
359 
360 	/***
361 	 * Introspects a content object and adds fields to the list
362 	 * 
363 	 * @param content the content
364 	 * @param fields the list with fields to edit
365 	 */
366 	private static void introspectContent(final Content content,
367 			final List fields)
368 	{
369 		// the ## are used to indicate that we are dealing with an info field
370 		fields.add(new IntrospectedFieldData("General Info", "##", false));
371 		fields.add(new IntrospectedFieldData("Sender", content.getSender()
372 				.getName(), false));
373 		fields.add(new IntrospectedFieldData("Sender place", content
374 				.getSender().getLocationDescription(), false));
375 		fields.add(new IntrospectedFieldData("Receiver", content.getReceiver()
376 				.getName(), false));
377 		fields.add(new IntrospectedFieldData("Receiver place", content
378 				.getReceiver().getLocationDescription(), false));
379 		fields.add(new IntrospectedFieldData("Product", content.getProduct()
380 				.getName(), false));
381 	}
382 
383 	/***
384 	 * Introspects a rfq object and adds fields to the list
385 	 * 
386 	 * @param rfq the request for quote
387 	 * @param fields the list with fields to edit
388 	 */
389 	private static void introspectRFQ(final RequestForQuote rfq,
390 			final List fields)
391 	{
392 		// the ## are used to indicate that we are dealing with an info field
393 		fields.add(new IntrospectedFieldData("Request for Quote Info", "##",
394 				false));
395 		fields.add(new IntrospectedFieldData("Amount", new Double(rfq
396 				.getAmount()), false));
397 		fields.add(new IntrospectedFieldData("Earliest delivery date",
398 				IntrospectionUtil.formatTime(rfq.getSender().getSimulator(),
399 						rfq.getEarliestDeliveryDate()), false));
400 		fields.add(new IntrospectedFieldData("Latest delivery date",
401 				IntrospectionUtil.formatTime(rfq.getSender().getSimulator(),
402 						rfq.getLatestDeliveryDate()), false));
403 	}
404 
405 	/***
406 	 * Introspects a quote object and adds fields to the list
407 	 * 
408 	 * @param quote the quote
409 	 * @param fields the list with fields to edit
410 	 */
411 	private static void introspectQuote(final Quote quote, final List fields)
412 	{
413 		// the ## are used to indicate that we are dealing with an info field
414 		fields.add(new IntrospectedFieldData("Quote Info", "##", false));
415 		fields.add(new IntrospectedFieldData("Proposed amount", new Double(
416 				quote.getAmount()), false));
417 		fields.add(new IntrospectedFieldData("Proposed delivery date",
418 				IntrospectionUtil.formatTime(quote.getSender().getSimulator(),
419 						quote.getProposedDeliveryDate()), false));
420 		fields.add(new IntrospectedFieldData("Proposed price", new Double(quote
421 				.getPrice()), false));
422 	}
423 
424 	/***
425 	 * Introspects an order object and adds fields to the list
426 	 * 
427 	 * @param order the order
428 	 * @param fields the list with fields to edit
429 	 */
430 	private static void introspectOrder(final Order order, final List fields)
431 	{
432 		// the ## are used to indicate that we are dealing with an info field
433 		fields.add(new IntrospectedFieldData("Order Info", "##", false));
434 		fields.add(new IntrospectedFieldData("Amount", new Double(order
435 				.getAmount()), false));
436 		fields.add(new IntrospectedFieldData("Delivery date", IntrospectionUtil
437 				.formatTime(order.getSender().getSimulator(), order
438 						.getDeliveryDate()), false));
439 	}
440 
441 	/***
442 	 * Introspects an order confirmation object and adds fields to the list
443 	 * 
444 	 * @param orderConfirmation the order confirmation
445 	 * @param fields the list with fields to edit
446 	 */
447 	private static void introspectOrderConfirmation(
448 			final OrderConfirmation orderConfirmation, final List fields)
449 	{
450 		// the ## are used to indicate that we are dealing with an info field
451 		fields.add(new IntrospectedFieldData("Order Confirmation Info", "##",
452 				false));
453 		fields.add(new IntrospectedFieldData("Status", orderConfirmation
454 				.getStatusString(), false));
455 	}
456 
457 	/***
458 	 * Introspects a bill object and adds fields to the list
459 	 * 
460 	 * @param bill the bill
461 	 * @param fields the list with fields to edit
462 	 */
463 	private static void introspectBill(final Bill bill, final List fields)
464 	{
465 		// the ## are used to indicate that we are dealing with an info field
466 		fields.add(new IntrospectedFieldData("Bill Info", "##", false));
467 		fields.add(new IntrospectedFieldData("Price", new Double(bill
468 				.getPrice()), false));
469 	}
470 
471 	/***
472 	 * Introspects a payment object and adds fields to the list
473 	 * 
474 	 * @param payment the payment
475 	 * @param fields the list with fields to edit
476 	 */
477 	private static void introspectPayment(final Payment payment,
478 			final List fields)
479 	{
480 		// the ## are used to indicate that we are dealing with an info field
481 		fields.add(new IntrospectedFieldData("Payment Info", "##", false));
482 		fields.add(new IntrospectedFieldData("Price", new Double(payment
483 				.getPayment()), false));
484 	}
485 
486 	/***
487 	 * Method formatTime formats the given time to a String representing the
488 	 * date.
489 	 * 
490 	 * @param simulator the simulator to use
491 	 * @param time the time
492 	 * @return returns a string representing the date
493 	 */
494 	private static String formatTime(final SimulatorInterface simulator,
495 			final double time)
496 	{
497 		try
498 		{
499 			if (IntrospectionUtil.startTime == 0L)
500 			{
501 				IntrospectionUtil.startTime = simulator.getReplication()
502 						.getRunControl().getTreatment().getStartTime();
503 			}
504 
505 			if (IntrospectionUtil.timeUnit == null)
506 			{
507 				IntrospectionUtil.timeUnit = simulator.getReplication()
508 						.getRunControl().getTreatment().getTimeUnit();
509 			}
510 
511 			// convert the time to milliseconds
512 			double timeInMilliSeconds = TimeUnit.convert(time, timeUnit,
513 					TimeUnitInterface.MILLISECOND);
514 
515 			IntrospectionUtil.calendar
516 					.setTimeInMillis(IntrospectionUtil.startTime
517 							+ (long) timeInMilliSeconds);
518 		} catch (Exception exception)
519 		{
520 			Logger.severe(IntrospectionUtil.class, "formatTime", exception);
521 		}
522 		return IntrospectionUtil.dateFormat.format(IntrospectionUtil.calendar
523 				.getTime());
524 	}
525 }