Adaptive Object-Oriented Software Development                   Fall 1998
COM 3360/NTU SE737
---------------------------------------------------------------------------

Assignment 3

Due date: Tuesday, Oct. 29 ---------------------------------------------------------------------------
This is a two week assignment.

This assignment is stored in file $AOO/hw/3

in file assign.html

THEMES:

=========

On CCS Northeastern machines,

OO=/proj/adaptive/www/course/
AOO=/proj/adaptive/www/course/f98/

On the WWW, OO = http://www.ccs.neu.edu/research/demeter/course/
On the WWW, AOO = http://www.ccs.neu.edu/research/demeter/course/f98
The course directory is:

AOO

---------------------------------------------------------------------------

READING:

Read chapters 8, 11, 12 of the AP book.

The class dictionary for Demeter/Java which defines the Demeter/Java notation. It is reachable from the Demeter/Java and AP-Studio resources page: http://www.ccs.neu.edu/research/demeter/DemeterJava/

Plus additional reading as described below.

--------------------------------------------------------------------------

The homework consists of 6 parts. PART 2 and PART 3 _can_ be done in group mode. Groups of size 1 are fine. PART 1 and PART 4 and PART 5 and 6 must be done individually.

Organize your solution directory hw3 as follows: (files terminating in / are directories)

individual/
  README part1/ part4/ part5/ /part6
group/
  README part2/ part3/
Put additional files as needed.

To submit your individual homework, go to hw3/individual and call:

submit3360

This will automatically collect all the files (including files in subdirectories) and send them to com3360-grader. Eliminate all generated class files *.class before submission.

Use the following header for your electronic homework submissions and put it at the top of the file README (for assignments which are done individually):

Course number: COM 3360 
Name:
Account name:
Assignment number:
Date:
Whenever you have questions about course material, please send e-mail to:

mail lieber com3360-grader@ccs.neu.edu ccs.courses.com3360@ccs.neu.edu

==================================================================

It is time to build groups now if you want to do your project in group mode. Each group should consist of no more than 3 students. The group you select now should also do the project together. Individual projects are often preferred by graduate students. Again you can do all your course work individually.

For the assignments which are done in a group, use the following header at the top of file README:

Course number: COM 3360 
Names:
Account name of leader:
Assignment number:
Date:

The following applies to parts 2 and 3. We want to achieve good team work using a traversal/visitor style of programming. Therefore, you should design the interfaces of traversal functions, and visitors together and then split the work of implementing the traversals and visitors. Therefore, turn in the following

Team members: ....
abstract Visitor: 			responsible: team member 1
Visitor class CountVisitor: 		responsible: team member 1
Visitor class PrintVisitor: 		responsible: team member 2
Visitor class DisplayVisitor: 	 	responsible: team member 3
The traversal and class definitions code can also be split by class subsets:
              classes A, B, C, D, E, F, G, H, X
team member 1         .  .  .  
team member 2                  .  .  .
team member 3                           .  .  .
integration testing of traversal: team member 2

integration testing of complete program: team member 3

writing rest of program:

team member 1        countG() 
team member 2        print() 
team member 3        display() 
Of course, fill in the complete names of the team members.

The abstract Visitor and traversal code needs to be written very early since it is a prerequisite for testing the three concrete visitors.

It is important that each team member tests his or her code carefully before integration testing. This will require the writing of some additional code.

==================================================================

PART 1:

=======

PURPOSE: See Demeter/Java in action. Study an application of the concepts covered so far in the context of a Java application.

Taks to be done: Read and work through the Laboratory Guide using Demeter/Java and hand in all files modified.

Browse through the Demeter/Java User's Guide available from the Demeter/Java and AP-Studio Resources page http://www.ccs.neu.edu/research/demeter/DemeterJava/

The User's Guide is still under development since the system itself is still evolving. We do our best to update it as soon as possible. Together with the lectures and the possibility to ask questions, you will be able to use Demeter/Java productively.

If you think you have found a bug in Demeter/Java and its graphical User Interface APStudio and/or its documentation, please report it to dem@ccs.neu.edu This way you reach the entire Demeter/Java development team, including

Joshua Marshall (Demeter/Java, Synchronization and Remote Invocation Aspect)
Doug Orleans (principal designer and implementor of Demeter/Java, maintains AP Studio)
Johan Ovlinger (Demeter/Java, TAO and Laboratory Guide)
and myself. Please don't hesitate to report any bugs you encounter. We will try to suggest a workaround and we will repair them with a time-plan which depends on the seriousness of the bug you found.

