CSU 670 Programming Assignment 4

Due: October 6, 2006


We are taking an incremental approach to software development in this course: In hw 1 you read and analyzed Java programs, in hw 2 you filled in missing information in Java programs that use DJ, in hw 3 you modified programs that use class dictionaries (similar to XML schemas) and a tiny bit of DemeterJ (the weaving feature offered by Class1 { {{ ... }} } Class2 { {{ ... }} } ... ).

Now in hw 4 you write from scratch a simple Java program using DJ and a tiny bit of DemeterJ (the weaving feature) but you are given the class dictionary.

This homework has two parts: The first part is the Laboratory Guide and the second part is a simple UNIX shell. The Laboratory Guide introduces you to DemeterJ (formerly called Demeter/Java) but most of what you learn will help you to write Java Programs with DJ and a tiny bit of DemeterJ. So when you go through the Laboratory Guide, focus on class dictionaries and how you would express the adaptive programs you encounter directly using DJ without using the adaptive programming language of DemeterJ.

Name substitution throughout Laboratory Guide and other documents:
Demeter/Java -> DemeterJ
demjava -> demeterj


Please note that homework submission is electronic 
to black board and hardcopy submission. 
Follow the submission format from earlier homeworks!
This will allow the teaching assistant
to run your programs and mark your printed copies.

Please package up your whole directory, but 
without any .class or .java
files. The teaching assistant
will be generating them from the DemeterJ files.

THEMES:

READING:

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

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

Plus additional reading as described below.


PART 1:

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

Tasks to be done: Read and work through the Laboratory Guide using DemeterJ and hand in all files modified.

Browse through the DemeterJ User's Guide available from the DemeterJ and AP-Studio Resources page http://www.ccs.neu.edu/research/demeter/DemeterJava/. Focus on class dictionaries and only a little bit on the adaptive programming language of DemeterJ.

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. Turn in all the files you modified.

Please send mail to 

csu670-grader@ccs.neu.edu 

if you find something not clear in the Laboratory Guide.

From a happy user of the Demeter/C++ lab guide. The DemeterJ 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!
 
...

PART 2:

We simulate a few Operating System file management commands by building our own data structures that represent what the commands do. You are not allowed to use Runtime and Process objects in this homework. Your program should be able to handle inputs like:

mkdir a
mkdir b
cd a
mkdir c
mkdir d
cd d
mkdir x
cd ..
cp -r d e
cd ..
echo "before du"
du
find . -name x -print
and produce the same output as UNIX, except that the disk usage command "du" prints only the file structure and no sizes. More precisely, your program should be able to execute a list of commands as defined by the Extended Backus-Naur-Form (EBNF) grammar below (in Demeter class dictionary notation):
The full cd is also here:
Full cd in file os.cd.

FileSystem = CompoundFile EOF.
File : SimpleFile | CompoundFile common < f > FileName.
SimpleFile = "simple".
CompoundFile = "compound" < contents > PList(File). 

Commands = List(Command) EOF.
Command : Simple.
Simple : MakeDirectory | ChangeDirectoryUp | ChangeDirectoryDown |
RecursiveCopy | DiskUsage | Find | Echo |
SymbolicLink | RemoveDirectory | CreateEmptyFile | RemoveFile.
MakeDirectory = "mkdir" DirectoryName.
ChangeDirectoryUp = "cd ..".
ChangeDirectoryDown = "cd" DirectoryName.
RecursiveCopy = "cp -r" < source > DirectoryName < target > DirectoryName.
DiskUsage = "du .".
SymbolicLink = "ln -s" < from > FileName < to > FileName.
RemoveDirectory = "rmdir" DirectoryName.

// "touch f" creates an empty file called f.
CreateEmptyFile = "touch" FileName.
RemoveFile = "rm" FileName.

Find = "find . -name" DirectoryName "-print".

Echo = "echo" Message.

FileName = Ident.
DirectoryName = Ident.
Message = String.

PList(S) ~ "(" {S} ")".
List(S) ~ {S}.

Main = .
For the input
mkdir a
mkdir b
cd a
mkdir c
mkdir d
cd d
mkdir x
cd ..
cp -r d e
cd ..
echo "before du"
du
find . -name x -print
your program should produce an output similar to:
before du
      ./a/c
      ./a/d/x
      ./a/d
      ./a/e/x
      ./a/e
      ./a
      ./b
      ./
      .
