XAspects: Extensible Plug-ins for
Aspect-Oriented Programming
Demonstration - Committee Example

Macneil Shonle
Ankit Shah
Version 1.1
12 March 2003

Explanation

Committee ClassDictionary provides the structure of the committee, while Log implements a typical Log. These 2 have been made as independent as possible. The Committee is Good-for-nothing while the log may be used as a Travel Log too.

What we want to do is use these 2 different class hierarchies together in the following fashion.
We want every committee and / or its sub-committee to have a log of activities. This log will be populated by members from that particular committee only.

What does the example do?

The committee is first populated and then the logs of various people are parsed in. Then it prints out the committee structure, diaries for each committee and money matters of each committee. These amply demonstrate the traversals and the visitors used in the example.

Technicalities

As can be seen, there are links from committee's to person's and from person's to committee's too. However, manual double coding of this is avoided using an aspect. The aspect, updates the person object by setting its belongs to reference to the committee object for the committee he belongs to.
Also, at the time of creating the committee, an empty log is added to each committee object generated.
When a log is parsed in, we only input the name of the person making an entry in the log. Another aspect will use the internal linking and ensure that the activity is added to right committee's log.
At the end of all the parsing, we wish to traverse the input data and print out the logs and the net expenses by each of the committees.

Sources

Download Example files

MyCommittee.java

// Structure of a Committee
// author: Ankit Shah

import java.util.*;
import java.io.*;

aspect(ClassDictionary) Committee{
Committee = 	"Committee: "  String
		"Chair: "  Person
		["Vice Chair: "  Person]
		"Responsibility:"  String
		["Reports to:"  Committee]
		["Sub-Committees: " "("  List(Committee)")"]
		"Members: ""["List(Person)"]"
		"Budget: "Double.

Person =	"Name: "String
		"Age: "Integer
		["Member of: "Committee]";".

List(S)~	{ S }.

}


aspect(AspectJ) CommitteeLogging {
// CommitteeLogging Aspect.....
// Adds Logging capability to Committees...........
// author: Ankit Shah

	// Temporary variables to retain data between pointcuts
	Log tmpLog;
	Committee topCommittee;

	// Make every committee have a list of Logs
	public Diary Committee.diary = new Diary();

	// Methods to add and access logs
	public void Committee.addLog (Log l) {
		if (diary.logs == null)		// If diary has not been initiated, initiate it!!!
			diary.logs = new Log_List();
		diary.logs.add(l);
	}

	public Iterator Committee.getLogs () {
		return diary.logs.iterator();
	}

	// Pointcuts
	pointcut newCommittee(File f) : call(* Committee.parse(..)) && args(f);
	pointcut newLog(File f) : call(* Log.parse(..)) && args(f);
	pointcut topCommittee(File f) : newCommittee(f) && withincode(* Main.main (..));

	// We get the top-most Committee handle here
	Committee around(File f) : topCommittee(f) {
		Committee comm = proceed(f);
		topCommittee = comm;
		topCommittee.linkPpltoComm();
		return comm;
	}

	// A new Log has been created. Visit all persons to try and find the person responsible for the log
	// Once the person is found, go to the committee he belongs to and attach the log to that committee.
	Log around (File f): newLog(f) {
		tmpLog = proceed(f);
		Person p = topCommittee.gotoPersons(tmpLog.name);
		if (p == null) {
			System.out.println("Bad Name: " + tmpLog.name + ". Log: " + tmpLog.id + " Discarded");
			return null;
		}
		p.belongsto.addLog(tmpLog);
		return tmpLog;
	}
}


MyLog.java

// Log.cd
// author: Ankit Shah

// Any general Diary.....
// Wanna use it during your trip to Miami Beach ???

// The Diary has been added last after an attempt to inter-type declare an ArrayList of
// Logs into Committee failed to be supported by the traversals

aspect(ClassDictionary) MyLog {
Diary =	"^" List(Log).

Log =		"Log" "ID" String
		"by" String
		"(" List(Activities) ")"
		["Approved by: "OfficialTag].
Activities =	"Date: "Date
		"Desc:"String
		["$"Integer]
		["(""$"Integer")"]
		["Notes: "String]"#".

// Before ppl grumble..... on Miami beach, strolling may not cost neither get you money !!! Though I very much doubt the former

Date =		Integer "/"
		Integer "/"
		Integer.

OfficialTag=	"Name: "String
		"Dsg: "String
		"on: "Date.

// I believe on compiling simultaneously List from other file definition may be used here. If not then..... well I'll come here and add it.
// I believed wrong !! Here is the List.

List(S)~	{ S }.
}


