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 }