HowTos
Aus Salespoint
The following so called "HowTos" will help you in developing with the SalesPoint framework by providing little examples and explanations for the most common framework components.
Application Architecture
Shop
Implement a Shop
Description: The Shop is the central class of a SalesPoint application. You can run Processes on the Shop itself, you can add SalesPoints where you can run Processes too. The Shop provides a central MenuSheet, where important actions could be invoked (save, load, quit etc.). You may wish to change this MenuSheet in order to open SalesPoints or to offer additional user interaction. All sorts of central data should be stored here, like Catalogs, Stocks, Currencys, MoneyBags, UserManagers and other attributes that are of global use. It is important to make a singleton instance of Shop and declare it with the static Shop.setTheShop() method. If you need the Shop instance, call Shop.getTheShop(). Don´t try to use the instance, you can get in your Shops methods, because very unlovely runtime errors may be the result.
ToDo:
- Create a subclass of Shop.
- Add constructor to create singleton instance of Shop. You can initialize some data in the constructor
- For invoking the Shop, create a simple class with the public static void main (String[] noArgs) method.
- Therein create an instance of the Shop and initialize its attribute of the singleton Shop instance by the setTheShop(Shop TutorialShop) method. Furthermore call the start() method to start the Shop.
- The main class is also used to add a SalesPoint (or even more) to the Shop here.
Example Source Code:
Shop class:
import sale.AutoTimer;
import sale.CalendarTime;
import sale.Shop;
import sale.events.TimerEvent;
import sale.events.TimerListener;
1
public class ArchitectureShop extends Shop
{
2
public ArchitectureShop()
{
super();
CalendarTime calendarTime = new CalendarTime();
calendarTime.setTimeToCount(CalendarTime.SECOND);
AutoTimer autoTimer = new AutoTimer(calendarTime, (long) 992);
autoTimer.addTimerListener(new TimerListener()
{
public void onGoneAhead(TimerEvent timerEvent)
{
System.out.println(timerEvent.getTime());
}
public void onTimeSet(TimerEvent timerEvent)
{
}
public void onIntervalSet(TimerEvent timerEvent)
{
}
});
autoTimer.start();
}
}
Main class:
// necessary imports
import sale.Shop;
import application_architecture.ArchitectureSalesPoint;
import application_architecture.ArchitectureShop;
import display.DisplaySalesPoint;
3
public class Tutorial
{
public static void main(String[] args)
{
4
ArchitectureShop myTutorialShop = new ArchitectureShop();
Shop.setTheShop(myTutorialShop);
myTutorialShop.start();
5
myTutorialShop.addSalesPoint(new ArchitectureSalesPoint("ArchitectureSalesPoint"));
myTutorialShop.addSalesPoint(new DisplaySalesPoint("DisplaySalesPoint"));
}
}
React on adding and removing SalesPoints
Description: This is required whenever reaction on adding or removing SalesPoints is needed, such as manipulating menus or notifying SalesPoints already opened.
ToDo:
- Open the designated Shop class.
- Override the protected void onSalesPointAdded() and/or protected void onSalesPointRemoved() methods to react on the adding or removing. Be sure to synchronize your method with the one of the original Shop by first calling it's super.onSalesPoint....
Example Source Code:
1
public class ArchitectureShop extends Shop
{
.
.
.
2
protected void onSalesPointAdded(SalesPoint sp) {
super.onSalesPointAdded(sp);
. . . react on adding a SalesPoint . . .
}
protected void onSalesPointRemoved(SalesPoint sp) {
super.onSalesPointRemoved(sp);
. . . react on removing a SalesPoint . . .
}
}
SalesPoint
Implement a SalesPoint
Description: The SalesPoint is a part of the Shop. A Shop usually consists of one SalesPoint at least. SaleProcesses take place at a Point of Sale in most cases. They could be startet easily with the public void runProcess(SaleProcess p) method. The SalesPoint appeares in a seperate Frame, a JDisplayFrame. In the JTabbedPane of the Shop appeares the SalesPoints StatusDisplay. It could be used for different purposes. The FormSheets could be both added with MenuSheets, which could be used to start Processes or to open other SalesPoints or to close them.
ToDo:
- Create a class that extends SalesPoint
- Add the class constructor. You can also set the SalesPoints frame size here for example.
- Implement protected FormSheet getDefaultFormSheet() to define the SalesPoints FormSheet. Change the FormSheet content by adding a FormSheetContentCreator. This can be done by creating a subclass of FormSheetContentCreator.
- Implement protected MenuSheet getDefaultMenuSheet() to define the SalesPoints MenuSheet.
Example Source Code:
SalesPoint class:
// necessary imports
import java.awt.Rectangle;
import sale.FormSheet;
import sale.MenuSheet;
import sale.SalesPoint;
1
public class ArchitectureSalesPoint extends SalesPoint
{
2
public ArchitectureSalesPoint(String sPointName)
{
super(sPointName);
setSalesPointFrameBounds(new Rectangle(0, 0, 640, 480));
}
3
protected FormSheet getDefaultFormSheet()
{
FormSheet fs = new FormSheet("TutorialFormSheet",
new ArchitectureSPointFormSheetCC(), false);
return fs;
}
4
protected MenuSheet getDefaultMenuSheet()
{
return null;
}
}
FormSheetContentCreator class:
public class ArchitectureSPointFormSheetCC extends FormSheetContentCreator
{
protected void createFormSheetContent(FormSheet fs)
{
fs.removeAllButtons();
// add ui code here
}
}
Change quit behaviour
Description: A SalesPoint quit´s with a MessageForm, asking "Are you sure, you want to close this SalesPoint? YES/NO". If you want to change this behavior, overwrite the protected boolean onCanQuit() method.
ToDo:
- Open the designated SalesPoint class.
- Implement the protected boolean onCanQuit() method to change the quit behaviour. The return of true will close the SalesPoint against the return of false will keep the SalesPoint opened.
Example Source Code:
1
public class ArchitectureSalesPoint extends SalesPoint
{
2
protected boolean onCanQuit()
{
return true;
}
}
Processes
Implement a SalesProcess
Description: SalesProcesses are the implementation of a deterministic finite automat. It shall be deemed to be a vectored graph with nodes called Gates and edges called Transitions. Make sure that user interaction only takes place in Gates while Transitions should be rather short without any user interaction. The reason is that a Process could only be suspended at a Gate and so the Shop is only able to persistify the Process at a Gate. Often the display of a Gate is to be influenced by the user interaction before. In this case, Gates should be prepared with a Transition leading to this Gate. SaleProcesses could be startet in the Shop itself, but in most cases Processes take place in a SalesPointFrame and are startet from the SalesPoint.
ToDo:
- Create a subclass of SalesProcess.
- Add the constructor and therein call the super method to inherit from the Superclass, the SaleProcess. You can add any attribute to the constructor, most common is the Process' name.
- Every Process needs a starting Gate, the getInitialGate(). Add this method to the SaleProcess.
Define the UIGate itself and add the necessary FormSheet etc. (see related topics). A basic implementation of SaleProcess is now done.
- To start the SaleProcess you can add a button to your SalesPoint's FormSheet and therewith lead to an action that runs the Process on your SalesPoint. Therefore an action was defined that runs any SalesProcess on a SalesPoint by passing the relevant process and a possible necessary DataBasket. (This structure was for example used in the "Videoautomat" tutorial).
Example Source Code:
SaleProcess class:
1
public class ArchitectureProcess extends SaleProcess
{
2
public ArchitectureProcess(String sName) {
super(sName);
// your (initializing) code here
}
3
protected Gate getInitialGate()
{
UIGate uig_initial = new UIGate(null, null);
// set FormSheet and other necessary things
return uig_initial;
}
}
SalesPoint class and relevant:
4
// SalesPoint class:
// set the FormSheet and call FormSheetContentCreator
protected FormSheet getDefaultFormSheet()
{
FormSheet fs = new FormSheet("TutorialFormSheet",
new ArchitectureSPointFormSheetCC(), false);
return fs;
}
// FormSheetContentCreator class:
public class ArchitectureSPointFormSheetCC extends FormSheetContentCreator
{
protected void createFormSheetContent(FormSheet fs)
{
fs.removeAllButtons();
// add a button that calls the TutorialRunProcessAction for running the TutorialProcess
fs.addButton(
"Start Tutorial Process",
2,
new ArchitectureRunProcessAction(new ArchitectureProcess("TutorialProcess"), null));
}
}
// TutorialRunProcessAction:
public class ArchitectureRunProcessAction implements Action
{
private SaleProcess process;
private DataBasket basket;
public ArchitectureRunProcessAction(SaleProcess process, DataBasket basket)
{
this.process = process;
this.basket = basket;
}
public void doAction(SaleProcess p, SalesPoint spoint) throws Throwable
{
if(basket != null)
spoint.runProcess(process, basket);
else
spoint.runProcess(process);
}
}
Change quit behaviour
Description: If a Process should have a certain task before finishing, you have to change its quit behaviour. Therefore overwrite the method onFinished().
ToDo:
- Choose the SaleProcess you want to modify.
- implement the method protected void onFinished().
- add the code that should be run on quiting the process to the newly created method.
Example Source Code:
1
// SaleProcess class
public class ArchitectureProcess extends SaleProcess
{
2
// method to be overwritten
protected void onFinished()
{
3
// your code here
}
}
Change start behaviour
Description: If a Process should have a certain task after being resumed or started, you have to change its start behaviour. Therefore overwrite the method onResumeOrStart().
ToDo:
Choose the SaleProcess you want to modify. Implement the method protected void onResumeOrStart(boolean fIsResume). Add the code that should be run on starting/resuming the process to the newly created method.
Example Source Code:
1
// SaleProcess class
public class ArchitectureProcess extends SaleProcess
{
2
// method to be overwritten
protected void onStartOrFinish(boolean fIsResume)
{
3
// your code here
}
}
Define a UIGate
Description: Gates are a part of SalesProcesses. In contrast to normal Gates, UIGates are made for user interaction and can display FormSheets and MenuSheets. If you need to display something, use UIGates. Use JOptionPanes or JDialogs only to display short user interaction and information, because after serialisation they won´t be restored. If you want to incorporate a UIGate not as StartGate, it may be mandatory to affect the view of the UIGate during Process. In this case, you have to use a Transition.
ToDo:
- Select the SaleProcess where you want to define your UIGate.
- Implement a method that returns a Gate as the class UIGate implements Gate.
- Create an instance of UIGate. Set the attributes to null as they will be set later.
- Define a FormSheet for the UIGate and add a FormSheetContentCreator that modifies the FormSheet.Use any FormSheet-Class and modify the FormSheetContentCreator and possible data accordingly.Available FormSheet-Classes are: LogOnForm, LogTableForm, MsgForm, SingleTableFormSheet, TextInputForm, TwoTableFormSheet, UserTableFormSheet.
- Assign the recently instantiated FormSheet to the UIGate.
- Return the instance of UIGate.
Example Source Code:
1
public class ArchitectureProcess extends SaleProcess
{
2
protected Gate getTutorialInteractionGate()
{
3
UIGate uig_tutorial = new UIGate(null, null);
4
FormSheet fs_tutorial = new FormSheet("Tutorial", new ArchitectureSProcessFormSheetCC(), false);
5
uig_tutorial.setFormSheet(fs_tutorial);
6
return uig_tutorial;
}
}
Define a Gate
Description: Gates are a part of SalesProcesses. If you want to implement user interaction, use UIGates. Normal Gates are preferably used to decide at which UIGate the Process continues. They are also very suitable for the implementation of background Processes. If you need data to prepared before the Gate goes into action, implement a Transition to the Gate.
ToDo:
- Select the SaleProcess where you want to define your Gate.
- Implement a method that returns a Gate.
- Instantiate the Gate and add the needed public Transition getNextTransition(SaleProcess process, User user) throws InterruptedException method.
- Add your code that selects the next GateChangeTransition.
- Return the Gate.
Example Source Code:
1
public class ArchitectureProcess extends SaleProcess
{
2
protected Gate getTutorialNoInteractionGate()
{
3
Gate decisionGate = new Gate()
{
public Transition getNextTransition(SaleProcess process, User user)
throws InterruptedException
{
4
if(myCatalog.size(myDataBasket) == 0)
{
return GateChangeTransition.CHANGE_TO_QUIT_GATE;
}
else
{
return new GateChangeTransition(targetGate);
}
}
};
5
return decisionGate;
}
}
Define a Transition
Description: Transitions main task is to prepare Gates while the application is running. The idea is to manipulate the view of a Gate in order to react to user interaction or collected data of the past. It is possible to prepare normal Gates and UIGates as well.
ToDo:
- Create a class that implements Transition.
- In this class add the public Gate perform(SaleProcess process, User user) method. It's the place where the actions between two gates will be defined.
- Add your code to this method and return the Gate that will be the next target. Therefore you should cast the SaleProcess in the perform method to your Process class that currently calls the Transition. Then simply return the needed Gate from there.
Example Source Code:
Transition class:
1
public class ArchitectureTransition implements Transition
{
2
public Gate perform(SaleProcess process, User user)
{
3
ArchitectureProcess processTutorial = (ArchitectureProcess) process;
// your code here
return processTutorial.getReturnGate();
}
}
SalesProcess class:
public Gate getReturnGate()
{
UIGate uig_return = new UIGate(null, null);
// some other code
return uig_return;
}
Define Transition that just changes to a Gate
Description: During a running Process it may be volitional to change just to a Gate. This could be a not user interactive Gate, which has no Transition leading to it. It could be also an already prepared UIGate, to which you want to change after a "Back"-Button, when it should be not refreshed by the Transition. This might be the case, when the user interaction should be not erased.
ToDo:
- Use constructor of class GateChangeTransition: Transition t = new GateChangeTransition(Gate gTarget);.
Example Source Code:
// ...
Gate tmpGate0 = new Gate()
{
public Transition getNextTransition(SaleProcess process, User user)
throws InterruptedException
{
return new GateChangeTransition(targetGate);
}
};
// ...
Define Transition that changes to a special Gate
Description: There are six predefined Process Gates. All of them except the ErrorGate have a predefined Transition, leading to it.
- If you need the singelton instance of a special Process Gate, use the get-method of class SaleProcess:
getCommitGate()
getErrorGate(int nErrorNesting)
getLogGate()
getRollbackGate()
getStopGate()
getQuitGate() - If you need a Transition, leading to a special Process Gate, use the static attributes of class GateChangeTransition:
GateChangeTransition.CHANGE_TO_COMMIT_GATE
GateChangeTransition.CHANGE_TO_LOG_GATE
GateChangeTransition.CHANGE_TO_ROLLBACK_GATE
GateChangeTransition.CHANGE_TO_STOP_GATE
GateChangeTransition.CHANGE_TO_QUIT_GATE
Example Source Code:
// ...
Gate tmpGate1 = new Gate()
{
public Transition getNextTransition(SaleProcess process, User user)
throws InterruptedException
{
return GateChangeTransition.CHANGE_TO_QUIT_GATE;
}
};
// ...
Time Management
Incorporate a Timer
Description:
A Timer is able to manage the current time in your application. A Timer needs a Time Object to be referenced on.
If you want to react to different TimerEvents, you will have to incorporate a TimerListener.
Time management in SalesPoint Framework is abutted on several Java classes:
java.util.Calendar
java.util.GregorianCalendar
java.util.Timer
java.util.TimerTask
java.util.TimeZone
java.sql.TimeStamp
ToDo:
- Create a new instance of Time. In this case CalendarTime is used. Set the time interval.
- Create a new instance of Timer. In this case AutoTimer is used.
- If you need, add a TimerListener and add the methods the class must inherit from the base class. Add your code to react to the events.
- Finally start the timer.
Example Source Code:
1
// initialize with current system time and increase time by second
CalendarTime calendarTime = new CalendarTime();
calendarTime.setTimeToCount(CalendarTime.SECOND);
2
// initialize Timer with CalendarTime and 992ms delay from step to step
(1000ms are too much, because the autotimer is delayed additionally)
AutoTimer autoTimer = new AutoTimer(calendarTime, (long) 992);
3
// if it becomes more complicate it will be clearer to realize
a subclass of TimerAdapter or to implement TimerListener
autoTimer.addTimerListener(new TimerListener()
{
public void onGoneAhead(TimerEvent timerEvent)
{
System.out.println(timerEvent.getTime());
}
public void onTimeSet(TimerEvent timerEvent)
{
}
public void onIntervalSet(TimerEvent timerEvent)
{
}
});
4
// start the timer
autoTimer.start();
Select a time type
Description: A Time Object is used by the Timer. It gives the Timer it´s certain shape. The Time Object defines which time field will be increased by goAhead method of the Timer. Choose the following timer types:
- Date: "01.01.00" a simple date of the format: "dd.mm.yy".
- Step: "26" a Long value is used to represent the time.
- CalendarTime: "Sat Jul 20 15:38:53 CEST 2002" a time which is represented as a Gregorian Calendar.
ToDo:
- Create a new instance of Time. In this case CalendarTime is used. Set the time interval.
Example Source Code:
1
// initialize with current system time and increase time by second
CalendarTime calendarTime = new CalendarTime();
calendarTime.setTimeToCount(CalendarTime.SECOND);
Select a timer type
Description: A Timer is able to manage the current time in your application. It is referenced to a Time which gives the Timer it´s shape. Choose of following Timer types:
- StepTimer: very simple implementation, is increased manually by goAhead() method.
- AutoTimer: special step timer which inceases automatically with a certain time delay.
ToDo:
- Create a new instance of Timer. In this case AutoTimer is used.
Example Source Code:
1
// initialize Timer with CalendarTime and 992ms delay from step to step
(1000ms are too much, because the autotimer is delayed additionally)
AutoTimer autoTimer = new AutoTimer(calendarTime, (long) 992);
Common
Set default Databasket for SalesPoints
Description: This is required if processes executed on a SalesPoint are to run in a specific transactional context. The specified DataBasket will be attached to every process running on the SalesPoint and will determine its transactional context. By default, no DataBasket is attached to a SalesPoint so that processes run outside any transactional context.
ToDo: This can be achieved in several ways. In any case, you need to attach the DataBasket to the SalesPoint:
- a. Open the designated Shop class.
b. Override the protected void onSalesPointAdded() method according to your needs. - a. Open the designated SalesPoint class.
b. Attach the DataBasket in it's constructor.
Example Source Code:
1 a
public class ArchitectureShop extends Shop
{
.
.
.
1 b
protected void onSalesPointAdded(SalesPoint sp) {
super.onSalesPointAdded(sp);
sp.attach(new DataBasketImpl());
}
}
2 a
public class ArchitectureSalesPoint extends SalesPoint
{
.
.
.
2 b
public ArchitectureSalesPoint(String sPointName, DataBasket db) {
super(sPointName);
attach(db);
}
}
Data Management
Catalog
Define a simple CatalogItem
Description: CatalogItems are Catalogs/SubCatalogs or items itself. They are meant to represent a named category of items and to be stored in a Catalog. It is often usefull to subclass CatalogItemImpl, but implementing the interface CatalogItem may also be necessary. This tutorial shows you a basic implementation of CatalogItemImpl for a simple CatalogItem.
ToDo:
- Create a subclass of CatalogItemImpl.
- Define the constructor. In this case the item will have a name and a value as the standard implementation of CatalogItemImpl offers. You have to invoke the super constructor either with name or name and value. Thus the simplest implementation of CatalogItemImpl would only have a name.
- You also have to implement the protected CatalogItemImpl getShallowClone() method that will return a copy of the current CatalogItemImpl. Simply instantiate a new CatalogItemImpl of your class and set the current values to the constructor.
- Additionally you can define a method for setting the items value.
Example Source Code:
import data.ooimpl.CatalogItemImpl;
1
public class DataBasicCatalogItem extends CatalogItemImpl
{
2
public DataBasicCatalogItem(String name, Value value)
{
super(name, value);
}
3
protected CatalogItemImpl getShallowClone()
{
return (CatalogItemImpl)
new DataBasicCatalogItem(this.getName(), this.getValue());
}
4
public void setValue(Value value)
{
this.setValue(value);
}
}
Define an advanced CatalogItem
Description: As shown in #Define a simple CatalogItem now a CatalogItemImpl will be defined with some more possibilities. Normally you will need more than just a name and a value for your item. You can define as many values as you want for your item, in this case there are two prices and a Catalog (you can nest as you want) defined in addition to the name. You can as well define values that not directly represent the item and you can add some helper/setter/getter methods as needed.
ToDo:
- Create a subclass of CatalogItemImpl.
- Define the variables for the additional values. price1, price2 and catalog are the values that directly represent the CatalogItem (and therefore later will be set in the constructor). comparePrice is an additional variable used for internal calculations and does not represent the item.
- Define the constructor and invoke the super constructor with the name. Assign the CatalogItem values as defined before.
- Implement the protected CatalogItemImpl getShallowClone() method and return a copy of your current CatalogItemImpl class.
- Sometimes it is needful to define a toString() method that will output the items values as string.
- You can calculate with your item variables and so define a get method for returning the sum of the two prices in this case.
- As mentioned before, comparePrice is not a representating variable of the item and thus not set by the items constructor. Define set-methods if you need to initialize your other variables.
Example Source Code:
1
public class DataAdvancedCatalogItem extends CatalogItemImpl
{
2
private int price1;
private int price2;
private Catalog catalog;
private int comparePrice;
3
public DataAdvancedCatalogItem(String name, int price1, int price2, Catalog catalog)
{
super(name);
this.price1 = price1;
this.price2 = price2;
this.catalog = catalog;
}
4
protected CatalogItemImpl getShallowClone()
{
return (CatalogItemImpl)
new DataAdvancedCatalogItem(this.getName(), this.price1, this.price2, this.catalog);
}
5
public String toString()
{
return this.getName() + "-" + this.price1 + "-" +
this.price2 + "-" + this.catalog.getName();
}
6
public int getSum()
{
return (this.price1 + this.price2);
}
7
public void setComparePrice(int value)
{
this.comparePrice = value;
}
}
Incorporate a Catalog
Description: An important data structure is the Catalog. It provides all what is needed to store and administrate the Shop´s goods. It is also easy to display a Catalog´s contents with already implemented FormSheets, the JCatalogTable and the DefaultCatalogItemTED for example. The use of CatalogImpl, the implementation of the interface Catalog is recommended.
ToDo:
- Create a new instance of CatalogItemImpl.
- Define a fitting subclass of CatalogItemImpl and add it to the newly created CatalogImpl. Therefore refer to Define a simple CatalogItem or Define an advanced CatalogItem.
In this case the simple CatalogItemImpl from above is used.
Assign an instance of DataBasket if the addition to the catalog is performed within a SaleProcess. - Add the catalog to the shop's global list of catalogs.
- If you remove an item do not forget to catch the VetoException that can be possible if another process currently handles the catalog.
Example Source Code:
1
Catalog<DataBasicCatalogItem> simpleCatalog = new CatalogImpl<DataBasicCatalogItem>(SimpleCatalog);
2
simpleCatalog.add(
new DataBasicCatalogItem("item1", new IntegerValue(10)),
null);
3
Shop.getTheShop().addCatalog(simpleCatalog);
4
try
{
simpleCatalog.remove("item1", null);
}
catch (VetoException e)
{
e.printStackTrace();
}
Define a new AbstractCurrency
Description: EUROCurrencyImpl provides the recent german currency EURO. If you want to incorporate a new currency you will have two alternatives. You may implement interface Currency extending CatalogImpl and make your own implementation of CurrencyImpl. But this would force you to define your own MoneyBag extending CountingStockImpl, because MoneyBagImpl needs an instance of CurrencyImpl in it´s constructor. Second alternative is create a subclass of AbstractCurrency and to overwrite the key methods like done here.
ToDo:
- Create a subclass of AbstractCurrency.
- Add the currency's constructor and invoke the super constructor with the fitting Locale.
- Implement the protected CurrencyItemData[] getCurrencyItemData() method. Therein you define the names and values of your currency as CurrencyItemData.
Example Source Code:
1
public class DataAbstractCurrency extends AbstractCurrency
{
2
public DataAbstractCurrency(String name)
{
super(name, Locale.GERMANY);
}
3
protected CurrencyItemData[] getCurrencyItemData()
{
CurrencyItemData[] currencyArrayReturn =
{
// add CurrencyItemData with name and value
new CurrencyItemData("1-Bronze-Taler", 10),
new CurrencyItemData("1-Silber-Taler", 100),
new CurrencyItemData("1-Gold-Taler", 1000)
};
return currencyArrayReturn;
}
}
Incorporate a Currency
Description: A Currency is a special Catalog. Its CatalogItems are CurrencyItems that represent the different denominations of the Currency.
ToDo:
- Eventually create a new Currency. Therefore refer to Define a new AbstractCurrency.
- Create a new instance of CurrencyImpl. In this case the above defined CurrencyEuro class was used.
- Add the currency to the Shop's global list of catalogs.
Example Source Code:
2
AbstractCurrency abstractCurrency = new DataAbstractCurrency("SimpleCurrency");
3
Shop.getTheShop().addCatalog(abstractCurrency);
DataBasket
Use a DataBasket
Description: The task of this data structure is to backup transactions between framework components. It enables undo actions called RollBack. Remember that DataBaskets can't undo actions with components which doesn´t belong to the framework. This components have to be restored manually. To ensure that a framework component is able to be rolled back, the protected getShallowClone() method of the Item will have to be overwritten if the Item has additional attributes (Note: Be careful with the clone method, mistakes could be hard to detect). Use the public get(String sKey, DataBasket db, boolean fForEdit) method implemented in Catalog and Stock to get the Item which is to be modified. A DataBasket could be displayed like Catalogs and Stocks with the JDataBasketTable, the DefaultCatalogItemDBETableEntryDescriptor , the DefaultCountingStockDBETableEntryDescriptor and the DefaultStoringStockDBETableEntryDescriptor.
ToDo:
- Make a new instance of DataBasket or use the relevant of your class. In this case the use is implemented in a SaleProcess so the DataBasket of the process is used by getBasket().
- Get the object to be modified. In this case we first get the catalog and then the desired item.
(the structure of Incorporate a Catalog and Define a simple CatalogItem is used) - Modifiy the item.
- If you want to keep the changes, commit the DataBasket by calling basket.commit(). This will force the basket to go through the Commit-Gate and commit the modifications. If you do not want to keep the changes, rollback the DataBasket by calling basket.rollback(). This will force the basket to go through the RollBack-Gate and rollback the modifications.
Example Source Code:
1
final DataBasket basket = this.getBasket();
2
// get the catalog from the shop's global list of catalogs
final Catalog catalog = Shop.getTheShop().getCatalog("SimpleCatalog");
// define the CatalogItemImpl and set to null
DataBasicCatalogItem item = null;
// get the item with the DataBasket "watching" the action
try
{
item = (DataBasicCatalogItem) catalog.get("item1", basket, true);
}
// catch the relevant exceptions
catch(VetoException ve)
{
// exception handling here
}
catch(NotEditableException nee)
{
// exception handling here
}
catch(DataBasketConflictException dbce)
{
// exception handling here
}
3
item.setValue(new IntegerValue(99));
4
// decide whether to commit or to rollback (doCommit is ot type boolean)
if(doCommit)
{
// commit the changes
basket.commit();
}
else
{
// rollback the changes
basket.rollback();
}
Use a DataBasketCondition
Description: DataBasketConditions are important to display the contents of a DataBasket in a FormSheet (e.g. JDataBasketTable). They could also be used to summ the content's values using public Value sumBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit). The already implemented DataBasketConditionImpl provides all what is needed.
ToDo:
- There are three ways to use a DataBasketCondition:
- Use the constructor to create an instance of DataBasketConditionImpl.
- Use the provided static fields:
- ALL_CATALOG_ITEMS
- ALL_STOCK_ITEMS
- ALL_ENTRIES
- Use provided static methods to get the needed instance:
- allCatalogItemsWithDest(Catalog cDest)
- allCatalogItemsWithSource(Catalog cSource)
- allStockItemsWithDest(Stock stDest)
- allStockItemsWithSource(Stock stSource)
- specificCatalogItem(CatalogItem ci)
- specificStockItem(StockItem si)
Example Source Code:
DataBasketCondition dbc;
1 a
dbc = new DataBasketConditionImpl(
// filters entries of a certain kind
// (DataBasketKeys.CATALOG_ITEM_MAIN_KEY, DataBasketKeys.STOCK_ITEM_MAIN_KEY)
(String) mainKey,
// filters Entrys with given name ((StockItem) si).getName()
(String) secondaryKey,
// filters Entrys from given source (Catalog or Stock)
(DataBasketEntrySource) dbesSource,
// filters Entrys from given destination (Catalog or Stock)
(DataBasketEntryDestination) dbedDestination,
// filters only given object (if it is null, all DataBasketEntrys will be checked)
(Object) value);
1 b
dbc = DataBasketConditionImpl.ALL_CATALOG_ITEMS;
1 c
dbc = DataBasketConditionImpl.allCatalogItemsWithSource(catalog);
Create a DataBasketEntryGrouper
Description: DataBasketEntryGroupers are important to display the contents of a DataBasket in a FormSheet. Every movement creates a new DataBasketEntry. So it could happen, that one CatalogItem for example has created many CatalogItemDataBasketEntrys in the DataBasket. If you want to display a DataBasket with DBEntrys of StockItems in a CountingStock, the already implemented CountingStockDBEGrouper will be sufficent. If you don´t want grouping at all, use the NOPDataBasketEntryGrouper. If you want to implement your own grouping, follow the instructions.
ToDo:
- Implement the interface DataBasketEntryGrouper in a class.
- Implement the method public boolean canGroup(DataBasketEntry dbe0, DataBasketEntry dbe1).
Here you have to decide which DataBasketEntrys should be grouped and which not. - Implement the method public DataBasketEntry group(DataBasketEntry dbe0, DataBasketEntry dbe1).
Here you have to implement how two DataBasketEntrys merge into one Entry.
Example Source Code:
import data.swing.DataBasketEntryGrouper;
import data.DataBasketEntry;
1
public class DataDataBasketEntryGrouper implements DataBasketEntryGrouper
{
2
public boolean canGroup(DataBasketEntry dbe0, DataBasketEntry dbe1)
{
// items of same kind (MainKey) and same name (SecondaryKey) can be grouped
if(dbe0.getMainKey().equals(dbe1.getMainKey()) &&
dbe0.getSecondaryKey().equals(dbe1.getSecondaryKey()))
{
return true;
}
else
{
return false;
}
}
3
public DataBasketEntry group(DataBasketEntry dbe0, DataBasketEntry dbe1)
{
// the first item will be displayed for both
return dbe0;
}
}
Filter
Incorporate a CatalogFilter on a Catalog
Description: A CatalogFilter is a Catalog itself, in which defined elements are filtered. This CatalogFilter could be used in the same way as a normal Catalog.
ToDo:
- Create a subclass of CatalogFilter.
- Add the constructor and invoke the super constructor.
- Implement the protected boolean match(CatalogItem ci) method. It determines for each CatalogItem, whether it will be in the CatalogFilter or not.
- Make an instance of your CatalogFilter where you want to use it.
Use it instead of the source catalog, where you only want the unfiltered items. But do not use a CatalogFilter as a replacement for a 'real' Catalog, e.g., as an item in another Catalog.
Example Source Code:
CatalogFilter class:
1
public class DataCatalogFilter extends CatalogFilter<DataBasicCatalogItem>
{
2
public DataCatalogFilter(Catalog<DataBasicCatalogItem> originalCatalog)
{
super(originalCatalog);
}
3
protected boolean match(DataBasicCatalogItem catalogItem)
{
if (catalogItem.getName().equals("Item which is unfiltered"))
return true;
else
return false;
}
}
class that uses the CatalogFilter:
4
DataCatalogFilter dataCatalogFilter = new DataCatalogFilter(Shop.getTheShop().getCatalog(SimpleCatalog));
Incorporate a CountingStockFilter on a CountingStock
Description: A CountingStockFilter is a CountingStock itself, in which a certain number of elements are filtered. Thereby the public int countItems(String key, DataBasket dataBasket) defines which items should be displayed or not. This CountingStockFilter could be used in the same way as a normal CountingStock.
ToDo:
- Create a subclass of CountingStockFilter.
- Add the constructor and invoke the super constructor.
- Implement the public int countItems(String key, DataBasket basket) method.
It determines how many items of the StockItem with the key key will be in the CountingStockFilter. - Implement the public Object clone() method that will return a copy of the filter.
- Make an instance of your CountingFilter where you want to use it.
Use it instead of the source stock, where you only want the unfiltered items.
Example Source Code:
CountingStockFilter class:
1
public class DataCountingStockFilter extends CountingStockFilter<StockItem, CatalogItem>
{
2
public DataCountingStockFilter(CountingStock<StockItem, CatalogItem> sourceStock)
{
super(sourceStock);
}
3
public int countItems(String key, DataBasket dataBasket)
{
// create iterator for all StockItems with the given key
Iterator iterator = m_stSource.get(key, dataBasket, false);
// instantiate a number for the items which should not be filtered
int number = 0;
// search for items that should not be filtered by iterating through the StockItems
StockItem stockItem;
while (iterator.hasNext())
{
stockItem = (StockItem) iterator.next();
if(stockItem.getName().equals("Item which is unfiltered"))
number ++;
}
return number;
}
4
public Object clone()
{
DataCountingStockFilter dataCountingStockFilter =
new DataCountingStockFilter((CountingStock<StockItem, CatalogItem>) m_stSource.clone());
return dataCountingStockFilter;
}
}
class that uses the CountingStockFilter:
5
DataCountingStockFilter dataCountingStockFilter =
new DataCountingStockFilter((CountingStock<StockItem, CatalogItem>)
Shop.getTheShop().getStock(CountingStock));
Incorporate a CurrenyFilter on a Currency
Description: A CurrencyFilter is a Currency itself, in which defined elements are filtered. The procedure is very similar to Incorporate a CatalogFilter on a Catalog. This CurrencyFilter could be used in the same way as a normal Currency.
ToDo:
- Create a subclass of CurrenyFilter.
- Add the constructor and invoke the super constructor.
- Implement the protected boolean match(CatalogItem catalogItem) method.
It determines for each CurrencyItem, whether it will be in the CurrencyFilter or not. - Make an instance of your CurrenyFilter where you want to use it.
Use it instead of the source catalog, where you only want the unfiltered items.
Example Source Code:
CurrenyFilter class:
1
public class DataCurrencyFilter extends CurrencyFilter<CurrencyItem>
{
2
public DataCurrencyFilter(Currency<CurrencyItem> currency)
{
super(currency);
}
3
protected boolean match(CurrencyItem item)
{
// all notes and coins less worth than 5 Euro are filtered out
if(((NumberValue)item.getValue()).getValue().intValue() < 500)
return false;
else
return true;
}
}
class that uses the CurrencyFilter:
4
DataCurrencyFilter dataCurrencyFilter = new DataCurrencyFilter((Currency<CurrencyItem>)Shop.getTheShop().getCatalog(EuroCatalog));
Incorporate a MoneyBagFilter on a MoneyBag
Description: A MoneyBagFilter is a MoneyBag itself, in which a certain number of coins and notes are filtered. This MoneyBagFilter could be used in the same way as a normal MoneyBag.
ToDo:
- Create a subclass of MoneyBagFilter.
- Add the constructor and invoke the super constructor.
- Implement the public int countItems(String key, DataBasket dataBasket) method.
It determines how money items of the MoneyBag will be in the MoneyBagFilter. - Implement the public Object clone() method that returns a copy of the filter.
- Make an instance of your MoneyBagFilter where you want to use it.
Use it instead of the source MoneyBag, where you only want the unfiltered items.
Example Source Code:
MoneyBagFilter class:
1
public class DataMoneyBagFilter extends MoneyBagFilter
{
2
public DataMoneyBagFilter(MoneyBag moneyBag)
{
super(moneyBag);
}
3
public int countItems(String key, DataBasket dataBasket)
{
// get the item codes of the currency (EUROCurrencyImpl is used here)
int currencyItemCode = -1;
AbstractCurrency euroCurrency = new EUROCurrencyImpl("Euro_Catalog");
CurrencyItemData[] euroData = euroCurrency.getCurrencyItemData();
for(int i = 0; i < euroData.length; i++)
{
if (((CurrencyItemData) euroData[i]).getName().equals(key))
{
currencyItemCode = i;
}
}
// filter all item less worth than 5 Euro
if (currencyItemCode == -1)
{
return 0;
}
else
{
if (((CurrencyItemData) euroData[currencyItemCode]).getValue() < 500)
return 0;
else
return m_stSource.countItems(key, dataBasket);
}
}
4
public Object clone()
{
return new DataMoneyBagFilter((MoneyBag) m_stSource.clone());
}
}
class that uses the MoneyBagFilter:
5
DataMoneyBagFilter dataMoneyBagFilter = new DataMoneyBagFilter((MoneyBag) Shop.getTheShop().getCatalog("MoneyBag"));
Incorporate a StoringStockFilter on a StoringStock
Description: A StoringStockFilter is a StoringStock itself, in which defined elements are filtered. This StoringStockFilter could be used in the same way as a normal StoringStock.
ToDo:
- Create a subclass of StoringStockFilter.
- Add the constructor and invoke the super constructor.
- Implement the public boolean contains(StockItem stockItem, DataBasket dataBasket) method.
It determines for each StockItem if it will be in the StoringStockFilter or not. - Implement the public Object clone() method that returns a copy of the filter.
- Make an instance of your StoringStockFilter where you want to use it.
Use it instead of the source StoringStock, where you only want the unfiltered items.
Example Source Code:
StoringStockFilter class:
1
public class DataStoringStockFilter extends StoringStockFilter<StockItem, CatalogItem>
{
2
public DataStoringStockFilter(StoringStock<StockItem, CatalogItem> sourceStock)
{
super(sourceStock);
}
3
public boolean contains(StockItem stockItem, DataBasket dataBasket)
{
if(stockItem.getName().equals("Item which is unfiltered"))
return true;
else
return false;
}
4
public Object clone()
{
return new DataStoringStockFilter((StoringStock<StockItem, CatalogItem>) m_stSource.clone());
}
}
class that uses the StoringStockFilter:
5
DataStoringStockFilter dataStoringStockFilter =
new DataStoringStockFilter((StoringStock<StockItem, CatalogItem>) Shop.getTheShop().getStock(StoringStock));
Stock
Define a StockItem
Description: StockItems are Stocks (more precisely: SubStocks) or items itself. If they are used in a CountingStock, they'll be the representation of a certain amount of items of a special category. If they are used in a StoringStock, they'll represent an individual item of a special category. It is often usefull to make a subclass of StockItemImpl, but implementing the interface StockItem may also be necessary. If you wanted to use a CountingStock, you may make an instance of StockItemImpl itself.
ToDo:
- Create a subclass of StockItemImpl.
- Add constructors to set the items name and other attributes.Every constructor must invoke super(name), therefore a StockItem has to have a name.
- Add other useful methods.
Example Source Code:
1
public class DataStockItem extends StockItemImpl
{
2
public DataStockItem(String name)
{
super(name);
}
3
public void setOwner(User user)
{
this.setOwner(user);
}
public User getOwner()
{
return this.getOwner();
}
}
Fill a Stock with Values
Description: Every Stock implements the public method FillStockWithValue(DataBasket db, Value vTarget, StockFromValueCreator sfvc). With this method, a CountingStock can be filled with a certain Value by using the DefaultCountingStockFromValueCreator. An existing Stock can be filled with the contents of another Stock by using a StockFromStockCreator. The transaction will be canceled, if the StockFromValueCreator doesn't find enough fitting items to fill the Stock with the certain Value. If backtracking is needed to guarantee that a solution will be found if there is one, use StockFromStockCreatorBT. These three implementations of the interface StockFromValueCreator are sufficient in most cases.
ToDo:
- Make a new instance of your used implementation of Stock (or select an existing instance of Stock).
- Use Stock.fillStockWithValue(DataBasket db, Value vTarget, StockFromValueCreator sfvc) to fill selected Stock with a certain Value.
- Use the implementation StockFromStockCreator, if you want to fill one Stock with the Values of another Stock (the needed items will be removed from the second Stock).
Example Source Code:
// first define an AbstractCurrency for the Moneybags
AbstractCurrency euroCurrency = new EUROCurrencyImpl("Euro-Catalog");
Shop.getTheShop().addCatalog(euroCurrency);
1
// create a new MoneyBag and add to Shop
MoneyBagImpl moneyBag1 = new MoneyBagImpl("MoneyBag1", euroCurrency);
Shop.getTheShop().addStock(moneyBag1);
2
// fill the MoneyBag with 357, 12 Euros
moneyBag1.fillStockWithValue((DataBasket) null,
new IntegerValue(35712),
new DefaultCountingStockFromValueCreator(
new CatalogItemValue())
);
1
// create a new MoneyBag and add to Shop
MoneyBagImpl moneyBag2 = new MoneyBagImpl("MoneyBag2", euroCurrency);
Shop.getTheShop().addStock(moneyBag2);
3
// fill the second Stock with items of the first
// using StockFromValueCreator with BackTracking
moneyBag2.fillStockWithValue((DataBasket) null,
new IntegerValue(17854),
new StockFromStockCreatorBT(
(Stock) moneyBag1,
new CatalogItemValue())
);
Incorporate a CountingStock on a Catalog
Description: This data structure is meant to count the number of entrys in the associated Catalog. It should be preferred if it was not important to remember differences between single items of a category. The CountingStock could be displayed in the same way as the Catalog itself. The use of CountingStockImpl, the implementation of the interface CountingStock, is sufficient in most cases.
ToDo:
- Get the instance of Catalog you want to incorporate a CountingStock on.
(See also: #Incorporate a Catalog). - Make a new instance of CountingStock associated with the Catalog.
- Define (if necessary) a fitting subclass of StockItemImpl.
(See also Define a #StockItem). -
- Add and remove StockItems as you wish.
- It is not always necessary to add StockItems, but it may also sufficient only to increase the number of counted CatalogItems.
- Add the Stock to the Shop's global list of stocks.
Example Source Code:
1
Catalog<DataBasicCatalogItem> catalog = Shop.getTheShop().getCatalog(SimpleCatalog);
2
CountingStock<StockItemImpl, DataBasicCatalogItem> countingStock =
new CountingStockImpl<StockItemImpl, DataBasicCatalogItem>(
"simpleCatalogCS",
catalog);
4 a
for(int i = 0; i < 5; i++)
{
countingStock.add(new StockItemImpl("Screw"), (DataBasket) null);
}
4 b
countingStock.add("Screw Nut", 5, (DataBasket) null);
5
Shop.getTheShop().addStock(countingStock);
Incorporate a StoringStock on a Catalog
Description: This data structure is meant to store individual representations of entrys in the associated Catalog. In contrast to the CountingStock it is meant to remember the differences between the StockItems. The StoringStock could be displayed in the same way as the Catalog itself. Using StoringStockImpl, the implementation of the interface StoringStock, is sufficient in most cases.
ToDo:
- Get the instance of Catalog you want to incorporate a StoringStock on.
(See also: #Incorporate a Catalog). - Make a new instance of StoringStock associated with the Catalog.
- Define a fitting subclass of StockItemImpl.
(See also: Define a #StockItem). - Add and remove StockItems as you wish.
- Add the Stock to the Shop's global list of stocks.
Example Source Code:
1
Catalog<DataBasicCatalogItem> catalog = Shop.getTheShop().getCatalog(SimpleCatalog);
2
StoringStock<StockItemImpl, DataBasicCatalogItem> storingStock =
new StoringStockImpl<StockItemImpl, DataBasicCatalogItem>("simpleCatalogSS", catalog);
4
for(int i = 0; i < 5; i++)
{
storingStock.add(new StockItemImpl("Shoes"), (DataBasket) null);
}
5
Shop.getTheShop().addStock(storingStock);
Incorporate a MoneyBag on a Currency
Description: This data structure is meant to count the number of coins and notes in the associated Currency. It is a special CountingStock on a special Catalog. The use of MoneyBagImpl, the implementation of the interface MoneyBag, is sufficient in most cases.
ToDo:
- Get the instance of AbstractCurrency you want to incorporate a MoneyBag on.
- Make a new instance of MoneyBag associated with the Currency.
- Add the MoneyBag to the Shop's global list of stocks.
- Add and remove coins and notes as you wish.
Example Source Code:
1
AbstractCurrency euroCurrency = new EUROCurrencyImpl("Euro_Catalog");
2
MoneyBagImpl moneyBag = new MoneyBagImpl("MoneyBag", euroCurrency);
3
Shop.getTheShop().addStock(moneyBag);
4
for(int i=0; i < euroCurrency.getCurrencyItemData().length; i++)
{
moneyBag.add((euroCurrency.getCurrencyItemData())[i].getName(), 5, (DataBasket) null);
try
{
moneyBag.remove(
(euroCurrency.getCurrencyItemData())[i].getName(),
5,
(DataBasket) null);
}
catch (VetoException e)
{
e.printStackTrace();
}
}
Common
Common information about generic SalesPoint containers
Description:
Since Version 3.3, SalesPoint containers are generic. In order to use this functionality most conveniently, there are special identifiers that store the generic information. So the developer will not have to take care of casting to appropriate container types. Container types in SalesPoint are Catalogs and Stocks.
Catalogs: Catalogs are typed by the kind of CatalogItem they will contain. The same goes for their identifiers.
CatalogIdentifier<DataBasicCatalogItem> SimpleCatalog = new CatalogIdentifier<DataBasicCatalogItem>("SimpleCatalog");
Catalog<DataBasicCatalogItem> simpleCatalog = new CatalogImpl<DataBasicCatalogItem>(SimpleCatalog);
Once they are created you can register them with the Shop and get them back with the identifier they were created with:
Shop.getTheShop().addCatalog(simpleCatalog);
Catalog<DataBasicCatalogItem> catalog = Shop.getTheShop().getCatalog(SimpleCatalog);
Stocks:
Catalogs are typed by the kind of StockItem they will contain and the kind of CatalogItem the Catalog contains that is associated with the stock. The same goes for their identifiers.
StockIdentifier<StockItemImpl, DataBasicCatalogItem> Stock =
new StockIdentifier<StockItemImpl, DataBasicCatalogItem>("CountingStock");
CountingStock<StockItemImpl, DataBasicCatalogItem> stock =
new CountingStockImpl<StockItemImpl, DataBasicCatalogItem>(Stock, simpleCatalog);
Once they are created you can register them with the Shop and get them back with the identifier they were created with in a similar manner as you can do that with Catalogs.
As you can see (especially in the case of Stocks), setting these up can easily get quite extensive. Therefore it is recommended to derive your own classes from CatalogImpl, CountingStockImpl and/or StoringStockImpl and fit their generic parameters to your needs. You simply have to implement the constructor that takes the corresponding identifier and delegate it to the super constructor.
Display
FormSheet
Create a FormSheet
Description: Although it is quite easy to initialize and display a FormSheet, it is a bit difficult to describe how to do it because of the vast opportunities to display them and the many possibilities to use the FormSheets and combine them with Components or even JComponents. The first thing you should do before you create your own FormSheet is to consult the framework API and take a closer look at FormSheet. There you will find all methods and of course the subClasses of FormSheet, like the LogOnForm, the MsgForm or the TableForms, which are all specialized FormSheets of great use. Talking about the elements, every FormSheet needs a caption, which is normally defined by calling the constructor and at least some JComponents to display. These can be put together either by a FormSheetContentCreator or by creating and overwriting methods within a new FormSheet instance before or using it's methods during runtime. It is though recommended to use the ContentCreator as shown in this example, for it is able to serialize the Components and all their properities, esp. their behaviour, which will be lost if you just use FormSheet methods and serialize the Shop's status.
ToDo:
- Instantiate a subclass of FormSheetContentCreator.
- Add the constructor and call the superclass to inherit its methods.
- Add the protected void createFormSheetContent(FormSheet fs) method and therein add/remove your components to the FormSheet.
- Add the FormSheetContentCreator to your FormSheet class by calling the method addContentCreator(FormSheetContentCreator or by initializing in the constructor.
Example Source Code:
FormSheetContentCreator class:
1
public class DisplayFormSheetContentCreator extends FormSheetContentCreator
{
2
public DisplayFormSheetContentCreator()
{
super();
}
3
protected void createFormSheetContent(FormSheet fs)
{
// add/remove your components here
fs.removeAllButtons();
fs.addButton("TutorialButton", 1, new ArchitectureCustomAction());
}
}
Class that uses a FormSheet:
4
// adding the FormSheetContentCreator in FormSheet constructor
FormSheet fs = new FormSheet("DisplayFormSheet",
new DisplayFormSheetContentCreator(),
false);
// adding the FormSheetContentCreator by using the addContentCreator method
SingleTableFormSheet fs = SingleTableFormSheet.create(
"DisplayFormSheet",
catalog,
uigate);
fs.addContentCreator(new DisplayFormSheetContentCreator());
Define a FormSheet for a SalesPoint
Description: The recommended way to define a FormSheet for a SalesPoint is to redefine the method getDefaultFormSheet() of your Salespoint instance, which is used by the Framework to resolve the FormSheet which shall be displayed at that SalesPoint. You may also add a FormSheet during runtime by using the method setFormSheet(SaleProcess sp, FormSheet fs). As you can see, this method is also used to add a SalesProcess to the SalesPoint, which itself is able to display FormSheets. This example describes how to add a FormSheet to the SalesPoint, while the FormSheet itself should be assembled in a FormSheetContentCreator.
ToDo:
- Redefine the method getDefaultFormSheet() in the SalesPoint class.
- Return the FormSheet you want to use.
Example Source Code:
public class DisplaySalesPoint extends SalesPoint
{
public DisplaySalesPoint(String sName)
{
super(sName);
}
1
protected FormSheet getDefaultFormSheet()
{
2
return new FormSheet("DefaultFormSheet",
new DisplayFormSheetContentCreator(), false);
}
}
Define a StatusFormSheet for a SalesPoint
Description: SalesPoints are being displayed in a separate window but also have a StatusDisplay at the Shop, which is the TabbedPane in the Shop's Frame, labled the name of the SalesPoint. By bringing it on top, it shows what is defined as the StatusDisplay in your SalesPoint instance and also adds the Menu defined as StatusMenuSheet in the SalesPoint instance to the Shop's MenuSheet. By default, both, the StatusFormSheet and the StatusMenuSheet are empty. Feel free to use the StatusDisplay and MenuSheet, which are equally handled to the DefaultFormSheet and the DefaultMenuSheet except that due to the strong division of the Shop and it's SalesPoints it is not possible to have processes running on it. You may trigger a Processes on it, but they will always be displayed by the SalesPoint's window. Therefor a more suitable name would be "Statical Display". For further information on Processes refer to the section "Processes". This example describes how to define a FormSheet as the SalesPoint's StatusFormSheet, while the FormSheet itself should be assembled in a FormSheetContentCreator.
ToDo:
- Redefine the method getDefaultStatusFormSheet() in the SalesPoint class.
- Return the FormSheet you want to use.
Example Source Code:
public class DisplaySalesPoint extends SalesPoint
{
public DisplaySalesPoint(String sName)
{
super(sName);
}
1
protected FormSheet getDefaultStatusFormSheet()
{
2
return new FormSheet("StatusFormSheet",
new DisplayFormSheetContentCreator(), false);
}
}
Change the standard OK or CANCEL button behaviour
Description: A FormSheet initially has two buttons, one labeled "ok" and one with "cancel" on it. But they don't do anything by default, so you have to define their behaviour. There are mainly three ways to define the behaviour of the buttons. One is to create your own FormSheet and implement the methods ok() and cancel() and the other one is to remove the buttons and add your own ones with a FormSheetContentCreator. The second one is more commonly used, because it is less effort to add two buttons instead of creating lots of new FormSheets just to define the behaviour of a single click on a button. The third one is almost as common but due to the lack of influence on the button's look and feel less used. It's because here you only set an action to the standard button by resolving it with getButton(int id) and using setAction(Action action) on it. The button's ids are stored as static int in the FormSheet, where BTNID_CANCEL stands for the cancel button and BTNID_OK for the ok button. In order to make the behaviour serializable you have to define it within a FormSheetContentCreator. Otherwise the information will be lost after loading. It is also possible to alter the buttons design and caption when adding new ones.
ToDo:
- Initialize a new FormSheet.
- Create a new FormSheetContentCreator for the FormSheet and add it.
- In the protected void createFormSheetContent(FormSheet fs) method of the FormSheetContentCreator remove the OK-Button by calling removeButton(int id).
- Add a new FormButton using the addButton method. Like here you can shortly add an sale.Action to the button.
- Set a new sale.Action to the CANCEL-Button by fetching it with the getButton(int id) method and setting the action with setAction(Action).
Example Source Code:
class where FormSheet is set:
1
FormSheet sheet = new FormSheet("DisplaySheet", null);
2
sheet.addContentCreator(new DisplayFormSheetContentCreator());
FormSheetContentCreator class:
2
public class DisplayFormSheetContentCreator extends FormSheetContentCreator
{
3
protected void createFormSheetContent(FormSheet fs)
{
fs.removeButton(FormSheet.BTNID_OK);
4
fs.addButton("Ok", 102, new DisplayCustomAction());
5
fs.getButton(FormSheet.BTNID_CANCEL).setAction(new DisplayCustomAction());
}
}
Define an Action for a FormSheet Button
Description: The reason for adding buttons to your application is of course to let the user interact with it. By default, buttons don't have any functions, so you have to add a sale.Action to the button either by the method setAction(Action aAction) provided by the button or by already initializing the button with an action. Remember to put all this into a FormSheetContentCreator in order to have the Actions serialized as you save the Shop's status. Otherwise all the information will be lost after loading and the buttons will be useless.
ToDo:
- There are two ways of defining an Action:
- Create a class implementing the interface Action and add the public void doAction(SaleProcess process, SalesPoint point) throws Throwable method to it. Therein define what to do if the action was called.
- Create an anonymous implementation of Action and define what to do if triggered.
Example Source Code:
1 a
public class DisplayCustomAction implements Action
{
public void doAction(SaleProcess process, SalesPoint point) throws Throwable
{
// define what shall be done when triggering the action
point.runProcess(new DisplaySaleProcess("SampleProcess"));
}
}
1 b
public class DisplaySalesPoint extends SalesPoint
{
protected FormSheet getDefaultFormSheet()
{
FormSheet fs = new FormSheet("FormSheet", null);
fs.getButton(FormSheet.BTNID_OK).setAction(
new Action()
{
public void doAction(SaleProcess process, SalesPoint point) throws Throwable
{
// define what shall be done when triggering the action
point.runProcess(new DisplaySaleProcess("SampleProcess"));
}
});
}
}
Define an Action for a Button in a Component
Description: Sometimes you will need to add Buttons in the component part of a FormSheet, since the Button bar at the bottom may be not sufficient (e.g. too small). This has to be done within a FormSheetContentCreator when serialization is needed.
ToDo:
- Create a new FormSheetContentCreator with the standard constructor and add the createFormSheetContent() method.
- Create the JButton according to your needs.
- Associate the Action that should be executed when clicking with the Button. This is done with the ActionActionListener, that takes the Action itself aswell as the FormSheet that contains the button.
- Add the button to the FormSheet component. In this case this is done by creating a container for the button and setting it as FormSheet component. You could also use the getComponent() method of the FormSheet and manipulate the component directly (e.g. if it is already a special FormSheet).
Example Source Code:
1
public class DisplayFormSheetContentCreator extends FormSheetContentCreator
{
public DisplayFormSheetContentCreator()
{
super();
}
protected void createFormSheetContent(FormSheet fs)
{
2
JButton button = new JButton("TutorialButton");
3
button.addActionListener(new ActionActionListener(fs, new DisplayCustomAction()));
4
JPanel container = new JPanel();
container.add(button);
fs.setComponent(container);
}
}
Set error handling for a FormSheet
Description: According to the type of FormSheet you use (and the purpose you use them for), it could be useful to react on errors occurring in different ways. So it is, for example, not always senseful to stop the currently running process, whenever an error occurs if you are able to correct it programatically.
Most of the FormSheets use their own strategies for handling their tasks. Here you can setup the error handlers.
ToDo:
- Create a new FormSheetContentCreator with the standard constructor and add the createFormSheetContent() method.
- Get the FormSheet of the type you need. In our case we assume, we've been given a TwoTableFormSheet and therefore can use it's strategy.
- Set the error handler you need. Basically, there are three possibilities:
- If the process has to be aborted immediately and an error message has to be displayed for the user, you can use the FormSheetStrategy.DEFAULT_ERROR_HANDLER. This is also set by default, so normally you don't have to do anything in this case.
- If the process can be continued, but a popup dialog shall be used displaying the error message for the user, you can use the FormSheetStrategy.MSG_POPUP_ERROR_HANDLER.
- In any other case, you will have to implement your own ErrorHandler.
Example Source Code:
1
public class DisplayFormSheetContentCreator extends FormSheetContentCreator
{
public DisplayFormSheetContentCreator()
{
super();
}
protected void createFormSheetContent(FormSheet fs)
{
2
TwoTableFormSheet ttfs = (TwoTableFormSheet) fs;
3 a
ttfs.getStrategy().setErrorHandler(FormSheetStrategy.DEFAULT_ERROR_HANDLER);
3 b
ttfs.getStrategy().setErrorHandler(FormSheetStrategy.MSG_POPUP_ERROR_HANDLER);
3 c
ttfs.getStrategy().setErrorHandler(new FormSheetStrategy.ErrorHandler() {
public void error(SaleProcess p, int nErrorCode) {
System.out.println(p.getErrorMsg(nErrorCode));
}
});
}
}