View
218
Download
0
Embed Size (px)
Citation preview
Génie logicielSoftware Engineering
C. Petitpierre
Thèmes du cours
• UML (Unified modeling language)• Processus de développement• Design Patterns• Java Server Faces, EJB3• JET, use of templates• Chargement de databases en XML
SAX, FSM, JavaCC• Langage permettant la validation:
CCS et threads
Personnages et sites
• Martin Fowlerhttp://martinfowler.com/
• Scott Amblerhttp://www.ambysoft.com/
Projet de génie logiciel
• Gestion des commandes des clients dans un restaurant:
Prendre une commande
L’afficher au comptoir et à la cuisine
Indiquer quand elle est prête
Faire les factures
…
• Plan de travail disponible sur le Web:http://ltiwww.epfl.ch/~petitp/GenieLogiciel
UMLUnified Modeling Language
(OMG object management group)
http://www.uml.org/ http://www.omg.org/
UML usage
• UML is particularly useful to sketch IT applications (pencil and eraser or PowerPoint)
• It can be adapterused to document applications (reverse engineering consists of recreating the diagrams from the code, which is a cheap way of making a“cheap” documentation)
• It is used by ArcStyler and OptimalJ (among others) to create applications(it is difficult to enter the application details in these systems)
UML / RUP: history• Rational founded in 1981 around a processor and
development environments for Ada and C++
• In 1990, Grady Booch develops Rose for Rational, a tool to specify models
• In 1995, Rational hires James Rumbaugh, buys Ivar Jacobson’ company, and the three “amigos” define UML (unified modeling language)
• Same year, Philippe Kruchten defines RUP (Rational unified process)
• In 2002, IBM buys the company for ~ $2B (beside UML, they are consultants for many customers)
UML Rules of Thumb(features not bugs !)
• Nearly everything in UML is optional
• UML models are rarely complete
• UML is designed to be open to interpretation
• UML is intended to be extended
UML 2.0 in a nutshell, Dan Pilone, O’Reilly, 2005
UML
• Use cases
• Class diagrams
• Sequence diagrams
• Collaboration diagrams
• State diagrams / Activity diagrams
UML
Use case diagram for an ATM (automate de banque)
Only lists the names of the use case scenarios, which describe the various functions defined by the application
Withdraw Money
Check Balance
TransferMoney
Open Account <<include>>
<<include>>
UML: Use case (scenario)
1. A client inserts a card into the ATM.
2. The system reads and validates the card information.
3. The system prompts for a PIN.
4. The client enters the PIN. (client, system = actors)
5. The system validates the PIN
6. The client selects “Withdraw Money”
7. The client enters the requested amount.
8. The system requests the amount from the client’s account.
9. The system asks the client to remove the card.
10.The system dispenses the requested amount from the banknote buffer(only done if the requested amount has been granted in 8).
UML: Use case (extension)
1. A client inserts a card into the ATM.
2. The system reads and validates the card information.
3. The system prompts for a PIN.
4. The client enters the PIN. (client, system = actors)
5. The system validates the PIN
Extensions
5a. If the PIN is invalid, the system rejects the card
6. The client selects “Withdraw Money”
7. The client enters the requested amount
8. . . .
UML: Dictionary
If you work in a new domain (bank, assurances, production…) you will need to create a small dictionary
Class diagram with visibility
Bill
- numberOfItems : int
+ addItem(item:Item)- getFirstItem() : Item# getNextItem() : Item
+ public visible from everywhere
- private local to the class
# protected visible only from the derived class
~ package visible from the package (Java)
(actually this is too far into the details for a sketch)
Class diagram: inheritance
SomeClass AnotherClass
SuperClass
UML: note(Omondo on Eclipse)
Class diagram: association
SomeClass AnotherClass
Weakest form of relationship
(Can be used, for example, to indicate that a method may call a method in another class)
Class diagram: aggregationBill
- numberOfItems : int
+ addItem(item:Item)- getFirstItem() : Item# getNextItem() : Item
BillItem
+ ProductNumber : int+ NumberOfPieces : int+ UnitPrice : int
Customer
name : String
10..*
Used to show that an object instantiated from that class contains another object (in an attribute) or a collection of objects (collection in an attribute)
Class diagram: composition
Window menu1
1..*
Strong relationship. If the main class is deleted, the subclass has no meaning any more and is destroyed.
Not often used.
Sequence diagram
Main
Object B
run ( )
start ( )
foo ( )
return
Object X
xMeth ( )
new ( )
Collaboration diagram (Omondo)
Collaboration diagram(context of the application)
controller
temporarydataset
GUI
printer_A
remotedatabaseproxy
DBinter-face
printer_B
employee
Non standard use of specific icons
State diagram / state charts
doing
02
1
displayapplication
exit
enter
start
enter
display_francs
next
ignore
ignore
display_euros
previous
timeout
State diagram / state charts
A
action
start
E
timeout
Entry point
Exit
Relation between a state chart and a use case
collaborationdiagram with use case 1
state chartdiagram of
a component
collaborationdiagram withuse case 2
02
1
component
Collaboration diagram: actors and repositories
Use case: what they do and in what sequence
State chart: all actions of a component
Activity diagrams
• Inspired from the Petri nets
• May fork and join threads
• The use of forks and joins is not a very good idea !! It makes it very difficult to follow the state of a program
Activity diagram: an example
close
find
log register
[registration]
[quit]Fork
Two threads
Join
<< Stereotypes >>
<< CMP EJB >>Bill- numberOfItems : int
Customer
name : String
<< hasBills >>
UML: a technique to start a development
Identifying classesBorrowing The library contains books and journals. It may have several copies of a given book. Some of the books are for short term loans only. All other books may be borrowed by any library member for three weeks. Members of the library can normally borrow up to six items at a time, but members of staff may borrow up to twelve items at one time. Only members of staff may borrow journals.
library is out of scope item is vague
short term loan is an event time is out of scope
week is a measure of time system is not part of the domain
We keep:
book – journal – copy (of book) – library member – staff member
Use of a sequence diagram
Main
Object B
Start of the thread(calls method run)
Method executed at the same time asmethod run or method xMeth.
run ( )foo ( )
start ( )
return
Object X
xMeth ( )
new ( )
The example explains how the Java threads work
Corresponding code: 1st solution
public class Main { static ActiveObject a1; static public void main (String [] args) { a1 = new ActiveObject (); // create an active object a1.start(); // start the active object a1.foo();} }
class ActiveObject extends Thread { public void foo() { } public void run () { for (;;) { System.out.println("ActiveObject running"); try { Thread.sleep(3000); } catch (Exception e) {}} } }
Corresponding code: 2nd solution
public class Main { static AnObject a2; static public void main (String [] args) { a2 = new AnObject (); // create an active object new Thread(a2).start(); // start the active object a2.foo() { }} }
class AnObject implements Runnable { public void foo() { } public void run () { for (;;) { System.out.println("AnObject running"); try { Thread.sleep(2000); } catch (Exception e) {}} } }
Use of sequence diagramsThreaded object 2
position of the wait ( ) (blocking)
run ( )
Threaded object 1
run ( )
Semaphore
position of the notify ( ) (non blocking)
stop ( )
kick ( )
Explains how the wait-notify commands work: see code next slide
positions of synch- ronized statements
allows only one thread at a time
stop(), kick() areuser-defined methods
Code corresponding to the sequence diagram
class T1 implements Runnable { Sem tm; public T1 (Sem t) { tm = t; } public void run () { for (;;) { tm.stop(); } }}
class Sem { public void kick () { synchronized (this) { notify(); } } public synchronized void stop () { try { wait(); } catch (InterruptedException ie) {} }}
class T2 implements Runnable { Sem tm; public T2 (Sem t) { tm = t; }
public void run () { for (;;) { tm.kick(); Thread.sleep(1000); } }}
There are two different ways of using synchronized.
Creation of the previous threads
sem = new Sem();
new Thread(new T1(sem)).start();
new Thread(new T2(sem)).start();
Yet another way of using synchronize
class Sem { String s = “” // empty string, or any object public void kick () { synchronized (s) { notify(); } } public void stop () { synchronized (s) { try { wait(); } catch (InterruptedException ie) {} } }}
Every Java object has provision for thread synchronization
Development Process
Well-known development processes
• RUP (Rational Unified Process)
• CMMI (Capability Maturity Model Integration)
• Agile / XP (eXtreme Programming)
Waterfall Development Process
Analysis
Design
Implementation
Testing
Maintenance
Civil engineering:
One first draws plans and only then are they realized !
Why wouldn’t it work with software ?
?
Iterative Process (spiral)
AnalysisDesign
ImplementationTest
Superficial analysis, design, implementation and test of the whole application, then better analysis, design, implementation and test, and improvement at each revolution ?
or ?
Thorough analysis, design, implementation and test of a part, and then analysis, design, implementation and test of a new part at each revolution ?
Development
Maintenance
Agile Development Process
Most important characteristics:
No long term planning. Deliver working
software frequently
(from a couple of weeks to a couple of months,
with a preference to the shorter timescale).
RUP
RUP: Rational Unified Process
• For the project manager
• What is important in a project
• What leads to failures
• Working team composition
• Management tools
• Very little on how to do things !
RUP: Warning
What is Rational's RUP ?
It is a methodology. But to sell it, Rational supplies a bunch of html pages (about 16000 pages). There are some documentation templates also. A warning though, if you are thinking to get into a good methodology in reasonable time, then you need to rethink, RUP will take a looooooooooong time to grasp, implement and finally there is a 50% risk that you will never reap the benefits.
http://www.theserverside.com
RUPAvailable on a CD-Rom that must be customized.Accompanied by : P. Kruchten,“The Rational Unified Process: an Introduction”,
Characteristics and best practices:1. Iterative process
2. Requirement management
3. Architecture and use of components
4. Modeling and the UML
5. Quality of development process and product
6. Change management (tools)
RUP: 1 - Iterations
RUP2 - Requirement management
– Requirements are stakeholders’ needs, goals and wishes. (stakeholders - parties prenantes = contractor, customers, users, employees)
– (FURPS: functionality, usability, reliability, performance and supportability)
– URSP = non functional requirements
RUP3 - Architecture
• Components:– A nontrivial piece of software, a module, a
package, a subsystem, a layer, major classes– CORBA (remote objects), ActiveX, JavaBeans,
a Web page, database table– Mechanisms such as persistency and
communication tool, patterns (MVC, ORB object request broker . . .)
RUP4 - UML and modeling
Use case diagram for an ATM
(automate de banque)
Withdraw Money
Check Balance
TransferMoney
Open Account <<include>>
<<include>>
RUP: 4 - UML and modelingA scenario or use case (the term use case is more general)
1. A client inserts a card into the ATM.
2. The system reads and validates the card information.
3. The system prompts for a PIN.
4. The client enters the PIN. client, system = actors
5. The system validates the PIN
6. The client selects “Withdraw Money”
7. The client enters the requested amount.
8. The system requests the amount from the client’s account.
9. The system asks the client to remove the card.
10.The system dispenses the requested amount from the banknote buffer(only done if the requested amount has been granted in 8).
RUP: 4 - Collaboration diagram
P. Kruchten, The Rational Unified Process – An Introduction, Addison-Wesley
RUP: 5 - Quality• Quality:
– Reliability (no bug)– Functionality (does what it is meant for)– Performance
• Tests:– Unit tests– Integration tests (interactions of units)– System test (complete application)– Acceptance test (same, but performed by the buyer)– Regression tests (repetition of previous tests to
assure that the new features have not introduced bugs in the former part)
RUP: 6 - Change management
• ClearQuest – Tool for change request management
• ClearCase– Tool for configuration management
CMMI
CMM - CMMI
• Capability Maturity Model(developed by the Software Engineering Institute of CMU)
• Capability Maturity Model Integration(upgrade of CMM)
• http://ltiwww.epfl.ch/~petitp/GenieLogiciel/CToMFeb02.pdf
(article by a RUP vendor, excerpts on the next slides)
CMMI
Level 1 (initial)
Represents a process maturity characterized by
unpredictable results.
Ad hoc approaches, methods, notations, tools, and reactive management translate into a process dependent predominantly on the skills of the team to succeed.
CMMILevel 2 (managed)
Represents a process maturity characterized by
repeatable project performance.
The organization uses foundation disciplines for
requirements management; project planning; project
monitoring and control; supplier agreement management;
product and process quality assurance; configuration
management and measurement/analysis.
For Level 2, the key process focus is on project-level
activities and practices.
CMMILevel 3 (defined)
Represents a process maturity characterized by improving project performance within an organization. Consistent, cross-project disciplines for Level 2 kpas (key process areas) are emphasized to establish organization-level activities and practices.
Additional organizational process areas include:
Requirements development, technical solution, product integration, verification, validation, risk management, organizational training, organizational process focus, decision analysis and resolution, organizational process definition, integrated project management
What are risks ?
If your customers need a new system by a specific date the risk is high. If that system is a new challenge for your software group the risk is even greater. If that system is a new challenge to the entire software industry the risk is still greater even.
Unsatisfied employees also participate in the risk.
CMMILevel 4 (quantitatively managed)
Represents a process maturity characterized by improving organizational performance.
Historical results for Level 3 projects can be exploited to make trade offs, with predictable results, among competing dimensions of business performance (cost, quality, timeliness).
Additional Level 4 process areas include:– Organizational process performance: setting norms and
benchmarks for process performance.– Quantitative project management: executing projects
based on statistical quality-control methods.
CMMILevel 5 (optimized)
Represents a process maturity characterized by rapidly reconfigurable organizational performance as well as quantitative, continuous process improvement.
Additional Level 5 process areas include:
– Causal analysis and resolution: proactive fault avoidance and best practice reinforcement.
– Organizational innovation and deployment: establishing a learning organization that organically adapts and improves.
(few companies arrive at that level)
Agile Development Processes
XProgramming
Agile Development Process
Most important characteristics:
No long term planning. Deliver working
software frequently, from a couple of weeks
to a couple of months, with a preference to
the shorter timescale.
http://agilemanifesto.orghttp://martinfowler.com
http://www.ambysoft.com/
XProgramming
Complaint and excuse of the programmer:
“the problem with this project is that the requirements are always changing”
Actually it is always and will always be the case, we must thus do with it !
XProgramming
(http://www.extremeprogramming.org)
XProgramming
PlanningDesigningCodingTesting
2-6 weeksof a slice
XProgramming
• No global up front planning• Slices (meaningful slices) defined with
the customer• Created quickly (2-6 weeks)• Accepted by the customer
Early feedback and impact on the requirements
• Pair programming !!
XProgramming
Planning
– User stories are written.– Release planning creates the schedule.– Make frequent small releases.– The project velocity is measured.– The project is divided into iterations.– Iteration planning starts each iteration.– Move people around.– A stand-up meeting starts each day.– Fix XP when it breaks.
XProgrammingDesigning
– Simplicity– Choose a system metaphor.– Use CRC1 cards for design sessions.– Create spike solutions (for difficult problems)
to reduce risk.– No functionality is added early.– Refactor whenever and wherever possible.
1 Class-Responsibility-Collaboration
XProgramming
Coding
– The customer is always available.– Code must be written to agreed standards.– Code the unit test first.– All production code is pair programmed.– Only one pair integrates code at a time (CVS).– Integrate often.– Use collective code ownership.– Leave optimization till last.– No overtime.
XProgramming
Testing
– All code must have unit tests.– All code must pass all unit tests before
it can be released.– When a bug is found tests are created.– Acceptance tests are run often and the
score is published.
XProgramming
• Documentation is a planned task and the customer pays for it (some documentation must be integrated in the source code)
• Teams are from 2 to 12 persons
• A CVS (concurrent versioning system) is of course mandatory
XProgramming
http://www.extremeprogramming.org
TDD
TDD: Test Driven Development
– Write a test before any real code writing
On Eclipse
– JUnit 1.3.8: Java 1.4
– JUnit 4: Java 5.0 with annotations
TDD: Test Driven Development
Documentation Eclipse:
• Help > Help Contents > Java Development User Guide > Getting Started > Basic Tutorial > Writing and running JUnit tests (1.3.8)
• Help > Help Contents > Java Development User Guide > What’s new > JUnit Toolings (4)
• (http://www.junit.org) JUnit 4.0 in 10 minutes
TDD
• Create a Java project as usual
• Project > Build Path > Add Libraries… > JUnit 4
• Right-click the class that will be under test (possibly with an empty method) > New > JUnit Test Case (creates a case of the version entered into the build path)
• Define a package that is going to contain the tests
TDDEdit the test class:
– Create an instantiation of the Class under test in the @BeforeClass method (once for all methods) or in the @Before method (once for every method)
– Put one test (CTRL-space) per test method. If you put several tests, they will not be identified separately when there is an error.
– The message in an assertion is displayed if there is an error
– It is possible to check the kind of Exception returned by the method under test (see doc)
TDD
package unitTests;
import static org.junit.*;
public class ComputationTest1 {static example.Computation computation = null;
/** * This method is executed only once before all tests * Here, it creates an environment to test the modules */
@BeforeClasspublic static void setUp() throws Exception {
computation = new example.Computation();computation.setResult(0);
}
TDDpackage unitTests;import static org.junit.Assert.*;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.junit.BeforeClass;
public class ComputationTest1 {static example.Computation computation = null;
/** * The following method is repeated before every test */
@Before public void setUpRepeated() throws Exception {
computation = new example.Computation();computation.setResult(0);
} . . .
TDD. . .@Afterpublic void tearDown() throws Exception {}@Testpublic void testComputation() {}@Testpublic void testAdd1() {
computation.add(7);// To show an error in a test, the next check is incorrect !assertTrue("Error 1 of computation", computation.getResult()==6);
}@Testpublic void testAdd2() {
computation.add(-12);assertTrue("Error 2 of computation", computation.getResult()==-5);
}}
TDD: running the tests
• Select some test classes or the test package
• Right click the selection > Run As > JUnit Test
• The result is displayed in the view JUnit (same window as the Package Explorer)
Exercice
/****** This is a test program with 5 lines of code//*****//***/// Slightly pathological comment ending...
public class Hello { public static void main(String [] args) { // comment // Say hello System./*wait*/out./*for*/println/*it*/(“Hello/*”); }}
Develop a class with a method that counts the lines that are not empty nor just contain comments, creating tests for each new code increment.
As soon as you have a idea to start the problem, create a test, implement this first part and test it. For each new increment, do the same.
Don’t think too much beforehand, just try and test, but keep all tests and improve/refactor your code
Use pair programming
Design Patterns
http://en.wikipedia.org/wiki/Design_Patternshttp://en.wikipedia.org/wiki/Design_pattern_(computer_science)
See also anti-patterns in Wikipedia and http://ltiwww.epfl.ch/~petitp/GenieLogiciel/PointCounterPoint.pdf
Design Patterns
Christopher Alexander is an architect noted for his theories about design. He produced and validated a pattern language designed to empower any human being to design and build at any scale.
The concept of design patterns has been reused in IT by E. Gamma, R Helm, R. Johnson and J. Vlissides: also known as the Gang of Four or GoF. They have sold 500.000 copies of their book.
Abstract factory, Adapter, Composite, Decorator, Factory methods, Observer, Strategy, Template method…
E. Gamma is currently heading the Eclipse project
Other authors have written books about patterns
Design Patterns (basic concepts)
class SomeClass extends SuperClass{
private String str;
public String getString() {
return str;}
}
Signature
All signatures = type (or subtype)
The whole = class (subclass or derived class)
The subclass inherits (extends) the superclass
Design Patterns
• An object is an instance of a class
• An abstract class has abstract operations
• A concrete class is one that is not abstract
• A concrete class that inherits an abstract class must override the abstract operations
• A first-class operator or function is one that can be defined during the execution of a program, stored in a variable, passed as arguments to other functions, and returned as the values of other functions.
Inheritance / Composition / Forward
class SuperClass {
private String name;
public String getName() { return name; }
}
class InheritanceClass extends SuperClass{
}
ci = new InheritanceClass ();
ci.getName();
Inheritance / Composition / Forward
class SuperClass {
private String name;
public String getName() { return name; }
}
class CompositionClass {
SuperClass element;
}
cc = new CompositionClass()cc.element.getName()
Inheritance / Composition / Forward
class SuperClass {
private String name;
public String getName() { return name; }
}
class ForwardClass {
SuperClass element;
public String getName() {
return element.getName();
}
} Cd = new ForwardClass();cd.getName();
Delegation = composition + forward + environment
class SuperClass {
private String name;
public String getName(DelegationClass env) {
return env.xxxxx+name;
} }
cd = new DelegationClass()
class DelegationClass { cd.getName()
SuperClass element;
String xxxxx;
public String getName() { return element.getName(this); }
} // see GoF’s state design pattern for an example
Two GoF principles
• Program to an interface, not an implementation
• Favor object composition over class inheritance
Declare interfaces, not classes,
public interface Interface {
public String method();
}
public class SubClass implements Interface{
public String method() {
return null;
}
} BUT…
…debugging and maintenance become difficult !Example:
public class Application {
Interface intf;
public static void main(String[] args) {
Application a = new Application();
a.intf = new SubClass();
a.execute(args);
}
void execute(String[] args) {
intf.method();} } Where do I find the
source of method() ?
Note that you don’t need to implement a new interface to insert new attributes !! If an attribute is added to an object, it does not disturbs the other uses of the object.
Refactoring may also be called to rescue !
The GoF patterns
Design Patterns – Elements of Reusable Object-Oriented Software,E. Gamma…
Inheritance (in GoF’s book)
Aaaa
Operation()
Bbbb
Operation()
Leaf
Operation()
Aaaa
Operation()
Bbbb
Operation()
Leaf
Operation()
identical
SingletonClass1
Class2
Class3
Singleton
Only one singleton is instantiated
One does not know the order in which the Classi are instantiated
All Classi are not always present, they have no references to each other and there may be other such classes later
Who does create the Singleton ?
Singleton
public class ClassicSingleton {
private static ClassicSingleton instance = null;
protected ClassicSingleton() { // Exists only to defeat instantiation }
public static ClassicSingleton getInstance() {
if (instance == null) {
instance = new ClassicSingleton();
}
return instance; used in place of new ClassicSingleton()
} }
// from Javaworld
Singletons
• Global variable !
• Don’t overuse them !
• Only for data that are meaningful in the whole program
• Not more than a few singletons in a program
(Complex) example of use
Threadlocal is a singleton that may be attached to the current thread, the one that executes the code of the current method.
It is managed by the JVM.
TheadLocal singleton
Thread1
Thread2
The class introduced into ThreadLocal can be accessed in the following way:
x = HolderObject.get() x = HolderObject.get() x = HolderObject.get()
y = HolderObject.get() y = HolderObject.get()
Thanks to the JVM, there is one singleton per thread,
x != y, x always the same, y always the same
(Complex) example of useclass HolderObject {
public static ThreadLocal<MyObject> // definition of an attribute
currentObject = new ThreadLocal<MyObject>() {
protected synchronized MyObject initialValue() {
return new MyObject();
}
};
public static MyObject get() { // any object can be kept in ThreadLocal
return currentObject.get();
} }
// At the initialization, the object ThreadLocal calls method initValue(),
// defined by the developer. This method returns an object.
// This object is then stored in ThreadLocal located in currentObject.
// Moreover, the JVM saves and restores this object at each context switch
(Complex) example of useclass HolderObject {
public static ThreadLocal< HolderObject >
currentObject = new ThreadLocal< HolderObject >() {
protected synchronized HolderObject initialValue() {
return new HolderObject ();
}
};
public static HolderObject get() {
return currentObject.get();
}
Type userAttribute = “initval”;
}
// Usually the object that is stored is the HolderObject itself
ThreadLocal
• Process support (task control block, semaphore, identifier)
• Transaction manager (EJB3)
Can be used to replace parameters in a method call, but in very specific situations, such as
Exercice 2
Créer un object dont la méthode run est exécutée sur un thread.
Cette méthode contient une boucle infinie et affiche toutes les secondes un identificateur unique mémorisé dans le singleton ThreadLocal du thread.
Créer deux de ces objets et lancez-les sur deux threads.
Pour générer des identificateurs uniques, utiliser un deuxième singleton (global celui-là).
Attention, sa méthode doit utiliser synchronized !
class Singleton { static Singleton single = null; int i = 10; public static Singleton instantiate() { if (single == null) { single = new Singleton(); } return single; } public int get() { return i++; }}
Quand on a obtenu un ID, il ne faut plus utiliser le singleton depuis le même thread, car les autres objets feront changer le contenu de la variable utilisée pour générer les Ids.
new Thread(new Thr()).start();new Thread(new Thr()).start();
Singleton retournant un ID unique
class HolderObject { public static ThreadLocal<HolderObject> currentObject = new ThreadLocal<HolderObject>() { protected synchronized HolderObject initialValue() { return new HolderObject(); } }; public static String getString() { return currentObject.get().str; } public static void setString(String s) { currentObject.get().str = s; } String str = null;}
Un singleton par thread. On peut faire plusieurs fois getString() dans chaque thread. Chaque thread verra sa propre valeur.
Creation d’un objet ThreadLocal
Class or object adapter
Goal:
The class adapter implements a new interface that provides a new functionality or an existing functionality under a new name.
In the class adapter, the code that implements or support the new functionality is called from an auxiliary object that extends the object with the available functionality.
The object adapter delegates the call to the available object.
Design Pattern: Adapter (Inheritance / Composition / Delegation)+Interface
Existing class
Required interface
Adapter
Just forwards the calls
client
Design Pattern: Adapter
Existing class
Required interface
Adapter
Just forwards the calls
client
Existing object
Two different possibilities
Also possible
Java implementation of a class adapter
/* Adapt DList class to Stack interface */
class DListImpStack extends DList implements Stack {public void push (Object o) {
insertTail (o);} // forwards the calls
} Adapter
interface Stack {void push (Object o);
}
/* Double Linked List */
class DList {public void insertTail (Object o) { ... }
}
Java implementation of a object adapter
/* Adapt DList class to Stack interface */
class DListImpStack implements Stack {DList dl = new DList ();public void push (Object o) {
dl.insertTail (o);}
}
interface Stack {void push (Object o);
}
/* Double Linked List */
class DList {public void insertTail (Object o) { ... }
}
Composite: tree structure(one must be able to walk through and
add/remove groups or leaves in the same way)
supergroup
group square line
circle square
Model of a diagram
for a graphical application
Composite
Component
Operation()Add(Component)Remove(Component)getChild(int)
Composite
Operation()Add(Component)Remove(Component)getChild(int)
Leaf
Operation()
Implementation of the Compositeabstract class GraphicComponent {
abstract public void print();}
class CompositeGraphic extends GraphicComponent {
private ArrayList<Graphic> mChildGraphics = new ArrayList<Graphic>();
public void print() {for (Graphic graphic : mChildGraphics) {graphic.print();
}}
} Groupclass Ellipse extends GraphicComponent {
public void print() {System.out.println("Ellipse");
}} Leaf- All nodes are GraphicComponent
with a print() method
- root.print() prints all leaves
Composite: object diagram
supergroup
group square line
circle square
Each object extends abstract class GraphicComponent to make all objects compatibles
Composite: other implementation details
• Parent references may be handled
• But if parents are multiple reverse path ambiguous Flyweight
Composite: other implementation details
Where to define the children list and the getChild method ?
• Component class (abstract class)–all elements similar (transparency)
–but one may try to retrieve a child from a leaf
• Composite class –one can check that elements are retrieved
only from composite (safety)
–But elements are dissimilar
Decorator
BufferedReader br =
new BufferedReader(
new InputStreamReader(
new ByteArrayInputStream(
“A text to read”.getBytes()
) )
);
Decorator (class diagram)
Decorator
• More flexibility than inheritance• Several identities (addresses) !• Keep interface simple• Decorator’s abstract class optional• For more complex cases Strategy
Decorator (object diagram)
HScrollDecorator
Constructor(x) { super(x) }
VScrollDecorator
Constructor(x) { super(x) }
SimpleWindow
WindowDecorator
Window decorWindow
Constructor(x) { decorWindow = x }
WindowDecorator
Window decorWindow
Constructor(x) { decorWindow = x }
Window (interface)
draw()
Window (interface)
draw()
Window (interface)
draw()
x = new HScrollDecorator (new VScrollDecorator (new SimpleWindow()));
Decorator
HScrollDecorator
Constructor(x) { super(x) }
draw()
VScrollDecorator
Constructor(x) { super(x) }
draw()
SimpleWindow
draw()
public void draw() {
drawVerticalScrollBar();
decoratedWindow.draw();
}
public void draw() {
drawHorizontalScrollBar();
decoratedWindow.draw();
}
Window (interface)
draw()
Decorator
HScrollDecorator
Constructor(x) { super(x) }
getDescription()
VScrollDecorator
Constructor(x) { super(x) }
getDescription()
SimpleWindow
getDescription()
public String getDescription() {
return decoratedWindow.getDescription() + ", including vertical scrollbars";
}
public String getDescription() {
return decoratedWindow.getDescription() + ", including horizontal scrollbars";
}
public String getDescription() {
return "simple window";
}
Decorator (without the intermediary abstract classes)
HScrollDecorator
Window decorWindow
Constructor(x) { super(x) decorWindow = x }
VScrollDecorator
Window decorWindow
Constructor(x) { super(x) decorWindow = x }
SimpleWindow
Window (interface)
draw()
Window (interface)
draw()
Window (interface)
draw()
Façade(simply relays calls to a set of methods
an example is the Session Enterprise Java Beans)
Class
Class
Class
Class
Facade
Bridge
(avoids the following situation)Window
OS independ.
XWindow
OS X
YWindow
OS Y
XWindow
OS X
YWindow
OS Y
NewWindow
OS independ.
One must create two distinct sources, but their sole difference is
“extends Window”
versus “extends NewWindow”
Two hierarchies interleaved
OS Implementation
OS independent hierarchy
abstract class
Bridge (the red-blue lines)
Window
XWindow
OS X
YWindow
OS Y
IconWindowSimpleWindow
FramedIcWindow
Either red or blue is instantiated, at run time
BridgePatternExample.java(as in the CVS, project Patterns)
Shape
drawingAPI
resizeByPercentage()
DrawingAPI1
drawCircle()
DrawingAPI
drawCircle()
CircleShape
draw()
<<exclusifs>>
another variant
drawingAPI
DrawingAPI2
drawCircle()
Factories• Abstract Factory
The client gets a factory corresponding to a specified domain and then generates objects by calling it. All generated objects belong to the specified domain.
• Factory MethodThe client gets objects that are created according to some individual characteristic.The objects of the same application may belong to different domains.
Abstract factories are implemented with the help of factory methods !
Abstract Factory
Factory1
create()<<create object>>
Client
Factory2
create()<<create object>>
ConcreteWindow1 ConcreteWindow1
Of course the factories also inherits a common interface, not shown here
Abstract factory (with interfaces)
AbstractFactory
staticCreateFactory()
CreateProductA()CreateProductB()
ConcreteFactory1
CreateProductA()CreateProductB()
ConcreteFactory2
CreateProductA()CreateProductB()
AbstractProductA
ConcreteProductA1 ConcreteProductA2
AbstractProductB
ConcreteProductB1 ConcreteProductB2
<<create>>
Client
Factory Method
AbstractCreator
factoryMethod(param)
ConcreteCreator
factoryMethod(param) // knows from param // which object to build // “xx.jpg” / “xx.gif”
Product
Client
create object of type x or y
The same factory creates different objects
A Simple Factory Method Pattern
class Complex {public static Complex fromCartesian(double real, double imag) {
return new Complex(real, imag);}
public static Complex fromPolar(double rho, double theta) {return new Complex(rho * java.lang.Math.cos(theta),
rho * java.lang.Math.sin(theta));}
private Complex(double a, double b) {// ...
} }
One method for each kind of object because it is not possible to differentiate them by their parameters.
Créer une factory qui génère les composants suivants:
1. Factory de fenêtres GUI2. Factory d'objets qui utilisent la console d'Eclipse
● La factory à créer est spécifiée dans une propriété● Les méthodes read du GUI et de la console sont
bloquantes
Exercice 3
// From the user properties:
Properties myProperties = new Properties();FileInputStream
in = new FileInputStream("myProp.properties");myProperties.load(in);in.close();String dType = myProperties.getProperty("displayType");
// From the system properties:
dtype = System.getProperty("terminalType");
Properties
Object semaphore = new Object();
synchronized (semaphore) { semaphore.wait();
}
synchronized (semaphore) {semaphore.notify();
};
Thread 1 Thread 2
1
3
2
Synchronisations of threads
If the call to notify() is done before the call to wait() the wait() will wait the next call to notify(). One should thus memorize the signal in an attribute
suspended
Instanciation
String dType = "pack.Classname";
Class dFactoryClass = Class.forName(dType);
dFactoryClass.newInstance();
Appel d'une classe statique
Class dFactoryClass = Class.forName(dType);
Method meth = dFactoryClass.getMethod("getDisplayFactory", new Class[0]);
DisplayFactory dFactory = (DisplayFactory)meth.invoke(null, new Object[0]);
Objet sur lequel la méthode est appelée (pas d'objet ici, donc appel statique)
Paramètres (type, puis valeurs)
Java Reflection
Drawback of Reflection
• The availability of the class called in by reflection is only checked at runtime.
• In order to clean an application, an IT team in a bank had eliminated all programs that had not been used for 6 months. At the December, 31 closure, the programs had been destroyed …..
Flyweight(poids mouche, poids plume)
• the program must handle lists containing many pieces
• many of them are similar
• using an object for each one would use up much memory space
• a typical example is a text, in which each letter has specific parameters (font, face, size…)
HashMap<SpecialChar>
Flyweight
GraphicCharFactory
pool
get (char, font): SpecialCharH
ArialL
TimesE
TimesO
Arial
ArrayList <SpecialChar> text (only pointers)get returns the pointer to the SpecialChar, and creates it if it does not exist already.
(flyweight pool)
Flyweight
The GoF book describes the flyweight within a hierarchy, but the hierarchy is independent from the flyweight pattern.
The Flyweight is used only at the line level.
page
paragraph paragraph paragraph
line line line line
GraphicCharFactory
pool
get(character, fontFace)
FlyweightExample
text
main()
<<HashMap>>
<<ArrayList>>
FlyweightExample.java(as in CVS, project Patterns)
GraphicChar
String fontFace
printAtPosition(c, x)
Observer
Subject
Observer
Observer
callcall
callEach time the subject is called, it calls a method in all the observers.
Thus at the end of each method called in the subject, there is a call to the list of observers.
The observer are registered, sometimes by themselves
The observers must of course have a common interface.
They are also called listeners!
Observer
Subject
void notify() { for (observer o: pool) { o.update() }}
pool
Observer1
update()
Observer2
update()
Observer3
update()
Event
Observer
Observer– update()– it often register itself in the subject
Subject– notify()– addObserver()– removeObserver()
Template (simple use of inheritance)
Algorithm expressed as a sequence of operations.
These operations must be replaced easily in the source
operation 1 operation 2 …
Template Method (trivial)
AbstractClass
templateMethodprimitiveOperation1primitiveOperation2
ConcreteClass
primitiveOperation1primitiveOperation2
void templateMethod() {
primitiveOperation1();
for (i=0; i<5;i++)
primitiveOperation2();
}
Different concrete classes implement different algorithms with the same sequence of operations
Strategy
• A user class requires one algorithm chosen among a set of algorithms for its execution
• The algorithm is performed within some context, which contains the data.
• At initialization time, the current algorithm is set and then called by delegation
• Template with delegation• Similar to bridge
(Bridge structure; Strategy behavior)
Strategy
Context
currentAlgorithm
contextInterface()
<< interface >>Strategy
algorithm()
ConcreteStrategyB
algorithm()
ConcreteStrategyA
algorithm()
ConcreteStrategyC
algorithm()
Client
1. Set the algorithm reference in the context
2. The algorithm is called from the context
3. The algorithm may have access to the context
Strategy (usage)
// Three contexts following different strategies
context = new Context(new ConcreteStrategyA());
context.execute();
context = new Context(new ConcreteStrategyB());
context.execute();
context = new Context(new ConcreteStrategyC());
context.execute();
Builder
Builder (similar to the strategy)
RTFReader
builder
ParseRTF()
<< interface >>TextConverter
ConvertChar(char)ConvertFontChange()ConvertParagraph()
TeXConverter
ConvertChar(char)ConvertFontChange()ConvertParagraph()getTeXText()
ASCIIConverter
ConvertChar(char)
getASCIIText()
TextWidgetConverter
ConvertChar(char)ConvertFontChange()ConvertParagraph()getTextWidget()
while (getNextToken) { switch (token) { case CHAR: builder.convertChar(token) break; case FONT: builder.convertFontChange(token) break; case PARA: . . .
Builder
Director
builder
Construct()
<< interface >>Builder
buildPart()
ConcreteBuilder
buildPart()getResult()
for all objects in structure {
builder.buildPart()
}
Chain of responsibility
This pattern allows a request to pass
along a chain of objects until one of
them handles it, thus giving multiple
objects a chance to handle the request.
Chain of responsibility
aClientaHandler aConcreteHandler
successor anotherConcreteHandlersuccessor
Chain of responsibility
Client
<< interface >>Handler
handleRequest()
ConcreteHandler1
handleRequest()
ConcreteHandler2
handleRequest()
successor
Command
1. First class operation
2. The command is provided with the object that must be processed
3. The command may be stored and performed later
Command Pattern
ConcreteCommand_2documentexecute()
Document
action()
ConcreteCommand_1documentexecute()
ConcreteCommand_3documentexecute()
The command is defined within an object. It can thus be passed over to other objects, it can be stored in a list, called at any time later and so on.
It is thus a mean to create a command as a first class operation.
The command also binds a command to a receiver (document)
Command Pattern: undo
Useful to create undoable commands:
– The command receives the state of the document before the command is executed
– It defines an undo function that restores the previous state
– The commands are kept in a list, in the order of their execution
– Can be combined with the memento pattern
Command Pattern
Invoker Command
execute()
ConcreteCommand
execute()
Receiver
action()
<<create and add command>>
<<create>>
Initializer
2
3
1
The commands can be stored in invokers, as in the observer pattern,
but here, the command contains a reference to a document.:
new ConcreteCommand(receiver)
Command: when to use it
• A document is read and then operations triggered by menu items are performed when a user clicks them (~callback, listener)
• One must prepare a context and have operations performed at a later time
• Support undo (stack the commands)
• Support transactions (which may have to be undone)
Memento
• Object used to save and later restore a state
OriginatorstatesetTo(Memento m)createMemento()
MementostategetState()setState()
CaretakerstatesetTo(Memento m)createMemento()
return new Memento(state) state = m.getState()
• The caretaker should no see the state, it must only store and retrieve it.
• Can be used by a Command object to maintain the state of the undo.
Prototype
• Several objects with few variations must be created
• Their attribute values are only known at run time
• One instantiates a prototype and then clones it
Cloning objects in Java
public class ToBeCloned implements Cloneable {
String s = “xxx";
int[] i = new int[] { 0,4,2 };
public ToBeCloned clone() {
try { // copies the simple attributes
ToBeCloned temp = (ToBeCloned ) super.clone();
temp.i = i.clone(); // an array must be cloned explicitly
return temp; // - Java defines clone() in arrays
} catch (CloneNotSupportedException cnse) {
return null;
}
}
} // for special constructs, the program must copy the contents explicitly
(not a pattern, butused in prototype)
PrototypeClient
operation()
<< interface >>Prototype
clone()
ConcretePrototypeA
clone()
ConcretePrototypeB
ConcreteSubPrototypeC
clone()
ConcreteSubPrototypeD
clone()
operation () {
prototype = new ConcretePrototypeC();
p = prototype.clone();
}
Proxies• Remote proxy
(proxy that forwards the calls to an object located in a remote site)
• Virtual proxy(e.g. an object that represents a figure that is loaded only when the figure appears on the current page. Used for optimization purpose)
• Protection proxy(an object that intercepts all calls to an object and checks if the caller has the right to call it)
• Handle(e.g. to maintain the number of links to the object to destroy it when the last link has been freed, referencing a persistent object to load it when it is first referenced or to map it on a key, to lock it, and so on)
Proxy
aClientsubject aProxy
realSubject aRealSubject
Client
request()
<< interface >>Subject
request()
Proxy
request()
RealSubject
request()
Iterator
Iterator
Item Item Item Item ItemHead
<<create>>
• Provides a way to traverse an aggregation according to different policies, without introducing the policy into the list object
• Filtering can be introduced
• The iterator references the list and vice-versa: a creator method in the list must connect the two ( can be implemented as a factory)
Iterator
Iterator: object diagramClient
Iterator
first()next()isDone()currentItem()removeItem()
ListIterator
Skip
AbstractList
createIterator()count()append(Object)removeItem(Object) . . .
SkipList
List
It is important to have a remove operation within the iterator, otherwise one cannot remove an element during the scan, because if the current element is removed without the iterator knowing it, it would not skip the missing element.
Iterator: class diagramClient
Iterator
first()next()isDone()currentItem()
ConcreteIterator
Aggregate
createIterator()
ConcreteAggregate
createIterator()
return new ConcreteIterator(this)
Mediator
• Many objects (called the colleagues) have connections between them
• The connections between the objects are managed by a central object: the mediator
Mediator: implementation
• The accesses to the colleagues may be implemented according to the observer pattern or simply by delegation
• The colleagues may pass a reference to themselves (this) within the call to the mediator, to identify the callers
Mediator: an object diagram
Colleague
ConcreteColleague1
ConcreteColleague2
Mediator
ConcreteMediator
State, Interpreter and Visitor Patterns
• These patterns will be used in relation with the analysis of languages (XML or other)
• We will thus study SAX, DOM and Javacc (Java compiler’s compiler) in relation with these patterns.
• (See http://www.epflpress.org/Book_Pages/petitpierre.html)
State Pattern
02
1
enter
display_francs
next
ignore
display_euros
previous
State Pattern (one method to handle all actions)
Context
staterequest()
<< interface >>State
handle()
ConcreteStateA
handle()
ConcreteStateB
handle()
state.handle()
The handle() method determines the nature of the event that has occurred.
If it is not foreseen in some state, the method takes some corrective action.
State Pattern (one method per action)
Context
stateaction1( )action2( )action3( )
State
action1( )action2( )action3( )
ConcreteStateA
action1( )
ConcreteStateB
action1( )
action3( )
EventGenerator
The action is determined by the event generator. If it is not foreseen in some state, the default action is called. The later ignores the action or handles the error.
State Pattern (State interface)
abstract class State {
public void action1(Context context) {}
public void action2(Context context) {}
public void action3(Context context) {}
}
State Pattern (context)
class Context {
State state = First.getState();
public void changeState(State s) { state = s; } public void action1() { state.action1(this); } public void action2() { state.action2(this); } public void action3() { state.action3(this); }}
State Pattern (State object)
class Second extends State { static State state = null; public static State getState() { if (state == null) { state = new Second(); } return state; } public void action2(Context context) { System.out.println("action 2");
// go to First State context.changeState(First.getState()); } public void action3(Context context) { System.out.println("action 3"); context.changeState(Third.getState()); }}
State Pattern (based on a switch)
Not official, but more efficient: use a switch (in theory, a state is a position in a program and the set of all the values of its variables)
class Context3 { enum State {a, b, c}; State state = State.a; public int number; public void handle(char inp) { switch (state) { case a: if (inp == '1') { System.out.println("action 1"); state = State.b; } break; case b: if (inp == '2') { System.out.println("action 2"); state = State.a; } else if (inp == '3') { System.out.println("action 3"); number = 0; state = State.c; } break; . . .
A SAX Parser with the State Pattern can be used to read
XML files.
XML: EXtensible Markup Language
<!-- A comment -->
<person>
<firstName>Robert</firstName>
<name/> <!-- empty -->
<address category="private" duration="permanent">
<street>Carrigton allee</street>
<town>Philadelphia</town>
</address>
</person>
& ' < > "
DTD: Data Type Definition(defined in the XML standard)
<!ELEMENT person (firstName, name, address)> <!ELEMENT firstName (#PCDATA)><!ELEMENT name (#PCDATA)><!ELEMENT address (street, town)><!ELEMENT street (#PCDATA)><!ELEMENT town (#PCDATA)><!ATTLIST address category CDATA IMPLIED duration (permanent|temporary) "temporary“>
PCDATA = parsed character data (the > are translated)
Commande ATTLIST
<!ATTLIST symbolOfTheElement
symbolOfTheAttribute1 attributeType1 characteristic1
symbolOfTheAttribute2 attributeType2 characteristic2
>
attributeTypes possibles dans une DTD
CDATA "xxx yyy "”
NMTOKEN "v1.2“ without space
NMTOKENS "v1.2 xxx www“ a list of tokens
ID "aUniqueId“ this ID should appear only once in
the whole XML file
IDREF "anExistingID“ there should be the same ID
somewhere in the XML file
IDREFS "anID anotherOne“ a list of IDREF
Enumeration (Monday | Tuesday | Wednesday)
NOTATION see the standard
ENTITY see the standard
characteristics possibles dans une DTD
"defaultValue“ used after an enumeration, itrepresents the default value, used if the attribute is not defined
#IMPLIED the attribute is optional
#REQUIRED the attribute is mandatory
Example of XML file
In order to dump a table of MySQL in XML
C:\>mysqldump – –xml databaseName tableName
SAX: Simple API for XML
SAXParserstartDocument()
startElement ()
endElement()
characters()
XML data
refers to
DTD
MyHandler
DefaultHandler
SAX Handler (available in J2SE)
Exercise 5.1
Create a program to read the following file:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE a SYSTEM "test6.dtd">
<a> <b> <c>xxx xx</c> <c>yyyy y</c> <c>zz zzz</c> </b> <d/> <b> <c>u u u </c> </b></a>
Exercise 5.1 (continuation)The preceding file correspond to this DTD:
<!ELEMENT a ( (b | d)* )><!ELEMENT b (c)*><!ELEMENT c (#PCDATA)><!ELEMENT d EMPTY>
/a </a>23
4
5
10a
b
chars
c
/dd
/c
/b
end/a
Using SAX
public static void main(String args[]) throws Exception {
MySAXHandler handler = new MySAXHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
SAXParser saxParser = factory.newSAXParser();
File readFile = new File(args[i]);
try {
saxParser.parse(readFile, handler);
} catch (Exception e) {
System.out.println("Error caught: " + e.getMessage());
}
}
SAX + State Pattern: My handler
/**
* SAX Handler methods
*/
State state = state0;public void startElement( String uri, String name, String qName, Attributes atts ) {
state = state.handle(0, qName);
}
. . .
SAX + State Pattern: a state
class State0 extends State {
State handle(int no, String s) {
if ((s.equals("a")) && (no == 0))
return state1;
else
error(s, no);
return null;
}
}
SAX + State Pattern
In the previous case, a switch statement to implement the states would be more efficient !
Exercise 5.2
• Use the state pattern to create a tokenizer that decode the following tokens:“x” “abc” unknown (all other characters)
• It should be capable of returning the following tokens “a” “x” “a” “abc”from this sequence “axaabc”
Exercise 5.2
A test sequence and an object that contains part of the solution is available (see list of exercises).
// read the token located at the beginning of the buffer, // from 0 to the current position
public void extractImage()
// Same thing when the first character is not a token
public void extractFirstChar() {
// Called by the client to get the next token
public int getNext() throws IOException {
// Sets the next state
public void setState(State _state) {
Exercise 5.2
For example, for the tokens “abc” “xyz”, the state machine tries the following paths:
State0 ‘a’ (continuation in State1), ‘x’ (continuation in State2), other (extract the first character as the next token and return unknown)
State1 ‘b’ (continuation in State3), other (idem)
State3 ‘c’ (extractImage and return the token number)
State2 ‘y’ (continuation in State4), other (idem)
State4 ‘z’ (extractImage and return the token number)
Exercise 5.2: (JUnit Test)
@Test
public final void test4() throws IOException {
assertTrue("a, x, a, abc not found",
readTokens(
new int[] {0,1,0,2},
new String[]{"a","x","a","abc"},
new ByteArrayInputStream(
"axaabc".getBytes()) ) );}
DOM: document object model (tree)
<address category="private" duration="permanent">
Attributes
Node
attributes
childrenNode
attributes
childrenNode
attributes
children
Node
attributes
children
DOM: initializationtry {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) {…}
public void fatalError(SAXParseException exception) {…}
public void warning(SAXParseException exception) {…}
});
document = builder.parse(new File(argv[0]));
PrintTree.print(document, "");
} catch (Exception ioe) {
ioe.printStackTrace();
} }
DOM: walking through the tree
public static void print(Node node, String indentation) {
System.out.print(indentation + node.getNodeValue());
if (node.hasAttributes()) {
NamedNodeMap map = node.getAttributes();
for (int j = 0; j < map.getLength(); j++)
System.out.println(indentation + map.item(j).getNodeName());
}
NodeList nl = node.getChildNodes();
if (nl != null)
for (int i = 0; i < nl.getLength(); i++) {
print(nl.item(i), (indentation + " |"));
} }
Interpreter
42 2 1 - + // expression in Reverse Polish notation
push(42) push(2) push(1) push(-pop+pop) push(pop+pop)
1
2 2 1
42 42 42 42 43
Interpreter for the previous notation (object diagram)
ParseTreeArrayList<Expression>
TerminalExpression_Numbervalue
constructor(int)interpret(Stack)
TerminalExpression_Plusvalue
interpret(Stack)
Expression
interpret(Stack)
Expression
interpret(Stack)
StackArrayList<Integer>
Parser
parse(String)evaluate()
Interpreter: class diagram
Client
AbstractExpression
interpret(Context)
TerminalExpression
interpret(Context)
NonTerminalExpression
interpret(Context)
Context
JavaCC: Java compiler’s compiler
Site Web: https://javacc.dev.java.net
(go to : Documentation > ... the JavaCC grammar file ... )
Download the version 4.0
(See http://www.epflpress.org/Book_Pages/petitpierre.html)
JavaCC: compilation of a .jj file(the sources are contained in .jj files)
In external tools:
JavaCC: parts
1. Options
2. Program header
3. Tokens
4. Productions
JavaCC: (1) options
options { STATIC = true; DEBUG_PARSER = true;}
The first block of the source contains the options. Here are the two main ones.
JavaCC: (2) the program header
PARSER_BEGIN(XMLParserSKIP)package parser;import java.io.FileReader;
public class XMLParserSKIP { public static void main(String args[]) throws Exception { FileReader in = new FileReader(args[0]); XMLParserSKIP parser = new XMLParserSKIP(in); parser.rootProduction(); } // possibility to define attributes and methods}PARSER_END(XMLParserSKIP)
JavaCC: (3) ignored tokens
SKIP :{ " "| "\r"| "\t"| "\n"}
Specifies that the spaces, carriage returns, tabulations, new lines are ignored (skipped).
These tokens separate the other ones, of course, but they are not transmitted to the analyzer.
JavaCC: simple tokens
TOKEN :
{ < LT: "<" >
| < GT: ">" >
| <UNDERLINE: "_">
| <COLON: ":">
| <DOT: ".">
| <MINUS: "-">
| <ENDTAG: "</">
}
This source defines a few tokens
JavaCC: composed tokens
TOKEN :
{ <ID: ( <LETTER> | "_" | ":" )
( <DIGIT> | <LETTER> | "_" | ":" | "." | "-" )* >
| <#LETTER: ["a"-"z", "A"-"Z"] >
| <#DIGIT: ["0" - "9"] >
}
#LETTER defines a token known only locally
JavaCC: attention!
TOKEN :
{ < COLON: ":" >
| < ID: ":" (["a"-"z])* >
}
Input text:
:234 <COLON> 2 3 4
:aaa <ID>
The token is the longest possible path, but if two
tokens have the same length, the first one is returned
JavaCC: definition of spaces
TOKEN : // not correct (a single space is SPACE != S )
{
<SPACE: " ">
| <CR: "\r">
| <TAB: "\t">
| <NL: "\n">
| <S: (<SPACE> | <CR> | <TAB> | <NL>)+ >
}
JavaCC: correct definition
TOKEN : // correct
{
<S: (<SPACE> | <CR> |<TAB> | <NL>)+ >
| <#SPACE: " "> // local definition
| <#CR: "\r">
| <#TAB: "\t">
| <#NL: "\n">
}
JavaCC: special token definition
SPECIAL_TOKEN :
{
<S: (<SPACE> | <CR> |<TAB> | <NL>)+ >
| <#SPACE: " "> // local definition
| <#CR: "\r">
| <#TAB: "\t">
| <#NL: "\n">
}
JavaCC: queue of tokens and special tokens
Token
next
specialToken
Token
next
specialTokenToken
next
specialTokenToken
next
specialToken
Token
next
specialToken
Special tokens
in reverse order
JavaCC: special token and token class
package tree;
public class Token {
public int kind;
public int beginLine, beginColumn, endLine, endColumn;
public String image;
public Token next;
public Token specialToken;
public String toString() { return image; }
public static final Token newToken(int ofKind) {
switch(ofKind) { // to create specific tokens
default : return new Token();
} } }
JavaCC: token in contexts
<![CDATA[ x x x x x x ]]>
<DEFAULT> MORE : { "<![CDATA[" : IN_CDATA }
<IN_CDATA> MORE : { < ~[] > }
<IN_CDATA> TOKEN : { <CDATA : "]]>"> : DEFAULT }
In the default mode (start of the program), introduce token "<!
[CDATA[" in the current token and jump to context <IN_DATA>. In this context, add any character to the same token, add the token "]]>", return to the default mode and return a single token made of all pieces appearing in the MORE lines. The final token is named CDATA. (not in the book!)
JavaCC: (4) production
void tag() :{ }{ "<“ <ID> ">"}
String tag() :{ String s; Token t; // extra token}{ "<" t =<ID> ">" { return t.image; }}
String tag() :{ }{ "<" <ID> ">" { return token.image; }}
token exists by default
JavaCC: repetitions
void product() :
{ }
{
tag()
( <ID> )*
endTag()
}
void tag() : { }
{ "<" <ID> ">" }
void endTag() : { }{ "</" <ID> ">" }
( x )* 0 – n times
( x )+ 1 – n times
( x )? optional
[ x ] same as above
JavaCC: choices
void product() :
{ }
{
"("
(
<ID>
|
tag()
)
endTag()
}
Either
"(" <ID> endTag()
or
"(" tag() endTag()
void command() throws Exception :
{ String s, t; }
{
s = tag()
( (<ID>)+
| (command())+ <a> ID ID </a>
| <CDATA> <a> <c>x</c> </a>
) <a> <![CDATA[ x x x ]]>
</a>
t = endTag()
{ if (!s.image.equals(t.image)) // Java code
throw new Exception("end tag != from start tag");
}
}
JavaCC: another production
JavaCC: token versus production
TOKEN:
{
<TAG: "<" <ID> ">" >
}
String tag () :
{ String s; }
{
"<" <ID>{s=token.image;} ">"
{ return s; }
}
JavaCC: lookahead
void product() :
{ }
{ ( tag() | endTag() (
}
void tag() : { }
{ "<" <ID> ">" }
void endTag() : { }{ "<" "/" <ID> ">" }
both continue with "<"
void product() :
{ }
{ ( LOOKAHEAD (2) tag() | endTag() (
}
void tag() : { }
{ "<" <ID> ">" }
void endTag() : { }{ "<" "/" <ID> ">" }
JavaCC: LOOKAHEAD
The LOOKAHEAD command is placed at the beginning of the alternative
Three possibilities:
LOOKAHEAD (2)
LOOKAHEAD ( "<" id() ">" )
LOOKAHEAD ( { methodReturningABoolean() } ) . . .
JAVACODEboolean methodReturningBoolean() { if (…) return true; else return false;}
JavaCC: JAVACODE command
TOKEN:{ <UNDEFINED: ~[]>}void command() { }{ tag() <CDATA_START> {getCDATA();} endTag()}
JAVACODE
void getCDATA() {
while ( getNextToken().kind != CDATA_END)
System.out.print("-"+getToken(0).image+"-");
if (getToken(1).kind==S)
getNextToken();
} // getToken(1) == lookahead
JavaCC: an example ( CVS project Patterns,
package tree, compile Parser.jjt with javacc for now )
SKIP :
{
" "
| "\r"
| "\t"
| "\n"
}
TOKEN :
{ compiler for expressions like this one
< LPAR: "(" > 4*(8+7-9)+(6*(7-2))
| < RPAR: ")" >
| < PLUS: "+" >
| < MINUS: "-" >
| < INTEGER: (["0" - "9"])+ >
}
JavaCC: productions of the example
void expression() throws Exception :{ }{ multExpression() ( "+“ multExpression() | "-" multExpression() 4*(8+7-9)+(6*(7-2)) )*}void multExpression() throws Exception :{ }{ primaryExpression() ( "*" primaryExpression() )* }void primaryExpression() throws Exception :{ }{ ( <INTEGER> | "(" expression() ")" )}
JavaCC: creation of an interpreter tree
AbstractExpression expression() throws Exception :{ AbstractExpression ae, m;}{ m = multExpression() ( "+" ae = multExpression() {m=new PlusExpression(m,ae);} | "-" ae = multExpression() {m=new MinusExpression(m,ae);} )* {return m;}}
PlusExpression
multExpression()multExpression()
MinusExpression
multExpression()
JavaCC: creation of an interpreter tree
AbstractExpression multExpression() throws Exception :{ AbstractExpression ae, m;}{ m = primaryExpression() ( "*" ae = primaryExpression() {m=new MultExpression(m, ae);} )* { return m; }}
MultExpression
primaryExpression()
primaryExpression()
JavaCC: creation of an interpreter tree
AbstractExpression primaryExpression() throws Exception :{ AbstractExpression m;}{ ( <INTEGER> {m = new Terminal(Integer.parseInt(token.image));} | "(" m=expression() ")" ) {return m;}}
Terminal expression()
JavaCC: class for loading the constant
public class Terminal extends AbstractExpression{
public Terminal( AbstractExpression left, AbstractExpression right) { super(left, right); }
int value;
public Terminal(int value) {
super(null,null);
this.value = value;
}
public void interpret(Stack stack) {
stack.push(value);
} }
JavaCC: class for adding the values returned by 2 subtrees
public class PlusExpression extends AbstractExpression {
public PlusExpression(AbstractExpression left, AbstractExpression right) { super(left, right); }
public void interpret(Stack stack) {
left.interpret(stack);
right.interpret(stack);
stack.push(stack.pop()+stack.pop());
} }
// Available in project Patterns, package tree, ParserDump.jjt
The following slides show how to print a file after it has been treated by the compiler
Tokennext
Tokennext
Tokennext
Tokennext
Tokennext
Tokennext
Tokennext
The tokens are stored in a list
(See also the special tokens in a previous slide)
Returning the first token of the list(first solution)
Token rootProduction() throws Exception :{Token first;}{ {first = getToken(1);} command() {return first;}}void command() throws Exception :{}{ tag() (command())* endTag()}// can also be retrieved from the first node, // as shown on one of the following pages
Printing all the tokens(not the special ones): in main()
. . .
Token first = parser.rootProduction();
while (first != null) {
System.out.println(first.image);
first = first.next;
}
. . .
Printing the special tokens
// To be called before every true token
// It inverts the order of the special tokens
static void followSpecialTokens (Token t){
if (t == null) return;
followSpecialTokens(t.specialToken);
System.out.print(t.image);
}
jjtree: creating a tree automatically (AST-abstract syntax tree)
Take the same file as before, but change extension to .jjt and call
jjtree Xxxx.jjt
You obtain Xxxx.jj and a list of classes. Call
javacc Xxxx.jj
Compile with javacc and execute as usual, the tree is created, but of course the program does not use it yet.
// Source available in project Patterns, package tree, ParserDump.jjt
Tree: a node for each production(which node is generated can be controlled)
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
dump() is recursive, it calls the dump() of the children rootNode.dump()walks through all the nodes
How to link the tokens to the nodes
With the options:
NODE_SCOPE_HOOK=true;
The compiler jjtree enters the following commands at the beginning and the end of the every productions:
jjtree.openNodeScope(jjtn000);
. . .
jjtreeCloseNodeScope(jjtn000);
Add this in file .jjt
static void jjtreeOpenNodeScope(Node node) {
((SimpleNode)node).setBeginToken(getToken(1));
}
static void jjtreeCloseNodeScope(Node node) {
((SimpleNode)node).setEndToken(getToken(0));
}
… and this in SimpleNode.java
private Token beginToken;
private Token endToken;
public Token getBeginToken() { return beginToken; }
public void setBeginToken(Token beginToken) {
this.beginToken = beginToken;
}
public Token getEndToken() { return endToken; }
public void setEndToken(Token endToken) {
this.endToken = endToken;
}
… then you obtain the following structure (Design Pattern!)
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
SimpleNodechildrendump()
Token Token Token Token Token Token Token Token Token
beginTokenendToken
beginToken endToken
JavaCC: Controlling which node is generated
options { Node_DEFAULT_VOID=true; MULTI=true; NODE_PREFIX=“EPFL_”}expression() #Xxxx: // creates a node with { } // name EPFL_Xxxx{ multExpression() ( "+“ multExpression() | "-" multExpression() )*}
Getting the first token of the list(second solution)
The first token can also be retrieved from the first node:
node = parser.rootProduction()
where
SimpleNode rootProduction() : { } { . . .
{return jjtn000;}
}
Printing the tokens covered by each node
public void dump(String prefix) {
Token t = getBeginToken();
Token te = getEndToken();
System.out.print(toString(prefix) + " !");
while (t != null) {
System.out.print(t.image);
if (t == te)
break;
t = t.next;
}
System.out.println("!");
// writes marks that represent the tree
. . .
Printing the tokens covered by each node
public void dump(String prefix) {
. . .
System.out.println("!"); // create lines that
if (children != null) { // highlight the tree
for (int i = 0; i < children.length; ++i) {
SimpleNode n = (SimpleNode) children[i];
if (n != null) {
if (i != (children.length - 1))
n.dump(prefix + "!"); // recursion
else
n.dump(prefix + " ");
} } } }
Visitor Design Pattern (GoF)handled by javacc
Used to perform work on the AST (abstract syntax tree).
The tree is walked through and the code of a visitor object
is executed within each node.
Different visitors can perform different tasks (type checking,
code generation) over the same tree, one after the other.
In the following, the example of the expressions interpreter
will be handled with the help of the visitor pattern
The nodes in the tree of the previous example
void expression() throws Exception :{ }{ multExpression() ( "+“ multExpression() | "-" multExpression() )*}void multExpression() throws Exception :{ }{ primaryExpression() ( "*" primaryExpression() )* }void primaryExpression() throws Exception :{ }{ ( <INTEGER> | "(" expression() ")" )}
Nodes created :
ASTexpression ASTmultExpression ASTprimaryExpression
The nodes of the tree of the previous example
ASTexpression
ASTmultExpressionASTmultExpressionASTmultExpression
ASTprimaryExpressionASTprimaryExpression
ASTexpression <INTEGER>
SimpleNode
SimpleNode
SimpleNode
Visitor Pattern: each node accepts a visitor(located in SimpleNode, see previous slide))
/** Accept the visitor. * */
public Object jjtAccept(MyExpressionsVisitor visitor,
Object data) {
return visitor.visit(this, data);
}
The call is forwarded to the visitor, which contains one method per node. The method is identified by its argument. It receives this to allow the visitor to create the task determined by the node. Thus, different visitors can be executed with the same structure.
Visitor Pattern: the visitor contains a method for each kind of node
public class MyVisitor implements MyExpressionsVisitor {
public Object visit (ASTexpression node, Object data)
{ your code }
public Object visit (ASTmultExpression node, Object data)
{ your code }
public Object visit (ASTprimaryExpression node, Object data)
{ your code }
}
// MyExpressionsVisitor is generated automatically
// each task requires only a class like this one (no changes in the parser)
Visitor Pattern: in the main
// visit the nodes
node.childrenAccept(new MyVisitor(), null);
Visitor Pattern: recursive walk through the nodes (located in SimpleNode)
/** Accept the visitor. * */
public Object childrenAccept ( MyExpressionsVisitor visitor, Object data) {
if (children != null) {
for (int i = 0; i < children.length; ++i) {
((SimpleNode)children[i]).childrenAccept(visitor, data);
}
}
jjtAccept(visitor, data);
return data;
} These 2 lines had to be modified
Example of visitor
public Object visit(ASTprimaryExpression node, Object data) {
if ( ((SimpleNode) node).jjtGetNumChildren() == 0 ) {
node.ae = new Terminal(Integer.parseInt(node.getBeginToken().image));
} else {
node.ae = ((SimpleNode) node.jjtGetChild(0)).ae;
}
return null;
}
Either create a Terminal node { found <Integer>, namely no child }
or pass the expression found in the subtree { found "(" expression() ")" }
A field AbstractExpression has been defined in SimpleNode
Example of visitor (2)(how to check if it is a “+” or a “-”)
expression: multExpression() ( "+“ multExpression() | "-" multExpression() )*
expressionchildrendump()
multExpressionchildrendump()
multExpressionchildrendump()
multExpressionchildrendump()
Token Token Token
+
Token Token Token
-
beginToken endToken
Token Token Token
Another example of visitor (2)public Object visit (ASTexpression node, Object data) {
if ( ((SimpleNode) node).jjtGetNumChildren() > 0 ) {
SimpleNode nodeX = ((SimpleNode) node.jjtGetChild(0));
node.ae = nodeX.ae; // pass the subexpression
int operation = nodeX.getEndToken().next.kind; // previous operation
for (int i = 1; i < ((SimpleNode) node).jjtGetNumChildren(); i++) {
nodeX = (SimpleNode) node.jjtGetChild(i);
if (operation == MyExpressionsConstants.PLUS) {
node.ae = new PlusExpression(node.ae, nodeX.ae);
} else {
node.ae = new MinusExpression(node.ae, nodeX.ae);
}
operation = nodeX.getEndToken().next.kind;
} }
return null;
} gets the token to see if it is a “+” or < “-”
(next slide)
In summary
ParserVisitor.jjt previous .jj file complemented with the definition of jjtreeOpenNodeScope
SimpleNode.java generated node complemented with the handling of beginNode and endNode, as well as an attribute for an AbstractExpression
MyVisitor.java one method per node to generate the interpretertree; generated entirely by the developer
AbstractExpression.java classes for the Interpreter Pattern used
MinusExpression.java in previous slides; the visitor creates them and
MultExpression.java embed them within an interpreter tree
PlusExpression.java
Terminal.java
Stack.java
Source available in project Patterns, package visitor
Example of the use of JavaCC:Synchronous Javascript
http://ltiwww.epfl.ch/sJavascript/
http://ltiwww.epfl.ch/~petitp/GenieLogiciel/test_s.html
Example of sJavascript
process Consumer(name) { var count this.put = function(counter) { // a method count = counter } this.run = function () { // the thread method for (;;) { accept put document.getElementById("consDisplay").innerHTML = count} } }
Example of sJavascriptprocess Producer(name) { var ON = true var counter = 0 this.run = function() { for (;;) { select { case startStop.clicked() // synchronizing call ON = !ON document.getElementById("startStop") .style.backgroundColor = ON?"green":"red" case when (ON) waituntil(now()+1000) // synchronizing call counter++ consumer.put(counter)} } } }
// sJavascriptprocess Consumer(name) { var count this.put = function(counter) { count = counter } this.run = function () { for (;;) { accept put document.getElementById ("consDisplay") .innerHTML = count} } }
// standard Javascriptextending(Consumer, TaskControlBlock)function Consumer(name) { Consumer.baseConstructor.call(this, name) var count this.$JM_put = 1 this.put = function(counter) { count = counter } this.$J_state = 10000 this.run = function () { for (;;) { switch(this.$J_state) { case 10000: this.$J_init() this.$J_accept(10002, this.$JM_put) this.$J_syncWait(29) return case 10002: document.getElementById ("consDisplay").innerHTML = count this.$J_state = 10000;} } } }
scheduler
Compilation
sJavascript
The transformation on the last page has been performed with the Visitor Design Pattern