// DateToString Aspect....
// author: Ankit Shah

// Adds methods to the date class to print it out in a nice string format

aspect(AspectJ) DateToString
{
	// Formatting the date
	public String Date.toString () {
		return ("" + month + "/" + date + "/" + year);
	}
}

CommitteeTraversals.java

// CommitteeTraversals - Repository of all traversals used in the example
// author: Ankit Shah

aspect(Traversal) CommitteeTraversals
{
	// Specialized Strategies USED in this example
	declare strategy:
		allPersons: "intersect(fromCommittee, toPersons, down)";
	declare strategy:
                allActivities: "intersect(fromCommittee, toActivities, noBelongsto, noReportsto)";
	declare strategy:
		allCommittees: "intersect(fromCommittee, toCommittee, down)";
	declare strategy:
		allDiary: "intersect(fromCommittee, toDiary, noBelongsto, noReportsto)";

	// Highly Generic Strategies
	declare strategy:
		noBelongsto: "from * bypassing -> *, belongsto, * to *";
	declare strategy:
		noReportsto: "from * bypassing -> *, reporstto, * to *";
	declare strategy:
		nothrudiary: "from * bypassing -> *, diary, * to *";
	declare strategy:
		down: "intersect(noBelongsto, nothrudiary, noReportsto)";
	declare strategy:
		fromCommittee: "from Committee to *";
	declare strategy:
		toPersons: "from * to Person";
	declare strategy:
		toActivities: "from * to Activities";
	declare strategy:
		toCommittee: "from * to Committee";
	declare strategy:
		toDiary: "from * to Diary";
	declare strategy:
		toExpend: "from * through -> *, expense, * to *";
	declare strategy:
		toIncome: "from * through -> *, income, * to *";

	// Calculate net expense for the committee and its sub-committees
        declare traversal:
                Integer  netExpense() : allActivities(NetSummingVisitor);

	// Calculate gross expense for the committee and its sub-committees
        declare traversal:
                Integer  totalExpenses() : allActivities(SummingVisitor);

	// Find a particular person whose name is provided
	declare traversal:
		Person gotoPersons(String name) : allPersons(FindVisitor);

	// Visits all committees and prints them out
	declare traversal:
		void gotoAll() : allCommittees(PrintVisitor);

	// Traverses to all Committees and for every person there,
	// set its belongsto link to the appropriate committee
	declare traversal:
		void linkPpltoComm() : allCommittees(LinkPplToCommVisitor);

	// Visits all diaries and prints them out.
	declare traversal:
		void allDiaries() : allDiary(PrintVisitor);

}

Visitor.java

// SummingVisitor
// author: Ankit Shah

import java.util.Iterator;
import edu.neu.ccs.demeter.dj.*;

aspect(TraversalAdvice) NetSummingVisitor
{
	int sum = 0;

	// Sum up all the expenses and deduct income
	void before (Activities a) {
		int expense, income;
		expense = income = 0;
		if (a.expense != null)
			expense = a.expense.intValue();
		if (a.income != null)
			income = a.income.intValue();
		sum += expense - income;
	}		

	// Return result
	public Object getReturnValue () {
		return new Integer(sum);
	}

}

aspect(TraversalAdvice) SummingVisitor
{
	int sum;

	// Sum up all the expenses
	void before (Activities a) {
		int expense = 0;
		if (a.expense != null)
			expense = a.expense.intValue();
		sum += expense;
	}		

	// return result
	public Object getReturnValue () {
		return new Integer(sum);
	}

}

// PrintVisitor
// This PrintVisitor is really hardcoded to print the Committee and Log dictionaries.
// Ideally this should be generated by DAJ since print is the opposite function of parse

