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    }