./a/d/x
./a/e/x
It is important to do this part in stages. You get full credit if your program works correctly for the sublanguage that includes the commands
Simple = MakeDirectory | ChangeDirectoryUp | ChangeDirectoryDown | 
DiskUsage | RecursiveCopy | Find | CreateEmptyFile.
You get a 10% extra point credit for this homework if you implement the full language. It is recommended that you start with primitive commands like CreateEmptyFile, MakeDirectory, ChangeDirectoryUp, ChangeDirectoryDown. Hint: Try to use the CopyVisitor of DemeterJ to do the copying:
CompoundFile{

traversal toAll(UniversalVisitor) { to *;}

public CompoundFile copy() {{
CopyVisitor cv = new CopyVisitor(CompoundFile.class);
this.toAll(cv);
return (CompoundFile) cv.get_return_val();
}}

}
Another example: for the input
mkdir a 
mkdir b
cd a
mkdir c 
mkdir d 
cd .. 
du
your program should produce the output:
      ./a/c
      ./a/d
      ./a
      ./b
But you should write your program in such a way that it would be easy to add the rest of the commands. Note that RecursiveCopy, DiskUsage and Find have significant common behavior that you should factor out.

You should focus your efforts on correct inputs as described by the above grammar. It is not important that you give a good error message in case of a syntax error. But your program needs to handle correct inputs correctly.

To represent your file system you may want to use Java classes similar to the ones described below.

FileSystem =  CompoundFile EOF.
File : SimpleFile | CompoundFile common  FileName.
SimpleFile = "simple".
CompoundFile = "compound"  PList(File).

Solution approach: Use the data-binding approach of DemeterJ to generate Java class definitions and parser and pretty printer from a schema (class dictionary).

Start with the DemeterJ program in $SD/hw/4/os-phase1-better and add more behavior files to the program. For illustration purposes, I constructed a FileSystem-object manually and I printed it out with the display method.


PART 3:

This part is about capturing edge traversals. For the traversal: "from Commands to FileName" started on a Commands-object, we want to print the names of the classes of objects that are traversed and that have an outgoing edge to a FileName-object.

The program in os-phase1-better approximates what we want but it prints too much:

 object visited Commands command_list Command_List
 object visited Command_List first Nonempty_Command_List
 object visited Nonempty_Command_List it CreateEmptyFile
 object visited CreateEmptyFile filename FileName
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it CreateEmptyFile
 object visited CreateEmptyFile filename FileName
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it MakeDirectory
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it MakeDirectory
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it ChangeDirectoryDown
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it MakeDirectory
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it MakeDirectory
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it CreateEmptyFile
 object visited CreateEmptyFile filename FileName
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it RecursiveCopy
 object visited RecursiveCopy source FileName
 object visited RecursiveCopy target FileName
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it ChangeDirectoryUp
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it Echo
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it DiskUsage
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it CreateEmptyFile
 object visited CreateEmptyFile filename FileName
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it Find
 object visited Nonempty_Command_List next Nonempty_Command_List
 object visited Nonempty_Command_List it CreateEmptyFile
 object visited CreateEmptyFile filename FileName
It prints too much because it should only print lines like:
object visited CreateEmptyFile filename FileName
object visited CreateEmptyFile filename FileName
..
The above output is produced by:
// File incomingToFileName.beh

// print all objects that have one or more FileName parts
// and that are visited during the traversal
Commands {
{{
  void incomingToFileName(TraversalGraph where) {
    FileNameVisitor cV = new FileNameVisitor();
    where.traverse(this, cV);
  }
}}
}

FileNameVisitor {
{{
  void before(Object source, String s, Object target) {
     System.out.println(" object visited " + 
     source.getClass().getName() + " " +
     s + " " + 
     target.getClass().getName());
   }
}}
}
You need to modify this program so that it prints only the lines where the target object is a FileName-object. You are not allowed to use any explicit conditional statements. The documentation for this capability is a little hidden in the DJ API. Study method "invokeMethods" in the Visitor class. "n" is either "before" or "after".


PART 4:

Consider the following design task: Given a relation over domain {0,1} of at most rank 3, determine the variables that are forced. For example, Or(x) forces x to 1. if x=0 the relation is guaranteed to be unsatisfied.

The relation given by the truth table:
(nothing means 0)

xyz
000
001
010 1
100
011
101
110 1
111

forces y=1 and z=0. E.g., if y=0, the relation is guaranteed to be unsatisfied.

xyz
000 1
001
010 1
100
011
101
110 1
111

forces z=0.
Design a function forced: int forced(int relationNumber, int variablePosition) which returns 0, 1 or -1. -1 means that the variable at variablePosition is not forced. 0 or 1 means that the variable at variablePosition is forced to 0 or 1, respectively.