aspect(TraversalAdvice) PrintVisitor
{
	void before (Committee c) {
		System.out.println("Committee Name: " +c.name);
		System.out.println("\tChair: " + c.chair.name);
		if (c.vicechair != null) System.out.println("\tViceChair: " +c.vicechair.name);
		System.out.println("\tResponsibility: " + c.responsibility);
		if (c.reportsto != null) System.out.println("\tReports to: " + c.reportsto.name);
		if (c.subcommittees != null) {
			Iterator it = c.subcommittees.iterator();
			System.out.println("\tSub - Committees: ");
			while (it.hasNext())
				System.out.println("\t\t" + ((Committee)it.next()).name);
		}
		if (c.consists != null) {
			Iterator it = c.consists.iterator();
			System.out.println("\tMembers: ");
			while (it.hasNext())
				System.out.println("\t\t" + ((Person)it.next()).name);
		}
		System.out.println("\tbudget: " + c.budget);
	}

	void before (Diary d) {
		if ((d.logs != null) && (d.logs.size() != 0)) {
			System.out.println("Diary: ");
			Iterator itr = d.logs.iterator();
			while (itr.hasNext()) {
				Log l = (Log)itr.next();
				System.out.println("Log:\tID = " +l.id + "\tBy: " + l.name);
				System.out.println("\tActivities: ");
				Iterator it = l.log.iterator();
				while (it.hasNext()) {
					Activities activity = (Activities)it.next();
					System.out.println("\t\t" + activity.date + "\t" + activity.activity);
					if (activity.expense != null)
						System.out.println("\t\t\t\tExpense = $" + activity.expense);
					if (activity.income != null)
						System.out.println("\t\t\t\tIncome = ($" + activity.income + ")");
					if (activity.comments != null) System.out.println("\t\t\t\tNotes: " + activity.comments);
				}
				if (l.approvedby != null) System.out.println("\tApproved by: " + l.approvedby.name);
			}
		}
	}
}


// FindVisitor

aspect(TraversalAdvice) FindVisitor
{
	// Variables
	String target;
	Person  foundTarget;

	// Constructor is Parameterized to receive object to be searched
	FindVisitor (String name) {
		target = name;
		foundTarget = null;
	}

	// Actual Visitor function
	void before(Person host) {
		if (host.name == null)
			return;
		if (host.name.equalsIgnoreCase(target))
			foundTarget = host;
	}

	// Return it
	public Object getReturnValue () {
		return foundTarget;
	}
}

// FindVisitor
// author: Ankit Shah

aspect(TraversalAdvice) LinkPplToCommVisitor
{
	// For every committee, get a list of ppl belonging to it and 
	// set the corresponding persons belongsto member to point to
	// its committee object
	void before(Committee committee) {
		committee.chair.belongsto = committee;
		if (committee.vicechair != null)
			committee.vicechair.belongsto = committee;
		java.util.Iterator itr = committee.consists.iterator();
		while (itr.hasNext())
			((Person)itr.next()).belongsto = committee;
	}
}

// CompareVisitor
// author: Ankit Shah

aspect(TraversalAdvice) CompareVisitor
{
	// Compare Visitor calls SummingVisitor's net Expense calculator and then compares it with the committees budget
	// to flag whether they've exceeded the budget or not.
	void before (Committee c) {
		double spent = c.netExpense().doubleValue();
		if (spent > c.budget.doubleValue())
			System.out.println(c.name + " has exceeded the budget by $" + (spent - c.budget.doubleValue()));
	}
}

Main.java

// Main.java
// author: Ankit Shah
// Here I admit.. I'm taking help from Prof. Lieberherr's Main.java in the OS example

import java.io.*;

class Main {
  public static void main(String[] args) {
    try {

	// Read in the files and parse them.
	// Remember we read in independent logs and not the whole diary !!!
	Committee com = Committee.parse(new File(args[0]));
	Log logs[];
	if (args.length > 1) {
		logs = new Log[args.length - 1];
		for (int i = 1; i < args.length; i++)
			logs[i - 1] = Log.parse(new File(args[i]));
	}

	// Various visitors called to demonstrate ease of work
	// Printing out all committees
	System.out.println("Invoking Print Visitor to list all Committee and their Sub-Committees");
	System.out.println("=====================================================================");
	com.gotoAll();
	System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

	// Printing out all committees and their diaries
	System.out.println("Printing Committee-wise Logs");
	System.out.println("============================");
	com.allDiaries();
	System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

	// Net Expenses so far
	System.out.println("\n\n\nInvoking Net Expense Calculator to calculate Net Expenditure by the Committee and their Sub-Committees");
	System.out.println("------------------------------------------------------------------------------------------------------");
	System.out.println("Net Expense of "+ com.name + " = $" + com.netExpense().toString());
	System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

	// Gross expenses only by sub-committees
	System.out.println("Invoking Gross Expense Visitor on each of the sub-committees of " + com.name);
	java.util.Iterator it = com.subcommittees.iterator();
	while (it.hasNext()) {
		Committee sc = (Committee)it.next();
		System.out.println("Sub-Committee: " + sc.name + "\tGross Expense = $" + sc.totalExpenses().toString());
	}

      } catch (Exception e) {
      System.err.println("Hi... its me, Ankit"); System.err.println(e);
    }
  }
}

