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 }