001 package market; 002 003 import java.util.Iterator; 004 import java.util.TreeMap; 005 006 import market.event.OfferEventListener; 007 import users.UserManager; 008 import data.CountingStock; 009 import data.StockItem; 010 import data.events.VetoException; 011 import data.ooimpl.CountingStockImpl; 012 import data.ooimpl.StockItemImpl; 013 014 /** 015 * A StockItemImpl that is used as a representation of a {@link UCustomer}. 016 * It can be used to add it to an instance of {@link SSListenable} which represents 017 * a waiting-queue at the tills or at the warehouse. 018 * The implementation of {@link OfferEventListener} makes it possible to inform about the whole 019 * inventory of the market this is useful if shortages occurred, 020 * the only correction of the {@link CSOffer} wouldn't be suffice because already selled articles and 021 * those in the shopping-basket of customers aren`t in it anymore. 022 */ 023 public class SICustomer extends StockItemImpl implements OfferEventListener{ 024 025 /** 026 * ID for serialization. 027 */ 028 private static final long serialVersionUID = 6083299542075784885L; 029 030 /** 031 * A short message of what articles can`t get sold. 032 * Only important at the till-queue. 033 */ 034 private String missingArticles = new String(""); 035 036 /** 037 * The time since this SICustomer exist 038 * used to prove when this SICustomer arrived in the Stock. 039 * Only important at the till-queue. 040 */ 041 private Long l_tillTime; 042 043 /** 044 * A TreeMap containing all orders which aren`t yet processed. 045 */ 046 private TreeMap<Long, CSOrder> tm_orders = new TreeMap<Long, CSOrder>(); 047 048 /** 049 * The monitor used to synchronize access to the TreeMap of CSOrders. 050 */ 051 private transient Object o_ordersLock; 052 053 /** 054 * Stores to which Stock this SICustomer belongs to. 055 */ 056 private String queue; 057 058 059 //################################### Constructor ########################################### 060 061 /** 062 * @param customer name of the {@link UCustomer} as the new ID, all keys of the UCustomers 063 * are stored in a CatalogImpl. 064 */ 065 public SICustomer(String customer){ 066 super(customer); 067 l_tillTime = new Long(System.currentTimeMillis()); 068 } 069 070 /** 071 * Adds a new {@link CSOrder} to the order-set. 072 * 073 * @param cs the StockItems of this CountingStock will be added to the new CSOrder. 074 * @param active if true the new CSOrder will be active, otherwise not. 075 */ 076 public void addOrderToQueue(CountingStock cs, boolean active){ 077 CSOrder cso = CSOrder.create(this.getName(), active); 078 cso.addStock(cs, null, true); 079 synchronized(getOrderLock()){ 080 tm_orders.put(cso.getTime(),cso); 081 } 082 SMarket.fireUpdateWorkerScreen(); 083 } 084 085 /** 086 * @return the oldest, active {@link CSOrder} of this SICustomer and removes it from the order-set. 087 */ 088 public CSOrder removeOrderFromQueue(){ 089 CSOrder cs = null; 090 synchronized(getOrderLock()){ 091 Iterator it = tm_orders.values().iterator(); 092 while(it.hasNext()){ 093 cs = (CSOrder)it.next(); 094 if(cs.isActive()){ 095 tm_orders.remove(cs.getTime()); 096 break; 097 } 098 } 099 } 100 SMarket.fireUpdateWorkerScreen(); 101 return cs; 102 } 103 104 /** 105 * Puts a given {@link CSOrder} back to the order-set of this SICustomer. 106 * 107 * @param order the CSOrder that will be put back. 108 */ 109 public void rollbackOrder(CSOrder order){ 110 synchronized(getOrderLock()){ 111 tm_orders.put(order.getTime(), order); 112 } 113 SMarket.fireUpdateWorkerScreen(); 114 } 115 116 /** 117 * @return an identical SICustomer. 118 */ 119 public Object clone() { 120 SICustomer sic = new SICustomer(this.getName()); 121 sic.l_tillTime = this.l_tillTime; 122 sic.tm_orders = this.tm_orders; 123 return sic; 124 } 125 126 /** 127 * @return the monitor used to synchronize access to the TreeMap of CSOrders. 128 */ 129 private final Object getOrderLock() { 130 if (o_ordersLock == null) { 131 o_ordersLock = new Object(); 132 } 133 return o_ordersLock; 134 } 135 136 /** 137 * @return the number of {@link CSOrder}s of this SICustomer. 138 * 139 * @param active if true only active CSOrders will be counted. 140 */ 141 public int getOrderCount(boolean active){ 142 int i = 0; 143 synchronized(getOrderLock()){ 144 Iterator it = tm_orders.values().iterator(); 145 while(it.hasNext()){ 146 CSOrder cso = (CSOrder)it.next(); 147 if(active){ 148 if(cso.isActive()) i++; 149 } 150 else i++; 151 } 152 } 153 return i; 154 } 155 156 /** 157 * @return the {@link UCustomer} this SICustomer represents. 158 */ 159 public UCustomer getCustomer(){ 160 return (UCustomer)UserManager.getGlobalUM().getUser(this.getName()); 161 } 162 163 public String getMissingArticles() { 164 return missingArticles; 165 } 166 167 /** 168 * @return the waiting-time of the oldest order of this SICustomer in milliseconds. 169 */ 170 public Long getOrderQueueTime(){ 171 Long l = new Long(System.currentTimeMillis()); 172 synchronized(getOrderLock()){ 173 Iterator it = tm_orders.values().iterator(); 174 while(it.hasNext()){ 175 CSOrder cso = (CSOrder)it.next(); 176 if(cso.isActive()){ 177 l = cso.getTime(); 178 break; 179 } 180 } 181 } 182 return l; 183 } 184 185 /** 186 * Compares this SICustomer to another one using l_tillTime. 187 * 188 * @param o a SICustomer that should be compared to this one 189 * @return 0 if the argument SICustomer is equal to this one; 190 * a value less than 0 if this SICustomer is older than the given one; 191 * and else a value greater than 0. 192 */ 193 public int compareTo(Object o) { 194 return l_tillTime.compareTo(((SICustomer)o).l_tillTime); 195 } 196 197 198 199 /** 200 * Reaction on event: An article is unavaible. 201 * 202 * @param articleKey the key of the unavaible article. 203 */ 204 public void offerEmpty(String articleKey) { 205 // if this SICustomer contains to the till-queue 206 if(queue.compareTo(SMarket.STK_TILLQUEUE)==0){ 207 CountingStockImpl csi = getCustomer().getShoppingBasket(); 208 if(csi.contains(articleKey, null)){ 209 missingArticles += new String("\n"+String.valueOf(csi.countItems(articleKey, null)) + 210 "x " + SMarket.getArticleCatalog().get(articleKey).getArticleName()); 211 SMarket.getOffer().add(articleKey, csi.countItems(articleKey, null), null); 212 try { 213 csi.remove(articleKey, csi.countItems(articleKey, null), null); 214 } catch (VetoException e) { 215 System.err.println(e.getMessage()); 216 } 217 } 218 } 219 // if this SICustomer contains to the warehouse-queue 220 if(queue.compareTo(SMarket.STK_WAREHOUSEQUEUE)==0){ 221 CountingStockImpl csi_current = new CountingStockImpl("current", SMarket.getArticleCatalog()); 222 synchronized(getOrderLock()){ 223 Iterator it = tm_orders.values().iterator(); 224 while(it.hasNext()){ 225 CSOrder cs = (CSOrder)it.next(); 226 if(cs.contains(articleKey, null)){ 227 csi_current.add(articleKey, cs.removeAll(articleKey), null); 228 } 229 } 230 } 231 if(csi_current.contains(articleKey, null)) addOrderToQueue(csi_current, false); 232 } 233 } 234 235 /** 236 * Reaction on event: a new delivery arrived the market. 237 */ 238 public void wakeUpOrders() { 239 synchronized(getOrderLock()){ 240 Iterator it = tm_orders.values().iterator(); 241 while(it.hasNext()){ 242 CSOrder cso = (CSOrder)it.next(); 243 if(!cso.isActive() && SMarket.getOffer().containsStock(cso, null)){ 244 Iterator it_article = cso.iterator(null, false); 245 while(it_article.hasNext()){ 246 String key = ((StockItem)it_article.next()).getName(); 247 try { 248 SMarket.getOffer().remove(key, null); 249 } catch (VetoException e) { 250 System.err.println(e.getMessage()); 251 } 252 } 253 cso.setActive(true); 254 } 255 } 256 } 257 } 258 259 /** 260 * Reaction on event: a SProcessWorker needs the count of all existing articles. 261 * 262 * @param articleKey the name of the article. 263 * @param spw the SProcessWorker that is affected. 264 */ 265 public void countArticles(String articleKey, SProcessWorker spw) { 266 int count = 0; 267 if(queue.compareTo(SMarket.STK_TILLQUEUE)==0){ 268 if(getCustomer().getShoppingBasket().contains(articleKey, null)){ 269 count += getCustomer().getShoppingBasket().countItems(articleKey, null); 270 spw.addDatabaseCount(count, SProcessWorker.TILLQUEUE); 271 } 272 } 273 if(queue.compareTo(SMarket.STK_WAREHOUSEQUEUE)==0){ 274 synchronized(getOrderLock()){ 275 Iterator it = tm_orders.values().iterator(); 276 while(it.hasNext()){ 277 count += ((CountingStock)it.next()).countItems(articleKey, null); 278 } 279 if(count>0){ 280 spw.addDatabaseCount(count, SProcessWorker.WAREHOUSEQUEUE); 281 } 282 } 283 } 284 } 285 286 /** 287 * Adds a UCustomer to the global till-queue(a Stock). 288 * 289 * @param customer the UCustomer that will be added. 290 */ 291 public static void addToTillQueue(UCustomer customer){ 292 SICustomer sic = new SICustomer(customer.getName()); 293 SMarket.getTillQueue().add(sic, null); 294 sic.queue = SMarket.STK_TILLQUEUE; 295 } 296 297 /** 298 * Adds the shoppingbasket of a UCustomer as a CSOrder to the warehouse-queue(a Stock). 299 * 300 * @param customer the UCustomer who`s shoppingbasket will be added. 301 */ 302 public static void addToOrderQueue(UCustomer customer){ 303 SSListenable queue = SMarket.getWarehouseQueue(); 304 String key = customer.getName(); 305 Iterator it = queue.get(key, null, true); 306 if(it.hasNext()){ 307 SICustomer sic = (SICustomer)it.next(); 308 sic.addOrderToQueue(sic.getCustomer().getShoppingBasket(), true); 309 } 310 else{ 311 SICustomer sic = new SICustomer(key); 312 queue.add(sic, null); 313 sic.addOrderToQueue(sic.getCustomer().getShoppingBasket(), true); 314 sic.queue = SMarket.STK_WAREHOUSEQUEUE; 315 } 316 } 317 }