Upload
sibyl-robinson
View
217
Download
2
Embed Size (px)
Citation preview
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.
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
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
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.
SecuritySystem
status infomation
NetworkedComputer
temp
BP - Chain of Responsibility
smoke
motion
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
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.
messageArea Objecttemp
BP - Chain of Responsibility
TempSensor
messageWarehouse
messageFloorBuilding
message
The generalized solution involves
CommandHandler
command
BP - Chain of Responsibility
ConcreteCH
CommandSender
ConcreteCHConcreteCH
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 - Chain of Responsibility
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
AbstractCommand
create ConcreteCommand
InvokerCommandManager
manages
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.
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 - Command
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.
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()) gotoState(PARAM_DIRTY); break;
default: 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; } // saveParam()
private boolean applyParam() { //... return true; } // applyParam()
private boolean revertParam() { //... return true;} // revertParam()
} // class Procedural
BP - State
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.
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 - Strategy
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.
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, e.getMessage(),
"Authentication Failure", JOptionPane.ERROR_MESSAGE);
} // try
}
// Authentication successful
waitDialog.setVisible(false);
logonDialog.setVisible(false);
notifyAuthentication(authenticationToken);
} // logon()
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); blinkThread.start();
addWindowListener(new MyWindowAdapter());
} // constructor(Frame)
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()) { wait(); } // while !isShowing
} // synchronized
} // if
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 - Template Method
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.
/**
* 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) parent.changeNotification();
} // 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; } // getFileName()
/** * Return an array of TocLevel objects. */
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 // of document.
/** * Constructor. * @param document The Document to be visited. */
DocumentVisitor(Document document) {
this.document = document;
} // constructor(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 (Paragraph)docElement;
}
return null;
} // getNextParagraph()
//...
} // class DocumentVisitor
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; } // if
//...
} // for
} // if
} // if
} // while
return toc;
} // buildTOC()
} // class TOCVisitor
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
BP - Visitor