How is it all Installed

committee/ - This folder has all the .java files
committee/input/ - This folder has all the .input files
committee/cls/ - Here all the generated .class files will be placed

Input Files

committee.input

Committee: "Event Organizing Committee"
	Chair: Name: "Roger Morris" Age: 49;
	Vice Chair: Name: "Adrianna Harrison" Age: 37;
	Responsibility: "Overall Organization of the Event"
	Sub-Committees: (
		Committee: "Finance Sub-Committee"
			Chair: Name: "Peter Rhodes" Age: 51;
			Vice Chair: Name: "Alec Martin" Age: 33;
			Responsibility: "Finance Management"
			Members: [
				Name: "Julianna Castle" Age: 35;
				Name: "John Grenada" Age: 30;
				Name: "Heidi McDermott" Age: 25;
				]
			Budget: 15000.00
		Committee: "Logistics Sub-Committee"
			Chair: Name: "Sean Lambert" Age: 26;
			Responsibility: "Logistic Support"
			Members: [
				Name: "Larry Hopkins" Age: 20;
				Name: "Harry Blacksmith" Age: 19;
				Name: "Mark Mills" Age: 22;
				]
			Budget: 48000.00
		)
	Members: [
		Name: "Greame Hick" Age: 32;
		Name: "Laura Smith" Age: 40;
	]
	Budget: 63000.00

log1.input

Log	ID "MM001"	by "Mark Mills"
	(
		Date: 2/15/2003 Desc: "Light System"  $ 1500 Notes: "To be Setup by Next Weekend" #
		Date: 2/15/2003 Desc: "Projector Setup"  $ 700 #
	)
	Approved by: Name: "Sean Lambert" Dsg: "Chair, Logisitics Sub-Committee" on: 2/17/2003

log2.input

Log	ID "HB001"	by "Harry Blacksmith"
	(
		Date: 2/14/2003 Desc: "Leasing out Banner Space" ($ 2000) Notes: "3 Banner Spaces to Coca Cola" #
	)
	Approved by: Name: "Sean Lambert" Dsg: "Chair, Logisitics Sub-Committee" on: 2/17/2003

log3.input

Log	ID "SL001"	by "Sean Lambert"
	(
		Date: 2/17/2003 Desc: "Progress Report Sent to Chair" Notes: "Copies of MM and HB's Logs enclosed" #
		Date: 2/15/2003 Desc: "Finance Report Sent to Finance Sub-Committee" #
	)
	Approved by: Name: "Greame Hick" Dsg: "Member, Event Organizing Committee" on: 2/18/2003

log4.input

Log	ID "JC001"	by "Julianna Castle"
	(
		Date: 2/17/2003 Desc: "Re-imbursed Logistics Sub-Committee" Notes: "Amt is $200.00" #
		Date: 2/17/2003 Desc: "Payroll" $ 3000 #
	)
	Approved by: Name: "Alec Martin" Dsg: "Vice Chair, Finance Sub-Committee" on: 2/18/2003

Compiling and Executing

I'm assuming this a Sun Solaris machine and xajc script is in the path and all the necessary libraries are in the classpath. Refer usage for details about usage and installation.
$ pwd
committee/

