Behavioral Patterns

Download Report

Transcript Behavioral Patterns

Behavioral Patterns
Chain of Responsibility
State
Command
Null Object
Little Language
Strategy
Mediator
Template Method
Snapshot
Visitor
Observer
BP - Chain of Responsibility
Synopsis:
Allows an object to send a command without
knowing what objects will receive the command.
Context:
Suppose you have physical devices detecting
temperature threats in a building, room freezer.
You can detect the temp for each of the objects and
delegate the behavior to the domain structure.
BP - Chain of Responsibility
Forces:
You wish an object to be able to send a command to
another object without specifying the receiver.
More than one object may be able to receive and
handle a command.
Solution:
Create a Command Sender, Command Handler and
any ConcreteCommand Handler Objects.
BP - Chain of Responsibility
Suppose you with to have a security system that detects
temp, smoke, motion, etc and reports status information
regarding the unit you are securing to a networked computer.
temp
smoke
motion
Security
System
Networked
status
infomation Computer
Need a scalable system – from a small room to large office bldg.
Design – object per sensor with the sensor object delegating
ACTION to another higher level domaion object
BP - Chain of Responsibility
Thus the temp detection is recognized by a method in the
temp sensor and that object sends a message to the area
object who may in turn send the message up the chain of
facilities until the controlling facility reacts.
temp
Temp
Sensor
message
Area Object
message
message
Building
Warehouse
message
Floor
Need a scalable system – from a small room to large office bldg.
Design – object per sensor with the sensor object delegating
ACTION to another higher level domaion object
BP - Chain of Responsibility
The generalized solution involves
Command
Sender
command
Command
Handler
ConcreteCH
ConcreteCH
ConcreteCH
BP - Chain of Responsibility
CommandSender
– sends commands to the first object in the chain
and may indeed handle the command.
CommandHandler
– superclass of all concrete command handlers.
ConcreteCommandHandler1,2,…..
handles the commands
BP - Command
Synopsis:
Encapsulate commands in objects so you can
control their selection and sequencing, queue them,
undo them and otherwise manipulate them.
Context:
Suppose you have a word processing program that
allows you to redo and undo commands.d
BP - Command
Solution:
The word processor, rather than performing a comand,
creates and instance of the subclass of Abstract Command
corrresponding to the command. Once this is done, the wp
can call the object to doIt and execute the command. . This
allows the keeping of a history of the commands allowing
undo and redo commands.
Invoker
Abstract
Command
manages
Command
Manager
create
Concrete
Command
BP - Command
AbstractCommand – superclass of classes that
encapsulates the commands
ConcreteCommand – encapsulates the specific
command.
Invoker – creates concrete commands
CommandManager – manages a collection of
command objects created.
BP - Little Language
Synopsis:
A sophisticated technique for designing and
implementing languages.
Context:
A program searches a collection of files to find a
string sequence or combinations.
BP - Little Language
Solution:
You don’t want to write individual searches for all
the combinations of perhaps complex string
combinations so you define a little language that is
specified as a grammar to describe the needed
combinations and then you write one program that
finds the search argument.
BP - Mediator
Synopsis:
Uses one object to coordinate state changes between
other objects. Putting the logic in one object to
manage state changes of other objects, instead of
distributing logic over other objects.
Context:
BP - Snapshot
Synopsis:
Simpler form of the Memento Pattern.
Context:
You are writing a program to play a game and you
wish to allow the saving of the state of the game
since it may last a long time.
BP - Observer
Synopsis:
A widely used well known pattern that allows
objects to dynamically register dependencies
between objects, so that an object will notify those
objects that are dependent on it when its state
changes.
Context:
You wish to allow your program with devices to
easily adapt to new devices.
BP - State
Synopsis:
Encapsulates the states of an object as discrete
objects, each belonging to a separate subclass of an
abstract state class.
Context:
Some objects are stateful objects. Suppose you are
writing a dialogue box with states and you wish to
manage any conflicting states.
BP - State
import java.awt.*;
class DirtyState {
// Symbolic constants for events
public static final int DIRTY_EVENT = 1;
public static final int APPLY_EVENT = 2;
public static final int SAVE_EVENT = 3;
public static final int REVERT_EVENT = 4;
// Symbolic constants for states
private final BothDirty bothDirty = new BothDirty();
private final FileDirty fileDirty = new FileDirty();
private final ParamDirty paramDirty = new ParamDirty();
private final NotDirty notDirty = new NotDirty();
private Parameters parameters;
private Button apply, save, revert;
BP - State
/**
* This constructor should be private to prevent other classes from instanciating it.
* iIt is not private because subclasses of this class are implemented as inner classes
* and Java 1.2 does not support access of a private constructor by inner classes.
*/
DirtyState() {
} // constructor()
BP - State
/**
* Initialize the state machine and return its initial state.
* @param p The parameters object that this object will work with
* @param apply The apply button to be enabled/disabled
* @param save The save button to be enabled/disabled
* @param revert The revert button to be enabled/disabled
*/
public static DirtyState start(Parameters p, Button apply, Button save, Button revert){
DirtyState d = new DirtyState();
d.parameters = p;
d.apply = apply;
d.save = save;
d.revert= revert;
return d.notDirty;
} // start(Button, Button, Button)
BP - State
/**
* Respond to a given event.
* All subclasses of this class are expected to override this method.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected value.
*/
public DirtyState processEvent(int event) {
// This non-overridden method should never be called.
throw new IllegalAccessError();
} // processEvent(int)
// This method is called when this object is becomes the current state.
protected void enter() { }
BP - State
// Class to represent state when dialog fields do not match file or param values.
private class BothDirty extends DirtyState {
// Respond to event. @param event An event code.
* @return the next state.
// @exception IllegalArgumentException if event is an unexpected value.
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
return this;
case APPLY_EVENT:
if (parameters.applyParam()) { fileDirty.enter(); return fileDirty;
} // if
case SAVE_EVENT:
if (parameters.saveParam()) { paramDirty.enter(); return paramDirty; } // if
case REVERT_EVENT:
if (parameters.revertParam()) { paramDirty.enter(); return paramDirty; } // if
default:String msg="unexpected event "+event;throw new illegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)
BP - State
// This method is called when this object is becomes the current state.
protected void enter() {
apply.setEnabled(true);
revert.setEnabled(true);
save.setEnabled(true);
} // enter
} // class BothDirty
// Class to represent state when dialog fields match working param values not in file
private class FileDirty extends DirtyState {
BP - State
/**
* Respond to a given event.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected value.
*/
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT: bothDirty.enter();
return bothDirty;
case SAVE_EVENT:
if (parameters.saveParam()) {notDirty.enter(); return notDirty;
} // if
case REVERT_EVENT:
if (parameters.revertParam()) { paramDirty.enter(); return paramDirty; } // if
default: String msg="unexpected event "+event; throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)
BP - State
// This method is called when this object is becomes the current state.
protected void enter() {
apply.setEnabled(false);
revert.setEnabled(true);
save.setEnabled(true);
} // enter
} // class FileDirty
// Class to represent state when dialog fields match file but not the working param values
private class ParamDirty extends DirtyState {
// Respond to a given event. @param event An event code.
* @return the next state.
// @exception IllegalArgumentException if event is an unexpected value.
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
bothDirty.enter(); return bothDirty;
case APPLY_EVENT:
if (parameters.applyParam()) {notDirty.enter(); return notDirty;
} // if
default: String msg="unexpected event "+event; throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)
BP - State
// This method is called when this object is becomes the current state.
protected void enter() {
apply.setEnabled(true);
revert.setEnabled(false);
save.setEnabled(false);
} // enter
} // class ParamDirty
// Represents state for when dialog fields match the file and working parameter values
private class NotDirty extends DirtyState {
// Respond to a given event. @param event An event code.
* @return the next state.
// @exception IllegalArgumentException if event is an unexpected value.
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
bothDirty.enter();
return bothDirty;
default:String msg="unexpected event "+event; throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)
BP - State
/** This method is called when this object is becomes the current state.
protected void enter() {
apply.setEnabled(false);
revert.setEnabled(false);
save.setEnabled(false);
} // enter
} // class ParamDirty
} // class DirtyState
*/
BP - State
class Parameters {
//...
boolean saveParam() {
//...
return true;
} // saveParam()
boolean applyParam() {
//...
return true;
} // applyParam()
boolean revertParam() {
//...
return true;
} // revertParam()
} // class Parameters
BP - State
import java.awt.*;
class Procedural extends Dialog {
// Symbolic constants for events
public static final int DIRTY_EVENT = 1;
public static final int APPLY_EVENT = 2;
public static final int SAVE_EVENT = 3;
public static final int REVERT_EVENT = 4;
// Symbolic constants for states
private static final int BOTH_DIRTY = 101;
private static final int FILE_DIRTY = 102;
private static final int PARAM_DIRTY = 103;
private static final int NOT_DIRTY = 104;
Button applyButton, saveButton, revertButton;
private int state = NOT_DIRTY;
BP - State
/**
* Constructor
* @param parent The parent Frame
*/
Procedural(Frame parent) {
super(parent, "Parameter Editor");
//...
gotoState(NOT_DIRTY);
} // Constructor()
/**
* respond to events based on the current state.
* @param event An event code.
* @exception IllegalArgumentException if event is an unexpected value.
* @exception InternalError if the current state is corrupted.
*/
private void processDirtyStateEvent(int event) {
switch (state) {
BP - State
case BOTH_DIRTY:
switch (event) {
case DIRTY_EVENT:
// Do nothing
break;
case APPLY_EVENT:if (applyParam()) gotoState(FILE_DIRTY); break;
case SAVE_EVENT:if (saveParam()) gotoState(PARAM_DIRTY); break;
case REVERT_EVENT: if (revertParam()) gotoState(PARAM_DIRTY); break;
default: throw new IllegalArgumentException("unexpected event "+event);
} // switch (event)
break;
case FILE_DIRTY:
switch (event) {
case DIRTY_EVENT: gotoState(BOTH_DIRTY);
break;
case SAVE_EVENT: if (saveParam()) gotoState(NOT_DIRTY);
break;
case REVERT_EVENT:
if (revertParam())
default:
gotoState(PARAM_DIRTY);
break;
throw new IllegalArgumentException("unexpected event "+event);
} // switch (event)
break;
BP - State
case PARAM_DIRTY:
switch (event) {
case DIRTY_EVENT:
gotoState(BOTH_DIRTY);
break;
case APPLY_EVENT:if (applyParam()) gotoState(NOT_DIRTY); break;
default: throw new IllegalArgumentException("unexpected event "+event);
} // switch (event)
break;
default:
throw new InternalError("Unknown state event " + event);
} // switch (state)
} // processDirtyStateEvent(int)
BP - State
// Set current state and perform entry actions for the state .
private void gotoState(int newState) {
switch (newState) {
case NOT_DIRTY:
applyButton.setEnabled(false);
revertButton.setEnabled(false);
saveButton.setEnabled(false);
break;
case FILE_DIRTY:
applyButton.setEnabled(false);
revertButton.setEnabled(true);
saveButton.setEnabled(true);
break;
BP - State
case BOTH_DIRTY:
applyButton.setEnabled(true);
revertButton.setEnabled(true);
saveButton.setEnabled(true);
break;
case PARAM_DIRTY:
applyButton.setEnabled(true);
revertButton.setEnabled(false);
saveButton.setEnabled(false);
break;
} // switch
state = newState;
} // gotoState(int)
BP - State
//...
private boolean saveParam() {
//... return true;
private boolean applyParam() { //...
private boolean revertParam() {
} // class Procedural
//...
} // saveParam()
return true; } // applyParam()
return true;} // revertParam()
BP - Null Object
Synopsis:
Provides an alternative to using null to indicate the
absence of an object to delegate an operation to
when using other patterns. Instead of using null,
the null object pattern uses a reference to an object
that does not do anything in the needed method.
BP - Strategy
Synopsis:
Encapsulates related algorithms in classes that are
subclasses of a common superclass. Allows the
selection of algorithm to vary by object and also
allows it to vary temporally.
BP - Strategy
import java.util.Date;
/** * Skeletal definition of class to display a calendar */
class CalendarDisplay {
private Holiday holiday;
private static final String[]noHoliday = new String[0];
//...
// Instances of this private class used to cache information about dates to be displayed
private class DateCache {
private Date date;
private String[] holidayStrings;
BP - Strategy
DateCache(Date dt) {
date = dt;
//...
if (holiday == null) { holidayStrings = noHoliday; }
else {
holidayStrings = holiday.getHolidays(date);
} // if
//...
} // constructor(Date)
} // class DateCache
} // class CalendarDisplay
BP - Strategy
import java.util.Date;
// Class determines if a date is according to a collection of Holiday objects.
public class CompositeHoliday extends Holiday {
private Holiday[] holidayArray;
/** Constructor * @param h An array of Holiday objects
public CompositeHoliday(Holiday[] h) {
holidayArray = new Holiday[h.length];
System.arraycopy(h, 0, holidayArray, 0, h.length);
} // CompositeHoliday
*/
BP - Strategy
// Method returns a array of strings describing holidays that fall on a given date.
// If no holidays fall on the given date, then method returns an array of length zero.
// @param dt The date to check.
public String[] getHolidays(Date dt) {
return getHolidays0(dt, 0, 0);
} // getHolidays(Date)
private String[] getHolidays0(Date dt, int offset, int ndx) {
if (ndx >= holidayArray.length) {
return new String[offset];
} // if
String[] holidays = holidayArray[ndx].getHolidays(dt);
String[] result = getHolidays0(dt, offset+holidays.length, ndx+1);
System.arraycopy(holidays, 0, result, offset, holidays.length);
return result;
} // getHolidays0(Date, int, int)
} // class USHoliday
BP - Strategy
import java.util.Date;
/**
* This abstract class is the superclass of classes that can determine
* if a date is a holiday. Subclasses of this class will be specific to
* nations or religions.
*/
public abstract class Holiday {
protected final static String[] noHoliday = new String[0];
/**
* This method returns a array of strings that describe the holidays
* that fall on the given date. If no holidays fall on the given
* date, then this method returns an array of length zero.
* @param dt The date to check.
*/
abstract public String[] getHolidays(Date dt) ;
} // class Holiday
BP - Strategy
import java.util.Date;
/** * This class determines if a particular date is a U.S. holiday. */
public class USHoliday extends Holiday {
/**
* This method returns a array of strings that describe the holidays
* that fall on the given date. If no holidays fall on the given
* date, then this method returns an array of length zero.
* @param dt The date to check.
*/
public String[] getHolidays(Date dt) {
String[] holidays = noHoliday;
//...
return holidays;
} // getHolidays(Date)
} // class USHoliday
BP - Template Method
Synopsis:
Provides an abstract class that contains only part of
the logic and organize the class so its concrete
methods call an abstract method where missing
logic might appear. Provide the missing logic in
subclass methods that override the abstract
methods.
BP - Template Method
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.swing.JDialog;
import java.awt.swing.JLabel;
import java.awt.swing.JOptionPane;
/** * This is an abstract class for authenticating a user for a program. */
public abstract class AbstractLogon {
BP - Template Method
/**
* This method authenticates a user.
* @param frame Frame that will be the parent of any dialogs that this method pops up.
* @param programName Program name as should appear when prompting user logon.
*/
public void logon(Frame frame, String programName) {
Object authenticationToken;
LogonDialog logonDialog;
logonDialog = new LogonDialog(frame, "Log on to "+programName);
JDialog waitDialog = createWaitDialog(frame);
while(true) {
waitDialog.setVisible(false);
logonDialog.setEnabled(true);
logonDialog.setVisible(true);
logonDialog.setEnabled(false);
waitDialog.setVisible(true);
BP - Template Method
try {
String userID = logonDialog.getUserID();
String password = logonDialog.getPassword();
authenticationToken = authenticate(userID, password);
break;
} catch (Exception e) {
// Tell user that authentication failed.
JOptionPane.showMessageDialog(frame,
"Authentication Failure",
} // try
}
// Authentication successful
waitDialog.setVisible(false);
logonDialog.setVisible(false);
notifyAuthentication(authenticationToken);
} // logon()
e.getMessage(),
JOptionPane.ERROR_MESSAGE);
BP - Template Method
private JDialog createWaitDialog(Frame parent) {
JDialog waitDialog = new AuthenticationDialog(parent);
return waitDialog;
} // createWaitDialog()
private static class AuthenticationDialog extends JDialog
implements Runnable {
private JLabel authenticating = new JLabel("Authenticating Logon");
private Thread blinkThread;
private static final int blinkInterval = 500;
AuthenticationDialog(Frame parent) {
super(parent, "Please Wait");
authenticating.setOpaque(true);
getContentPane().add(authenticating, BorderLayout.CENTER);
pack();
blinkThread = new Thread(this);
addWindowListener(new MyWindowAdapter());
} // constructor(Frame)
blinkThread.start();
BP - Template Method
public void show() {
super.show();
synchronized (this) {
notifyAll();
} // synchronized
} // show
/**
* Running in its own thread, this blinks dialog's label.
*/
public void run() {
try {
while (!blinkThread.isInterrupted()) {
if (!isShowing()) {
synchronized (this) {
while (!isShowing()) {
} // synchronized
} // if
wait();
} // while !isShowing
BP - Template Method
synchronized (this) {
Color foreground = authenticating.getForeground();
Color background = authenticating.getBackground();
authenticating.setForeground(background);
authenticating.setBackground(foreground);
} // synchronized
authenticating.repaint();
blinkThread.sleep(blinkInterval);
} // while !isInterrupted
} catch (InterruptedException e) {
} // try
} // run()
BP - Template Method
// Respond to window events
private class MyWindowAdapter extends WindowAdapter {
synchronized public void windowOpened(WindowEvent e) {
notifyAll();
if ( !blinkThread.isAlive()
|| blinkThread.isInterrupted()) {
blinkThread = new Thread(AuthenticationDialog.this);
blinkThread.start();
} // if
} // windowOpened(WindowEvent)
synchronized public void windowClosed(WindowEvent e) {
blinkThread.interrupt();
} // windowClosed
BP - Template Method
synchronized public void windowDeiconified(WindowEvent e) {
notifyAll();
} // windowDeiconified(WindowEvent)
synchronized public void windowActivated(WindowEvent e) {
notifyAll();
} // windowActivated(WindowEvent)
} // class MyWindowAdapter
} // class AuthenticationDialog
BP - Template Method
/**
* Authenticate the user based on the supplied user id and password.
* @param userID the supplied user id
* @param password the supplied password
* @return Object that encapsulates what data is needed, if any, to prove authentic user
* @exception Throws an Exception if user id and password cannot be authenticated.
*
Exception should have a message suitable for displaying to a user.
*/
abstract protected Object authenticate(String userID, String password) throws Exception;
/**
* Notify the rest of the program that the user has been authenticated.
* @param authenticationToken Object returned by the
authenticate method.
*/
abstract protected void notifyAuthentication(Object authenticationToken) ;
} // class AbstractLogon
BP - Template Method
public class Logon extends AbstractLogon {
//...
protected Object authenticate(String userID, String password)
throws Exception {
if (userID.equals("abc") && password.equals("123"))
return userID;
throw new Exception("bad userID");
} // authenticate
protected void notifyAuthentication(Object authenticationToken) {
//...
} // notify(Object)
} // class Logon
BP - Template Method
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.swing.*;
/** * General purpose logon dialog */
public class LogonDialog extends JDialog {
private JTextField userField;
private JTextField passwordField;
/**
* constructor
* @param parent The logon dialog's parent frame.
* @param title The title to display at the top to the dialog.
*/
public LogonDialog(Frame parent, String title) {
super(parent, title, true);
BP - Template Method
GridBagLayout gb = new GridBagLayout();
JPanel centerPanel = new JPanel(gb);
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.anchor = GridBagConstraints.NORTHWEST;
labelConstraints.insets = new Insets(5,0,0,0);
GridBagConstraints fieldConstraints = new GridBagConstraints();
fieldConstraints.anchor = GridBagConstraints.NORTHWEST;
fieldConstraints.gridwidth = GridBagConstraints.REMAINDER;
fieldConstraints.insets = new Insets(5,3,0,0);
JLabel userLabel = new JLabel("User ID:");
gb.setConstraints(userLabel, labelConstraints);
centerPanel.add(userLabel);
userField = new JTextField(10);
gb.setConstraints(userField, fieldConstraints);
centerPanel.add(userField);
BP - Template Method
JLabel passwordLabel = new JLabel("Password:");
gb.setConstraints(passwordLabel, labelConstraints);
centerPanel.add(passwordLabel);
passwordField = new JPasswordField(10);
gb.setConstraints(passwordField, fieldConstraints);
centerPanel.add(passwordField);
getContentPane().add(centerPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
JButton okButton = new JButton("OK");
buttonPanel.add(okButton);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
okButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent evt) {setVisible(false); }
} );
pack();
} // LogonDialog(Frame, String)
BP - Template Method
/**
* Return the user id that the user specified.
*/
public String getUserID() {
return userField.getText();
} // getUserID()
/**
* Return the password that was specified
public String getPassword() {
return passwordField.getText();
} // getPassword()
} // class LogonDialog
*/
BP - Visitor
Synopsis:
Provides logic in each of the classes in a complex
use case to support the needed operations. Allows
logic to be varied by using different visitors.
Context:
You have a word processor and you wish to add a
reature to produce a TOC.
BP - Visitor
/**
* Instances of this class represent a column.
*/
class Column extends CompositeDocumentElement {
//...
} // class Column
BP - Visitor
import java.util.Vector;
// Instances of this class are composite objects that contain DocumentElement objects.
abstract class CompositeDocumentElement extends DocumentElement {
// Collection of this object's children
private Vector children = new Vector();
// The cached value from the previous call to getCharLength or -1 to
// indicate that charLength does not contain a cached value.
private int cachedCharLength = -1;
BP - Visitor
/**
* Return the child object of this object that is at the given position.
* @param index The index of the child.
*/
public DocumentElement getChild(int index) {
return (DocumentElement)children.elementAt(index);
} // getChild(int)
/**
* Return the numver of children this object has.
public int getChildCount() {
return children.size();
*/
} // getChildCount()
BP - Visitor
/**
* Make the given DocumentElement a child of this object.
public synchronized void addChild(DocumentElement child) {
synchronized (child) {
children.addElement(child);
child.parent = this;
changeNotification();
} // synchronized
} // addChild(DocumentElement)
*/
BP - Visitor
/** * Make the given DocumentElement NOT a child of this object.
public synchronized void removeChild(DocumentElement child) {
synchronized (child) {
if (this == child.parent)
child.parent = null;
children.removeElement(child);
changeNotification();
} // synchronized
} // removeChild(DocumentElement)
//...
*/
BP - Visitor
/**
* A call means one of this object's children has changed in a way that invalidates
* whatever data this object may be cahcing about its children.
*/
public void changeNotification() {
cachedCharLength = -1;
if (parent != null)
} // changeNotification()
parent.changeNotification();
BP - Visitor
/**
* Return the number of characters that this object contains.
*/
public int getCharLength() {
int len = 0;
for (int i = 0; i < children.size(); i++) {
len += ((DocumentElement)children.elementAt(i)).getCharLength();
} // for
cachedCharLength = len;
return len;
} // getCharLength()
} // class CompositeDocumentElement
BP - Visitor
/**
* Instances of this class represent a character in a document.
*/
class DocChar extends DocumentElement {
//...
/**
* Return the number of characters that this object contains.
*/
public int getCharLength() {
return 1;
} // getCharLength()
} // class DocChar
BP - Visitor
/** * Instances of this class represent a document. */
class Document extends CompositeDocumentElement {
private String fname;
private TocLevel[] levels = new TocLevel[0];
//...
/**
* Return the name of the file this document is stored in.
public String getFileName() {
/**
return fname;
* Return an array of TocLevel objects.
} // getFileName()
*/
TocLevel[] getTocLevels() {
TocLevel[] myLevels = new TocLevel[levels.length];
System.arraycopy(levels, 0, myLevels, 0, levels.length);
return levels;
}
//...
} // class Document
*/
BP - Visitor
import java.awt.Font;
/** * All elements of a document belong to a subclass of this abstract class. */
abstract class DocumentElement {
// This is the font associated with this object. If the font
// variable is null, then this object's font will be inherited
// through the container hierarchy from an enclosing object.
private Font font;
// The name of the style associated with this element
private String style;
CompositeDocumentElement parent; // this object's container
//...
BP - Visitor
/**
* Return this object's parent or null if it has no parent.
public CompositeDocumentElement getParent() {
*/
return parent;
} // getParent()
/**
* Return the Font associatiated with this object. If there is no
* Font associated with this object, then return the Font associated
* with this object's parent. If there is no Font associated
* with this object's parent the return null.
*/
public Font getFont() {
if (font != null)
return font;
else if (parent != null)
return parent.getFont();
else
return null;
} // getFont()
//
* Associate a Font with this object. * @param font font to associate with this object
public void setFont(Font font) {
this.font = font;
} // setFont(Font)
BP - Visitor
/**
* Return the number of characters that this object contains.
*/
public abstract int getCharLength() ;
/**
* Return the name of the style associated with this document element.
public String getStyle() { return style; }
/**
* Set the namd of the style associated with this document element.
public void setStyle(String style) { this.style = style; }
} // class DocumentElement
*/
*/
BP - Visitor
// Superclass for classes that visit objects that make up a document and manipulate them.
abstract class DocumentVisitor {
private Document document;
private int docIndex = 0; // This index used to navigate children
/**
* Constructor.
* @param document The Document to be visited.
DocumentVisitor(Document document) {
this.document = document;
} // constructor(Document)
/**
// of document.
* return the document that this object is manipulating
protected Document getDocument() { return document; }
*/
*/
BP - Visitor
/**
* Return the next paragraph that is a direct part of the document
* being manipulated or null of there is no next paragraph.
*/
protected Paragraph getNextParagraph() {
Document myDocument = document;
while (docIndex < myDocument.getChildCount()) {
DocumentElement docElement;
docElement = myDocument.getChild(docIndex);
docIndex += 1;
if (docElement instanceof Paragraph)
}
return null;
} // getNextParagraph()
//...
} // class DocumentVisitor
return (Paragraph)docElement;
BP - Visitor
/**
* Instances of this class represent a line of text.
*/
class LineOfText extends CompositeDocumentElement {
//...
} // class LineOfText
BP - Visitor
/**
* Instances of this class represent a line of text.
*/
class Paragraph extends CompositeDocumentElement {
//...
} // class Paragraph
BP - Visitor
/** * Instances of this class reorganize a document into multiple documents. */
class ReorgVisitor extends DocumentVisitor {
private TocLevel[] levels;
ReorgVisitor(Document document, int level) {
super(document);
this.levels = document.getTocLevels();
Paragraph p;
while ((p = getNextParagraph()) != null) {
//...
} // while
} // constructor(Document)
} // class ReorgVisitor
BP - Visitor
/**
* Instances of this class represent a table of contents in a document
*/
class TOC extends CompositeDocumentElement {
//...
} // class TOC
BP - Visitor
/** * Instances of this class describe a level of organization for a table of * contents. */
class TocLevel {
private int level;
private String style;
//...
/**
* Return the level.
*/
public int getLevel() { return level; }
/**
* Return the style name.
*/
public String getStyle() { return style; }
//...
} // class TocLevel
BP - Visitor
import java.util.Hashtable;
/** * Instances of this class build a table of contents */
class TOCVisitor extends DocumentVisitor {
private Hashtable tocStyles = new Hashtable();
TOCVisitor(Document document) {
super(document);
TocLevel[] levels = document.getTocLevels();
// put styles in a hashtable.
for (int i=0; i < levels.length; i++) { tocStyles.put(levels[i].getStyle(), levels[i]); } // for
} // constructor(Document)
TOC buildTOC() {
TOC toc = new TOC();
Paragraph p;
BP - Visitor
while ((p = getNextParagraph()) != null) {
String styleName = p.getStyle();
if (styleName != null) {
TocLevel level = (TocLevel)tocStyles.get(styleName);
if (level != null) {
LineOfText firstLine = null;
for (int i = 0; i < p.getChildCount(); i++) {
DocumentElement e = p.getChild(i);
if (e instanceof LineOfText) { firstLine = (LineOfText)e; break;
//...
} // for
} // if
} // if
} // while
return toc;
} // buildTOC()
} // class TOCVisitor
} // if
BP - Visitor
import java.util.Vector;
/** * This class contains the top level logic for a word processor */
public class WordProcessor {
// The doucment currently being editied
private Document activeDocument;
//...
/**
* Reorganize a document into subfiles.
*/
private void reorg(int level) {
new ReorgVisitor(activeDocument, level);
} // reorg
/**
* Build a table of contents
*/
private TOC buildTOC() {
return new TOCVisitor(activeDocument).buildTOC();
} // buildTOC()
} // class WordProcessor