package ca.ubc.cs.spl.aspectPatterns.patternLibrary; /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This file is part of the design patterns project at UBC * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * either https://www.mozilla.org/MPL/ or https://aspectj.org/MPL/. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ca.ubc.cs.spl.aspectPatterns. * * For more details and the latest version of this code, please see: * https://www.cs.ubc.ca/labs/spl/projects/aodps.html * * Contributor(s): */ import java.util.WeakHashMap; import java.util.Collection; import java.util.LinkedList; import java.util.Iterator; import ca.ubc.cs.spl.aspectPatterns.patternLibrary.Command; import ca.ubc.cs.spl.aspectPatterns.patternLibrary.CommandInvoker; import ca.ubc.cs.spl.aspectPatterns.patternLibrary.CommandReceiver; /** * This is the abstract Command protocol. * * Note that this implementation allows only for exactly one command per * invoker. That is usually sufficient, but alternate implementations * could account for multiple commands by using composite * (macro) commands (either with or without defined order). * * To allow for some flexibility, commands can either be explicitly * set or removed by Clients, or this can be done via * pointcuts. * * @author Jan Hannemann * @author Gregor Kiczales * @version 1.1, 02/17/04 */ public abstract aspect CommandProtocol { //////////////////////////////// // Invoker -> Command mapping // //////////////////////////////// /** * stores the mapping between CommandInvokers and Commands */ private WeakHashMap mappingInvokerToCommand = new WeakHashMap(); /** * Sets a new command for an invoker * * @param invoker the object which will invoke the command * @param command the command to be set * @return the former command */ public Object setCommand(CommandInvoker invoker, Command command) { return mappingInvokerToCommand.put(invoker, command); } /** * Removes a command from an invoker * * @param invoker the object which will no longer invoke the command * @param command the command to be removed * @return the former command */ public Object removeCommand(CommandInvoker invoker) { return setCommand(invoker, null); } /** * Returns the command for an invoker * * @param invoker the object for which to return the command * @return the current command for the invoker */ public Command getCommand(CommandInvoker invoker) { return (Command) mappingInvokerToCommand.get(invoker); } ///////////////////////////////// // Command -> Receiver mapping // ///////////////////////////////// /** * stores the mapping between Coammnds and Receivers */ private WeakHashMap mappingCommandToReceiver = new WeakHashMap(); /** * Sets a new receiver for a command * * @param command the command to be set * @param receiver the object to be manipulated by the command's * execute() method * @return the former receiver */ public Object setReceiver(Command command, CommandReceiver receiver) { return mappingCommandToReceiver.put(command, receiver); } /** * Returns the receiver for a particular command * * @param invoker the object for which to return the command * @returns the current command for the invoker */ public CommandReceiver getReceiver(Command command) { return (CommandReceiver) mappingCommandToReceiver.get(command); } /////////////////////////////////////// // Command Execution via PC & advice // /////////////////////////////////////// /** * The join points after which to execute the command. * This replaces the normally scattered Command.execute() calls. * * @param invoker the object invoking the command */ protected abstract pointcut commandTrigger(CommandInvoker invoker); /** * Calls executeCommand() when the command is triggered. * * @param invoker the object invoking the command */ after(CommandInvoker invoker): commandTrigger(invoker) { Command command = getCommand(invoker); if (command != null) { CommandReceiver receiver = getReceiver(command); command.executeCommand(receiver); } else { // Do nothing: This Invoker has no associated command } } ////////////////////////////////// // setCommand() via PC & advice // ////////////////////////////////// /** * The join points after which to set a command for an invoker. * This replaces the normally scattered Invoker.add(Command) calls. * The pointcut is provided in addition to the setCommand() method above, * to allow all pattern code to be removed from concrete invokers. * * This PC is non-abstract, to make it optional for sub-aspcects to define * it. * * @param invoker the invoker to attach the command to * @param command the command to be attached to the invoker */ protected pointcut setCommandTrigger(CommandInvoker invoker, Command command); /** * Calls addCommand() when a command should be set. * * @param invoker the invoker to attach the command to * @param command the command to be attached to the invoker */ after (CommandInvoker invoker, Command command): setCommandTrigger(invoker, command) { if (invoker != null) { setCommand(invoker, command); } else { // If the invoker is null, the command cannot be set. // Either ignore this case or throw an exception } } ///////////////////////////////////// // removeCommand() via PC & advice // ///////////////////////////////////// /** * The join points after which to remove a command from an invoker. * This replaces the normally scattered Invoker.remove(Command) * calls. * * The pointcut is provided in addition to the removeCommand() * method above, to allow all pattern code to be removed from * concrete invokers. * * This PC is non-abstract, to make it optional for sub-aspcects to define * it. * * @param invoker the invoker to remove the command from */ protected pointcut removeCommandTrigger(CommandInvoker invoker); /** * Calls removeCommand() when a command should be removed. * * @param invoker the invoker to remove the command from */ after(CommandInvoker invoker): removeCommandTrigger(invoker) { if (invoker != null) { removeCommand(invoker); } else { // If the invoker is null, the command cannot be removed. // Either ignore this case or throw an exception } } //////////////////////////////////////////// // Command default method implementations // //////////////////////////////////////////// /** * Provides a deault implementation for the isExecutable method defined * in the Command interface. * * @return true (default implementation). Can be overwritten by concrete * aspects or even concrete commands. */ public boolean Command.isExecutable() { return true; } }