The CHANGES and BUG files (or the same files in the src directory of the Demeter/Java distribution) tell you about the history of the project and the bugs we know and are working on. The CHANGES file is currently the most reliable source of documentation for Demeter/Java. Some parts of the User Manual are dated. The BUGS file.

Browsing the User's Guide will help you to better understand the Laboratory Guide available from the same resource page.

The purpose of this laboratory exercise is to see adaptive programming in operation in the Java world.

PART 2:

=======

PURPOSE: Apply the concepts you learned (e.g., the Visitor pattern) at the Java level, without Demeter/Java. To make you bored writing all the tedious Java code and to make you appreciate the capabilities of Demeter/Java. You might find this part tedious; if it is too tedious, let me know. The advantage of applying the Visitor pattern manually is that you will better understand what Demeter/Java does in the background.

For this homework part you are encouraged to write a Java program from scratch. You should not use any tools except a Java compiler and interpreter (no Demeter tools) and of course, an editor and a machine which can run and compile Java.

Write a program for the class dictionary in: $AOO/hw/3/visitor-by-hand/program.cd

The relevant classes are:

A = "a" <b1> B <c1> C [D] "enda".
B : E | F.
C = .
D = "d" .
E = "e".
F = <g1> G <h1> H <a1> A.
G =.
H = "h".
Write three methods in one program for this class dictionary but the three methods are allowed to use only one traversal function traverse(Visitor) attached to all of the above classes.

The three tasks to be done by the three methods are:

Test your functions on three different A-objects using code like:
in class Main:
 static public void main(String args[]) throws Exception {
   A a = ... // write constructor calls to build an A-object. See:
   // /proj/adaptive/www/sources/DemeterJava/examples/j-c-bypassing/program.beh
   // for an example.

   System.out.println("print:");
   a.g_print(); 
   System.out.println();
   System.out.println("tree structure:");
   a.display();
   System.out.println("count:");
   int result = a.countG();
   System.out.println(result + " done ");  
}

   print() should be implemented by a call traverse(p)
   where p is a PrintVisitor-object.

   display() should be implemented by a call traverse(t)
   where t is a DisplayVisitor-object.
 
   countG() should be implemented by a call traverse(s)
   where s is a CountVisitor-object.
To achieve what you want, you need to introduce an abstract Visitor class. For an example, see the abstract UniversalVisitor class which Demeter/Java uses. See file program.xcd in the generated directory (usually called gen). See also the the context object paper (journal version) for an extended discussion.

You need an abstract class:

Visitor : PrintVisitor | DisplayVisitor | CountVisitor.
For the abstract Visitor class you define empty before and after methods which have a host argument. In the subclasses you override the methods where the behavior needs to be non-empty.

Your traversal functions will call the before and after methods of the visitor. First the before method is called, then the traversal is done and then the after method is called.

Turn in your complete Java-program and the output produced for your three test cases.

PART 3:

=======

PURPOSE: Show you how brittle Java programs are. Structural changes are not easy. Maintenance is tedious.

Repeat PART 2 for the class dictionary in: $AOO/hw/3/visitor-by-hand2/program.cd

The relevant classes of the cd are:

A = "a" X C [D].
X = B.
B : E | F.
C = .
D = "d" .
E = "e".
F = G H.
G =.
H = "h".
Notice that the class dictionary is only slightly different from the one in PART 2. If you work in a team, you have to figure out what each team member needs to update in the code assigned to him/her. The figuring out what needs to be updated should be done as a group but the implementation is completed by each team member.

PART 4:

=======

PURPOSE: Show you how Demeter/Java simplifies the job.

Now the restriction of using Demeter/Java is lifted. You can now take advantage of the brain power which CCS students and faculty have put into Demeter/Java. We hope you will be pleased by how the task is now simplified so that you can do it easily yourself in 30 minutes.

Do PART 2 and PART 3 using Demeter/Java. Copy the directories:

