001 package sale; 002 003 import java.util.*; 004 import java.io.IOException; 005 006 import java.awt.Rectangle; 007 008 import javax.swing.*; 009 import users.*; 010 import log.*; 011 012 import sale.events.*; 013 014 import data.DataBasket; 015 import data.Stock; 016 import data.Catalog; 017 018 import util.*; 019 020 /** 021 * A single point of sale in a shop. 022 * 023 * <p>SalesPoints represent single points of sale in a {@link Shop} at which user interaction occurs. 024 * Examples for such SalesPoints are cash counters, box offices, ticket vending machines etc.</p> 025 * 026 * <p>Normally, you will have at least one SalesPoint in your application.</p> 027 * 028 * <p>Services available at SalesPoints are implemented as {@link SaleProcess processes}. There can be at most 029 * one process running at a SalesPoint at any given point of time.</p> 030 * 031 * <p>SalesPoints are {@link ProcessContext process contexts} to the processes running at them. They provide 032 * data and log access, as well as a display and current user. When a SalesPoint is created, a 033 * {@link Display display} is attached to it, which will be used by the process.</p> 034 * 035 * <p>A {@link User user} can be attached to the SalesPoint. Its capabilities will determine what can and cannot 036 * be done at the SalesPoint.</p> 037 * 038 * @author Steffen Zschaler 039 * @version 2.0 15/07/1999 040 * @since v1.0 041 */ 042 public class SalesPoint extends Object implements ProcessContext, FormSheetListener, SerializableListener { 043 044 /** 045 * ID for serialization. 046 */ 047 private static final long serialVersionUID = 127266087990287650L; 048 049 /** 050 * The process currently running on this SalesPoint, if any. 051 * 052 * @serial 053 */ 054 protected SaleProcess m_pCurProcess; 055 056 /** 057 * The monitor synchronizing process access. 058 */ 059 private transient Object m_oProcessLock; 060 061 /** 062 * Return the monitor synchronizing process access. 063 * 064 * @override Never 065 */ 066 protected final Object getProcessLock() { 067 if (m_oProcessLock == null) { 068 m_oProcessLock = new Object(); 069 } 070 071 return m_oProcessLock; 072 } 073 074 /** 075 * The name of this SalesPoint. 076 * 077 * @serial 078 */ 079 private String m_sName; 080 081 /** 082 * ID of this SalesPoint. As SalesPoints can share the same name, the ID is used as unique 083 * identifier. 084 * 085 * @serial 086 */ 087 private int m_iID = -1; 088 089 /** 090 * A stack which saves the process on this SalesPoint. When a new process is started on the SalesPoint 091 * the currently running process is pushed onto the stack. 092 */ 093 private Stack<SaleProcess> m_stkProcess = new Stack<SaleProcess>(); 094 095 096 /** 097 * The display currently attached to this SalesPoint, if any. This will be saved/restored by the Shop. 098 */ 099 private transient Display m_dDisplay; 100 101 /** 102 * The monitor synchronizing access to the display. 103 */ 104 private transient Object m_oDisplayLock; 105 106 /** 107 * Return the monitor synchronizing access to the display. 108 * 109 * @override Never 110 */ 111 private final Object getDisplayLock() { 112 if (m_oDisplayLock == null) { 113 m_oDisplayLock = new Object(); 114 } 115 116 return m_oDisplayLock; 117 } 118 119 /** 120 * The status display currently attached to this SalesPoint, if any. 121 * 122 * @serial 123 */ 124 private Display m_dStatus; 125 126 /** 127 * The monitor synchronizing access to the status display. 128 */ 129 private transient Object m_oStatusDisplayLock; 130 131 /** 132 * Return the monitor synchronizing access to the status display. 133 * 134 * @override Never 135 */ 136 private final Object getStatusDisplayLock() { 137 if (m_oStatusDisplayLock == null) { 138 m_oStatusDisplayLock = new Object(); 139 } 140 141 return m_oStatusDisplayLock; 142 } 143 144 /** 145 * Flag indicating whether or not the SalesPoint is currently suspended. 146 * 147 * @serial 148 */ 149 private boolean m_fSuspended = false; 150 151 /** 152 * The monitor synchronizing access to the closing-process. 153 */ 154 private transient Object m_oCloseLock; 155 156 /** 157 * Return the monitor synchronizing access to the closing-process. 158 * 159 * @override Never 160 */ 161 private final Object getCloseLock() { 162 if (m_oCloseLock == null) { 163 m_oCloseLock = new Object(); 164 } 165 166 return m_oCloseLock; 167 } 168 169 /** 170 * Flag indicating whether or not the SalesPoint is currently closing. 171 * 172 * @serial 173 */ 174 private boolean m_fIsClosing = false; 175 176 /** 177 * The DataBasket currently attached to this SalesPoint, if any. 178 * 179 * @serial 180 */ 181 private DataBasket m_dbBasket; 182 183 /** 184 * The monitor synchronizing access to the DataBasket. 185 */ 186 private transient Object m_oBasketLock; 187 188 /** 189 * Return the monitor synchronizing access to the DataBasket. 190 * 191 * @override Never 192 */ 193 private final Object getBasketLock() { 194 if (m_oBasketLock == null) { 195 m_oBasketLock = new Object(); 196 } 197 198 return m_oBasketLock; 199 } 200 201 /** 202 * The User currently attached to this SalesPoint, if any. 203 * 204 * @serial 205 */ 206 private User m_usrUser; 207 208 /** 209 * The monitor synchronizing access to the User. 210 */ 211 private transient Object m_oUserLock; 212 213 /** 214 * The SalesPoints Framebounds. 215 */ 216 private Rectangle m_rSalesPointFrameBounds = null; 217 218 //private static int m_iInt = 0; 219 220 /** 221 * Return the monitor synchronizing access to the User. 222 * 223 * @override Never 224 */ 225 private final Object getUserLock() { 226 if (m_oUserLock == null) { 227 m_oUserLock = new Object(); 228 } 229 230 return m_oUserLock; 231 } 232 233 /** 234 * SalesPoint store no data except the default serializable fields. This method exists only for debugging 235 * purposes. 236 */ 237 private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { 238 util.Debug.print("Writing SalesPoint: \"" + getName() + "\".", -1); 239 240 oos.defaultWriteObject(); 241 242 util.Debug.print("Finished writing SalesPoint: \"" + getName() + "\".", -1); 243 } 244 245 /** 246 * Create a new SalesPoint. 247 * 248 * @param sName the name of the SalesPoint. 249 */ 250 public SalesPoint(String sName) { 251 super(); 252 253 m_sName = sName; 254 } 255 256 /** 257 * Return the name of this SalesPoint. 258 * 259 * @override Never 260 */ 261 public String getName() { 262 return m_sName; 263 } 264 265 /** 266 * Return the ID of this SalesPoint; 267 */ 268 public int getID() { 269 return m_iID; 270 } 271 272 /** 273 * Computes a new ID for this SalesPoint.<br/> 274 * 275 * @param points The SalesPoints to be taken into consideration when computing a unique ID. 276 * 277 * @override never 278 */ 279 public void createNewID(Collection<SalesPoint> points) { 280 boolean found = false; 281 do { 282 int i = new Double(Math.random() * 1000000000).intValue(); 283 if (points == null) { 284 m_iID = i; 285 found = true; 286 break; 287 } 288 Iterator<SalesPoint> it = points.iterator(); 289 290 int spid = -1; 291 while (it.hasNext() && spid != i) { 292 Object o = it.next(); 293 if (o instanceof SalesPoint) { 294 spid = ((SalesPoint)o).getID(); 295 } 296 } 297 //iterated over all points, but id was not found -> use it, it is unique 298 if (spid != i) { 299 m_iID = i; 300 found = true; 301 } 302 //id already used, try again 303 } while (!found); 304 } 305 306 307 /** 308 * Check whether this SalesPoint can be closed. Unless the SalesPoint has been 309 * {@link #suspend suspended} before calling <code>canQuit</code> the result of this method may not 310 * be reliable. 311 * 312 * <p>By default, if a process runs on the SalesPoint its {@link SaleProcess#canQuit canQuit} 313 * method is called. If no process is running, and <code>fNoPersistence</code> is <code>true</code>, 314 * {@link #onCanQuit} is called which opens a MsgForm to ask the user whether he/she really wants to close 315 * the SalesPoint. If <code>fNoPersistence</code> is <code>false</code> and no process is running on the 316 * SalesPoint, the default implementation always returns <code>true</code>. 317 * 318 * <p>This method is usually not called directly.</p> 319 * 320 * @override Sometimes See above for an description of the default implementation. 321 * 322 * @param fNoPersistence <code>true</code> if the call to <code>canQuit</code> resulted from an 323 * explicit call to {@link #quit} or from a call to {@link Shop#shutdown Shop.shutdown (false)}. If 324 * <code>fNoPersistence == false</code> you can assume, the state of the SalesPoint will be made persistent 325 * before it is closed. 326 */ 327 protected boolean canQuit(boolean fNoPersistence) { 328 synchronized (getProcessLock()) { 329 if (m_pCurProcess != null) { 330 return m_pCurProcess.canQuit(fNoPersistence); 331 } else { 332 if (fNoPersistence) { 333 return onCanQuit(); 334 } else { 335 return true; 336 } 337 } 338 } 339 } 340 341 /** 342 * Hook method called to determine whether a SalesPoint with no process running on it can be closed by an 343 * explicit quit call. In this method you can assume that the state of the SalesPoint will not be saved and 344 * therefore will not be restoreable. 345 * 346 * @override Sometimes The default implementation opens a {@link sale.stdforms.MsgForm} asking the user if 347 * they really want to close the SalesPoint. 348 * 349 * @return true if the SalesPoint can be closed, false otherwise. 350 */ 351 protected boolean onCanQuit() { 352 JDisplayDialog jddConfirmer = new JDisplayDialog(); 353 354 final boolean[] abResult = { 355 true}; 356 final sale.stdforms.MsgForm mf = new sale.stdforms.MsgForm("Closing \"" + getName() + "\"", 357 "Are you sure, you want to close this SalesPoint?"); 358 mf.removeAllButtons(); 359 mf.addButton("Yes", 0, new sale.Action() { 360 private static final long serialVersionUID = -2170479158438829524L; 361 public void doAction(SaleProcess p, SalesPoint sp) { 362 mf.close(); 363 } 364 }); 365 366 mf.addButton("No", 1, new sale.Action() { 367 private static final long serialVersionUID = -1797203559731519531L; 368 public void doAction(SaleProcess p, SalesPoint sp) { 369 abResult[0] = false; 370 mf.close(); 371 } 372 }); 373 374 jddConfirmer.setVisible(true); 375 try { 376 jddConfirmer.setFormSheet(mf); 377 } 378 catch (InterruptedException ie) { 379 return false; 380 } 381 382 return abResult[0]; 383 } 384 385 /** 386 * Close the SalesPoint. 387 * 388 * <p>First {@link #suspend suspends} the SalesPoint, then calls {@link #canQuit}. If that returns 389 * <code>false</code>, <code>quit</code> will {@link #resume} the SalesPoint and return. Otherwise, 390 * it {@link SaleProcess#quit quits} the current process, if any, and 391 * {@link Shop#removeSalesPoint removes the SalesPoint from the Shop}.</p> 392 * 393 * @override Never 394 */ 395 public void quit() { 396 SaleProcess p = null; 397 398 synchronized (getCloseLock()) { 399 if (!m_fIsClosing) { 400 m_fIsClosing = true; 401 402 synchronized (getProcessLock()) { 403 try { 404 suspend(); 405 } 406 catch (InterruptedException e) {} 407 408 if (!canQuit(true)) { 409 resume(); 410 m_fIsClosing = false; 411 return; 412 } 413 414 p = m_pCurProcess; 415 m_pCurProcess = null; 416 } 417 418 if (p != null) { 419 try { 420 // This will resume the process and block until it is finished. 421 p.quit(true); 422 } 423 catch (InterruptedException e) {} 424 } 425 426 Shop.getTheShop().removeSalesPoint(this); 427 } 428 } 429 } 430 431 // logging 432 /** 433 * Hook method called when the SalesPoint is added to a Shop. You can write a log entry here. 434 * 435 * @override Sometimes The default implementation does nothing. 436 * 437 * @see Shop#addSalesPoint 438 * @see Log 439 * @see LogEntry 440 */ 441 protected void logSalesPointOpened() {} 442 443 /** 444 * Hook method called when the SalesPoint is removed from a Shop. You can write a log entry here. 445 * 446 * @override Sometimes The default implementation does nothing. 447 * 448 * @see Shop#removeSalesPoint 449 * @see Log 450 * @see LogEntry 451 */ 452 protected void logSalesPointClosed() {} 453 454 // Process management 455 456 /** 457 * Starts a process on this SalesPoint. 458 * 459 * <p>The process will run in the context of this SalesPoint, and with the 460 * DataBasket attached to this SalesPoint.</p> 461 * 462 * <p>If there is already a process running on this SalesPoint it will be suspended until 463 * the new process has finished.</p> 464 * 465 * @override Never 466 * 467 * @param p the process to be run. 468 */ 469 public final void runProcess(SaleProcess p) { 470 runProcess(p, getBasket()); 471 } 472 473 474 /** 475 * Starts a process on this SalesPoint. 476 * 477 * <p>The process will run in the context of this SalesPoint, and with the 478 * DataBasket attached to this SalesPoint.</p> 479 * 480 * <p>If there is already a process running on this SalesPoint it will be suspended until 481 * the new process has finished.</p> 482 * 483 * @override Never 484 * 485 * @param p the process to be run. 486 * @param db the DataBasket to be attached to the new process 487 */ 488 public final void runProcess(SaleProcess p, DataBasket db) { 489 synchronized (getProcessLock()) { 490 if (!m_fSuspended) { 491 try { 492 if (m_pCurProcess != null) { 493 m_pCurProcess.suspend(); 494 } 495 } 496 catch (InterruptedException ex) { 497 } 498 m_stkProcess.push(m_pCurProcess); 499 m_pCurProcess = p; 500 p.attach((ProcessContext)this); 501 p.attach(db); 502 p.start(); 503 } 504 } 505 506 } 507 508 /** 509 * Get the process currently running on this SalesPoint, if any. 510 * 511 * @override Never 512 */ 513 public final SaleProcess getCurrentProcess() { 514 synchronized (getProcessLock()) { 515 return m_pCurProcess; 516 } 517 } 518 519 /** 520 * Suspend the SalesPoint. 521 * 522 * <p>If a process is running on the SalesPoint, it is {@link SaleProcess#suspend suspended}. 523 * The method will only return when the SalesPoint has been properly suspended.</p> 524 * 525 * @override Never 526 * 527 * @exception InterruptedException if an interrupt occurred while waiting for the SalesPoint to be 528 * suspended. 529 */ 530 public void suspend() throws InterruptedException { 531 synchronized (getProcessLock()) { 532 533 m_fSuspended = true; 534 535 if (m_pCurProcess != null) { 536 m_pCurProcess.suspend(); 537 } 538 } 539 } 540 541 /** 542 * Resume the SalesPoint. 543 * 544 * <p>If a process is running on the SalesPoint, it is {@link SaleProcess#resume resumed}.</p> 545 * 546 * @override Never 547 */ 548 public void resume() { 549 synchronized (getProcessLock()) { 550 551 if (m_pCurProcess != null) { 552 m_pCurProcess.resume(); 553 } 554 555 m_fSuspended = false; 556 } 557 } 558 559 // User management 560 /** 561 * Attach a user to this SalesPoint. 562 * 563 * <p>The user attached to a SalesPoint can be accessed by processes running 564 * on the SalesPoint an can be used to determine capabilities etc.</p> 565 * 566 * @override Never 567 * 568 * @param usr the user to be attached. 569 * @return the user that was previously attached to this SalesPoint, if any. 570 */ 571 public User attach(User usr) { 572 synchronized (getUserLock()) { 573 User usrOld = m_usrUser; 574 575 m_usrUser = usr; 576 577 return usrOld; 578 } 579 } 580 581 /** 582 * Detach any user currently attached to this SalesPoint. 583 * 584 * @override Never 585 * 586 * @return the user that was previously attached to this SalesPoint, if any. 587 */ 588 public User detachUser() { 589 return attach((User)null); 590 } 591 592 /** 593 * Get the user currently attached to this SalesPoint. 594 * 595 * @override Never 596 * 597 * @return the user currently attached to this SalesPoint, if any. 598 */ 599 public User getUser() { 600 synchronized (getUserLock()) { 601 return m_usrUser; 602 } 603 } 604 605 // DataBasket management 606 /** 607 * Attach a DataBasket to this SalesPoint. 608 * 609 * @override Never 610 * 611 * @param db the DataBasket to be attached. 612 * @return the DataBasket that was previously attached to this SalesPoint, if any. 613 */ 614 public DataBasket attach(DataBasket db) { 615 synchronized (getBasketLock()) { 616 DataBasket dbOld = m_dbBasket; 617 618 m_dbBasket = db; 619 620 return dbOld; 621 } 622 } 623 624 /** 625 * Detach any DataBasket currently attached to this SalesPoint. 626 * 627 * @override Never 628 * 629 * @return the DataBasket that was previously attached to this SalesPoint, if any. 630 */ 631 public DataBasket detachBasket() { 632 return attach((DataBasket)null); 633 } 634 635 /** 636 * Get the DataBasket currently attached to this SalesPoint. 637 * 638 * @override Never 639 * 640 * @return the DataBasket currently attached to this SalesPoint, if any. 641 */ 642 public DataBasket getBasket() { 643 synchronized (getBasketLock()) { 644 return m_dbBasket; 645 } 646 } 647 648 // display management 649 /** 650 * Attach a status display to the SalesPoint. 651 * 652 * <p>This display can be used to give status information about the SalesPoint. It can also 653 * be used to trigger background processes for the SalesPoint. It should not be used to trigger 654 * processes on the SalesPoint.</p> 655 * 656 * <p>If the given display is not <code>null</code>, it must be {@link Display#isUseableDisplay useable}.</p> 657 * 658 * @override Never 659 * 660 * @param dStatus the new status display for this SalesPoint. 661 * 662 * @return the previous status display, if any. 663 */ 664 protected Display attachStatusDisplay(Display dStatus) { 665 synchronized (getStatusDisplayLock()) { 666 if (m_dStatus != null) { 667 m_dStatus.closeFormSheet(); 668 m_dStatus.setMenuSheet(null); 669 } 670 671 Display dReturn = m_dStatus; 672 m_dStatus = dStatus; 673 674 setStatusFormSheet(getDefaultStatusFormSheet()); 675 setStatusMenuSheet(getDefaultStatusMenuSheet()); 676 677 return dReturn; 678 } 679 } 680 681 /** 682 * Detach the current status display. 683 * 684 * @override Never 685 * 686 * @return the previous status display, if any. 687 */ 688 protected Display detachStatusDisplay() { 689 return attachStatusDisplay(null); 690 } 691 692 /** 693 * Set a FormSheet in the SalesPoint's status display. 694 * 695 * <p>Status display FormSheet's are always nonmodal, which is why this method returns 696 * immediately after setting the FormSheet and can never throw an InterruptedException.</p> 697 * 698 * @override Never 699 * 700 * @param fs the FormSheet to be set. 701 */ 702 protected void setStatusFormSheet(FormSheet fs) { 703 synchronized (getStatusDisplayLock()) { 704 if (m_dStatus == null) { 705 return; 706 } 707 708 if (fs != null) { 709 fs.setWaitResponse(false); 710 fs.attach(this); 711 } 712 713 try { 714 m_dStatus.setFormSheet(fs); 715 } 716 catch (InterruptedException e) {} 717 } 718 } 719 720 /** 721 * Set a MenuSheet in the SalesPoint's status display. 722 * 723 * @override Never 724 * 725 * @param ms the MenuSheet to be set. 726 */ 727 protected void setStatusMenuSheet(MenuSheet ms) { 728 synchronized (getStatusDisplayLock()) { 729 if (m_dStatus == null) { 730 return; 731 } 732 733 if (ms != null) { 734 ms.attach(this); 735 } 736 737 m_dStatus.setMenuSheet(ms); 738 } 739 } 740 741 /** 742 * Get the default status MenuSheet for this SalesPoint. 743 * 744 * <p>Unless you specify differently through an explicit call to {@link #setStatusMenuSheet}, the Framework 745 * will use this MenuSheet for the SalesPoint's status display.</p> 746 * 747 * @override Sometimes The default implementation returns <code>null</code> to indicate no MenuSheet. 748 * 749 * @see #attachStatusDisplay 750 */ 751 protected MenuSheet getDefaultStatusMenuSheet() { 752 return null; 753 } 754 755 /** 756 * Get the default status FormSheet for this SalesPoint. 757 * 758 * <p>Unless you specify differently through an explicit call to {@link #setStatusFormSheet}, the Framework 759 * will use this FormSheet for the SalesPoint's status display.</p> 760 * 761 * @override Sometimes The default implementation returns an empty FormSheet. 762 * 763 * @see #attachStatusDisplay 764 */ 765 protected FormSheet getDefaultStatusFormSheet() { 766 767 FormSheetContentCreator fscc = new FormSheetContentCreator() { 768 private static final long serialVersionUID = 2456872618340060101L; 769 protected void createFormSheetContent(final FormSheet fs) { 770 771 fs.setComponent(new JPanel()); 772 773 fs.removeAllButtons(); 774 } 775 }; 776 777 return new FormSheet(getName(), fscc, false); 778 } 779 780 /** 781 * Internal communication method called by Shop to attach a display during restoration of the Shop 782 * from a stream. 783 * 784 * @override Never 785 * 786 * @param d the display just loaded. 787 */ 788 public void attachLoadedDisplay(Display d) { 789 synchronized (getDisplayLock()) { 790 if (hasUseableDisplay(null)) { 791 detachDisplay(); 792 } 793 794 m_dDisplay = d; 795 } 796 } 797 798 /** 799 * Attach a new display to the SalesPoint. 800 * 801 * <p>Any Form- or MenuSheets displayed on the current display will be removed, and the SalesPoint's 802 * default sheets will be set on the new display.</p> 803 * 804 * @override Never 805 * 806 * @param d the new display 807 * 808 * @return the previously attached display, if any. 809 * 810 * @see #getDefaultFormSheet 811 * @see #getDefaultMenuSheet 812 */ 813 public Display attach(Display d, boolean fSetDefaultSheets) { 814 synchronized (getDisplayLock()) { 815 if (hasUseableDisplay(null)) { 816 m_dDisplay.removeFormSheetListener(this); 817 if (fSetDefaultSheets) { 818 try { 819 m_dDisplay.setFormSheet(null); 820 } 821 catch (InterruptedException e) {} 822 m_dDisplay.setMenuSheet(null); 823 } 824 } 825 826 Display dOld = m_dDisplay; 827 828 m_dDisplay = d; 829 830 // We can't use the previous FormSheet on the new display, as it will have been cancelled by the 831 // setFormSheet call. 832 if (hasUseableDisplay(null)) { 833 m_dDisplay.addFormSheetListener(this); 834 if (fSetDefaultSheets) { 835 setDefaultSheets(); 836 } 837 } 838 839 return dOld; 840 } 841 } 842 843 public Display attach(Display d) { 844 return attach(d, true); 845 } 846 847 848 /** 849 * Detach the current display. 850 * 851 * <p>Any Form- or MenuSheet on the current display will be removed before detaching the display.</p> 852 * 853 * @override Never 854 * 855 * @return the detached display, if any. 856 */ 857 public Display detachDisplay() { 858 return attach((Display)null); 859 } 860 861 /** 862 * Return the display of this SalesPoint. 863 * 864 * @return the display of this SalesPoint. 865 */ 866 public Display getDisplay() { 867 return m_dDisplay; 868 } 869 870 /** 871 * Set the default Form- and MenuSheet on the currently attached display. 872 * 873 * @override Never 874 * 875 * @see #getDefaultMenuSheet 876 * @see #getDefaultFormSheet 877 */ 878 private void setDefaultSheets() { 879 synchronized (getDisplayLock()) { 880 if (hasUseableDisplay(null)) { 881 FormSheet fs = getDefaultFormSheet(); 882 883 if (fs != null) { 884 fs.setWaitResponse(false); 885 fs.attach(this); 886 } 887 888 try { 889 m_dDisplay.setFormSheet(fs); 890 } 891 catch (InterruptedException e) {} 892 893 MenuSheet ms = getDefaultMenuSheet(); 894 895 if (ms != null) { 896 ms.attach(this); 897 } 898 899 m_dDisplay.setMenuSheet(ms); 900 } 901 } 902 } 903 904 /** 905 * Get the default FormSheet for this SalesPoint. 906 * 907 * <p>The default FormSheet will be displayed whenever there is a current user (and, thus, a display), 908 * but no process is running and no other FormSheet is being displayed.</p> 909 * 910 * @override Always The default implementation returns a FormSheet that simply states the name of the 911 * SalesPoint. 912 * 913 * @return the default FormSheet, if any. 914 */ 915 protected FormSheet getDefaultFormSheet() { 916 return new sale.stdforms.MsgForm(getName(), "This is the default FormSheet of SalesPoint " + getName()); 917 } 918 919 /** 920 * Get the default MenuSheet for this SalesPoint. 921 * 922 * <p>The default MenuSheet will be displayed whenever there is a current user (and, thus, a display), 923 * but no process is running.</p> 924 * 925 * @override Always The default implementation returns <code>null</code> indicating no MenuSheet. 926 * 927 * @return the default MenuSheet, if any. 928 */ 929 protected MenuSheet getDefaultMenuSheet() { 930 return null; 931 } 932 933 // ProcessContext methods 934 /** 935 * Allow a process to set a FormSheet on the SalesPoint's current display. 936 * 937 * <p>The process launching the FormSheet as well as this SalesPoint will be attached to the 938 * FormSheet prior to displaying it. Thus, Actions triggered by the FormSheet will run in the 939 * correct context and will be able to access the process and the SalesPoint.</p> 940 * 941 * <p>If the FormSheet requests that the Framework wait for it being closed, 942 * <code>setFormSheet()</code> will block until the FormSheet was closed or an interrupt occured.</p> 943 * 944 * @override Never 945 * 946 * @param p the process that wants to display the FormSheet. 947 * @param fs the FormSheet to be displayed. 948 * 949 * @exception InterruptedException if an interrupt occurred while waiting for the FormSheet to be 950 * closed. 951 * 952 * @see sale.Action 953 * @see FormSheet#waitResponse 954 */ 955 public void setFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 956 957 if (fs != null) { 958 fs.attach(p); 959 fs.attach(this); 960 } 961 962 Display d; 963 synchronized (getDisplayLock()) { 964 d = m_dDisplay; 965 } 966 967 // not in synchronized, as this call might block, and we don't want dead locks. 968 d.setFormSheet(fs); 969 } 970 971 /** 972 * Sets the Framebounds of the SalesPoints assoziated Display (JDisplayFrame). 973 * 974 * <p>Example:<p> 975 * <code>sp.setSalesPointFrameBounds (new Rectangle (10,10,200,200));<code> 976 * 977 * This moves the SalesPointFrame to Position (10,10) with a size of (200,200). 978 * 979 * @override Sometimes 980 */ 981 public void setSalesPointFrameBounds(Rectangle r) { 982 if (getDisplay() == null) { 983 m_rSalesPointFrameBounds = r; 984 } else { 985 m_rSalesPointFrameBounds = r; 986 if (Shop.getTheShop() != null) { 987 if (Shop.getTheShop().getShopState() == Shop.RUNNING) { 988 getDisplay().setBounds(r); 989 990 } 991 } 992 } 993 } 994 995 /** 996 * Returns the Framebounds of the SalesPoints assoziated Display(JDisplayFrame). 997 * 998 * @override Sometimes 999 */ 1000 public Rectangle getSalesPointFrameBounds() { 1001 return m_rSalesPointFrameBounds; 1002 } 1003 1004 /** 1005 * Allow a process to pop up a FormSheet on the SalesPoint's current display. 1006 * 1007 * <p>The process launching the FormSheet as well as this SalesPoint will be attached to the 1008 * FormSheet prior to displaying it. Thus, Actions triggered by the FormSheet will run in the 1009 * correct context and will be able to access the process and the SalesPoint.</p> 1010 * 1011 * <p>If the FormSheet requests that the Framework wait for it being closed, 1012 * <code>popUpFormSheet</code> will block until the FormSheet was closed or an interrupt occured.</p> 1013 * 1014 * @override Never 1015 * 1016 * @param p the process that wants to display the FormSheet. 1017 * @param fs the FormSheet to be displayed. 1018 * 1019 * @exception InterruptedException if an interrupt occurred while waiting for the FormSheet to be 1020 * closed. 1021 * 1022 * @see sale.Action 1023 * @see FormSheet#waitResponse 1024 */ 1025 public void popUpFormSheet(SaleProcess p, FormSheet fs) throws InterruptedException { 1026 1027 if (fs != null) { 1028 fs.attach(p); 1029 fs.attach(this); 1030 } 1031 1032 Display d; 1033 synchronized (getDisplayLock()) { 1034 d = m_dDisplay; 1035 } 1036 1037 d.popUpFormSheet(fs); 1038 } 1039 1040 /** 1041 * Allow a process to set a MenuSheet on the SalesPoint's current display. 1042 * 1043 * <p>The process setting the MenuSheet as well as this SalesPoint will be attached to the 1044 * MenuSheet prior to displaying it. Thus, Actions triggered by the MenuSheet will run in the 1045 * correct context and will be able to access the process and the SalesPoint.</p> 1046 * 1047 * @override Never 1048 * 1049 * @param p the process that wants to display the MenuSheet. 1050 * @param ms the MenuSheet to be displayed. 1051 * 1052 * @see sale.Action 1053 */ 1054 public void setMenuSheet(SaleProcess p, MenuSheet ms) { 1055 1056 if (ms != null) { 1057 ms.attach(p); 1058 ms.attach(this); 1059 } 1060 1061 synchronized (getDisplayLock()) { 1062 m_dDisplay.setMenuSheet(ms); 1063 } 1064 } 1065 1066 /** 1067 * True, if the SalesPoint currently has a display and this display is useable. 1068 * 1069 * @override Never 1070 * 1071 * @param p the process querying, unused. 1072 * 1073 * @see Display#isUseableDisplay 1074 */ 1075 public boolean hasUseableDisplay(SaleProcess p) { 1076 synchronized (getDisplayLock()) { 1077 return ((m_dDisplay != null) && (m_dDisplay.isUseableDisplay())); 1078 } 1079 } 1080 1081 /** 1082 * Log the given Loggable. 1083 * 1084 * <p>The given loggable object will be logged into the global log file unless you override this method.</p> 1085 * 1086 * @override Sometimes 1087 * 1088 * @exception IOException if an error occurs while trying to write the log data. 1089 * 1090 * @param p the SalesProcess demanding logging, unused. 1091 * @param la the object to be logged. 1092 */ 1093 public void log(SaleProcess p, Loggable la) throws IOException { 1094 Shop.getTheShop().log(la); 1095 } 1096 1097 /** 1098 * Get the current user for the given process. This is the user, if any, 1099 * previously attached with the {@link #attach(User)} method. 1100 * 1101 * @override Never 1102 * 1103 * @param p the process querying, unused. 1104 * 1105 * @return the current user 1106 * 1107 * @see #getUser 1108 */ 1109 public final User getCurrentUser(SaleProcess p) { 1110 return getUser(); 1111 } 1112 1113 /** 1114 * Return a Stock for a given name. 1115 * 1116 * @override Sometimes 1117 * 1118 * @param sName the name of the Stock. 1119 */ 1120 public Stock getStock(String sName) { 1121 return Shop.getTheShop().getStock(sName); 1122 } 1123 1124 /** 1125 * Return a Catalog for a given name. 1126 * 1127 * @override Sometimes 1128 * 1129 * @param sName the name of the Catalog. 1130 */ 1131 public Catalog getCatalog(String sName) { 1132 return Shop.getTheShop().getCatalog(sName); 1133 } 1134 1135 /** 1136 * Notification that a process started on a SalesPoint. 1137 * 1138 * <p>This will remove all SalesPoint owned Form- and MenuSheets from the display, as to 1139 * make room for the process' sheets.</p> 1140 * 1141 * @override Never 1142 * 1143 * @param p the process that was just launched. 1144 */ 1145 public void processStarted(SaleProcess p) { 1146 synchronized (getDisplayLock()) { 1147 if (hasUseableDisplay(null)) { 1148 m_dDisplay.removeFormSheetListener(this); 1149 1150 try { 1151 m_dDisplay.setFormSheet(null); 1152 } 1153 catch (InterruptedException e) {} 1154 m_dDisplay.setMenuSheet(null); 1155 } 1156 } 1157 } 1158 1159 /** 1160 * Notification that a process finished running on this SalesPoint. 1161 * 1162 * <p>This will restore a previously interrupted SaleProcess or the SalesPoint's default sheets if 1163 * there is no more pending process.</p> 1164 * 1165 * @override Never 1166 */ 1167 public void processFinished(SaleProcess p) { 1168 synchronized (getProcessLock()) { 1169 //m_pCurProcess will be null if the stack has 0 or 1 entries 1170 if (m_stkProcess.size() > 0) {//normally not executed, just a check in case method was called directly 1171 m_pCurProcess = (SaleProcess)m_stkProcess.pop(); 1172 } else { 1173 m_pCurProcess = null; 1174 } 1175 if (m_pCurProcess != null) { 1176 m_pCurProcess.resume(); 1177 } else { 1178 synchronized (getDisplayLock()) { 1179 if (hasUseableDisplay(null)) { 1180 m_dDisplay.addFormSheetListener(this); 1181 setDefaultSheets(); 1182 } 1183 } 1184 } 1185 } 1186 } 1187 1188 // FormSheetListener interface methods 1189 /** 1190 * Dummy interface to conform by the FormSheetListener interface. 1191 * 1192 * @override Never 1193 */ 1194 public void formSheetSet(FormSheetEvent e) { 1195 } 1196 1197 /** 1198 * Implemented to make sure there always is a FormSheet. 1199 * 1200 * @override Never 1201 */ 1202 public void formSheetRemoved(FormSheetEvent e) { 1203 if (e.isExplicit()) { 1204 synchronized (getDisplayLock()) { 1205 if (hasUseableDisplay(null)) { 1206 FormSheet fs = getDefaultFormSheet(); 1207 1208 if (fs != null) { 1209 fs.setWaitResponse(false); 1210 fs.attach(this); 1211 } 1212 1213 try { 1214 m_dDisplay.setFormSheet(fs); 1215 } 1216 catch (InterruptedException ex) {} 1217 } 1218 } 1219 } 1220 } 1221 1222 /** 1223 * Return a String description of this SalesPoint: the name. 1224 * 1225 * @override Sometimes 1226 */ 1227 public String toString() { 1228 return getName(); 1229 } 1230 1231 /** 1232 * Put an object into the ProcessContext. 1233 * 1234 * @override Never 1235 * 1236 * @param sKey object's identifier 1237 * @param oData the data object 1238 * 1239 */ 1240 public void setProcessData(String sKey, Object oData) { 1241 Shop.getTheShop().setProcessData(sKey, oData); 1242 } 1243 1244 /** 1245 * Get an object from the ProcessContext. 1246 * 1247 * @override Never 1248 * 1249 * @param sKey object's key 1250 * 1251 * @return the object from ProcessContext 1252 */ 1253 public Object getProcessData(String sKey) { 1254 return Shop.getTheShop().getProcessData(sKey); 1255 } 1256 1257 }