001 package sale; 002 003 import java.util.*; 004 005 import java.io.*; 006 007 import javax.swing.*; 008 import java.awt.Rectangle; 009 010 import java.awt.event.*; 011 012 import users.*; 013 import log.*; 014 015 import sale.multiwindow.*; 016 import sale.events.*; 017 018 import data.NameContext; 019 import data.NameContextException; 020 021 import data.DataBasket; 022 import data.Stock; 023 import data.Catalog; 024 import data.DuplicateKeyException; 025 026 import resource.util.ResourceManager; 027 028 import util.*; 029 030 /** 031 * The central class in a SalesPoint application, responsible for central 032 * management tasks and for persistence. 033 * 034 * <p>There is only one instance of the Shop class per application, and you can 035 * obtain, or change this central, singleton instance through calls to 036 * {@link #getTheShop} or {@link #setTheShop}, resp.</p> 037 * 038 * <p>The Shop will manage the application's display, creating and removing 039 * additional SalesPoints' displays as necessary. Also, the Shop will offer a 040 * central MenuSheet, from which the user can select certain central, 041 * administrative actions, like shutdown, loadup, creating and removing 042 * SalesPoints, etc. This MenuSheet can, of course, be adapted. See 043 * {@link #createShopMenuSheet}, if you're interested in this.</p> 044 * 045 * <p>The Shop can make persistent the entire current state of the application 046 * by calling just one method: {@link #makePersistent}.</p> 047 * 048 * <p>The Shop serves as a {@link ProcessContext} for remote and background 049 * {@link SaleProcess processes}, which will be equipped with a 050 * {@link NullDisplay}. To find out about running processes at the Shop, see 051 * {@link #runProcess} and {@link #runBackgroundProcess}.</p> 052 * 053 * @author Steffen Zschaler 054 * @version 2.0 28/05/1999 055 * @since v2.0 056 */ 057 public class Shop extends Object implements SerializableListener { 058 059 /** 060 * ProcessContext data. 061 */ 062 protected Map m_pContext = new HashMap(); 063 064 /** 065 * Put an object into the ProcessContext. 066 * 067 * @override Never 068 * 069 * @param sKey object's identifier 070 * @param oData the data object 071 * 072 */ 073 protected void setProcessData(String sKey, Object oData) 074 { 075 m_pContext.put(sKey, oData); 076 } 077 078 /** 079 * Get an object from the ProcessContext. 080 * 081 * @override Never 082 * 083 * @param sKey object's key 084 * 085 * @return the object from ProcessContext 086 */ 087 protected Object getProcessData(String sKey) 088 { 089 return m_pContext.get(sKey); 090 } 091 092 /** 093 * The SalesPoints that belong to the system. 094 * 095 * @serial 096 */ 097 protected List m_lspSalesPoints = new LinkedList(); 098 099 /** 100 * The monitor synchronizing access to the list of SalesPoints. 101 */ 102 private transient Object m_oSalesPointsLock; 103 104 /** 105 * Return the monitor synchronizing access to the list of SalesPoints. 106 * 107 * @override Never 108 */ 109 protected final Object getSalesPointsLock() { 110 if (m_oSalesPointsLock == null) { 111 m_oSalesPointsLock = new Object(); 112 } 113 114 return m_oSalesPointsLock; 115 } 116 117 /** 118 * The current SalesPoint. 119 * 120 * @serial 121 */ 122 private SalesPoint m_spCurrent = null; 123 124 /** 125 * Flag indicating whether calls to {@link #setCurrentSalesPoint} are to have an effect or not. Used for 126 * optimization reasons. 127 * 128 * @serial 129 */ 130 private int m_nCurrentSalesPointIsAdjusting = 0; 131 132 /** 133 * The ShopFrames bounds. 134 * 135 * @serial 136 */ 137 protected Rectangle m_rShopFrameBounds = null; 138 139 /** 140 * A ProcessContext for one remote or background process. 141 */ 142 protected static class ProcessHandle implements ProcessContext { 143 144 /** 145 * The process for which this is the context. 146 * 147 * @serial 148 */ 149 protected SaleProcess m_p; 150 151 /** 152 * The display to be used. Defaults to {@link NullDisplay#s_ndGlobal}. 153 * 154 * @serial 155 */ 156 protected Display m_d = NullDisplay.s_ndGlobal; 157 158 /** 159 * The user to be used as the current user for the process. 160 * 161 * @serial 162 */ 163 protected User m_usr; 164 165 /** 166 * The DataBasket to be used. 167 * 168 * @serial 169 */ 170 protected DataBasket m_db; 171 172 /** 173 * Create a new ProcessHandle. 174 */ 175 public ProcessHandle(SaleProcess p, Display d, User usr, DataBasket db) { 176 super(); 177 178 if (d != null) { 179 m_d = d; 180 } 181 182 m_usr = usr; 183 184 m_p = p; 185 m_p.attach(db); 186 m_p.attach(this); 187 } 188 189 // ProcessContext methods 190 public void setFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 191 192 if (fs != null) { 193 fs.attach(p); 194 } 195 196 m_d.setFormSheet(fs); 197 } 198 199 public void popUpFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 200 201 if (fs != null) { 202 fs.attach(p); 203 } 204 205 m_d.popUpFormSheet(fs); 206 } 207 208 public void setMenuSheet(SaleProcess p, MenuSheet ms) { 209 if (ms != null) { 210 ms.attach(p); 211 } 212 213 m_d.setMenuSheet(ms); 214 } 215 216 public boolean hasUseableDisplay(SaleProcess p) { 217 return m_d.isUseableDisplay(); 218 } 219 220 public void log(SaleProcess p, Loggable la) throws IOException { 221 Shop.getTheShop().log(la); 222 } 223 224 public User getCurrentUser(SaleProcess p) { 225 return m_usr; 226 } 227 228 public Stock getStock(String sName) { 229 return Shop.getTheShop().getStock(sName); 230 } 231 232 public Catalog getCatalog(String sName) { 233 return Shop.getTheShop().getCatalog(sName); 234 } 235 236 public void processStarted(SaleProcess p) {} 237 238 public void processFinished(SaleProcess p) { 239 p.detachContext(); 240 241 synchronized (Shop.getTheShop().getProcessesLock()) { 242 Shop.getTheShop().m_lphProcesses.remove(this); 243 } 244 } 245 246 // other operations 247 /** 248 * Suspend the process that is handled by this ProcessHandle. 249 * 250 * @override Never 251 */ 252 public void suspend() throws InterruptedException { 253 m_p.suspend(); 254 } 255 256 /** 257 * Resume the process that is handled by this ProcessHandle. 258 * 259 * @override Never 260 */ 261 public void resume() { 262 m_p.resume(); 263 } 264 265 /** 266 * Check whether the process that is handled by this ProcessHandle can be quitted. 267 * 268 * <p>The default implementation simply calls 269 * <pre> 270 * m_p.{@link SaleProcess#canQuit canQuit (fContextDestroy)}; 271 * </pre> 272 * 273 * Called by {@link #canShutdown}.</p> 274 * 275 * @override Sometimes 276 */ 277 public boolean canShutdown(boolean fContextDestroy) { 278 return m_p.canQuit(fContextDestroy); 279 } 280 281 /** 282 * Sets the process context data. 283 */ 284 public void setProcessData(String sKey, Object oData) { 285 Shop.getTheShop().setProcessData(sKey, oData); 286 } 287 288 /** 289 * Gets the specified process context data. 290 */ 291 public Object getProcessData(String sKey) { 292 return Shop.getTheShop().getProcessData(sKey); 293 } 294 295 } 296 297 /** 298 * All remote or background processes currently running on this Shop, represented by their 299 * {@link ProcessHandle process handles}. 300 * 301 * @serial 302 */ 303 protected List m_lphProcesses = new LinkedList(); 304 305 /** 306 * The monitor synchronizing access to the list of processes. 307 */ 308 private transient Object m_oProcessesLock; 309 310 /** 311 * Return the monitor synchronizing access to the list of processes. 312 * 313 * @override Never 314 */ 315 protected final Object getProcessesLock() { 316 if (m_oProcessesLock == null) { 317 m_oProcessesLock = new Object(); 318 } 319 320 return m_oProcessesLock; 321 } 322 323 /** 324 * The global catalogs. 325 * 326 * @serial 327 */ 328 private Map m_mpCatalogs = new HashMap(); 329 330 /** 331 * The monitor synchronizing access to the Catalogs. 332 */ 333 private transient Object m_oCatalogsLock; 334 335 /** 336 * Return the monitor synchronizing access to the Catalogs. 337 * 338 * @override Never 339 */ 340 private final Object getCatalogsLock() { 341 if (m_oCatalogsLock == null) { 342 m_oCatalogsLock = new Object(); 343 } 344 345 return m_oCatalogsLock; 346 } 347 348 /** 349 * The global Catalogs' name context. ATTENTION: Currently rollback and/or commit of Catalog name changes 350 * are not supported. 351 * 352 * @serial 353 */ 354 // This should be done as soon as nested Catalogs are properly implemented. 355 private final NameContext m_ncCatalogContext = new NameContext() { 356 public void checkNameChange(DataBasket db, String sOldName, 357 String sNewName) throws NameContextException { 358 if (db != null) { 359 throw new NameContextException( 360 "Rollback/commit of name changes of global Catalogs not yet implemented."); 361 } 362 363 if (m_mpCatalogs.containsKey(sNewName)) { 364 throw new NameContextException("Name already spent!"); 365 } 366 } 367 368 public void nameHasChanged(DataBasket db, String sOldName, String sNewName) { 369 m_mpCatalogs.put(sNewName, m_mpCatalogs.remove(sOldName)); 370 } 371 372 public Object getNCMonitor() { 373 return getCatalogsLock(); 374 } 375 }; 376 377 /** 378 * The global Stocks. 379 * 380 * @serial 381 */ 382 private Map m_mpStocks = new HashMap(); 383 384 /** 385 * The monitor synchronizing access to the Stocks. 386 */ 387 private transient Object m_oStocksLock; 388 389 /** 390 * Return the monitor synchronizing access to the Stocks. 391 * 392 * @override Never 393 */ 394 private final Object getStocksLock() { 395 if (m_oStocksLock == null) { 396 m_oStocksLock = new Object(); 397 } 398 399 return m_oStocksLock; 400 } 401 402 /** 403 * The global Stocks' name context. ATTENTION: Currently rollback and/or commit of Stock name changes are 404 * not supported. 405 * 406 * @serial 407 */ 408 // This should be done as soon as nested Stocks are properly implemented. 409 private final NameContext m_ncStockContext = new NameContext() { 410 public void checkNameChange(DataBasket db, String sOldName, 411 String sNewName) throws NameContextException { 412 if (db != null) { 413 throw new NameContextException( 414 "Rollback/commit of name changes of global Stocks not yet implemented."); 415 } 416 417 if (m_mpStocks.containsKey(sNewName)) { 418 throw new NameContextException("Name already spent!"); 419 } 420 } 421 422 public void nameHasChanged(DataBasket db, String sOldName, String sNewName) { 423 m_mpStocks.put(sNewName, m_mpStocks.remove(sOldName)); 424 } 425 426 public Object getNCMonitor() { 427 return getStocksLock(); 428 } 429 }; 430 431 /** 432 * The current state of the Shop. One of {@link #DEAD}, {@link #RUNNING} or {@link #SUSPENDED}. 433 * 434 * @serial 435 */ 436 private int m_nShopState = DEAD; 437 438 /** 439 * The monitor synchronizing access to the Shop's state. 440 */ 441 private transient Object m_oStateLock; 442 443 /** 444 * Return the monitor synchronizing access to the Shop's state. 445 * 446 * @override Never 447 */ 448 private final Object getStateLock() { 449 if (m_oStateLock == null) { 450 m_oStateLock = new Object(); 451 } 452 453 return m_oStateLock; 454 } 455 456 /** 457 * The Shop's frame. 458 */ 459 protected transient JFrame m_jfShopFrame = null; 460 461 /** 462 * The title of the Shop's frame. 463 * 464 * @serial 465 */ 466 protected String m_sShopFrameTitle = "Shop"; 467 468 /** 469 * Temporary helper variable to be able to insert the MultiWindow MenuSheet into the Shop's menu. 470 */ 471 private transient MenuSheet m_msMultiWindowMenu; 472 473 /** 474 * The Timer used by this Shop for managing the simulation time. 475 * 476 * @serial 477 */ 478 protected Timer m_trTimer; 479 480 /** 481 * Objects that where registered to be made persistent. 482 * 483 * @serial 484 */ 485 protected Map m_mpToPersistify = new HashMap(); 486 487 /** 488 * The monitor synchronizing access to the persistent objects. 489 */ 490 private transient Object m_oPersistifyLock = null; 491 492 /** 493 * @return the monitor synchronizing access to the persistent objects. 494 * 495 * @override Never 496 */ 497 private final Object getPersistifyLock() { 498 if (m_oPersistifyLock == null) { 499 m_oPersistifyLock = new Object(); 500 } 501 502 return m_oPersistifyLock; 503 } 504 505 /** 506 * First writes the default serializable fields, then calls {@link #onSaveFrames}. 507 */ 508 private void writeObject(ObjectOutputStream oos) throws IOException { 509 util.Debug.print("Writing Shop!", -1); 510 511 synchronized (getPersistifyLock()) { 512 oos.defaultWriteObject(); 513 } 514 515 onSaveFrames(oos); 516 517 util.Debug.print("Finished writing Shop.", -1); 518 } 519 520 /** 521 * First reads the default serializable fields, then calls {@link #onLoadFrames}. 522 */ 523 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 524 util.Debug.print("Loading Shop...", -1); 525 526 // set the Shop to make sure that all content creators etc. use the correct shop!!! 527 setTheShop(this); 528 529 synchronized (getPersistifyLock()) { 530 ois.defaultReadObject(); 531 } 532 533 onLoadFrames(ois); 534 535 util.Debug.print("Finished loading Shop.", -1); 536 } 537 538 /** 539 * Sole constructor to enforce singleton pattern. 540 */ 541 protected Shop() { 542 } 543 544 /** 545 * Add a SalesPoint to the Shop. 546 * 547 * @override Never Instead, override {@link #onSalesPointAdded}. 548 * 549 * @param sp the SalesPoint to be added. 550 */ 551 public void addSalesPoint(final SalesPoint sp) { 552 synchronized (getStateLock()) { 553 if (getShopState() != RUNNING) { 554 try { 555 sp.suspend(); 556 } 557 catch (InterruptedException e) {} 558 } 559 560 synchronized (getSalesPointsLock()) { 561 //check whether this SalesPoint is already added 562 Iterator it = m_lspSalesPoints.iterator(); 563 while (it.hasNext()) { 564 SalesPoint sp_open = (SalesPoint)it.next(); 565 if (sp_open.equals(sp)) { 566 return; 567 } 568 } 569 //if not, add it 570 sp.createNewID(m_lspSalesPoints); 571 m_lspSalesPoints.add(sp); 572 573 /*((MultiWindow)getShopFrame()).addSalesPointDisplay(sp); 574 onSalesPointAdded(sp);*/ 575 try { 576 SwingUtilities.invokeAndWait(new Thread() { 577 public void run() { 578 ((MultiWindow)getShopFrame()).addSalesPointDisplay(sp); 579 onSalesPointAdded(sp); 580 } 581 }); 582 } 583 catch (Exception e) { 584 e.printStackTrace(); 585 } 586 } 587 } 588 } 589 590 private void runAndWait(Thread t) { 591 try { 592 SwingUtilities.invokeLater(t); 593 } 594 catch (Exception ex) { 595 System.err.println("Exception"); 596 ex.printStackTrace(); 597 } 598 599 } 600 601 602 /** 603 * Sets the view mode for the Shop. 604 * @param viewMode can be MultiWindow.WINDOW_VIEW, MultiWindow.TABBED_VIEW, MultiWindow.DESKTOP_VIEW 605 */ 606 public void setViewMode(int viewMode) { 607 ((MultiWindow)getShopFrame()).setViewMode(viewMode); 608 } 609 610 /** 611 * Hook method performing additional work when a SalesPoint was added. 612 * 613 * @override Sometimes Make sure to call the super class's method if overriding this method. 614 * 615 * @param sp the SalesPoint that was removed from the Shop. 616 */ 617 protected void onSalesPointAdded(final SalesPoint sp) { 618 MenuSheet ms = ((MultiWindow)getShopFrame()).getCurrentMenuSheet(); 619 620 if (ms != null) { 621 ms = (MenuSheet)ms.getTaggedItem(SET_CURRENT_SP_TAG); 622 623 if (ms != null) { 624 ms.add(new MenuSheetItem(sp.getName(), 625 "__TAG:_SALESPOINT_SELECTOR_" + sp.getName() + sp.getID(), new Action() { 626 public void doAction(SaleProcess p, SalesPoint _sp) throws IOException { 627 Shop.getTheShop().setCurrentSalesPoint(sp); 628 } 629 })); 630 } 631 } 632 633 setCurrentSalesPoint(sp); 634 sp.logSalesPointOpened(); 635 } 636 637 private String createTag(SalesPoint sp) { 638 Iterator it = getSalesPoints().iterator(); 639 int i = 0; 640 return ""; 641 } 642 643 /** 644 * Remove a SalesPoint from the Shop. 645 * 646 * <p>Prior to being removed from the Shop, the SalesPoint will be 647 * {@link SalesPoint#suspend suspended}.</p> 648 * 649 * @override Never Instead, override {@link #onSalesPointRemoved}. 650 * 651 * @param sp the SalesPoint to be removed 652 */ 653 public void removeSalesPoint(final SalesPoint sp) { 654 try { 655 sp.suspend(); 656 } 657 catch (InterruptedException e) { 658 Thread.currentThread().interrupt(); 659 } 660 661 synchronized (getSalesPointsLock()) { 662 if (m_lspSalesPoints.contains(sp)) { 663 m_lspSalesPoints.remove(sp); 664 665 try { 666 SwingUtilities.invokeAndWait(new Thread() { 667 public void run() { 668 ((MultiWindow)getShopFrame()).closeSalesPointDisplay(sp); 669 onSalesPointRemoved(sp); 670 } 671 }); 672 } 673 catch (Exception e) { 674 e.printStackTrace(); 675 } 676 677 } 678 } 679 } 680 681 /** 682 * Hook method called when a SalesPoint was removed from the Shop. 683 * 684 * @override Sometimes Make sure to call the super class's method if you override this method. 685 * 686 * @param sp the SalesPoint that was removed from the Shop. 687 */ 688 protected void onSalesPointRemoved(SalesPoint sp) { 689 if (getCurrentSalesPoint() == sp) { 690 if (m_lspSalesPoints.size() > 0) { 691 setCurrentSalesPoint((SalesPoint)m_lspSalesPoints.get(0)); 692 } else { 693 setCurrentSalesPoint(null); 694 } 695 } 696 697 MenuSheet ms = ((MultiWindow)getShopFrame()).getCurrentMenuSheet(); 698 699 if (ms != null) { 700 ms = (MenuSheet)ms.getTaggedItem(SET_CURRENT_SP_TAG); 701 702 if (ms != null) { 703 ms.remove("__TAG:_SALESPOINT_SELECTOR_" + sp.getName() + sp.getID()); 704 } 705 } 706 707 sp.logSalesPointClosed(); 708 } 709 710 711 /** 712 * Close a status display. 713 * 714 * @override Never 715 * 716 * @param d the status display to be closed. 717 */ 718 protected void removeStatusDisplay(Display d) { 719 //((MultiWindow)getShopFrame()).closeDisplay(d); 720 } 721 722 /** 723 * Get a list of all SalesPoints in the Shop. 724 * 725 * <p>The list is backed by the SalesPoint's queue, but is immutable.</p> 726 * 727 * @override Never 728 */ 729 public List getSalesPoints() { 730 synchronized (getSalesPointsLock()) { 731 return Collections.unmodifiableList(m_lspSalesPoints); 732 } 733 } 734 735 public SalesPoint getSalesPoint(int id) { 736 Iterator it = getSalesPoints().iterator(); 737 while (it.hasNext()) { 738 SalesPoint sp = (SalesPoint)it.next(); 739 if (sp.getID() == id) { 740 return sp; 741 } 742 } 743 return null; 744 } 745 746 /** 747 * Makes a SalesPoint the current SalesPoint. 748 * 749 * <p>This will bring the specified SalesPoint's window to the front.</p> 750 * 751 * @override Never 752 * 753 * @param sp the SalesPoint to be the current SalesPoint from now on. 754 */ 755 public void setCurrentSalesPoint(SalesPoint sp) { 756 m_spCurrent = sp; 757 if (isCurrentSalesPointAdjusting() || sp == null) { 758 return; 759 } 760 sp.getDisplay().toFront(); 761 } 762 763 /** 764 * Sets a flag that can be used to optimize setCurrentSalesPoint calls. As long as this flag is set, i.e. 765 * {@link #isCurrentSalesPointAdjusting} returns true, calls to {@link #setCurrentSalesPoint} will have no 766 * effect. You can reset the flag by calling {@link #resetCurrentSalesPointIsAdjusting}. 767 * 768 * @override Never 769 */ 770 public void setCurrentSalesPointIsAdjusting() { 771 ++m_nCurrentSalesPointIsAdjusting; 772 } 773 774 /** 775 * Reset a flag that can be used to optimize setCurrentSalesPoint calls. There must be one call to 776 * <code>resetCurrentSalesPointIsAdjusting</code> for each call to {@link #setCurrentSalesPointIsAdjusting}. 777 * Calls to this function must be followed by an explicit call to {@link #setCurrentSalesPoint}. 778 * 779 * @override Never 780 */ 781 public void resetCurrentSalesPointIsAdjusting() { 782 --m_nCurrentSalesPointIsAdjusting; 783 } 784 785 /** 786 * Return whether or not calls to {@link #setCurrentSalesPoint(sale.SalesPoint)} have any effect. 787 * 788 * @override Never 789 */ 790 public boolean isCurrentSalesPointAdjusting() { 791 return m_nCurrentSalesPointIsAdjusting > 0; 792 } 793 794 /** 795 * Returns the currently active SalesPoint of the Shop. 796 * 797 * @override Never 798 */ 799 public SalesPoint getCurrentSalesPoint() { 800 return m_spCurrent; 801 } 802 803 // background process management 804 /** 805 * Run a process on the Shop. 806 * 807 * @override Never 808 * 809 * @param p the process to be run. 810 * @param d the display to be used by the Shop. This can be <code>null</code>, then, a {@link NullDisplay} 811 * will be used. 812 * @param usr the user to be the current user for the process. 813 * @param db the DataBasket to be used by the process. 814 */ 815 public void runProcess(SaleProcess p, Display d, User usr, DataBasket db) { 816 synchronized (getStateLock()) { 817 synchronized (getProcessesLock()) { 818 m_lphProcesses.add(new ProcessHandle(p, d, usr, db)); 819 if (getShopState() == RUNNING) { 820 p.start(); 821 } else { 822 try { 823 p.suspend(); 824 } 825 catch (InterruptedException ie) {} 826 } 827 } 828 } 829 } 830 831 /** 832 * Run a background process on the Shop. A background process does not have a display. You can use 833 * background processes to perform tasks that do not need user communication. 834 * 835 * @override Never 836 * 837 * @param p the process to be run. 838 * @param usr the user to be the current user for the process. 839 * @param db the DataBasket to be used by the process. 840 */ 841 public void runBackgroundProcess(SaleProcess p, User usr, DataBasket db) { 842 runProcess(p, null, usr, db); 843 } 844 845 // Shop state related methods 846 /** 847 * Start the Shop. 848 * 849 * <p>This method must not be called after load up.</p> 850 * 851 * @override Never 852 */ 853 public void start() { 854 synchronized (getStateLock()) { 855 if (getShopState() == DEAD) { 856 JFrame jf = getShopFrame(); 857 858 if (getShopFrameBounds() != null) { 859 jf.setBounds(getShopFrameBounds()); 860 } else { 861 jf.pack(); 862 } 863 864 jf.setVisible(true); 865 866 m_nShopState = SUSPENDED; 867 resume(); 868 } 869 } 870 } 871 872 /** 873 * Sets the Framebounds of the Shops assoziated JFrame. 874 * 875 * <p>Example:<p> 876 * <code>Shop.getTheShop().setShopFrameBounds (new Rectangle (10,10,200,200));<code> 877 * 878 * This moves the JFrame to Position (10,10) with a size of (200,200). 879 * 880 * @override Sometimes 881 */ 882 public void setShopFrameBounds(Rectangle r) { 883 if (getShopState() == DEAD) { 884 m_rShopFrameBounds = r; 885 } else { 886 if ((m_rShopFrameBounds != null) && (getShopState() == RUNNING)) { 887 m_rShopFrameBounds = r; 888 getShopFrame().setBounds(r); 889 getShopFrame().hide(); 890 getShopFrame().show(); 891 } 892 } 893 } 894 895 /** 896 * Returns the Framebounds of the Shops assoziated JFrame. 897 * 898 * @override Sometimes 899 */ 900 public Rectangle getShopFrameBounds() { 901 return m_rShopFrameBounds; 902 } 903 904 /** 905 * Suspend a running Shop. Suspending the Shop includes suspending all SalesPoints currently in the Shop. 906 * 907 * @override Never 908 */ 909 public void suspend() { 910 synchronized (getStateLock()) { 911 if (getShopState() == RUNNING) { 912 913 // suspend all remote processes 914 synchronized (getProcessesLock()) { 915 for (Iterator i = m_lphProcesses.iterator(); i.hasNext(); ) { 916 try { 917 ((ProcessHandle)i.next()).suspend(); 918 } 919 catch (InterruptedException ie) {} 920 } 921 } 922 923 // suspend all SalesPoints 924 synchronized (getSalesPointsLock()) { 925 for (Iterator i = m_lspSalesPoints.iterator(); i.hasNext(); ) { 926 try { 927 ((SalesPoint)i.next()).suspend(); 928 } 929 catch (InterruptedException e) {} 930 } 931 } 932 933 m_nShopState = SUSPENDED; 934 } 935 } 936 } 937 938 /** 939 * Resume a suspended Shop. Resuming includes resuming all SalesPoints currently in the Shop. 940 * 941 * @override Never 942 */ 943 public void resume() { 944 synchronized (getStateLock()) { 945 if (getShopState() == SUSPENDED) { 946 947 // resume all remote processes 948 synchronized (getProcessesLock()) { 949 for (Iterator i = m_lphProcesses.iterator(); i.hasNext(); ) { 950 ((ProcessHandle)i.next()).resume(); 951 } 952 } 953 954 // resume all SalesPoints 955 synchronized (getSalesPointsLock()) { 956 for (Iterator i = m_lspSalesPoints.iterator(); i.hasNext(); ) { 957 SalesPoint sp = (SalesPoint)i.next(); 958 /*JDisplayFrame jdf = (JDisplayFrame)sp.getDisplay(); 959 jdf.setVisible(true);*/sp.resume(); 960 } 961 } 962 963 m_nShopState = RUNNING; 964 } 965 } 966 } 967 968 /** 969 * Close the Shop and quit the application. 970 * 971 * 972 * <p>This method is linked to the "Quit" item in the Shop's MenuSheet as well as to the close 973 * window gesture for the Shop frame.</p> 974 * 975 * @override Sometimes By default implemented as: 976 * <pre> 977 * if (Shop.{@link #getTheShop getTheShop()}.{@link #shutdown shutdown (true)}) { 978 * System.exit (0); 979 * }; 980 * </pre> 981 */ 982 public void quit() { 983 if (Shop.getTheShop().shutdown(true)) { 984 System.exit(0); 985 } 986 } 987 988 /** 989 * Close the Shop. 990 * 991 * <p>Calling this method will stop any processes currently running on any SalesPoints in 992 * the Shop after calling {@link #canShutdown} to check whether shutdown is permitted at 993 * the moment. The method must therefore not be called from within a process !</p> 994 * 995 * @override Never 996 * 997 * @param fPersistify if true, the current state of the Shop will be made persistent prior 998 * to actually closing the Shop. 999 * 1000 * @return true if the shutdown was successful. 1001 */ 1002 public boolean shutdown(boolean fPersistify) { 1003 synchronized (getSalesPointsLock()) { 1004 synchronized (getProcessesLock()) { 1005 boolean fRunning = (getShopState() == RUNNING); 1006 1007 if (!canShutdown(fPersistify)) { 1008 return false; 1009 } 1010 if (fPersistify) { 1011 try { 1012 makePersistent(); 1013 } 1014 catch (CancelledException ce) { 1015 if (fRunning) { 1016 resume(); 1017 } 1018 return false; 1019 } 1020 catch (Throwable t) { 1021 System.err.println("Exception occurred while making persistent: " + t); 1022 t.printStackTrace(); 1023 1024 if (fRunning) { 1025 resume(); 1026 } 1027 1028 return false; 1029 } 1030 } 1031 1032 clearInternalStructures(); 1033 1034 m_nShopState = DEAD; 1035 1036 return true; 1037 } 1038 } 1039 } 1040 1041 /** 1042 * Check whether shutdown can be permitted in the current state of the system. 1043 * 1044 * <p>In this method you can assume that you are the owner of {@link #getSalesPointsLock()} 1045 * and {@link #getProcessesLock()}, so that you can access the list of SalesPoints and the 1046 * list of processes without extra synchronization.</p> 1047 * 1048 * <p>The default implementation will first {@link #suspend} the Shop, should 1049 * {@link #getShopState its state} be {@link #RUNNING}. It will then check all processes running on the 1050 * Shop. If no such processes exist or if all of them confirm that shut down is possible, it will call the 1051 * {@link SalesPoint#canQuit} method of any {@link SalesPoint} in the system, passing 1052 * <code>!fPersistify</code> as the parameter. If all SalesPoints return true, the Shop stays suspended and 1053 * <code>canShutdown</code> returns true. In any other case, the Shop will be {@link #resume resumed} again, 1054 * and false will be returned.</p> 1055 * 1056 * <p>This method is usually not called directly, but if you do, make sure to call it 1057 * with synchronization on {@link #getSalesPointsLock()} and {@link #getProcessesLock()}.</p> 1058 * 1059 * @override Sometimes 1060 * 1061 * @param fPersistify if true, the Shop's state will be made persistent before shutdown. 1062 * 1063 * @return true to indicate shutdown is OK; false otherwise. 1064 */ 1065 protected boolean canShutdown(boolean fPersistify) { 1066 boolean fRunning = (getShopState() == RUNNING); 1067 1068 if (fRunning) { 1069 suspend(); 1070 } 1071 1072 boolean fCanQuit = true; 1073 1074 // check for background or remote processes 1075 for (Iterator i = m_lphProcesses.iterator(); i.hasNext() && fCanQuit; ) { 1076 fCanQuit = ((ProcessHandle)i.next()).canShutdown(!fPersistify); 1077 } 1078 1079 // check for SalesPoints 1080 for (Iterator i = m_lspSalesPoints.iterator(); i.hasNext() && fCanQuit; ) { 1081 fCanQuit = ((SalesPoint)i.next()).canQuit(!fPersistify); 1082 } 1083 1084 if (!fCanQuit) { 1085 if (fRunning) { 1086 resume(); 1087 } 1088 1089 return false; 1090 } 1091 1092 // all fine... 1093 return true; 1094 } 1095 1096 /** 1097 * Return the Shop's state, being one of {@link #DEAD}, {@link #RUNNING} or {@link #SUSPENDED}. 1098 * 1099 * @override Never 1100 */ 1101 public int getShopState() { 1102 return m_nShopState; 1103 } 1104 1105 /** 1106 * Make the current state of the Shop persistent. 1107 * 1108 * @override Never 1109 * 1110 * @exception IOException if an error occurred. 1111 * @exception CancelledException if the retrieval of the persistence stream was cancelled by the user. 1112 */ 1113 public synchronized void makePersistent() throws IOException, CancelledException { 1114 boolean fRunning = (getShopState() == RUNNING); 1115 if (fRunning) { 1116 suspend(); 1117 } 1118 try { 1119 OutputStream osStream = retrievePersistenceOutStream(); 1120 1121 synchronized (getSalesPointsLock()) { 1122 synchronized (getProcessesLock()) { 1123 1124 ObjectOutputStream oosOut = new ObjectOutputStream(osStream); 1125 1126 oosOut.writeObject(this); 1127 oosOut.writeObject(UserManager.getGlobalUM()); 1128 oosOut.writeObject(User.getGlobalPassWDGarbler()); 1129 //save global log file (if desired) 1130 /*File f = Log.getGlobalLogFile(); 1131 if (f != null && Log.getSaveToPersistence()) { 1132 FileInputStream fis = new FileInputStream(f); 1133 copy(fis, osStream); 1134 }*/File f = Log.getGlobalLogFile(); 1135 if (f != null && Log.getSaveToPersistence()) { 1136 oosOut.writeObject(new LogFileContent(f)); 1137 } 1138 1139 oosOut.flush(); 1140 oosOut.close(); 1141 osStream.close(); 1142 } 1143 } 1144 } 1145 finally { 1146 if (fRunning) { 1147 resume(); 1148 } 1149 } 1150 } 1151 1152 /** 1153 * Save the Shop's main frame's and the status frame's state to the given stream. 1154 * 1155 * @override Never 1156 * 1157 * @param oos the Stream to save to 1158 * 1159 * @exception IOException if an error occurred while saving the frames' states. 1160 */ 1161 protected void onSaveFrames(ObjectOutputStream oos) throws IOException { 1162 ((MultiWindow)getShopFrame()).save(oos); 1163 1164 // Save all SalesPoints' displays 1165 for (Iterator i = m_lspSalesPoints.iterator(); i.hasNext(); ) { 1166 ((SalesPoint)i.next()).getDisplay().save(oos); 1167 } 1168 } 1169 1170 /** 1171 * Restore the Shop's state from a Stream. 1172 * 1173 * <p><strong>Attention:</strong> Any old reference to the Shop is invalid afterwards. The new Shop can be 1174 * acquired through {@link #getTheShop Shop.getTheShop()}.</p> 1175 * 1176 * @override Never 1177 * 1178 * @exception IOException if an exception occurred while loading 1179 * @exception ClassNotFoundException if an exception occurred while loading 1180 * @exception CancelledException if the user cancels loading. 1181 */ 1182 public synchronized void restore() throws IOException, ClassNotFoundException, CancelledException { 1183 1184 InputStream isStream = retrievePersistenceInStream(); 1185 1186 if (!shutdown(false)) { 1187 throw new CancelledException(); 1188 } 1189 1190 synchronized (getSalesPointsLock()) { 1191 synchronized (getProcessesLock()) { 1192 1193 ObjectInputStream oisIn = new ObjectInputStream(isStream); 1194 // Setzt den Shop automatisch neu !!! 1195 oisIn.readObject(); 1196 UserManager.setGlobalUM((UserManager)oisIn.readObject()); 1197 User.setGlobalPassWDGarbler((users.PassWDGarbler)oisIn.readObject()); 1198 //create new logfile and load saved logs from persistence file (if they exist) into it 1199 File f = Log.getGlobalLogFile(); 1200 if (f != null && Log.getSaveToPersistence()) { 1201 Log.setGlobalLogFile(f.getName(), true, true); 1202 //FileOutputStream fos = new FileOutputStream(Log.getGlobalLogFile()); 1203 //copy(isStream, fos); 1204 try { 1205 LogFileContent lfc = (LogFileContent)oisIn.readObject(); 1206 1207 Log.getGlobalLog().addLogEntries(lfc); 1208 } 1209 catch (Exception e) { 1210 } 1211 } 1212 oisIn.close(); 1213 isStream.close(); 1214 } 1215 } 1216 1217 synchronized (getTheShop().getStateLock()) { 1218 /*for (Iterator it = getTheShop().getSalesPoints().iterator(); it.hasNext();) { 1219 getTheShop().onSalesPointAdded((SalesPoint)it.next()); 1220 }*/ 1221 getTheShop().m_nShopState = SUSPENDED; 1222 getTheShop().resume(); 1223 } 1224 } 1225 1226 /** 1227 * Copies bytes from an InputStream to an OutputStream 1228 */ 1229 private void copy(InputStream in, OutputStream out) { 1230 synchronized (in) { 1231 synchronized (out) { 1232 byte[] buffer = new byte[256]; 1233 while (true) { 1234 try { 1235 int bytesread = in.read(buffer); 1236 if (bytesread == -1) { 1237 break; 1238 } 1239 out.write(buffer, 0, bytesread); 1240 } 1241 catch (IOException ioe) { 1242 ioe.printStackTrace(); 1243 } 1244 } 1245 } 1246 } 1247 } 1248 1249 /** 1250 * Loads the Shop's main frame and the SalesPoints' frames' states from the given stream. 1251 * 1252 * @override Never 1253 * 1254 * @param ois the Stream to load from 1255 * 1256 * @exception IOException if an error occurred while loading the frames' states. 1257 * @exception ClassNotFoundException if an error occurred while loading the frames' states. 1258 */ 1259 protected void onLoadFrames(ObjectInputStream ois) throws IOException, ClassNotFoundException { 1260 ((MultiWindow)getShopFrame()).load(ois); 1261 1262 // Load all SalesPoints' displays 1263 for (Iterator i = m_lspSalesPoints.iterator(); i.hasNext(); ) { 1264 SalesPoint sp = (SalesPoint)i.next(); 1265 1266 Class c = (Class)ois.readObject(); 1267 Display d = null; 1268 MultiWindow mw = (MultiWindow)getShopFrame(); 1269 //is saved class a DisplayFrame or a subclass of DisplayFrame? 1270 if (MultiWindow.DisplayFrame.class.isAssignableFrom(c)) { 1271 d = mw.getNewWindow(sp); 1272 } 1273 //is saved class a TabbedFrame or a subclass of TabbedFrame? 1274 if (MultiWindow.TabbedFrame.class.isAssignableFrom(c)) { 1275 d = mw.getNewTab(sp); 1276 } 1277 //is saved class a DesktopFrame or a subclass of DesktopFrame? 1278 if (MultiWindow.DesktopFrame.class.isAssignableFrom(c)) { 1279 d = mw.getNewInternalFrame(sp); 1280 } 1281 d.load(ois); 1282 sp.attachLoadedDisplay(d); 1283 } 1284 } 1285 1286 /** 1287 * Helper method creating the dialog in which the user can select the persistence file. 1288 * 1289 * @override Never 1290 */ 1291 private JFileChooser getChooser() { 1292 JFileChooser jfcChooser = new JFileChooser(); 1293 1294 jfcChooser.setFileFilter(new javax.swing.filechooser.FileFilter() { 1295 public boolean accept(File fToAccept) { 1296 if (fToAccept == null) { 1297 return false; 1298 } 1299 1300 if (fToAccept.isDirectory()) { 1301 return true; 1302 } 1303 1304 StringTokenizer stName = new StringTokenizer(fToAccept.getName(), "."); 1305 1306 if (stName.hasMoreTokens()) { 1307 stName.nextToken(); 1308 } else { 1309 return false; 1310 } 1311 1312 String sSuffix = null; 1313 while (stName.hasMoreTokens()) { 1314 sSuffix = stName.nextToken(); 1315 } 1316 1317 if (sSuffix != null) { 1318 return (sSuffix.toLowerCase().equals("prs")); 1319 } else { 1320 return false; 1321 } 1322 } 1323 1324 public String getDescription() { 1325 return "Persistence Files (*.prs)"; 1326 } 1327 }); 1328 1329 jfcChooser.setFileSelectionMode(javax.swing.JFileChooser.FILES_ONLY); 1330 jfcChooser.setMultiSelectionEnabled(false); 1331 1332 return jfcChooser; 1333 } 1334 1335 /** 1336 * Retrieves the stream to which the Shop's state is to be written. 1337 * 1338 * @override Sometimes The default implementation allows the user to select a file name and creates a stream 1339 * for the specified file. 1340 * 1341 * @exception IOException if an exception occurred while creating the stream 1342 * @exception CancelledException if the user cancelled the save process. 1343 */ 1344 protected OutputStream retrievePersistenceOutStream() throws IOException, CancelledException { 1345 javax.swing.JFileChooser jfcChooser = getChooser(); 1346 1347 File fFile = null; 1348 1349 do { 1350 if (jfcChooser.showSaveDialog(null) == JFileChooser.CANCEL_OPTION) { 1351 throw new CancelledException("File choosing cancelled."); 1352 } 1353 1354 fFile = jfcChooser.getSelectedFile(); 1355 1356 if (fFile == null) { 1357 throw new CancelledException("No file selected."); 1358 } 1359 1360 if (!jfcChooser.getFileFilter().accept(fFile) && !fFile.exists()) { 1361 fFile = new File(fFile.getParent(), fFile.getName() + ".prs"); 1362 1363 } 1364 if ((jfcChooser.accept(fFile)) && (!fFile.exists())) { 1365 switch (JOptionPane.showConfirmDialog(null, 1366 fFile.getAbsolutePath() + " does not exist.\nCreate?", "Confirmation", 1367 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)) { 1368 case JOptionPane.NO_OPTION: 1369 fFile = null; 1370 break; 1371 1372 case JOptionPane.CANCEL_OPTION: 1373 throw new CancelledException("File choosing cancelled."); 1374 1375 case JOptionPane.YES_OPTION: 1376 fFile.createNewFile(); 1377 } 1378 } 1379 1380 } 1381 while (!jfcChooser.getFileFilter().accept(fFile) || fFile.isDirectory()); 1382 1383 return new java.io.FileOutputStream(fFile); 1384 } 1385 1386 /** 1387 * Retrieves the stream from which to read the Shop's state. 1388 * 1389 * @override Sometimes The default implementation allows the user to select a file name and creates a stream 1390 * for the specified file. 1391 * 1392 * @exception IOException if an exception occurred while creating the stream 1393 * @exception CancelledException if the user cancelled the save process. 1394 */ 1395 protected InputStream retrievePersistenceInStream() throws IOException, CancelledException { 1396 javax.swing.JFileChooser jfcChooser = getChooser(); 1397 1398 do { 1399 jfcChooser.getSelectedFile(); 1400 1401 if (jfcChooser.showOpenDialog(null) == javax.swing.JFileChooser.CANCEL_OPTION) { 1402 throw new CancelledException("File choosing cancelled."); 1403 } 1404 1405 } 1406 while (!jfcChooser.getSelectedFile().exists()); 1407 1408 return new java.io.FileInputStream(jfcChooser.getSelectedFile()); 1409 } 1410 1411 /** 1412 * Sets an object to be persistent. The object can be accessed at the given key. 1413 * 1414 * @override Never 1415 * 1416 * @param oKey the key at which the object can be accessed. 1417 * @param oToPersistify the object that is to be made persistent. 1418 * 1419 * @return the object previously stored at that key. 1420 */ 1421 public Object setObjectPersistent(Object oKey, Object oToPersistify) { 1422 synchronized (getPersistifyLock()) { 1423 Object oReturn = m_mpToPersistify.remove(oKey); 1424 m_mpToPersistify.put(oKey, oToPersistify); 1425 return oReturn; 1426 } 1427 } 1428 1429 /** 1430 * Set an object to be no longer persistent. 1431 * 1432 * @override Never 1433 * 1434 * @param oKey the key at which the object can be accessed. 1435 * 1436 * @return the object that was made transient. 1437 */ 1438 public Object setObjectTransient(Object oKey) { 1439 synchronized (getPersistifyLock()) { 1440 return m_mpToPersistify.remove(oKey); 1441 } 1442 } 1443 1444 /** 1445 * Get a persistent object. 1446 * 1447 * @override Never 1448 * 1449 * @param oKey the key that describes the object. 1450 * 1451 * @return the persistent object. 1452 */ 1453 public Object getPersistentObject(Object oKey) { 1454 synchronized (getPersistifyLock()) { 1455 return m_mpToPersistify.get(oKey); 1456 } 1457 } 1458 1459 /** 1460 * Get an iterator of all persistent objects. You can use the iterator's remove() method to make objects 1461 * transient. 1462 * 1463 * @override Never 1464 */ 1465 public Iterator getPersistentObjects() { 1466 synchronized (getPersistifyLock()) { 1467 return m_mpToPersistify.values().iterator(); 1468 } 1469 } 1470 1471 /** 1472 * Clear the internal structures maintained by the Shop, thus finishing off shutdown. 1473 * 1474 * @override Never 1475 */ 1476 protected void clearInternalStructures() { 1477 synchronized (getSalesPointsLock()) { 1478 while (m_lspSalesPoints.size() > 0) { 1479 removeSalesPoint((SalesPoint)m_lspSalesPoints.get(0)); 1480 } 1481 } 1482 1483 synchronized (getProcessesLock()) { 1484 m_lphProcesses.clear(); 1485 } 1486 1487 // clear and close displays 1488 if (m_jfShopFrame != null) { 1489 m_jfShopFrame.setVisible(false); 1490 m_jfShopFrame.dispose(); 1491 m_jfShopFrame = null; 1492 } 1493 } 1494 1495 /** 1496 * Set the Shop frame's title. Initially, this is "Shop". 1497 * 1498 * @override Never 1499 * 1500 * @param sTitle the new title. 1501 */ 1502 public void setShopFrameTitle(String sTitle) { 1503 m_sShopFrameTitle = sTitle; 1504 getShopFrame().setTitle(sTitle); 1505 } 1506 1507 public String getShopFrameTitle() { 1508 return m_sShopFrameTitle; 1509 } 1510 1511 /** 1512 * Gets the Shop's main frame. 1513 * 1514 * <p>The main Shop frame will be the frame in which the Shop's menu gets displayed.</p> 1515 * 1516 * <p>By default this creates a {@link sale.multiwindow.MultiWindow} with the title that you specified 1517 * in a call to {@link #setShopFrameTitle}.</p> 1518 * 1519 * @override Never, use {@link #createShopFrame} instead 1520 */ 1521 protected JFrame getShopFrame() { 1522 if (m_jfShopFrame == null) { 1523 MultiWindow mw = createShopFrame(); 1524 m_msMultiWindowMenu = mw.getMultiWindowMenuSheet(); 1525 MenuSheet ms = createShopMenuSheet(); 1526 m_msMultiWindowMenu = null; 1527 mw.setMenuSheet(ms); 1528 1529 mw.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 1530 mw.addWindowListener(new WindowAdapter() { 1531 public void windowClosing(WindowEvent e) { 1532 new Thread("Shop closer") { 1533 public void run() { 1534 Shop.getTheShop().quit(); 1535 } 1536 } 1537 1538 .start(); 1539 } 1540 }); 1541 1542 m_jfShopFrame = mw; 1543 } 1544 return m_jfShopFrame; 1545 } 1546 1547 /** 1548 * Creates and returns a new {@link MultiWindow} with window view mode set. 1549 * 1550 * @override Sometimes If you want to customize the Shop frame create and return yours here. The customized 1551 * Shop frame must be a MultiWindow or a subclass of it. 1552 */ 1553 protected MultiWindow createShopFrame() { 1554 return new MultiWindow(this, MultiWindow.WINDOW_VIEW); 1555 } 1556 1557 /** 1558 * Create and return the Shop's main MenuSheet. 1559 * 1560 * <p>The default implementation will provide two MenuSheets in the Shop's MenuSheet:</p> 1561 * 1562 * <table border> 1563 * <tr> 1564 * <th>MenuSheet (name/tag)</th> 1565 * <th>Item text</th> 1566 * <th>Item tag</th> 1567 * <th>Item action</th> 1568 * <th>Comments</th> 1569 * </tr> 1570 * <tr> 1571 * <td rowspan=7>Shop {@link #SHOP_MENU_TAG}</td> 1572 * <td>Set current SalesPoint</td> 1573 * <td>{@link #SET_CURRENT_SP_TAG}</td> 1574 * <td>{@link #setCurrentSalesPoint setCurrentSalesPoint()}.</td> 1575 * <td>This is a Sub-MenuSheet that shows all the SalesPoints in the Shop. The user can click the one 1576 * he or she wants to select. As long as this MenuSheet is found in the Shop's MenuSheet, it will 1577 * be updated by calls to {@link #addSalesPoint} and {@link #removeSalesPoint}. 1578 * </td> 1579 * </tr> 1580 * <tr> 1581 * <td><i>Separator</i></td> 1582 * <td>{@link #SEPARATOR_ONE_TAG}</td> 1583 * <td></td> 1584 * <td></td> 1585 * </tr> 1586 * <tr> 1587 * <td>Load...</td> 1588 * <td>{@link #LOAD_TAG}</td> 1589 * <td>Load a persistent Shop image.</td> 1590 * <td></td> 1591 * </tr> 1592 * <tr> 1593 * <td>Save...</td> 1594 * <td>{@link #SAVE_TAG}</td> 1595 * <td>Save current Shop state to create a persistant Shop image.</td> 1596 * <td></td> 1597 * </tr> 1598 * <tr> 1599 * <td><i>Separator</i></td> 1600 * <td>{@link #SEPARATOR_TWO_TAG}</td> 1601 * <td></td> 1602 * <td></td> 1603 * </tr> 1604 * <tr> 1605 * <td>Quit</td> 1606 * <td>{@link #QUIT_SHOP_TAG}</td> 1607 * <td>{@link #quit}.</td> 1608 * <td></td> 1609 * </tr> 1610 * <tr> 1611 * <td>MultiWindow {@link sale.multiwindow.MultiWindow#MULTIWINDOW_MENU_TAG}</td> 1612 * <td>see {@link sale.multiwindow.MultiWindow#getMultiWindowMenuSheet}</td> 1613 * <td></td> 1614 * <td></td> 1615 * </tr> 1616 * </table> 1617 * 1618 * @override Sometimes 1619 */ 1620 protected MenuSheet createShopMenuSheet() { 1621 MenuSheet msBar = new MenuSheet("Shop Menu"); 1622 MenuSheet msShop = new MenuSheet("Shop", SHOP_MENU_TAG, 'S'); 1623 //current SalesPoint 1624 MenuSheet msCurrent = new MenuSheet("Set current SalesPoint", SET_CURRENT_SP_TAG); 1625 //load 1626 MenuSheetItem msiLoad = new MenuSheetItem("Load...", LOAD_TAG, new sale.Action() { 1627 public void doAction(SaleProcess p, SalesPoint sp) throws Throwable { 1628 try { 1629 restore(); 1630 } 1631 catch (CancelledException cexc) { 1632 JOptionPane.showMessageDialog(null, cexc.getMessage(), "Loading cancelled", 1633 JOptionPane.ERROR_MESSAGE); 1634 } 1635 } 1636 }); 1637 msiLoad.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)); 1638 msiLoad.setMnemonic('L'); 1639 msiLoad.setDefaultIcon(LOAD_ICON); 1640 //save 1641 MenuSheetItem msiSave = new MenuSheetItem("Save...", SAVE_TAG, new sale.Action() { 1642 public void doAction(SaleProcess p, SalesPoint sp) throws Throwable { 1643 try { 1644 makePersistent(); 1645 } 1646 catch (CancelledException cexc) { 1647 JOptionPane.showMessageDialog(null, cexc.getMessage(), "Saving cancelled", 1648 JOptionPane.ERROR_MESSAGE); 1649 } 1650 } 1651 }); 1652 msiSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK)); 1653 msiSave.setMnemonic('S'); 1654 msiSave.setDefaultIcon(SAVE_ICON); 1655 //quit 1656 MenuSheetItem msiQuit = new MenuSheetItem("Quit", QUIT_SHOP_TAG, new sale.Action() { 1657 public void doAction(SaleProcess p, SalesPoint sp) { 1658 quit(); 1659 } 1660 }); 1661 msiQuit.setMnemonic('Q'); 1662 //put menu together 1663 msShop.add(msCurrent); 1664 msShop.add(new MenuSheetSeparator(SEPARATOR_ONE_TAG)); 1665 msShop.add(msiLoad); 1666 msShop.add(msiSave); 1667 msShop.add(new MenuSheetSeparator(SEPARATOR_TWO_TAG)); 1668 msShop.add(msiQuit); 1669 //add shop menu to menu bar 1670 msBar.add(msShop); 1671 //add view mode menu to menu bar 1672 if (m_msMultiWindowMenu != null) { 1673 msBar.add(m_msMultiWindowMenu); 1674 } 1675 return msBar; 1676 } 1677 1678 /** 1679 * Get the Shop's timer. If no timer has been set using {@link #setTimer}, the default timer will be a 1680 * {@link StepTimer} with a {@link Step} time. 1681 * 1682 * @override Never 1683 * 1684 * @return the Shop's Timer 1685 */ 1686 public Timer getTimer() { 1687 if (m_trTimer == null) { 1688 m_trTimer = new StepTimer(); 1689 } 1690 return m_trTimer; 1691 } 1692 1693 /** 1694 * Set the Shop's Timer. 1695 * 1696 * @override Never 1697 * 1698 * @param trTimer the Timer to be used from now on 1699 */ 1700 public void setTimer(Timer trTimer) { 1701 m_trTimer = trTimer; 1702 } 1703 1704 /** 1705 * Log a piece of information to the global log file. 1706 * 1707 * @override Never 1708 * 1709 * @param la the information to be logged. 1710 * 1711 * @exception IOException on any error while logging. 1712 */ 1713 public void log(Loggable la) throws IOException { 1714 Log.getGlobalLog().log(la); 1715 } 1716 1717 /// Stock management 1718 1719 /** 1720 * Add a Stock to the global list of Stocks. The Stock can later be identified by its name. 1721 * 1722 * @override Never 1723 * 1724 * @param st the Stock to be added to the global list of Stocks. 1725 * 1726 * @exception DuplicateKeyException if a Stock of the same name already exists in the global list of Stocks. 1727 */ 1728 public void addStock(Stock st) throws DuplicateKeyException { 1729 synchronized (getStocksLock()) { 1730 if (m_mpStocks.containsKey(st.getName())) { 1731 throw new DuplicateKeyException(st.getName()); 1732 } 1733 1734 m_mpStocks.put(st.getName(), st); 1735 st.attach(m_ncStockContext); 1736 } 1737 } 1738 1739 /** 1740 * Remove a Stock from the global list of Stocks. 1741 * 1742 * @override Never 1743 * 1744 * @param sName the name of the Stock to be removed. 1745 * 1746 * @return the removed Stock, if any. 1747 */ 1748 public Stock removeStock(String sName) { 1749 synchronized (getStocksLock()) { 1750 Stock st = (Stock)m_mpStocks.remove(sName); 1751 1752 if (st != null) { 1753 st.detachNC(); 1754 } 1755 1756 return st; 1757 } 1758 } 1759 1760 /** 1761 * Look up a Stock in the global Stock list. 1762 * 1763 * @override Never 1764 * 1765 * @param sName the name of the Stock to be looked up. 1766 * 1767 * @return the Stock, if any. 1768 */ 1769 public Stock getStock(String sName) { 1770 synchronized (getStocksLock()) { 1771 return (Stock)m_mpStocks.get(sName); 1772 } 1773 } 1774 1775 /// Catalog management 1776 1777 /** 1778 * Add a Catalog to the global table of Catalogs. The Catalog will be identifiable by its name. 1779 * 1780 * @override Never 1781 * 1782 * @param c the Catalog to be added to the global list of Catalogs 1783 * 1784 * @exception DuplicateKeyException if a Catalog of the same name already existed in the global list of 1785 * Catalogs. 1786 */ 1787 public void addCatalog(Catalog c) throws DuplicateKeyException { 1788 synchronized (getCatalogsLock()) { 1789 if (m_mpCatalogs.containsKey(c.getName())) { 1790 throw new DuplicateKeyException(c.getName()); 1791 } 1792 1793 m_mpCatalogs.put(c.getName(), c); 1794 c.attach(m_ncCatalogContext); 1795 } 1796 } 1797 1798 /** 1799 * Remove a catalog from the global table of Catalogs. 1800 * 1801 * @override Never 1802 * 1803 * @param sName the name of the Catalog to be removed. 1804 * 1805 * @return the Catalog that was removed, if any. 1806 */ 1807 public Catalog removeCatalog(String sName) { 1808 synchronized (getCatalogsLock()) { 1809 Catalog c = (Catalog)m_mpCatalogs.remove(sName); 1810 1811 if (c != null) { 1812 c.detachNC(); 1813 } 1814 1815 return c; 1816 } 1817 } 1818 1819 /** 1820 * Get a Catalog from the global list of Catalogs. 1821 * 1822 * @override Never 1823 * 1824 * @param sName the name of the Catalog to be returned. 1825 * 1826 * @return the associated Catalog, if any. 1827 */ 1828 public Catalog getCatalog(String sName) { 1829 synchronized (getCatalogsLock()) { 1830 return (Catalog)m_mpCatalogs.get(sName); 1831 } 1832 } 1833 1834 //////////////////////////////////////////////////////////////////////////////////////////////// 1835 // STATIC PART 1836 //////////////////////////////////////////////////////////////////////////////////////////////// 1837 1838 /** 1839 * Constant marking the Shop's state. DEAD means the Shop was either shut down or not started yet. 1840 */ 1841 public final static int DEAD = 0; 1842 1843 /** 1844 * Constant marking the Shop's state. RUNNING means the Shop was started and neither suspended nor shutdown. 1845 */ 1846 public final static int RUNNING = 1; 1847 1848 /** 1849 * Constant marking the Shop's state. SUSPENDED means the Shop was {@link #suspend suspended}. 1850 */ 1851 public final static int SUSPENDED = 2; 1852 1853 /** 1854 * MenuSheetObject tag marking the entire Shop MenuSheet. 1855 */ 1856 public static final String SHOP_MENU_TAG = "__TAG:_SHOP_MENU_"; 1857 1858 /** 1859 * MenuSheetObject tag marking the "Set Current SalesPoint" item. 1860 */ 1861 public static final String SET_CURRENT_SP_TAG = "__TAG:_SHOP_SET_CURRENT_SALESPOINT_"; 1862 1863 /** 1864 * MenuSheetObject tag marking the first separator. 1865 */ 1866 public static final String SEPARATOR_ONE_TAG = "__TAG:_SHOP_SEPARATOR_1_"; 1867 1868 /** 1869 * MenuSheetObject tag marking the "Load..." item. 1870 */ 1871 public static final String LOAD_TAG = "__TAG:_SHOP_LOAD_"; 1872 1873 /** 1874 * MenuSheetObject tag marking the "Save..." item. 1875 */ 1876 public static final String SAVE_TAG = "__TAG:_SHOP_SAVE_"; 1877 1878 /** 1879 * MenuSheetObject tag marking the second separator. 1880 */ 1881 public static final String SEPARATOR_TWO_TAG = "__TAG:_SHOP_SEPARATOR_2_"; 1882 1883 /** 1884 * MenuSheetObject tag marking the "Quit" item. 1885 */ 1886 public static final String QUIT_SHOP_TAG = "__TAG:_SHOP_QUIT_"; 1887 1888 /** 1889 * Icon MenuItem "Load". 1890 */ 1891 private static final ImageIcon LOAD_ICON = new ImageIcon(ResourceManager.getInstance().getResource( 1892 ResourceManager.RESOURCE_GIF, "icon.icon_load_16x16")); 1893 1894 /** 1895 * Icon MenuItem "Save". 1896 */ 1897 private static final ImageIcon SAVE_ICON = new ImageIcon(ResourceManager.getInstance().getResource( 1898 ResourceManager.RESOURCE_GIF, "icon.icon_save_16x16")); 1899 1900 /** 1901 * The singleton instance of the Shop, that is used throughout the entire application. 1902 */ 1903 private static Shop s_shTheShop; 1904 /** 1905 * The monitor used to synchronized access to the singleton. 1906 */ 1907 private static Object s_oShopLock = new Object(); 1908 1909 /** 1910 * Get the global, singleton Shop instance. 1911 */ 1912 public static Shop getTheShop() { 1913 synchronized (s_oShopLock) { 1914 if (s_shTheShop == null) { 1915 setTheShop(new Shop()); 1916 } 1917 1918 return s_shTheShop; 1919 } 1920 } 1921 1922 /** 1923 * Set the global, singleton Shop instance. 1924 * 1925 * <p>This method will only have an effect the next time, {@link #getTheShop} gets called. 1926 * So to avoid inconsistency, use this method only in the beginning of your program, to 1927 * install an instance of a subclass of Shop as the global, singleton Shop instance.</p> 1928 * 1929 * @param shTheShop the new global, singleton Shop instance 1930 */ 1931 public static void setTheShop(Shop shTheShop) { 1932 synchronized (s_oShopLock) { 1933 s_shTheShop = shTheShop; 1934 } 1935 } 1936 }