$AOO/hw/3/visitor-by-hand/* and $AOO/hw/3/visitor-by-hand2/*

and modify the program.cd and program.beh and program.input files if needed.

To regenerate:

demjava test

Note that sometimes it is necessary to use "demjava clean" before "demjava test".

Make sure you add the visitor classes to the class dictionary.

Please hand in only a short description of the modifications, if any, you did to program.cd and program.beh. What is the number of lines you wrote in your Java program? What is the number of lines in the program.beh and program.cd files? Turn in the two numbers and the ratio of "pure Java" divided by "Demeter/Java".

PART 5: (to be done individually)

=======

PURPOSE: Experience the behavior of the Java Java Compiler JavaCC. The JavaCC input is in gen/grammar.jj. Study error messages of JavaCC and map them back to the class dictionary level. Learn to debug class diagrams by verifying that desired objects can be represented by the classes in the class diagram.

To be good at object-oriented software development, you need to have several skills, including:

In this part you focus on defining the structure of your objects, i.e., on how to write class dictionaries.

We first start with simple class dictionaries. Therefore, first do the work described in 17.5.1 and 17.5.2 and 17.5.3 (except 17.5.3.3) on pages 551 and 552 of the AP-book. Instead of using Demeter/C++, use Demeter/Java to test your sentences. This will give you an opportunity to learn about the Java Compiler Compiler. Type "javacc" to find out about all the options JavaCC offers. Also visit the JavaCC website.

Specifically, this means that you will not use sem-check to test your class dictionaries, but

demjava test

to test your class dictionary and its inputs. It is possible that for some bugs in your class dictionary, the Java compiler will complain and not Demeter/Java nor the Java Compiler Compiler. Turn in your class dictionaries and your sentences and for each class dictionary a statement (comment) which says: My class dictionary was accepted by Demeter/Java and the Java Compiler Compiler without warning or error.

See http://www.ccs.neu.edu/research/demeter/DemeterJava/ on how to use Demeter/Java.

When writing your class dictionaries, follow the Terminal-Buffer rule described on page 393 of the AP book.

Also follow the following Repetition-Buffer rule: A repetition class should be used as the only part class of a construction class. Example:

Body                    = "{" [Initialize] PathDirective Wrappers "}".
Wrappers                = [ < wrappers> Wrapper_SList ] .       
Wrapper_SList 		~ Wrapper {  Wrapper } .
Here Wrappers is the buffer class for the wrapper list. The reason for buffering repetition classes is that their names might change later. If we need to pass around wrappers, it is better to pass around Wrappers-objects than Wrapper_SList-object since Wrapper_SList might be renamed to Wrapper_CVector later.

Now after you have gained experience in designing small class dictionaries, we go for a larger one. This class dictionary is about what you learn in the AP-book: propagation patterns.

Adaptive object-oriented programming has three key ingredients: class dictionaries, traversals and visitors. But sometimes, those ingredients require us to write long programs and therefore, we look for abbreviations, also called syntactic sugar. Propagation patterns are such a mechanism to make it easier to program with traversals and visitors.

Consider the task of computing the total of all salaries paid by a company. We need to define a method called sum_salaries for class Company. To implement the method we need to define a visitor class called SummingVisitor. We also need to define a traversal method "allSalaries". The body of method sum_salaries will instantiate the Visitor and call the method allSalaries with the SummingVisitor-object as argument. For a related example, see http://www.ccs.neu.edu/research/demeter/sources/DemeterJava/examples/j-c-holding/ in file holding.beh.

This all can be said with a propagation pattern:

Company {
  traversal-pp int sum_salaries() {
    initialize (@ 0 @)  // initialize return_val to 0
    to Salary		// traversal
    before Salary (@ return_val = return_val + host.get(v).intValue(): @)
  }
}
Propagation patterns are implemented in Demeter/Java (as adaptive methods) and the purpose of this part is to design a part of a class dictionary for a slightly different language than Demeter/Java.

Extend the class dictionary below so that it can handle the following input:

Company {
  traversal-pp int sum_salaries() {
    initialize (@ 0 @)  // initialize return_val to 0
    to Salary		// traversal
    before Salary (@ return_val = return_val + host.get(v).intValue(): @)
  }
}

Shape {
  traversal-pp Integer gp1x(){
    bypassing {-> *,p2,*, -> *,y,*} to Integer
    before Integer (@ return_val = this; @) 
  }
}

ClassGraph {
  traversal-pp void constrClassNames() {
      through :cdef ClassDef  to Construct
      // the class definition is made available in variable cdef
      before Construct
        (@ System.out.println(cdef.getClassName()); @)
  }
}

X {
  traversal-pp int f(float f, int i, A a) {
    initialize (@ 0 @)
    bypassing {X,Y, -> *,r,R} 
    through {:x X, :y Y, -> *,r,:rv R} 
     // the wrappers below may refer to x,y and rv
     to Z
    before {R,S} (@ ... @)
    after {U,V} (@ ... @)
  }
}
The class dictionary:
// -- class dictionary for Demeter/Java with propagation patterns

//////////////////////
// Behavior (methods).
//////////////////////
// man g_print (Demeter/C++) explains the pretty printing commands
// *l *s + -

ProgramBehavior		= [ <behavior> DList(ClassBehavior) ] .

ClassBehavior		= ClassName ClassMethods.

ClassMethods		= "{" *l + [ <methods> SList(Method) ] - "}" .

Method			: Traversal | TraversalPP| Behavior.
Behavior		: Wrapper | Verbatim.


// Class graph traversal specifications.
Traversal		= "traversal" TraversalName TraversalArgs "{" *l
			+ PathDirective ";" - *l
			"}".
TraversalPP		= "traversal-pp" 
			  <returnType> UNKNOWN1
			  UNKNOWN2
			  UNKNOWN3
			  UNKNOWN4.
Args			= "(" [ <l> Commalist(UNKNOWN5) ] ")".
Arg			= <typ> JavaTypeName  <arg> Variable. 
Body			= "{" [UNKNOWN6] UNKNOWN7 UNKNOWN8 "}".
Initialize		= "initialize" UNKNOWN9.

Wrappers		= *l + [ <wrappers> SList(UNKNOWN10) ] -  .

TraversalArgs		= "(" [ <visitors> Commalist(Visitor) ] ")" .

Visitor			= ClassName VisitorName.

PathDirective		= [ BypassingDirective ]
			  [ ThroughDirective ]
			  TargetDirective.

BypassingDirective	= "bypassing" <glob> GlobSpec.
ThroughDirective	= "through" <glob> GlobSpec.

TargetDirective		: To | ToStop *common* <targets> ClassGlobSpec.
To			= "to".
ToStop			= "to-stop".

GlobSpec		: OneGlob | GlobSet.
OneGlob			= Glob.
GlobSet			= "{" [ <globs> Commalist(Glob) ] "}".

Glob			: ClassGlob | EdgeGlob.
EdgeGlob		: PartGlob | SubclassGlob | SuperclassGlob.

ClassGlob		= <dest> ClassNameGlob.
PartGlob		= "->" <source> ClassNameGlob ","
			       <edge> PartNameGlob "," <dest> ClassNameGlob.
SubclassGlob		= "=>" <source> ClassNameGlob "," <dest> ClassNameGlob.
SuperclassGlob		= ":>" <source> ClassNameGlob "," <dest> ClassNameGlob.

ClassNameGlob		: ClassNameExact | AnyClass.
ClassNameExact		= [UNKNOWN11 Variable] ClassName.
AnyClass		= "*".

PartNameGlob		: PartNameExact | AnyPart.
PartNameExact		= PartName.
AnyPart			= "*".

ClassGlobSpec		: OneClassGlob | ClassGlobSet.
OneClassGlob		= ClassGlob.
ClassGlobSet		= "{" <classglobs> Commalist(ClassGlob) "}".


// Before and after wrappers.
Wrapper			: Before | After *common* <hosts> HostSpec JavaCode.

Before			= "before".
After			= "after".

HostSpec		: ClassGlobSpec.


// Verbatim java code.
Verbatim		= JavaCode.


// Terminal buffer classes.
DirName			= <name> Ident.
ClassName		= <name> Ident.
PartName		= <name> Ident.
TraversalName		= <name> Ident.
VisitorName		= <name> Ident.
MethodName		= <name> Ident.
JavaCode		= <code> Text.

JavaTypeName		= <name> Ident.
Variable		= <name> Ident.

// Parameterized class definitions.
List(S) ~ {S}.
SList(S) ~ S { *l S } *l .
DList(S) ~ S { *l *l S } *l .
Commalist(S) ~ S {"," S}.
Barlist(S) ~ S { *l "|" S}.

Main = .
Turn in the UNKNOWNs.

Discussion

Notice that it is not too hard to translate a propagation pattern back into traversal/visitor style. All the wrappers become the methods in a new visitor class called V. V is defined by:
V = <return_val> WhatEverType.
If the propagation pattern has arguments, we need an additional visitor class called ArgV which has as many parts as the propagation pattern has arguments. The initalization code is used to initialize return_val.

For example, the propagation pattern:

Company {
  traversal-pp int sum_salaries() {
    initialize (@ 0 @)  // initialize return_val to 0
    to Salary		// traversal
    before Salary (@ return_val = return_val + host.get(v).intValue(); @)
  }
}
is translated into
Company {
  (@
    int sum_salaries() {
      SummingVisitor sv = new SummingVisitor( 0 );
      this.allSalaries(sv);
      return sv.get_return_val();
  @)
  traversal allSalaries(SummingVisitor sv) {
    to Salary; }
}
 
SummingVisitor {
  before Salary (@ return_val = return_val + host.get(v).intValue(); @)
}
As this example shows, propagation patterns are an abbreviated form for certain kinds of adaptive programs. But propagation patterns don't take advantage of the full power of visitors: They use only very simple visitors.

===========================

PART 6: (To be done individually)

=======

PURPOSE: Implement a simple semantic checking tool. This simple tool shows you in the small how Demeter/Java works. Work with a class diagram which defines its own structure. Invites you to write structure-shy code so that your tool also works for the class dictionary:

// class dictionary
Cd_graph =  Adj.
Adj =  Vertex  Construct ".".
Construct = "="  Labeled_vertex  Labeled_vertex.
Labeled_vertex = "<"  Ident ">"
		      Vertex.
Vertex =  Ident.
Actually, I recommend that first you get your program running on this simplified class dictionary! This will show you incremental object-oriented software development in action. The motto is: First simplify your structure to the simplest, but still interesting special case and then grow your software from that base.

Implemement a tool which checks for the Terminal-Buffer rule, page 393 of the AP book. Use the class dictionary in $AOO/hw/1/graph/class-graph.cd

The following class definition should be rejected:

A = <x> Integer <y> Integer.
The following two are legal:
A = <x> Coord <y> Coord.
Coord = <l> Integer.

Treat any undefined class (i.e. one that does not appear on the left side of a class definition) as a terminal class. This includes, for example:

The Java wrapper classes (in package java.lang): Boolean Character Double Float Integer Long

The Java classes (in package java.lang): String StringBuffer

The classes (in package edu.neu.ccs.demeter): Ident Text

Terminal classes also include Java built-in types, e.g. int, boolean, char, float.

Turn in your complete *.cd and *.beh files. =====================================================================

Corrections to the Laboratory Guide:

Please send mail to 

lieber@ccs.neu.edu johan@ccs.neu.edu 

if you find something not clear in the Laboratory Guide.

From a happy user of the Demeter/C++ lab guide. The Demeter/Java Laboratory Guide is a translation of the Demeter/C++ Lab Guide.

Hi Crista:
 
I just finished working through the Demeter Lab Guide and already feel
a lot more comfortable with Demeter. The Lab Guide is concise, clear
and written with a great deal of empathy for the user. Congratulations
for doing such an excellent job with the Lab Guide!
 
...

E-mail submission

Please follow the following rules:

HOW

Prepare your submission directory with all the relevant information (including file README) and type

submit3360

in your directory.

For group submission use the same procedure, but please only one message per group.

COMPLETE SOLUTION

Send the complete text of your homework solution with one call to submit3360. Don't just send a message "my solution is in directory ...". The solution with the submit3360 command keeps your solution protected from access by others and allows for faster grading.

COMPUTER USAGE:

For doing the assignments you will need to use the Demeter Tools/Java. You can do that by Regarding 1 and 3, see URL: http://www.ccs.neu.edu/help/cluesheets/resources/

Options 1 and 3 require no installation on your part but access will be a bit slow depending on your location.

Option 2 will be faster but you need to install Demeter/Java following the instructions off the Demeter/Java resource page.

===========================

Regarding Project Selection


From dougo@ccs.neu.edu Wed Oct  8 04:51:37 1997

...

As a side note, it can be tough to use Demeter (in its current state)
to parse a predefined syntax.  You may want to make some sort of
preprocessor in awk or perl to translate the input file into an
intermediate form that Demeter can more easily parse, e.g. by putting
strings in double quotes or text in (@ @).

--Doug

------------

This theme comes up in every instance of this course. 
Try to find a project where
you control the input language and if you cannot, use a preprocessor
as described above.