$ xajc -d cls/ MyCommittee.java MyLog.java CommitteeTraversals.java Visitors.java Main.java
Initiating XAspects Compiler...
XAspects used: 11
From 5 source files
Initializing Compilation Environment...
Aspects being sent to appropriate plugins
Generating AspectJ code - Pass 1
--- WARNING!!! ---
MyCommittee.java:
Package Name / Imports / Modifiers / Inheritence info  have no meaning for ClassDictionary Type Aspect. Will be ignored
Compiling AspectJ code - Pass 1
Generating AspectJ code - Pass 2
Compiling AspectJ code - Pass 2
Writing Output files
XAJC Completed with 0 Errors; 1 Warnings.

---  Alternatively if you do not use the xajc script ---
$ java edu.neu.ccs.xaspects.XAJC -d cls/ MyCommittee.java MyLog.java CommitteeTraversals.java Visitors.java Main.java
should give you same comments as above.


$ cd cls

$ java Main ../input/committee.input ../input/log1.input ../input/log2.input ../input/log3.input ../input/log4.input

Output

Invoking Print Visitor to list all Committee and their Sub-Committees
=====================================================================
Committee Name: Event Organizing Committee
	Chair: Roger Morris
	ViceChair: Adrianna Harrison
	Responsibility: Overall Organization of the Event
	Sub - Committees: 
		Finance Sub-Committee
		Logistics Sub-Committee
	Members: 
		Greame Hick
		Laura Smith
	budget: 63000.0
Committee Name: Finance Sub-Committee
	Chair: Peter Rhodes
	ViceChair: Alec Martin
	Responsibility: Finance Management
	Members: 
		Julianna Castle
		John Grenada
		Heidi McDermott
	budget: 15000.0
Committee Name: Logistics Sub-Committee
	Chair: Sean Lambert
	Responsibility: Logistic Support
	Members: 
		Larry Hopkins
		Harry Blacksmith
		Mark Mills
	budget: 48000.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Printing Committee-wise Logs
============================
Committee Name: Event Organizing Committee
	Chair: Roger Morris
	ViceChair: Adrianna Harrison
	Responsibility: Overall Organization of the Event
	Sub - Committees: 
		Finance Sub-Committee
		Logistics Sub-Committee
	Members: 
		Greame Hick
		Laura Smith
	budget: 63000.0
Committee Name: Finance Sub-Committee
	Chair: Peter Rhodes
	ViceChair: Alec Martin
	Responsibility: Finance Management
	Members: 
		Julianna Castle
		John Grenada
		Heidi McDermott
	budget: 15000.0
Diary: 
Log:	ID = JC001	By: Julianna Castle
	Activities: 
		2/17/2003	Re-imbursed Logistics Sub-Committee
				Notes: Amt is $200.00
		2/17/2003	Payroll
				Expense = $3000
	Approved by: Alec Martin
Committee Name: Logistics Sub-Committee
	Chair: Sean Lambert
	Responsibility: Logistic Support
	Members: 
		Larry Hopkins
		Harry Blacksmith
		Mark Mills
	budget: 48000.0
Diary: 
Log:	ID = MM001	By: Mark Mills
	Activities: 
		2/15/2003	Light System
				Expense = $1500
				Notes: To be Setup by Next Weekend
		2/15/2003	Projector Setup
				Expense = $700
	Approved by: Sean Lambert
Log:	ID = HB001	By: Harry Blacksmith
	Activities: 
		2/14/2003	Leasing out Banner Space
				Income = ($2000)
				Notes: 3 Banner Spaces to Coca Cola
	Approved by: Sean Lambert
Log:	ID = SL001	By: Sean Lambert
	Activities: 
		2/17/2003	Progress Report Sent to Chair
				Notes: Copies of MM and HB's Logs enclosed
		2/15/2003	Finance Report Sent to Finance Sub-Committee
	Approved by: Greame Hick
~~~~~~~~~~~~~~~~~~~~~~~~~~~~



Invoking Net Expense Calculator to calculate Net Expenditure by the Committee and their Sub-Committees
------------------------------------------------------------------------------------------------------
Net Expense of Event Organizing Committee = $3200
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Invoking Gross Expense Visitor on each of the sub-committees of Event Organizing Committee
Sub-Committee: Finance Sub-Committee	Gross Expense = $3000
Sub-Committee: Logistics Sub-Committee	Gross Expense = $2200

References

XAspects Project Home Page, <http://www.ccs.neu.edu/research/demeter/xaspects>.

Author: Ankit Shah. Copyright © 2003. All rights reserved.