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    }