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