SlideShare a Scribd company logo
Active Software Documentation
using
Soul and IntensiVE
Johan Brichau, Kim Mens,
Coen De Roover, Andy Kellens,
Roel Wuyts

Département d’Ingénierie Informatique - Université catholique de Louvain

Monday 30 March 2009
Documenting Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}
public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

}
public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

2
Documenting Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}
public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

}
public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Calls to database

2
Documenting Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

(open + (read/write) + close)

Factory Methods
- Must be used to create
objects consistently

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

Calls to database

2
Documenting Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

Calls to database

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

2
Documenting Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

Discovering and
Documenting
Regularities
Architecture
Idioms
Patterns
Conventions
public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

Bugs
Bad smells

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

Calls to database

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

2
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

Calls to database

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

3
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

*/

}
public void run()
{

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

}

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
);
frame.setUndecorated(true);
JLabel label = new JLabel("."); label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");

*/

Calls to database

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

}

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

3
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

?

public class HappyNewYear implements Runnable
private
private
private
private
private

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

*/

}
public void run()
{

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
);
frame.setUndecorated(true);
JLabel label = new JLabel("."); label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");

*/

Calls to database

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

3
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

*/

}
public void run()
{

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

}

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
);
frame.setUndecorated(true);
JLabel label = new JLabel("."); label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");

*/

Calls to database

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

}

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

4
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

*/

}
public void run()
{

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

}

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
);
frame.setUndecorated(true);
JLabel label = new JLabel("."); label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");

*/

Calls to database

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

}

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

4
Evolving Software...
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());























































}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

?

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}

private
private
private
private
private

*/

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

}
public void run()
{

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}
public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
);
frame.setUndecorated(true);
JLabel label = new JLabel("."); label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");

}

}

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}

public class HappyNewYear implements Runnable

*/

Calls to database

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

}

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

4
Software Erosion Problem
• Architectural drift and design decay
• Documentation of source code becomes inconsistent
• Original design structure and architecture is eroded
import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;

*/
public class FileDownload {
public static void download(String
address, String localFileName) {
OutputStream out =
null;
URLConnection conn =
null;
InputStream in = null;
try {
URL url
= new URL(address);

source code
v1.0


public static Long copyFile(File srcFile, File


throws IOException {


InputStream in = new
FileInputStream(srcFile);


OutputStream out = new
FileOutputStream(destFile);


long millis =
System.currentTimeMillis();


CRC32 checksum = null;


if (verify) {



new CRC32();



checksum.reset();


}


byte[] buffer = new
byte[bufferSize];


int bytesRead;


while ((bytesRead =
in.read(buffer)) >= 0) {



{



checksum.update(buffer, 0, bytesRead);






out.write(buffer, 0, bytesRead);


}

destFile)

checksum =

if (verify)

}

*/
public class HappyNewYear implements Runnable
{
private static
NumberFormat formatter =
NumberFormat.getInstance();
private JFrame
frame;
private JLabel
label;
private long
newYearMillis;
private String
message;
public
HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year

documentation
v1.0

time

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

5
Software Erosion Problem
• Architectural drift and design decay
• Documentation of source code becomes inconsistent
• Original design structure and architecture is eroded
import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;

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

*/
public class FileDownload {
public static void download(String
address, String localFileName) {
OutputStream out =
null;
URLConnection conn =
null;
InputStream in = null;
try {
URL url
= new URL(address);

/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







source code
v1.0


public static Long copyFile(File srcFile, File


throws IOException {


InputStream in = new
FileInputStream(srcFile);


OutputStream out = new
FileOutputStream(destFile);


long millis =
System.currentTimeMillis();


CRC32 checksum = null;


if (verify) {



new CRC32();



checksum.reset();


}


byte[] buffer = new
byte[bufferSize];


int bytesRead;


while ((bytesRead =
in.read(buffer)) >= 0) {



{



checksum.update(buffer, 0, bytesRead);






out.write(buffer, 0, bytesRead);


}

if (verify)

}

source code
v2.0


public static Long copyFile(File srcFile, File


throws IOException {


InputStream in = new
FileInputStream(srcFile);


OutputStream out = new
FileOutputStream(destFile);


long millis =
System.currentTimeMillis();


CRC32 checksum = null;


if (verify) {



new CRC32();



checksum.reset();


}


byte[] buffer = new
byte[bufferSize];


int bytesRead;


while ((bytesRead =
in.read(buffer)) >= 0) {



{



checksum.update(buffer, 0, bytesRead);






out.write(buffer, 0, bytesRead);


}

destFile)

checksum =

// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;

*/
public class HappyNewYear implements Runnable
{
private static
NumberFormat formatter =
NumberFormat.getInstance();
private JFrame
frame;
private JLabel
label;
private long
newYearMillis;
private String
message;
public
HappyNewYear(JFrame frame, JLabel label)
{

destFile)

checksum =

if (verify)

}

this.label = label;
// compute beginning of next year

*/
public class HappyNewYear implements Runnable
{
private static
NumberFormat formatter =
NumberFormat.getInstance();
private JFrame
frame;
private JLabel
label;
private long
newYearMillis;
private String
message;
public
HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements

// store argument GUI elements
this.frame = frame;

*/
public class FileDownload {
public static void download(String
address, String localFileName) {
OutputStream out =
null;
URLConnection conn =
null;
InputStream in = null;
try {
URL url
= new URL(address);

*/
public class HappyNewYear implements Runnable
{
private static NumberFormat formatter =
NumberFormat.getInstance();
private JFrame frame;
private JLabel label;
private long newYearMillis;
private String message;
JLabel label)
elements

next year
GregorianCalendar();
cal.get(Calendar.YEAR) + 1;

this.frame = frame;
this.label = label;
// compute beginning of next year

public HappyNewYear(JFrame frame,
{

// store argument GUI

this.frame = frame;
this.label = label;
// compute beginning of
Calendar cal = new
int nextYear =

}

documentation
v1.0

documentation
v1.0

time

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

5
Active Software Documentation
Software Documentation

import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

Discovering,
documenting and
enforcing
Regularities
Architecture
Idioms
Patterns
Conventions
public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Bugs
Bad smells

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

- After state change
- Follows DB protocol
(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

Calls to database

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

6
Active Software Documentation
Code Software Documentation
CCallssadatabase
au to l
Queries
Lin state change
- Afterk
Discovering,
import java.io.*;
import java.util.zip.*;

/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}

documenting and
enforcing
Regularities
Architecture
Idioms
Patterns
Conventions
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Bugs
Bad smells

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

(open + (read/write) + close)

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}

}

- Follows DB protocol

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

6
Small programs vs. Large projects
import java.io.*;
import java.util.zip.*;
/**
* Command line program to copy a file to another directory.
* @author Marco Schmidt
*/
public class CopyFile {

// constant values for the override option

public static final int OVERWRITE_ALWAYS = 1;

public static final int OVERWRITE_NEVER = 2;

public static final int OVERWRITE_ASK = 3;







// program options initialized to default values
private static int bufferSize = 4 * 1024;
private static boolean clock = true;
private static boolean copyOriginalTimestamp = true;
private static boolean verify = true;
private static int override = OVERWRITE_ASK;
































public static Long copyFile(File srcFile, File destFile)

throws IOException {

InputStream in = new FileInputStream(srcFile);

OutputStream out = new FileOutputStream(destFile);

long millis = System.currentTimeMillis();

CRC32 checksum = null;

if (verify) {


checksum = new CRC32();


checksum.reset();

}

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


if (verify) {



checksum.update(buffer, 0, bytesRead);


}


out.write(buffer, 0, bytesRead);

}

out.close();

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

if (verify) {


return new Long(checksum.getValue());

} else {


return null;

}
}



















public static Long createChecksum(File file) throws IOException {

long millis = System.currentTimeMillis();

InputStream in = new FileInputStream(file);

CRC32 checksum = new CRC32();

checksum.reset();

byte[] buffer = new byte[bufferSize];

int bytesRead;

while ((bytesRead = in.read(buffer)) >= 0) {


checksum.update(buffer, 0, bytesRead);

}

in.close();

if (clock) {


millis = System.currentTimeMillis() - millis;


System.out.println("Second(s): " + (millis/1000L));

}

return new Long(checksum.getValue());
}

























/**
* Determine if data is to be copied to given file.
* Take into consideration override option and
* ask user in case file exists and override option is ask.
* @param file File object for potential destination file
* @return true if data is to be copied to file, false if not
*/
public static boolean doCopy(File file) {

boolean exists = file.exists();

if (override == OVERWRITE_ALWAYS || !exists) {


return true;

} else

if (override == OVERWRITE_NEVER) {


return false;

} else

if (override == OVERWRITE_ASK) {


return readYesNoFromStandardInput("File exists. " +



"Overwrite (y/n)?");

} else {


throw new InternalError("Program error. Invalid " +



"value for override: " + override);

}
}






















public static void main(String[] args) throws IOException {

// make sure there are exactly two arguments

if (args.length != 2) {


System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");


System.exit(1);

}

// make sure the source file is indeed a readable file

File srcFile = new File(args[0]);

if (!srcFile.isFile() || !srcFile.canRead()) {


System.err.println("Not a readable file: " + srcFile.getName());


System.exit(1);

}

// make sure the second argument is a directory

File destDir = new File(args[1]);

if (!destDir.isDirectory()) {


System.err.println("Not a directory: " + destDir.getName());


System.exit(1);

}

// create File object for destination file

File destFile = new File(destDir, srcFile.getName());











// check if copying is desired given overwrite option
if (!doCopy(destFile)) {

return;
}







// copy file, optionally creating a checksum
Long checksumSrc = copyFile(srcFile, destFile);

















// copy timestamp of last modification
if (copyOriginalTimestamp) {

if (!destFile.setLastModified(srcFile.lastModified())) {


System.err.println("Error: Could not set " +



"timestamp of copied file.");

}
}























}

// optionally verify file
if (verify) {

System.out.print("Verifying destination file...");

Long checksumDest = createChecksum(destFile);

if (checksumSrc.equals(checksumDest)) {


System.out.println(" OK, files are equal.");

} else {


System.out.println(" Error: Checksums differ.");

}
}












































}

*/
public class FileDownload {
public static void download(String address, String localFileName) {
OutputStream out = null;
URLConnection conn = null;
InputStream in = null;
try {
URL url = new URL(address);
out = new BufferedOutputStream(
new FileOutputStream(localFileName));
conn = url.openConnection();
in = conn.getInputStream();
byte[] buffer = new byte[1024];
int numRead;
long numWritten = 0;
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer, 0, numRead);
numWritten += numRead;
}
System.out.println(localFileName + "t" + numWritten);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ioe) {
}
}
}

/**
* Print a message to standard output and read lines from
* standard input until yes or no (y or n) is entered.
* @param message informative text to be answered by user
* @return user answer, true for yes, false for no.
*/
public static boolean readYesNoFromStandardInput(String message) {

System.out.println(message);

String line;

BufferedReader in = new BufferedReader(new InputStreamReader(


System.in));

Boolean answer = null;

try

{


while ((line = in.readLine()) != null) {



line = line.toLowerCase();



if ("y".equals(line) || "yes".equals(line)) {




answer = Boolean.TRUE;




break;



}



else



if ("n".equals(line) || "no".equals(line)) {




answer = Boolean.FALSE;




break;



}



else



{




System.out.println("Could not understand answer ("" +





line + ""). Please use y for yes or n for no.");



}


}


if (answer == null) {



throw new IOException("Unexpected end of input from stdin.");


}


in.close();


return answer.booleanValue();

}

catch (IOException ioe)

{


throw new InternalError(



"Cannot read from stdin or write to stdout.");

}
}

public static void download(String address) {
int lastSlashIndex = address.lastIndexOf('/');
if (lastSlashIndex >= 0 &&
lastSlashIndex < address.length() - 1) {
download(address, address.substring(lastSlashIndex + 1));
} else {
System.err.println("Could not figure out local file name for " +
address);
}
}
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
download(args[i]);
}
}

}

public class HappyNewYear implements Runnable
private
private
private
private
private

*/

{
static NumberFormat formatter = NumberFormat.getInstance();
JFrame frame;
JLabel label;
long newYearMillis;
String message;

public HappyNewYear(JFrame frame, JLabel label)
{
// store argument GUI elements
this.frame = frame;
this.label = label;
// compute beginning of next year
Calendar cal = new GregorianCalendar();
int nextYear = cal.get(Calendar.YEAR) + 1;
cal.set(Calendar.YEAR, nextYear);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
newYearMillis = cal.getTime().getTime();
// prepare a message
message = "Happy " + nextYear + "!";
}
public static int determineFontSize(JFrame frame,
int componentWidth, String fontName, int fontStyle,
String text)
{
int fontSize = componentWidth * 2 / text.length();
Font font = new Font(fontName, fontStyle, fontSize);
FontMetrics fontMetrics = frame.getGraphics().
getFontMetrics(font);
int stringWidth = fontMetrics.stringWidth(text);
return (int)(fontSize * 0.95 *
componentWidth / stringWidth);
}

}
public void run()
{

}

}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent event) {}
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
{
System.exit(0);
}
}
public void keyTyped(KeyEvent event) {}
}
);
frame.setUndecorated(true);
JLabel label = new JLabel(".");
label.setBackground(Color.BLACK);
label.setForeground(Color.WHITE);
label.setOpaque(true);
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().setFullScreenWindow(frame);
final int fontStyle = Font.BOLD;
final String fontName = "SansSerif";
int fontSizeNumber = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, formatter.format(88888888));
int fontSizeText = determineFontSize(frame,
Toolkit.getDefaultToolkit().getScreenSize().width,
fontName, fontStyle, "Happy 8888!");
label.setFont(new Font(fontName, fontStyle,
Math.min(fontSizeNumber, fontSizeText)));
new HappyNewYear(frame, label).run();

boolean newYear = false;
do
{
long time = System.currentTimeMillis();
long remaining = (newYearMillis - time) / 1000L;
String output;
if (remaining < 1)
{
// new year!
newYear = true;
output = message;
}
else
{
// make a String from the number of seconds
output = formatter.format(remaining);
}
label.setText(output);
try
{
Thread.sleep(1000);

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

7
Small programs vs. Large projects
;

()

ce

ar
Ye
ew
yN
pp

ic
bl
pu

cl

s
as

Ha

an

impo
impo rt ja
va
rt
*/
java .io.*;
/**
.uti
public class FileDownload {
l.zi
/**
* Co
p.*;
* Command line program to copy a file to another directory.
ge
* @a mmand
public static void download(String address, String localFileName) {
* @author Marco Schmidt
import java.io.*;
*/ uthor line
t.
OutputStream out = null;
publ
Marc prog
*/
import java.util.zip.*;
*/
ma
ic
o Sc ram

r
public class CopyFile {
clas
hmid to co
public class FileDownload URLConnection conn = null;
{

s Co
t

/**
// constant values for the override option
py
Fo
pyFi
a fi
InputStream in = null;


* Command linepublic static finalfile to another directory.
program to copy a int OVERWRITE_ALWAYS = 1;
le
er
public static void download(String address, String localFileName) {
le

{
to

* @author Marco Schmidt
import public static final int OVERWRITE_NEVER = 2;
java.io.*;
try { OutputStream out = null;
anot
mb
//

*/
import public static final int OVERWRITE_ASK = 3;
java.util.zip.*;
her
*/
co
Nu

dire
URL url = new URL(address);
publ nsta
;
public class CopyFile {
conn
null;
ctor
nt

=
public class FileDownload URLConnection = new=BufferedOutputStream( ce()
{
publ ic st


/**
// program options initialized to default values
// constant values for the override option
va
y.
out

InputStream in = null;
publ ic st atic lues fo
r


* Command linestatic int bufferSize = 4 * another directory.
private public static finalfile to 1024;
program to ecopy a int OVERWRITE_ALWAYS = 1;
an
public static void download(String address, String tlocalFileName) {
atic fina
ic

new
)


* @author Marco Schmidt t
private static boolean final inttrue;
public static clock = OVERWRITE_NEVER = 2;
stat
l r the
try { OutputStream out = null;FileOutputStream(localFileName));
ns

//
ic final int OV over


*/
private static boolean final int OVERWRITE_ASK= =true;
public static copyOriginalTimestamp
3;
el
pr
fina int
at
conn = url.openConnection();
ri
etI
ER

priv ogra
URL conn
l in OVER WRIT de op

public private staticrm
class CopyFile boolean verify = true; ab
{
*/
URLConnection url ==new URL(address);
null; t.g
m
priv ate st opti
t OV WRIT E_AL tion
in = conn.getInputStream();
l
o // constant values for the override option 1;



private static int override initialized to default values ;
// program options = OVERWRITE_ASK;
outin new BufferedOutputStream(
= = null; orma
ate
ERWR E_NE WAYS

atic ons
f
priv
InputStream buffer = new byte[1024];


private static int bufferSize = 4 s*
public static final int OVERWRITE_ALWAYS ) 1;
=
st
in
ITE_ VER = 1;
l
byte[]

F new FileOutputStream(localFileName));
{ private static boolean final inttrue;1024;
priv ate st atic int bu itia
ASK
t
=



public static Long copyFile(File e
public static clock = nt File destFile)( 2;
r
b srcFile,OVERWRITE_NEVER = +
try {
= 3; 2;
ber
priv ate st atic boolea ffer lized
);
r
int numRead; url.openConnection();
e
ma




private static throwsLacopyOriginalTimestamp d= =true;
public booleanIOException {
static final int OVERWRITE_ASKa R)
3;
Size to
ate
bo
conn = Num
at
n
URL = =
RY 

stat ic bo olean clock = 4 defaul
J
em true; yea en EA



boolean verify
or private static InputStream lin = new FileInputStream(srcFile);
long numWritten url0; new URL(address);
publ
ol
ic
co
*
= =
UA




 rF
private static OutputStream initializedFileOutputStream(destFile);
// program options = OVERWRITE_ASK; Y
int
l default values 
,
ic
int ean ve pyOr = true 1024 t valu
e
*/ while inter conn.getInputStream(); != -1) {
out

stat
;
es
s; privatelongoverride=out = new =to r.1024; AN
((numRead == new BufferedOutputStream(
in.read(buffer))
over rify igin ;



e
static int
System.currentTimeMillis(); 
xt
byte[] buffer = new byte[1024];
me millis bufferSize Ca4 * ;

ic
ride
new FileOutputStream(localFileName));
= alTi
Long
mat
li
mb e; public staticraCRC32 GUI




private static boolean clockan true; ) destFile) 
Long copyFile(Filenull; = daFile .J
checksum = ne
srcFile,
out.write(buffer, 0, numRead);

= OV true; mestam
copy
{
i en { r
for int numRead; url.openConnection();



private static throws IOException ea ar ); = true;
boolean
f if (verify) { f copyOriginalTimestamp 
e 
 Nu r
 m el; Mil ;
ERWR
p =
a

conn numWritten += numRead;
=
File
ITE_
true
;



 bl



private static; InputStream in checksum =1new CRC32();

 nt e boolean verify = new FileInputStream(srcFile);
Y d
o gor l true;

long numWritten l) 0;
=
thro (File
ASK;
;
mat
in = conn.getInputStream();
me
g
a
ic f 
ab ar 
 e
e,



private e am l; goverride aoutt= en ,

 static OutputStream = checksum.reset();
int
OVERWRITE_ASK;
new FileOutputStream(destFile);
()

); 

Inpu ws IO srcFil
For }
while byte[] labe = in.read(buffer)) != -1) {
((numRead
n re ( System.currentTimeMillis();
ra
um fr be long millisC=nex al TH 0
nn
Ex



}
at ame l Ye sa

yl
buffer = new byte[1024];
me
Outp tStrea cept e, Fi
ber
System.out.println(localFileName + "t" 0, numWritten);
le
C ON

CRC32
new

 St
a Long checksum = srcFile,
rg static buffer G=get , byte[bufferSize]; 
 Ti
Ru 
 st Fr
 bel n
 w mes 
 (JF public=byte[] nni w copyFile(Filenull;Y, File
 destFile)
e
out.write(buffer, + numRead);
long utStre m in ion { le de
int {el
Numame;
stFi
nab } catch (Exception; exception)abnumRead;

 s

e



r

 a
intlbytesRead;
if (verify) { , IOException {
throws
t

t
M A ;
CRC3 mill am ou = new
is
le)

 0) {
numWritten
n
= e i 
 e al. EAR Tin.read(buffer)) >=ge= new ";
a
is
t e J JLa ng ng
JL

); e);





= H F_ _D = new FileInputStream(srcFile);
CRC32();
on
Run
nts
if 2 chec = t = ne File
long numWritten =
tice fr bel; illexception.printStackTrace(); 0; += numRead;
e
re ame l whileg ((bytesReadInputStream in )checksumFileOutputStream(destFile);
nt
f
Inpu
me
= 
 c Y OutputStream out); {)


o

 b
if (verify) new
checksum.reset();
Y
o 
ta m
i
e
M
z
va
 t
a
} me, while e((numRead = in.read(buffer)) != -1) {
"! checksum.update(buffer, th(bytesRead); 
 (verify)ksum System.cw FileOu tStr
ON Y_O 
 OF 0 0 = ( .
nts
;
r
l
_ E

}
0, i
el

e
e s}Fra l l ear ge;
em 
 ri va te 
 tr 
 New 
 st .f
 abe t
 al = ar. .Mlong millis, = System.currentTimeMillis();nt 

finally {
{ = null urre
tp ea
r ar() + 1;
e 
fra
eme
System.out.println(localFileName + "t" 0, numWritten);
+ numRead);
ntTi utSt m(srcF
ng tS
;
i




 y



byte[] bufferU=}new byte[bufferSize];
CRC32 R
}
DA checksum = null; +
vat e J abe newYessa
pl 
 p pri iva ate e S
d
GUI
,
meMi ream ile)
yea out.write(buffer,
try e{
mpl

nd bytesRead; NUT N , m
p
// his s.l mpu r c ear eintdar r. H(verify) D{ Ti 

 ar 0, bytesRead);






if O out.write(buffer,
byte
am
le on .
llis (des ;
m
priivat e JL ng }g catch (Exception nexception) e{ t alen EAR)
im
m m
numWritten; += numRead;

e checksum 0)e{ e,
t
r i
(); tFil
)
e t e;
int [] bu



t


i co da }tY al while a r. MI CO= in.read(buffer)) >= a= new CRC32(); t. , f s()
;
JFr
pr
 iv vat 
 Hap
chec
if n x n!= null) {
(in C
.Y
e);
en ((bytesRead
ff
pr ivat e lo rin
r
exception.printStackTrace(); RY
whil bytesR er
tY checksum.reset(); 

 ex e c
Yea




 th

 n
out.close();
(verify) { fr Na
gum ram } ;
x C l
 nd a
 r. SE .ge if x

chec ksum
ar(
t)
p
 ri
UA
l g of oria ndarr); Ain.close();
e (( ead; =
ksum = ne )

t l i


p

c



in.close();

}
t
pr ivat e St } finally {e ar = f abeSystem.out.println(localFileName + "t" + numWritten);
New
a . al e 
e 
by
l
e
// a
 e ne if ( (Ca ale end dbyte[] buffer =nnew byte[bufferSize]; checksum.update(buffer, 0, bytesRead); tesR new byte
wYe
i
ex
g
J N
.res E CR


et t C 
 {
l




l
/ ty h
me fon
Pw
ppy
[buf
}
et() C32(
or ame try {nnin Gre (Cal tYeadar. ;
g
a cmillis+ out.write(buffer, 0, bytesRead); ap

ead
pr ivat
t .s
 e(clock)al en int rbytesRead;= }System.currentTimeMillis()
 - Smillis; h(t
yNe
C 
n
b
=

p
);
ferS CA
Ha
;





i
= in
t
)
} st r (Exception exception) n{null) {
catch
ra
w if nex !=
sa "
C
ize]
i al .s et( (} al end = ((bytesRead
 = in.read(buffer)) >=2+ 
nt Gr
pu 

.rea
pr
S ;
Hap






while s
System.out.println("Second(s):*" 0) { t
// is.f abel beg= ne l.ge ,(out le H, !=; null) {
if (in 1)

dt
ass
o (millis/1000L)); ;
a T
d(bu
JFif g
n
C l 
s
}
exception.printStackTrace();
{ 
i
if





 c al } .s et out.close(); me y
f
 e
_E
cl
ff

 e( ri (verify)h{
i
t
.l
lic

th finally e{al = ca YEAR H, CMON , 0 out.close();
out.
in.close();
p
l s t( {a l






 c
if (verify) C
in.close();

0,
;
gW

 z
t , 
 .g checksum.update(buffer, os bytesRead); }
 (verify) )) >= 0)
VK er
} his ompu c r
h)
pub
lic
in.c cl e(
St }
t







t (clock)
{
O T
ca
al. .se if ( Mil e a{Hapreturn newiLong(checksum.getValue());; in

c
ar. M} N OF_ DAY
e()
}
a

id me me )
ar
t.
{
dt
if lose );
pub
{
tS h,






} l
System.currentTimeMillis() - i

millis = out.write(buffer, t tr
se
t. n
c else .{ 
 ar ar 
 "
} catch (IOException ioe) );
// lend xtYe lendtry. { AY_ _OF_ (out != tTim
{} { ouvewrit
ar D
tW Na ra
 n 0, bytesRead);
W millis; 
 (clock();
*/
if 0 {
null) {







 ca l 
return n System.out.println("Second(s): " + (millis/1000L));
) {
t
e(bu
if (in
null) {
=

e p }

)
Ca t ne t(Ca lenddar. HOUR TE, 0); ).ge != ;out.close();
en nt f fo .s
Fo null;
E
}
ng
ff class chec






}
} e e out.close(); 
 d
,
}
ca ewY pr(verify) {
ksum
in.close();
nt t) ey public er, 0, by HappyNewYear implements Runnable
on fo = (
 cs * i
ne i
in l.se t(Ca len dar. MINU ND, ime(



}


if
in.close(); W
}
yle
.upd
if
"!"
n / 
 sag if (clock)n{ 

 return mp t( cs ics}
 i 5 str
{
tesR
ve ven = K
r 9




new Long(checksum.getValue());
mi e t
ate(
ca l.se t(Ca len dar. SECO}etT } r +

 (verif
tSt
t
ead)
i
mill
r n )
}
buff
/ }s



public static Long createChecksum(File { 


IOException on
{
c millis t System.currentTimeMillis() -} millis; y) t e e = Syst is =
;
private static NumberFormat formatter = NumberFormat.getInstance(); r. l.g
catch
ioe) {
e else file)ethrows 

 returno null; r = r e 0. /
er,
{
f n
else
ca l.se } (Ca len (IOException tifa(out != null)t { o
m
;em Sy






long millis = System.currentTimeMillis(); et Me tM publ h

 et po xt
Ye
0,
t
a

 () {
= w FSystem.out.println("Second(s): " + (millis/1000L));nt ()
byte
private JFrame frame;
}
n a
ic

en
d FileInputStream(file); t on 
* dt stat






InputStream in = }
}
new m
ca l.se t(Ca le{d = c ge
);
e, inout.close(); );
0) .out.pstem.cur
ex
} r
sRea
tM
te 
rint
re
ic




}

CRC32 checksum t ifc(verify) { ze ne n on f
= newo CRC32();
Ev Eve har
e
 i
d);
th(ize
ramame,
private JLabel label; public static void download(String address)lis essa + n
}
t(tu
ln(" ntTi
ne
re
ca l.se t(Ca
Long
}




checksum.reset();

return new Long(checksum.getValue());
}
Seco meMi
rn
ey ey yC
int lastSlashIndexa = address.lastIndexOf('/'); e f tN
in t else file) ithrowsfo etF = Siz 

 W
eng tS
ng 

 S =
t
crea e
xi
t
ne {
se Mil a m py "
private long newYearMillis; }
nd(s llis
c




public static Long createChecksum(File { nt t

byte[] buffer = nnew ibyte[bufferSize];IOException {n
}r
teCh (K
am on
}
()
s g return null;
t.l fon
):

.e return t) w Long(c
if (lastSlashIndex al.Year are catch (IOException fioe) {
)
; is long ecksum K Ke
d (
th nt ne
" + - mi






int bytesRead;i 
long millis = System.currentTimeMillis(); 
ic
t
c >= 0 && } "Hap
JFr
private String message;
heck
texle, s().
fonew n ric
S}
(mil llis
n null;
gs
() yL Inpu emiediset e tem






while ((bytesRead = in.read(buffer)) >= d0)fo po 
InputStream in = 
 fo FileInputStream(file);
{
s ll (Fil
sum.
i
e prep =
at
lastSlashIndex n< waddress.length() i e(t { ng
lis/ ;

public static void download(String gaddress) { tS- z1) ri

t =t
getV
fi
;





}

CRC32 checksum checksum.update(buffer, 0, bytesRead); me Ke
CRC32(); )( om
CRC3 tStreag =
2 / Sty hic + 1));
1000
e
ve
ar
es eas . m Syysle) th
}
gW t c
S
st checksum.reset();newet
alue

download(address, address.substring(lastSlashIndex ext)
// ss
in on

 tM
ch




}
; L));
chec in S
on th,
());
public HappyNewYear(JFrame frame, }JLabel label) lastSlashIndexa = address.lastIndexOf('/'); * font Grap
int
]
Pr ecl2 um t ksum = stem.cur rowse IO
in inthrows IOException { Fra ew
h
)
ks
t
t
ybyRe[] en.r





public in.close(); createChecksum(File r
static Long
byte[] buffer =
 new byte[bufferSize]; 
 g[
file)
new
F n
ic
}
} else { (lastSlashIndex >= 0 && neFWid
te
me
J (n
Exce
h(t
(
Fi ere
{
if
get
me
in
Wid e,






if
int
long millis = st
System.currentTimeMillis();
n
ke eyt byv buffeset()= new CRvlen ntTimeMi ption
bl (clock) { bytesRead;Fo = in.read(buffer)) >=
 0) {
e

rmi t
Inpu

 i
i t
System.err.println("Could notnt Nam me. local dfile name for " +
ra






 pu 
while ((bytesRead tin= rnnew FileInputStream(file); r millis; kwhil( tesR er = ;
InputStream
System.currentTimeMillis() e
E C32(); tStreallis() {
ew // store argument GUI elements
lastSlashIndex <dete nen
address.length() e figure out
1)

 millis =

public static void download(String oaddress) { pon-font {fra nt); ringW
tr
id
);








CRC32 checksumu checksum.update(buffer," 0, nbytesRead); 
 f e ((by ead; new by
System.out.println("Second(s): n + (millis/1000L));
;
(f
m(fi ;
ey
t)
in et = new CRC32(); 
mp eaddress);
tesR
}
te[b
this.frame = frame;
download(address,com t( s = (fo st
address.substring(lastSlashIndex + 1));
le);
(S
= te






}
}
checksum.reset();
vo id ini
th)
ER

int lastSlashIndex = xaddress.lastIndexOf('/');
ow
ead (K
int co
r
}
=






return new Long(checksum.getValue());
in.close(); 
 buffer = new byte[bufferSize]; s
byte[]
=
d in.r ufferSize]
in 

 me Li
Wid
c vo if .c{ se
this.label = label;
} else { (lastSlashIndex t>= 0 e&&
NT
Fontric rics ics.*
h,
lo
nd
ic int ing
e
if
i
t
E
{
ng



}


if (clock) {
int bytesRead;
ead(
z
(clo ();
;
}
ma } ra ey
e
t
). Wi
ew
dt ;
// compute beginning of next year
System.err.println("Could Metr figure i
}
yp
chec buff
ck
sta
bl c





while ((bytesRead = in.read(buffer)) >= 0) { i
millis = System.currentTimeMillis() -
 millis; )
.C
}

Str
lastSlashIndex ntSi = n ontM ntMe ntnot .95 { tr out local file name for " +
< address.length() - 1) s
f K
ksum er))
PE)

t( en
wi ))



/**




 
System.out.println("Second(s): " 0, (millis/1000L));
id checksum.update(buffer, + bytesRead); { eyT
pu ubl
f Fo
ts
.upd >=
Calendar cal = new GregorianCalendar();
/** d
fo nt s address); * 0 h /
lic
}
SCA
en re
download(address,fo
address.substring(lastSlashIndex + 1));
h,
). 88
t
ate( 0) n



* Determine if data is to be copied to } 


}
given file.
k
vo
me * ad
public static void main(String[]1;
p
retu
pub
buff {
int nextYear = cal.get(Calendar.YEAR)else args) {intnt fotric ge h = Size Widt
}
+
K_E
dt
ta er, onm lSc
e( 88



* Take into consideration override option Long(checksum.getValue()); Determ


return new
 and
in.close();
; ;
rn
{}
{
ra e.
*
newid
for (int i }
t.V
d
ic




* ask user in case file exists and override option{is ask. F am* Take ine
}

if (clock)
r0, l
wi
ns
iz 88
" ll K
FoSystem.err.println("Could not figure out local filet)
; mi) is) ;
cal.set(Calendar.YEAR, nextYear); = 0; i < args.length; i++) i{ tfont nent
}
tMe
if
as
n name for " +

) { ven


* @param file File object for potential
destination t

millis = System.currentTimeMillis() - millis; e) ". stC E) Sy
vo Long(check Sy A em.o= st Co
a file J r * @p k usinto consdata
vi Fu bytesRead) enS (88
download(args[i]);ingWt)( po
).
Fon
er
cal.set(Calendar.MONTH, Calendar.JANUARY);
evevent KeyE
g
u l( m.B



* @return true if data is to be copied 
 file, st
/**

to
false if not f System.out.println("Second(s): "c+ (millis/1000L)); L IT ut.p nem.c
su
ider is to
* @r aram
in
r()
, ; e at
En et
e(
tr (in com address); )
t
{
e
}
{ */ etur file case atio be ico

e =



*/
* Determine if data is to be copied to given file.

}
; c re
irintlnurs nts
H
tr e . getV
cal.set(Calendar.DAY_OF_MONTH, 1); }
public static void main(String[] args) n{ s n
me cr m
=
iz
n tr
ven nt
rgs
ten
bl
publ



public static boolean considerationfile) 
return new Long(checksum.getValue());ue File file} n over pied d( ab or .W alue() l) i("Se. TimeMi
* Take into doCopy(File override option and

}
 { lic
Sw
}
) co
i ttur
exis
);
(); Lis
ra S r
to L
cal.set(Calendar.HOUR_OF_DAY, for (int i = 0; i < args.length; i++) { [] a
0);
nS
eyE Eve r()
l or
if objectpu ts ride e gi




* ask user in boolean exists = file.exists();
}
case file exists andboverride option is 
ask.ic st
}
t( be ph e( nd(s):ll"isget .fo
data
t
atic
re
);
an opti J veo
}
ame Key
(f () r
;
d(K Key Cha
ee



* @param file if (overridefor
 OVERWRITE_ALWAYS || !exists) {
File object == potential destination file
cal.set(Calendar.MINUTE, 0);
download(args[i]);
is for po d a w on (an olle
ing
pu return true; 

bool
en la ra ic
r over Cnd fi
to
t(0
JFrnew
,




* @return true
if data is to 
be copied to file, false if not
/**
"; ze .+ (milmillScr ");
))
sse ed( Key
ean
be tentia ride (C ; . nm d( lG v ;
li is
Str
{
co e d
e D if Si () te me s/10;00
doCo
copi n l n d op )
exi
; cal.set(Calendar.SECOND, 0); }




*/
* Determine if data 
} else
is to be copied to given
 file.
Pre eas get
t)
public static void main(String[] args) { ain(
ti
newner(
py


de = edou destinat ligis ad oca nD OL er nt kit mat ra get 88! L));ex




public static boolean considerationfile) {
* Take into doCopy(File override option and
if (override == OVERWRITE_NEVER) {
bool (Fil
em.
to n ue on .
()
key Rel nt.
newYearMillis } cal.getTime().getTime();
=
m

{}
for (int i = 0; i < args.length; =iste {
i++)
fi
if ean n e l le r ou file, Aion ) ask. ee B
L





* ask 
user in boolean exists returnoverride
 option is ask.
case 
file exists file.exists();
= and false;
r l fa ( file r . sS Fo ol or (f ). 88 , eT
yst
me L
ce } // prepare a message
id key (eve
id
(oveU exis kg {
S
) r

n

ls





* @param file if (overridefor OVERWRITE_ALWAYS || !exists) {
} else File object == potential destination file
nt)
vo d
vo
download(args[i]);
etrrbe ts = gfi e(t ta e e et c nt an ne o f ze (
fra ey

le iz
id c
if
} el s





* @return true
if 
data is to be copied to 
 true;
if (override == OVERWRITE_ASK) {
return
file, false if not
ta
eve
ic voi if
ic
message = "Happy " + nextYear + "!";
o
r OV u .e
me addK
.se la tBea+== e qERleonxiPan t.g ltSnot "S mi ltT e, Si kit ppy ty tS
l
s
t

}
a
if
F
t





*/

} else
return readYesNoFromStandardInput("Fileeexists. " o
}

(ove
sts(
WR
{
n
}
pubblic
sta
} tIn
JFrame.
= ter au yl ont ol Ha ntS fon





public 
static boolean doCopy(File file) { 
if 
(override ==
OVERWRITE_NEVER) {
"Overwrite ; 
 m el rrid tF pa iz ITE_AL ); u =
ven
) (y/n)?");.se e O r ent meWAfa
}

}
ra ase
e






} else { 
boolean exists return false;
= file.exists();
pu
eyE
lic
fr
t
tu
fif elb l se == OV o t ren De YS e e de Def tSt eF tTo , " fo , ;

}







} 
else if (override new OVERWRITE_ALWAYS || !exists)L(o Invalid se+ tHERon ro rn trl || m
throw == InternalError("Program error. e l. "
{
.g
}
d(K
pub
WRIT
y ueNa !e= st t on in ul le , er )

t
ve e
xi
{

 J ab rr " .
public static int determineFontSize(JFrame frame,







if (override ==
OVERWRITE_ASK) {

return "value for override: id + override);i E_NE
true;

at
ype


 l
e exists.
b el . e C " re ge tSt t ; r .ges)f{ rm fa ty me mb n(
{






}

} else
return readYesNoFromStandardInput("File ==s et nv+ tu VER) n

int componentWidth, String fontName, int fontStyle,
}
rm
eyT
OV E
rn o { e t
} el la (y/n)?");
n fa
, te e S Na u ru



}



if (override ==
OVERWRITE_NEVER) {
"Overwrite ab el .g ERWR

*/
se

d k
Fo
;
String text)
);
{
f K) umb
l ab me ics ITE_ASo g f lse; ki me de etD ont nt zeN ).




return false;

} else { 
pu

L;
voi
ER)
er
public class HappyNewYear implements Runnable
ame





public static void main(String[] args) throws IOException blic new InternalError("Program error. Invalid "return { N ol Na =


} else
throw st
+
g f fo i el
l ra ph

 {
}
nt n read

 ri ze
00 {
atic
ENT
} blic
mb

 






// make sure there are exactly 

OVERWRITE_ASK) {
if (override ==
two arguments
"value for override: " + ioverride); To nt
(fr
;
t. , ( tS b
f ra
{
);
void
t Si
int fontSize = componentWidth * 2 / text.length();

 }
10
s.C






if (args.length != 2) {

return readYesNoFromStandardInput("File exists. " +YesNoFxt ki me nt on la
Nu
.
dow

ue) "." K);
pu
G
fo e romS a o f
private static NumberFormat formatter = NumberFormat.getInstance();

al 
thS n ne




}



System.err.println("Usage: main(S

CopyFile SRC-FILE-NAME DEST-DIR-NAME");
"Overwrite (y/n)?"); t
ant

t() Win
= ); /
Font font = new Font(fontName, fontStyle, fontSize);
*/
(trbel( BLAC E);
ta
T ol tNndarF n( e,
l row w
tr






} else System.exit(1);
{

in a fo
nst
private JFrame
// ing[]
men een
Inte e To
ted La
IT
dI
r.
FontMetrics class HappyNewYear implements Runnable
public fontMetrics = frame.getGraphics(). frame;

er is( me)
h,




public static void main(String[] args) throws IOException { new InternalError("Program f in

}

throw
error. Invalid " +iz al on "Oveminput("

rn
ew rwri am Fi
if make su args)
gCo
ron lScr
)
oraew J Colo r.WH
t

Er
private JLabel label;
t
f (n






// make sure the sure there are exactly a
readable file (args. re
// make 
source file 
is indeed two arguments 
"valueth
for f
override: " + override);ror( h. fr
l
in
idt ;
le
vi ul
ec n
getFontMetrics(font);
{
tS
s
lo
at ll ti
d(
(
leng there rows in
t "Progr te (y/n)? exis/







File
 srcFile = new File(args[0]);
if (args.length != 2) {
}
(Sw l); sEn

be
).w 8))
private long newYearMillis;
IO
Undl NumberFormat.getInstance(); etF
on
th SRC-FILE-NAME DEST-DIR-NAME"); n "valar am
at

*
"); ts. "
nd
int stringWidth = fontMetrics.stringWidth(text);
private static NumberFormat formatter e= = rounnd(Co;

rm Mi }





}
if (!srcFile.isFile() || !srcFile.canRead()) {

System.err.println("Usage: CopyFile ; != are ex Except f
}
*/ set
ent abe hic .s
ze( 888
la
+ o
2)
Fo M Yeue for error.
io

;o 1;
creturn (int)(fontSize * 0.95 * private String message;





 

System.err.println("Not a readable file: " ) srcFile.getName());{
System.exit(1);
+
f ime is
//
{ actly t n
h,
private JFrame frame;
); ame. lab ackggrou rue) ignmdd(l Grap ce()
,
nSi 888
over Inva
two
et
public class HappyNewYear implements Runnable

e(





public 
 
static void main(String[] args) throws IOException File ke su
}
System.exit(1);
{ ma
()
se +
el
ts
idt
ride lid { t tT ll
Syst in
ew
argu s

ame ree (8
fr abel setB ore ue(t alAl ).a ocal Devi
componentWidth / stringWidth);
private JLabel label;

sr file
: "
"a n
if






}
// make sure the sure there are exactly a readable cF recth
// make source file is indeed two arguments n
me
);
a R)
Syst em.err
r of r);
{ l. label)
ab
en
).w
; e(fr etSc rmat
m +e Mi
F aq
( tL en
l. nts pyN
;
+r

Y
public HappyNewYear(JFrame frame, JL e
a d A
em.e .pri e

 (!srcF ile = e sour
over r






// make sure the if (args.length != 2) directory
File srcFile = new File(args[0]);
second argument is a {
e(
private long newYearMillis; JLabel set Op
ta ne
ont ane ge re BOLD rif" iz .g .fo
JL

em}
p
xit( bntln

private static NumberFormat l.
NumberFormat.getInstance(); r
AR

sile.isFi w Fi ce fi
e E
ye lr n Yng
a






File destDir = new File(args[1]); !srcFile.canRead()) {
if (!srcFile.isFile() ||

System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME");
S
)
Fo rr ride);
Siz
t
.
c
a
e
{
.

lab formatterz = tP
ri
el

private String message;
Ha
l1); ("Us
}






if (!destDir.isDirectory()) {


System.err.println("Not a n
System.exit(1); I readable le() le" + le is
file: (arg srcFile.getName()); age:
NU
a .
t be ni
e,
private JFrame frame;GUIlabe l.seetHo nten ment ultS Font ansSeFont kit(atte e, reen
er ;cu Ye s;

in
s[
// store argument
elements o
||
w
Copy







}
System.err.println("Not a// ma et
System.exit(1);
directory: " + destDir.getName()); deed
publicCmai r); .JA
n static void main(String[] args)
exnum dar


!src 0]);
am
UI
be l.s tC
on efa =
e ;
"S min Tool orm fram etSc ");
mb em. new lli
File
ke
private JLabel= label;
a
n a n
G






}
// make
 sure the source file is File g a readable file Syst File
System.exit(1);
indeed su
u am l
neread
la
ir D
this.frame
frame;



fr
);
re
if t.
{ ri ee a r ;
public HappyNewYear(JFrame frame, e e.ge Enlabel) tyle me = ter ult e, f ze( ).g 888!
able
Syst em.err .canRe






// make sure the second new File(args[0]); (! destDi the
File srcFile = argument is a directory
e; SRC-FIN yst = e( rMi e;
t
le
r

 }
private long newYearMillis;b JLabel v get S
;

la m
LE-N b
o l
of ghe a(r Ye da 1)JFrame frame = new JFrame();
t))
em.e .pri ad()
e ; n; ;

 a dest r =
a
this.label = label;
file ls
l
8
de fa
Si t(






File destDir for destination file
if (!srcFile.isFile() ||
cs
;

 // create File object = new File(args[1]); !srcFile.canRead()) { second
{
Dir. new
ab
r! e ee e
a
xit( ntln ) {
ic S f lAME a ) g
e,
()

 m
private Stringbeginning of fra phiyear font ontNr = etDe tSty Font olki ppy yle, zeTex
C
am u umg m ; g ret (at xt en , )
isDi File argu






File destFile = new File(destDir, srcFile.getName());
if (!destDir.isDirectory()) {

System.err.println("Not a readable file: " + srcFile.getName());
1);
("No
// compute message;
next
ST

fa
s
nn
m
at = e lng ; YeDE1 a-DIR yea tr ssa fra bel in om rm ne al TH 0 frame.addKeyListener(new KeyListener()
or
}
yl
rect " + destDir.getName()); t a
me
// store argument GUI elements g f be .g fon ne To "Ha St Si
me
G t






System.err.println("Not a directory:or (args[ nt
System.exit(1);
Fr
g

Gra

-N =
C N ,
read
//
=u
R
r e e e
st Fe a bni nt w < s
y()) 1]); is a
Calendar cal = new frame;
intrin
St
rF
Ti
mi lt
nt nt






 
// check 
if copying is desired given overwrite option
}
System.exit(1); crea
w ge
a
me
this.frame = GregorianCalendar(); m lkit
w J AME");r = la inn efr .fo R, , MO AY 0);{

dire
e u
Syst {
public HappyNewYear(JFrame frame, JLabel Nlabel)me, er au le, fo fo
r ableefitim mai tpu ng m
r(
t
s
J
al
; ;
nt






if (!doCopy(destFile)) { sure the second argument is te directory
}
// make
be File de a File
;
et
ctor
H

int {}
n l
g g ar . A T; _ D 00;
ne r e
Syst em.err
public o void keyPressed(KeyEvent {event)nextYear = cal.get(Calendar.YEAR) ToontNa det tDeftSty me, er, ;
this.label = label; fin al SntSiz + 1;
y ea
nt






 

// create File return;for destination file stFile ob
File destDir = new File(args[1]);
object
at g e le:e L ou g ni g { / eaYea t r= ame l = be in te YE t) OF F_ 10) ; .g
() ze)
f
!"
um // ch
em.e .pri
= tc . uN _ O ( ) )
r J " +nsrin
cal.set(Calendar.YEAR, nextYear); yearfo
"
N
= ne
in fo







}
File destFile = new File(destDir, srcFile.getName()); ject fo
if 
(!destDir.isDirectory())if
{
wY
w
xi
t
r
me
nt
a
iv n t e l i cFile./ Yew pu o fr be e tr a r p O Y _ p, 0 (
public voidgth Si
keyReleased(KeyEvent event) // compute beginning foftGUI elements xt = t.ge fon ntNa Numb un()
{
t
eck
w Fi r de
// store argument next
+




 
+ destDir.getName()); lo ng ng ma
o
= System.err.println("Not a directory: " t(1); ln("None
e
.
r
a
getN
Ne n amt s s. la ut cS lrm = da ut M DA UR eTE , me
e
le

 (!doCo if co
le(d stin
pr iv vat te St
in
t
in
eTe
== KeyEvent.VK_ESCAPE) frame;
Calendar cal = new GregorianCalendar();i
py
nt
en if (event.getKeyChar() cal.set(Calendar.MONTH, Calendar.JANUARY);olk ame, nt(f tSiz el).







// copy file, optionally creating r checksum overwrite(d pyin
// check 
if copying is desired given
a
System.exit(1); option
estD atio
this.frame =
ar
}
a
n p a directpr rlo tri (re
py
l
// h))
ou e(i ;s. mp ra fo r en (o r. HO sNU ND Ti
m
i a e
e, ,
ar
ir,
cal.set(Calendar.DAY_OF_MONTH, 1); tSiz To ntN 1; o on ab

n
)estFilg is de






Long checksumSrc } 
 copyFile(srcFile, destFile);
if (!doCopy(destFile)) { e
=
.l fo{ ).
int nextYear = cal.get(Calendar.YEAR) + w F f
ory: S v at
srcF file a
n
t
a
t
ei
ap
.
} hi co ke = Ye al xt d da r. dMI CO et
i
p r" f
e))
am me

this.label = label; f
l
sire
Ye






// create Filetreturn;for//
object
destination file
el
ile.
);
fo n
cal.set(Calendar.HOUR_OF_DAY, 0); on
xt , s(
{
d gi
+
l
n


t / ma dt xt (C Te e en da er. SE .g
co
olge
System.exit(0); cal.set(Calendar.YEAR, nextYear); year e .min( me,
a
ar
p ri vdest c H
fr Na





// copy timestamp
 of last modification
}
File destFilea = new File(destDir, ab
srcFile.getName());
xt
Long py fi
ve
// compute beginning
xt
retu
te le ic
Di
cal.set(Calendar.MINUTE, 0); int of next ont( th fra
C
n
/ a
*/
rm
{ / lepu e et et a al en hda r. al e ne

b e tName());
i r.getN
r
; 1;n ovYo o
p


if (copyOriginalTimestamp) o{
checl le,
e nt
E)
rn;
er
cal.set(Calendar.MONTH, Calendar.JANUARY);

 
/ Sty aph } (te
op
new
t
ame(






// copy 
// check f (!destFile.setLastModified(srcFile.lastModified())) { w dwrite
file, optionally creating a checksum Sr tion
if if copying is desired l ksum overwrite option() +
given s
C nt
/ ut .s .s ( (C al en da c ag +
bl
//
T
e;
am fo
cal.set(Calendar.SECOND, =0); GregorianCalendar();
APCalendar cal
etF Ma ear(
t

 
));
al
N not set " option
} 2 nt Gr
cal.set(Calendar.DAY_OF_MONTH, s
1);
r

 if { t
r
L;






Long checksumSrc = copyFile(srcFile, pySystem.err.println("Error: Could )
(!doCopy(destFile)) if co be ti

{
destFile); c = co ly
+
s
i
o al el e et (C al en = ss "
{
Y
pu
th
SC int cal.getTime().getTime();

(cop 
 meen
newYearMillis = nextYear = cal.get(Calendar.YEAR) + 1;

 
el.
pyFi cra ti
a
a yO mstam
* fo et
d R) ppy Y
JF ng

return;




000
ar le eaA"timestamp of copied file.");
public void keyTyped(KeyEvent event) {} cal.set(Calendar.HOUR_OF_DAY, 0); yNew
R
c al
} ab . .s et (C al is me y
{
id

 
_E
;
cal.set(Calendar.YEAR, nextYear);
); / 1
lab

 JL
e lT







// copy timestamp of last modification rigina p of ye en (src ngaa A
} rm }
e( i
// prepare a message
se
pp
c al

l ry .s et (C ll a pp
HN
gW

th , .g
o
z}Str
VK
cal.set(Calendar.MINUTE, 0); Calendar.JANUARY);
h)
l YE FileU checks
imes last




}
if (copyOriginalTimestamp) { 
 ,
is( e)
)
el
cal.set(Calendar.MONTH,w Ha

el
c
t al .s et Mi e Ha

 rF copy file, optionally creating a checksumCmp r. di s A , dest um
ta (
message = "Happy " + nextYear + "!";

id me me ); in
Si );
t.
dt





//
xt nna ) mo ); J
ill tim
s; if (!destFile.setLastModified(srcFile.lastModified())) {
fi
File

s
I
me
ne
c
{ al .s ar ar "
cal.set(Calendar.DAY_OF_MONTH, 1);
{} { ven cal.set(Calendar.SECOND, 0);
);





 

// optionally
be Long file i

verify checksumSrc = copyFile(srcFile, destFile);a {r a . tion

System.err.println("Error: Could not set " +
tW Na ra nt tr
;
nt h,
Wi
a}
a nd a de
meM }
ne iru 
if (!l r ca ;
GU
newYearMillis = cal.getTime().getTime();
c al Ye ep =




 
if (verify) u
 me ;

{m

 fr

r
"timestamp of copied file."); ,
ll
)
en nt f fo .s
;
Fo dt frame.setUndecorated(true);
E
cal.set(Calendar.HOUR_OF_DAY, 0);
ng
st
e ec a )
tTi lis

// t





 


 e

 N
 ra //l copy timestamp of last modification id l 
 tc nd File.s
}
destination file...");
c ew pr ge
;
JLabel fo s = s( =cs * ri
// prepare a message
nt nt) Key
opti e
MiSystem.out.print("Verifying of o o a } iY e , 1 ); et
e (copyOriginalTimestamp)mon; ;
on ( label i new t JLabel(".");
ne tWi
n = createChecksum(destFile);
()
le
renrMil
e if (v
cal.set(Calendar.MINUTE, 0);
e
e
b r Long checksumDest er { ally g v g C ex
i





 


 l
}
if
Last e
n / sa
ur a
e
public vstatic int determineFontSize(JFrame frame, + "!"; se;
mp t c ic r 5 s
m
ty
Modi
label.setBackground(Color.BLACK);
message = "Happy " +
ic e
 f la 
 ea ag (checksumSrc.equals(checksumDest)) l{al TH 0
am 
ume ra ify) n veri t( nb
ab





if
if (!destFile.setLastModified(srcFile.lastModified())) {
e ve = int componentWidth, String nextYear 0); al
el i c r
rm en
cal.set(Calendar.SECOND, int fontStyle, em.cewYe
/ es
fi
Y
co Fon tri etr Met 0.9 /
fontName,
{ iG e
g
Ti Syst ed rc
tS
fy pu C System.err.println("Error:(s
F







// l
nn 
 tat am

optionally verify rfile r
 f
 } b nnSystem.out.println(" AOK, files are equal.");Could not set " +
} e =
a
ON Y,
w
 ss 
 else
te on t)
file
m
;
)
w
= f
newYearMillis = cal.getTime().getTime();t= (n
s
); ;
em.e File of n
ys
= label.setForeground(Color.WHITE); ent nt ()






 Ru

 s


et 
 ; "timestamp .l ocopied file.");)
r if e(verify) { }r(J { a

 = 
l i ubl l.g AR, H, _M D ;

w Me tM nt * th
rr.p
de mp ex
ne 
me
0)String text) prepare a message
label.setOpaque(true);
r(
ar
as
ne
ond
= S g
v e
_ ) file..."); "!"
h( ze








b

System.out.print("Verifying destination ; Checksums differ."); ri f tM
} g
System.out.println(" Error: .

a
//
eLong checksumDest a= createChecksum(destFile);g
e JF L
} g g 
a
s

nt
od
ze ne ont Fon fo ze Wid
t co t
{ E Ev har
me l = be =p c .YE ONTSyOFemF 0 )
t(
t ln("Er ifiet Si
wYe
ne


sec





st
y
g
public static int determineFontSize(JFrame frame, +timeainint;
{
at te J /*n in } wYe } tor ra
 (checksumSrc.equals(checksumDest)) 0{ e()
Long _O.o,
nt 
f t = Si nt
message = "Happy " ne nextYear g "!"; u
+
1)
Si = label.setHorizontalAlignment(SwingConstants.CENTER);
in t ng
e
Y_
+
ror: d()))
o


xi



}

 r
!
in
of ;
te
int {} int
fontSize = componentWidth * 2 / text.length();
Ke ey C
;

en ont ul {
if
s . if b e al = r .M DA UR checut, rim
String fontName, int utp g
Co .


me 
 riv va te l*SPr
TE ks.p
nt nt cs frame.getContentPane().add(label);Key
c in tri



}

 *
//e optionally f 
 a
t
ge th nt ne
remo fontStyle, yeartrue ge;
.e } Font font componentWidth, lean fontStyle,g fontSize); <
(che D umi nt(" OK,
er ng)
on
)
)
; Lis
e e,
}
)
d( d(K t
N (verify) s{ verifytfile ar nda ar .System.out.println("Vear files are ,equal."); .l "timesta d not
c
U ck TDe
f (

 O
l n
l
m
= text)
;

 
 le
s
)
o
m
in




if
} pu
e
N t
d o o
a
p i va te * st intpy
fo fo ri GraphicsEnvironment.getLocalGraphicsEnvironment(). String new Font(fontName,
ti
S )
t
w
umb ini
nt
mp ) set
// hi .
 else { Ye le nd ar System.out.println(" eError: gChecksums
}
p

 
.HelIN O sumS sttY rifyin ra m
te
an p a
lo ring m



/**

 r

eFontMetrics fontMetricsboo frame.getGraphics().ain { / ne ar = mess
rg
tof co " +
e( Key
ss ase .ge
Wi )(f omp
*
=
ta
m
a message p ri va ate
 @par dard messaglinesis com dar tSystem.out.print("Verifying destination file..."); differ.");e, ics
M createChecksum(destFile); N
r
 .= seEC ge rc.equ=alcreate fdest a
ex l
e nrema
E nt t et
s{
d
a
m
t h from
x
getDefaultScreenDevice().setFullScreenWindow(frame);

i
h
pied
s



* Print

 to standard * 
 Ha and in
output

d ;
t y h
/ wYe
x
i
ev
Ch t
public static int determineFontSize(JFrame t (re
=
p yes or no et am 
 readentered. }
s(
S frame,
re e t
Ca le n a } r
ng nt c
x Long checksumDest S { .
pu e to
) getFontMetrics(font);
Sy
AP i on tM

file
ra w
te
m t at(
e ecks in



* standard input until r iv */}@r(y ur messag t unt stan en e if (checksumSrc.equals(checksumDest)) {checks n um ation /

t p
int (fontSize = componentWidth * 2 if text.length(); put
/
[]
{)
p r pu c or n) is e by / lda n 
( Ca ale end da r. al ge ne
."); }
ne t
yP el en
i
ri final int fontStylee = Font.BOLD;
n us
F on
ic
Sym

um
fi S
e
ar 
SC
nt
JF n



* @param message informative text to be
answered til user rd et (
}

fro orm
run int = fontMetrics.stringWidth(text); int fontStyle,
c System.out.println("fo Dest(destFil tequal."); th(
info / a
a stem OK, files 2are le..ra
int stringWidth componentWidth, String fontName, fontSize);
bl
ng
ou
k yR ev
st (
F
final String fontName =e "SansSerif";
bl
ou
am
Ye@return user answer, p 
foriic stat 
 er forswer rmCyeivt .no tput (C Cal len nda = sa +


* 

true bl yes, false an no. at s or s } telse {
_E
ve
w r(
ng r.f
id Font font = new Font(fontName, fontStyle,
String text)
e
Fr ng .out )) { ne); G ."); d
Sy
* f
E}
ri
return (int)(fontSize fr 0.95 =
*
"

ic
, tr in e l
t rn int fontSizeNumber =id ke f (
s System.out.println(".o .printlno et differ."); );
pu



/**


 bo

 tand
ew */

tri tte
determineFontSize(frame, c vo FontMetrics fontMetrics * frame.getGraphics(). }
VK
ne ene
);
a te .s(y e ( a e
(
(J istem Error: Checksums gWi
ol and
pu
{li
("
ey
St
ut.p h ,



 yN public static boolean readYesNoFromStandardInput(String linesl from{or tn)(C ad l is m
* Print 
a message 
 standard output ean read uemessage)s e
to

);
in etu
h
to
cfoa xt } be re a line e y
componentWidth / stringWidth);
.g OK n
a Sorma
r
t
= Toolkit.getDefaultToolkit().getScreenSize().width, getFontMetrics(font);
t.

;
vo oid i {
ER

 {
re
ow
ze tr
t
nt m



 p

 
* standard input until yes or no (y or n) is Ye

System.out.println(message); ad entered. . s an isCenll s fr pp
}
(K
n(
dtrie lne ; ,ifile dt
put
r
l
,
c yes,
a
f
k
pub
is
00)
{} { {ven
NTint . fontSize = componentWidth * 2 / text.length();e
Syst sN by a l. se t( i tere
nd





* @param message 
}
String line;
informative text to be answeredoFro user false swered e d.Ha
Si , S
ed
ai
ap
me yLfontName, fontStyle,}formatter.format(88888888)); font = new th
t Erro
out
intCEstringWidth = fontMetrics.stringWidth(text); { / ma t =
Wi Nam ra (" ) trr: s i e eq
ic c v
c mS . e forM rby " om
W ar
(

Font Wi
Font(fontName, fontStyle, fontSize);
p
(10
m
a
H
St for no.
t
{




* @return userBufferedReaderfor = newfalse ri em.out a l InputStreamReader(
answer, true in yes, BufferedReader(new
ual.
g
ta
nt h
s r no user
f n s
/
Ch
bl i
() n
t) ) yE
.
pu
}
i );
ext
int fontSizeText l= determineFontSize(frame,
return t(int)(fontSized *
");
e
Buff ng li .pri and .
eep
Ty
fr dKe



*/
/**

 
System.in));ne c nt ardIea pa . =
s 
en nt = fo s.
FontMetrics fontMetrics =
in ecksums

Fo dt
ypublic static void main(String[] args).w 8) 0.95 * frame.getGraphics(). ut
id
l np
ered
pu ub
n ee
o
ln frome
ts
en nt Ke
etT
els
.sl


 as


public static boolean readYesNoFromStandardInput(String lines es ut(Se
* Print
a message to = null; output and read c message) {
Boolean answer standard 
diff
Re ;
Toolkit.getDefaultToolkit().getScreenSize().width, componentWidthh,/ stringWidth);

on fo s s( ic * tr

ne Wi
ke
vo
me ad
ca (mY
getFontMetrics(font);
Bo (y or
p
me cr
er."
an
() 88
ev eve ==
l.s


l



* standard input until yes or no olea n) aderentered. prgeg tring
try
System.out.println(message);is in ew sa );
}
ead

c
dt

mp t( ic ic tr 95 s
);
S
messi nt
try
fontName, fontStyle,{"Happy ; 8888!"); st
}) );
d
c
ra e.
= user
on lint stringWidth = fontMetrics.stringWidth(text); be




* @param message informative text to n answ
{
String line;
be answered n ne sa
by /
ze 88
t
;
Thr


la y

rmage
{
co Fon tr etr Me 0. /
frame = new JFrame();wi
oi



* @return userBufferedReaderfor = newfalse=for no. / w s ff != null) {n e) )

answer, true while ((line erin.readLine())
in yes, BufferedReader(new InputStreamReader(
ti
JF ram


.
on
ic 
Si 888
label.setFont(new Font(fontName, fontStyle,
); "." ACK E); JFramevir Ful return (int)(fontSize * 0.95 *
()
en ent r()
0)
= nu

r
e Bu = ered te o xt {
t
h
v
a

t

 bl



*/


System.in)); ; m Syst line.toLowerCase();= w
ll line em

 
f
frame.addKeyListener(new e()
KeyListener()
e


Me tM on * t
gC
en (
L T
ue ( public static nvoid
er
Ev v a
t(
st
Math.min(fontSizeNumber, fontSizeText))); t main(String[] args)
dRead p e
.in) {

 u



public 
Boolean answer 
 null;

=
if ("y".equals(line)
"yes".equals(line)) { d

 
{

 static boolean readYesNoFromStandardInput(String message) ); mer(new || e ne nt on f
{
ic
e, re at componentWidth / stringWidth);
z

in {; sE se
tr el .B I
{}
zanswer
en
ey yE Ch
xi
ze W

p




try
System.out.println(message); 

 
t co t Inpu fo= Boolean.TRUE; i
new HappyNewYear(frame,l label).run(); .WH

 


ic
} ub
}
am Sc orm
Si
Sw l) ic ).
}
d( ab or {
= tr
Si break; tF = Si nt
st
(K (Ke ey
.e



 
 


{
String 

tS

 
in t ng

 line;
t)
p
bl
fr frame void JFrame();
new
)
t( be ph e( JFrame public = een keyPressed(KeyEvent event) {}
et f
te JL ol or



 




BufferedReader whilenew BufferedReader(new InputStreamReader( eah nt ne
in = ((line = while in ri
}
in.readLine()) != null) { s eamR t

 
nt t c ge de
em
); yLi } sed ed etK
;

 
c

 else
en
pu
; e( public void keyReleased(KeyEvent



 



 




System.in));((line = line.toLowerCase(); o o
line
r ;
en la Gra vic
gs
.g r.
r(
ra w (C ol
frame.addKeyListener(new KeyListener() event) {

 
fo f n r "no".equals(line)) {
st
))

 if ("n".equals(line)o || i
St ("y".equals(line) Wid"yes".equals(line)) { me( Ke
ti
ev
{



 



 


Boolean answer 
 null;
=
if = in
|| (f mp
es eas t.g
);
l e D; if" Siz void , Sc ") ift)(event.getKeyChar() == KeyEvent.VK_ESCAPE)
ar
co ne nd (C ; nm d( public static() te memain(String[] args)

 
t

a
Sy
.r
) c}
o
t







 


try 



Boolean.FALSE;
l en
t
nt nteaMeinanswer ntanswer = Boolean.TRUE;ra ew
Prpublic void run()
me
x
dL
ng = break;
]
de = rou und ue) lig .ad oca{ nD OL er {t kit mat ra get 88!

s
e
n
y {
a
e







 


{ 



 i o
break;
e( i
n
[
e B S on
f
T{

 
JF n

JFrame public void keyPressed(KeyEvent event) {}
JFrame();
F ont li !=)) (i nu
ve










 


while ((line = }
in.readLine()) ner null) {
ke yR ev
fr
;
tU el kg ro tr lA () tL re t. ns eF ol or e( (). 88frame = new keyReleased(KeyEvent
t = != ll
System.exit(0);
ng
ic 
 }
e, ze

w r(
boolean newYear = false;










 


}

else
else F line ifs("n line } ) { ri
= line.toLowerCase();
yE
d ke (
t y public }void
w(
R)
se ab ac eg e( ta ane .ge Sc on Sa in To f izframe.addKeyListener(new KeyListener() event) {
bl
 
 {
yl Si




ne e e











if ("n".equals(line) || .toLow || "yes".equals(line)) {
if ("y".equals(line) t
"no".equals(line)) n{
t r y".equal
doTE
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)
Ke
ki p
oi id if
do

} ; me. l l etB For aqu zon tP nt ult F " rm ult le, tS l{ ap tSt nt


h,
pu 
 

 

s(li(S= answer













 in tu
System.out.println("Couldtnot understand answer ("" +
answererCase = = Boolean.TRUE;
Boolean.FALSE; v
}
}


d(
. in
ne
()
) ra be .s et Op ri en me fa = = te fa ty on oo "H n fo
{N
E


dt );














 el e 
break; ) break;is
"").o Please {
use y for yeseor n for no.");

v
public {void keyPressed(KeyEvent event) {}
|| ; line + c
r
in



keyTyped(KeyEventSystem.exit(0);
event) {}
p
() nW
L
f La el .s et Ho nt on De le

me "yes". i c
.C











}
}
} if se
wi time ,= System.currentTimeMillis(); me de De tS eF tT , public void public void keyReleased(KeyEvent event) {
fo ,
long 8)

 

 
ma
}
equa


t}n
Ty
nt ree
h






}


{
else
else ("n"
b el .s et Co - time)
ey

ra Kanswer ubl li(l
ts
). 88 remaining =J (newYearMillisir et Sty tNa/ =1000L; min ul le e, ber ();


 
y
e

 
.equ d
long
}









if (answer == null) {


{
if ("n".equals(line) || d break; p = Bo ls in {

la ab el .s et nv g t n r .ge fo r fa ty m m n
b olea
dt
if (event.getKeyChar() == KeyEvent.VK_ESCAPE)

als(

an
e( 88
i li e f d "no".equals(line)) e)

 
ke
nm Sc

 









throw new IOException("Unexpected end ofanswer fromu stdin.");)understand answer ("" + ;


input = Boolean.FALSE;{
not
e


n o e t , ); e S Na u ru }
}
l ab el .g sE

ne
p
wi

 
st
8
iz String output;
vo System.out.println("Could n.TRUE;
{

 
);







}




 am ).a break;
line + ""). Please use y for yes or n for no.");
ro ll


frame.setUndecorated(true); keyTyped(KeyEvent event) {}
else

fo f mb ki me et etD ont nt zeN ).
;

"n
id
).
; ") CK );
if (remaining < 1) l ab me ic
public void

 
r e ||an o".e
on
nS 88
} 


 }






in.close();

}
{}
vi Fu
System.exit(0);

t ng Nu ol Na d JLabelo label = new JLabel("."); 00L
ic
g f f i el
l ra ph
qu

 
sw
vo
e(
e) ". LA TE
if 








return answer.booleanValue();
}
JF ram brea er = als(li
gC
, ee t(

 else t
{
En t

in tri ize To ont t = it. e, t( } tS ab
}
f ra

 
(a == null) {





 

}


if (answer nswe
Bool ne))
c
iz
k;

 { ta
f
ru el( .B HI
in ); cs .se
me Scr rma
label.setBackground(Color.BLACK); }
n l
10
r ==
catc 
i
ea from {
// new year!
);
t b answer
l S tS
w
a
} 
G
f ex lk am on fo





 }

catch (IOException ioe)


System.out.println("Could not
+
} 
 s
}
{
nS
h (I
nullthrow new IOException("Unexpected end of input n.FALS stdin."); understand or .W ("" S
{
; /
in.c
F ( frame.setUndecorated(true);
T o N label.setForeground(Color.WHITE);
d( Please use y for yeslorhi efor no."); (fr et .fo
} E;bl






{


}


line + ""). La l
na
OExc
( e p n ()
)
c
newYear = true; al on
r
e,
ee
lo
epti
);
()
retu 
 se
}








 

throw new InternalError(
} { li}
ze To ont ew min amJLabel label = new JLabel(".");
pu
fi in
te J Co lo
Syst
label.setOpaque(true); public) void keyTyped(KeyEvent event) {}
nt ab ra c
; e .g r , cr );
on in.close(); ();
rn

output = message; f
is ime
ioe)
em.o
))








 }


return answer.booleanValue(); ro stdin or write to stdout.");
} answ "Cannot read th
from
b w

Si
}
f nt
f (n h. fr
ra ew d( Co
me d(l lG evi ; if" Siz t() tte me tS !"
t
ut.p
er.b ==
o






}
}

if (answer oo null)u{
new
s
ll t
p
}
label.setBackground(Color.BLACK);
(
D
t t label.setHorizontalAlignment(SwingConstants.CENTER);
rint
nt
n ( ; n
a e 88
i
a
lean
ex
} );



}


catch (IOException ioe)


throw IOExce
new IOException("Unexpected end of input from stdin."); e) ig ad c nD L er nt ki ma r g
ln("
ec n ou nd
nd
frame.getContentPane().add(label);
Mi Valu{
on Ma ear
O
fo
f
8
thro }
Coul
label.setForeground(Color.WHITE);
ptio
}




{

eT
e();
frame.setUndecorated(true);
w ne
co
nd = r ou ru Al ). Lo ee .B sS Fo ol or e( ). 8 e, zelse
d no
me s
n("U

Y GraphicsEnvironment.getLocalGraphicsEnvironment().
tF
t






throw new w In
in.close();
InternalError(
nexp
{
label.setOpaque(true);
line t lun kg r
se
Ti li
tU e cders (t al e( et cr nt an ne To f iz t( py yl Si
tern
JLabel tlabel = new JLabel(".");
ew
se
ecte
in
g







return answer.booleanValue();
getDefaultScreenDevice().setFullScreenWindow(frame);
alEr "Cannot read from stdin or write to dstdout.");
" e tand
// make a String from the .number of seconds label.setBackground(Color.BLACK); of );
label.setHorizontalAlignment(SwingConstants.CENTER);
n il
se ab+Ba "). Pl e nt Pan t.g ltS Fo "S mi lt e, tS lki ap St nt
end
yN
l




}
}
ror(
r uea answ
final int fontStyle = Font.BOLD;
g
re arM


}

catch (IOException ioe)
e. l
pp
be
se
}of;input l et tFo paq izo nt ern ("" = = ter fau tyl on oo "H ont fo
output = formatter.format(remaining); frame.getContentPane().add(label);
e au
"Can
label.setForeground(Color.WHITE); in
}


{
er n
e
fr
final GraphicsEnvironment.getLocalGraphicsEnvironment().
String cur Ye
fontName = "SansSerif";
Ha
la
not
) ram be omsste tO or te usm y ffo e + me de De tS eF tT , f r, ;
}



throw new InternalError( ad
re
r
mb i
label.setOpaque(true);
n l le , e ()
a l. s din.
on e l a
w
int fontSizeNumber = determineFontSize(frame,
m. new getDefaultScreenDevice().setFullScreenWindow(frame);
fr read from stdin or write to fstdout.");. se ")H on r tD ty Nyes = et on i u




"Cannotom
or
label.setText(output); ne
nu ema
elabel.setHorizontalAlignment(SwingConstants.CENTER);
JL abe el . et ; C vi ge S t
stdi
f m a y e b n
(


}
r g n for r ."f t am m u
n or
st Toolkit.getDefaultToolkit().getScreenSize().width,
e;
l ab el .s et En
try
final =int fontStyle; =e;Font.BOLD;e (r

}
;
nt on be t. e, tenoDe );tS tN Nu .r
e
writ
th at
fontName,r!fontStyle, formatter.format(88888888));
Sy frame.getContentPane().add(label);
ls
l ab el .g s
}
e to
{L
final GraphicsEnvironment.getLocalGraphicsEnvironment().
fo g f um lki am de et fon on ize l)
a tru sag
1)
stdo
l ab me ic
00
= ing ; String fontName = "SansSerif";
fa
e
g
determineFontSize(frame,
N
f
N
om rm
ut."
Thread.sleep(1000); = int fontSizeText = ye = es = determineFontSize(frame,
int fontSizeNumber
l ra ph
<
10
nt rin ze Too nt = t. , t( ntS ab
);
fr fo
me in ut Toolkit.getDefaultToolkit().getScreenSize().width,
);
m getDefaultScreenDevice().setFullScreenWindow(frame);
w

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

st

n
tI

Manual exploration
& documentation
do not scale

Tool Support Needed

i
t i e
l
f ra}
i
Toolkit.getDefaultToolkit().getScreenSize().width,
r
; /
finale ear = fontStyle, "Happy; 8888!");
int
r.
fo ex lk am Fon (fo ,
ti ema utp ing fontName, fontStyleng= eFont.BOLD; 000
l St tS
ea
{ / n fontName, fontStyle,t)formatter.format(88888888));
() )
} G
o i
na l on
eT oo tN w in me
u
ri t
ng r intn finalY String fontName fontStyle,p(1
wY
/ w Font(fontName,t = "SansSerif";
label.setFont(new put
is me
fi ina f
iz T fon ne .m fra
tp
St a
lo ong ing ema fontSizeText = determineFontSize(frame,
intnefontSizeNumber m= determineFontSize(frame,
ne
ds
e
t
ll ti
Math.min(fontSizeNumber, fontSizeText)));
f nt
tS
ou
a or
l tr (r
n
le
ou Toolkit.getDefaultToolkit().getScreenSize().width,
on
t( th r(
Mi i
new HappyNewYear(frame,Toolkit.getDefaultToolkit().getScreenSize().width,
label).run(); .s
t(
on
e = fontStyle, "Happy 8888!");
S f
ea
} fontName, f
ec
on Ma ea
me is
f
k
fontName, fontStyle, formatter.format(88888888));
s
F
i
ex
i
ol
}
ad
Y
label.setFont(newa ut
f );
et
int fontSizeText etTdetermineFontSize(frame,
=
tT ill
{ / m p Font(fontName, fontStyle,
nt
bo o
re
ew
o g
s
n
i
N
Math.min(fontSizeNumber, fontSizeText)));
/ ut Toolkit.getDefaultToolkit().getScreenSize().width,
d
Th
l.
.s label).run();
re arM
public void run() er nin
py
l
o fontName, fontStyle, "Happy 8888!");
{
new HappyNewYear(frame,
p
be
ur e
mb i
{
} abe
Ha
}
la
.c wY
nu ma
se
l ry
boolean newYear = false; label.setFont(new Font(fontName, fontStyle,
w
em ne
el
e () re
t
Math.min(fontSizeNumber, fontSizeText)));
;
ne
do
st (
e;
{
void
! e; ge public th n t( run()
new HappyNewYear(frame, label).run();
ru a
Sy =
{
ls
ar ru sa
{
d
1)
om rm
}
;
= ing ;
fa
ye = t es
long time = System.currentTimeMillis();
o.
boolean newYear = false;
fr i fo
n ut <
0)
m
v
=
w
0
me i p g
;
long remaining = (newYearMillis - time) / 1000L;
do
r
ne ear =
ng r
10
ti ema ut in {
t)
ic e public void run()
}
String output;
t
ea
{
ri t {pu
o n
p(
bl t
// ewY pu
ng r g ai
if (remaining < = System.currentTimeMillis();
wY
St a
ee
long time 1)
ut
puo m
n ut
boolean newYear = false;
lo ong in em
ne
a f{ r
{
sl
(o
o
long remaining = (newYearMillis - time) / 1000L;
l tr (r
do
n
e
d.
}
xt
S f
String // new year!
output;
ea
ak t =
{
ea
Te
l
newYear = 1)
true;
i
r
t
if (remaining < = System.currentTimeMillis();
{ / m pu
long time message;
oo
Th
output =
se
t
b o
/ u
{
.
long remaining = (newYearMillis - time) / 1000L;
d
o
}
el
{
String // new year!
output;
} ab
else
newYear = true;
l ry
se
if (remaining < 1)
{
t
output = message;
el
{
)
{
// make a String from the number of seconds
}
n(
// new year!
else output = formatter.format(remaining);
ru
newYear = true;
}
{
output = message;
id
label.setText(output); from the number of seconds
// make a String
vo
}
try
c
else output = formatter.format(remaining);
}
{
li
}
b
{
Thread.sleep(1000);
pu
label.setText(output); from the number of seconds
// make a String
}
{
try
output = formatter.format(remaining);
}
{
}
Thread.sleep(1000);
label.setText(output);
}
}
try
}
{
}
Thread.sleep(1000);
}
}

}
}

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

7
Querying Code
- (Simple) name-based searches
- Mostly textual matching

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

8
Querying Code
- (Simple) name-based searches
- Mostly textual matching

- Name-based searches
- Limited use of
structural reflection

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

8
Querying Code
- (Simple) name-based searches
- Mostly textual matching

- Name-based searches
- Limited use of
structural reflection

PQL
ASTLOG

.....

.....

JTL
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

.....

- Full query language
- Based on reflection,
metaprogramming,
analysis, etc..
8
SOUL
• Smalltalk Open Unification Language
• Prolog-based query language

• Hybrid (Prolog + Smalltalk)
• Inter-language Reflection
• Metalanguage over Smalltalk base language

• Execute queries over programs
• using full structural and computational reflection
• using combination of logic and imperative paradigms
• Declarative Reasoning about the Structure of Object-Oriented Systems, Roel Wuyts. Proc. TOOLS USA 1998, pp.

112-124. IEEE Computer Society Press, 1998.
• Co-evolution of Object-Oriented Software Design and Implementation. Theo D'Hondt, Kris De Volder, Kim Mens & Roel
Wuyts, Proc. SACT 2000. Kluwer Academic Publishers, 2000.
• A Logic Meta-Programming Approach to Support the Co-Evolution of Object-Oriented Design and Implementation. Roel
Wuyts. PhD thesis, Dept. of Computer Science, VUB, 2001.
• Supporting software development through declaratively codified programming patterns. Kim Mens, Isabel Michiels &
Roel Wuyts. Journal on Expert Systems with Applications, Volume 23, Issue 4, November 2002, Page 405, Elsevier.
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

9
Meta Programming
“Standard” Program

SQL query

Queries
Expenses Database

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

10
Meta Programming
“Standard” Program

Meta Program

SQL query

Soul query

Queries

Queries

Expenses Database

Software Database
Compiler compiles programs
JavaDoc generates documentation for programs
...

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

10
Meta Programming
“Standard” Program

Meta Program

SQL query

Soul query

Queries

Queries

Expenses Database

Software Database
Compiler compiles programs
JavaDoc generates documentation for programs
...

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

10
Meta Programming
“Standard” Program

Meta Program

SQL query

Soul query

Queries

Queries

Expenses Database

Software Database
Compiler compiles programs
JavaDoc generates documentation for programs
...

Meta Program

Programs

Works with
Program
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Data
10
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Family Structure

parent of

Sophie

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Frank

11
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Frank
parent of

Sophie

parentOf(Joe,Mark).
parentOf(Joe,Sarah).
parentOf(Mark,Kevin).
parentOf(Christine,Kevin).
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Family Structure

SOUL representation
of the
family Structure

11
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Frank
parent of

Sophie

parentOf(Joe,Mark).
parentOf(Joe,Sarah).
parentOf(Mark,Kevin).
parentOf(Christine,Kevin).
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Family Structure

SOUL representation
of the
family Structure
Logic fact
11
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Frank
parent of

Sophie

parentOf(Joe,Mark).
parentOf(Joe,Sarah).
parentOf(Mark,Kevin).
parentOf(Christine,Kevin).
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Family Structure

Predicate
SOUL representation
of the
family Structure
Logic fact
11
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Frank

Family Structure

parent of

Sophie

SOUL Constant
parentOf(Joe,Mark).
parentOf(Joe,Sarah).
parentOf(Mark,Kevin).
parentOf(Christine,Kevin).
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Predicate
SOUL representation
of the
family Structure
Logic fact
11
SOUL: Prolog-based query language
Joe
parent of

Christine

Mark

parent of parent of

Kevin

parent of

Sarah
parent of

Frank

Family Structure

parent of

Sophie

SOUL Constant
parentOf(Joe,Mark).
parentOf(Joe,Sarah).
parentOf(Mark,Kevin).
parentOf(Christine,Kevin).
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Predicate
SOUL representation
of the
family Structure
Logic fact
11
SOUL: Prolog-based query language
Joe
parent of

Christine

parent of

if
Mark parentOf(?parent,?child) Family Structure
Sarah
Frank

parent of parent of

Kevin

parent of

parent of

Sophie
?child -> Mark
?parent -> Joe SOUL Constant

parentOf(Joe,Mark).
?child -> Sarah Predicate
parentOf(Joe,Sarah).
?parent -> Joe
SOUL representation
parentOf(Mark,Kevin).
of the
...
parentOf(Christine,Kevin).
family Structure
parentOf(Sarah,Sophie).
parentOf(Frank,Sophie).

Logic fact

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

11
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

Mark

grandparent of

parent of

couple

Sarah

parent of parent of

parent of

Kevin

Frank
parent of

Deduced
Family Structure

Sophie
cousin

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

12
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

Mark

grandparent of

parent of

couple

Sarah

parent of parent of

parent of

Kevin

Frank
parent of

Deduced
Family Structure

Sophie
cousin

grandparentOf(?grandparent,?grandchild) if
parentOf(?grandparent,?parent),
parentOf(?parent,?grandchild).
couple(?personA,?personB) if
parentOf(?personA,?child),
parentOf(?personB,?child)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL deduction
rules

12
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

Mark

grandparent of

parent of

couple

Sarah

parent of parent of

parent of

Kevin

Deduced
Family Structure

Frank
parent of

Sophie
cousin

logic rule

grandparentOf(?grandparent,?grandchild) if
parentOf(?grandparent,?parent),
parentOf(?parent,?grandchild).
couple(?personA,?personB) if
parentOf(?personA,?child),
parentOf(?personB,?child)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL deduction
rules

12
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

Mark

grandparent of

parent of

couple

Sarah

parent of parent of

parent of

Kevin

Deduced
Family Structure

Frank
parent of

Sophie
cousin

logic rule SOUL variable

grandparentOf(?grandparent,?grandchild) if
parentOf(?grandparent,?parent),
parentOf(?parent,?grandchild).
couple(?personA,?personB) if
parentOf(?personA,?child),
parentOf(?personB,?child)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL deduction
rules

12
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

Mark

grandparent of

parent of

couple

Sarah

parent of parent of

parent of

Kevin

Deduced
Family Structure

Frank
parent of

Sophie
cousin

logic rule SOUL variable

grandparentOf(?grandparent,?grandchild) if
parentOf(?grandparent,?parent),
parentOf(?parent,?grandchild).
couple(?personA,?personB) if
parentOf(?personA,?child),
parentOf(?personB,?child)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL deduction
rules

12
SOUL: Prolog-based query language
grandparent of

Joe

parent of

couple

Christine

grandparent of

parent of

couple

if grandparentOf(?grandparent,?child)
Deduced
Mark
Sarah
Frank

parent of parent of

parent of

Kevin

parent of

Family Structure

Sophie

logic rule SOUL variable
?grandparent -> Joe
?child -> Kevin
grandparentOf(?grandparent,?grandchild) if
parentOf(?grandparent,?parent),-> Joe
?grandparent
?child -> Sophie
parentOf(?parent,?grandchild).
SOUL deduction
rules
couple(?personA,?personB) if
parentOf(?personA,?child),
parentOf(?personB,?child)
cousin

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

12
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Software
structure

13
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

class(Node).
class(StatementList).
subclassOf(StatementList,CompositeNode).
variableInClass(name,Node).
methodInClass(getChildren,CompositeNode).
....
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Software
structure
Reification

Representational
mapping

13
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

class(Node).
class(StatementList).
subclassOf(StatementList,CompositeNode).
variableInClass(name,Node).
methodInClass(getChildren,CompositeNode).
....
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Software
structure
Reification

Representational
mapping

13
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

if class(?x)
Statement

...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

class(Node).
class(StatementList). Smalltalk classes --- all
subclassOf(StatementList,CompositeNode).
variableInClass(name,Node).
methodInClass(getChildren,CompositeNode).
....
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Software
structure
Reification

Representational
mapping

13
SOUL: Reification
The SOUL results contain the *real* classes.
i.e.: the Smalltalk class values

idem for methods, packages, namespaces, etc...

Representational mapping is computed using a
logic rule that uses Smalltalk reflection

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

14
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

classInHierarchyOf(?class,?superclass) if
subclassOf(?class,?superclass).
classInHierarchyOf(?class,?superclass) if
subclassOf(?interclass,?superclass),
classInHierarchyOf(?class,?interclass).

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Deduced
Software structure

SOUL logic
reasoning

15
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Deduced
Software structure

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

logic alternatives

classInHierarchyOf(?class,?superclass) if
subclassOf(?class,?superclass).
classInHierarchyOf(?class,?superclass) if
subclassOf(?interclass,?superclass),
classInHierarchyOf(?class,?interclass).

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL logic
reasoning

15
SOUL: Prolog-based query language
Node
name
printOn:

Expression
...
...

Statement
...
...

Deduced
Software structure

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList
printOn:

logic alternatives

classInHierarchyOf(?class,?superclass) if
subclassOf(?class,?superclass).
classInHierarchyOf(?class,?superclass) if
subclassOf(?interclass,?superclass),
classInHierarchyOf(?class,?interclass).

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

SOUL logic
reasoning

15
SOUL: Prolog-based query language
Node
name
printOn:

if classInHierarchyOf(?class,[ApplicationModel])
Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

Deduced
Software structure

StatementList
printOn:

logic alternatives

classInHierarchyOf(?class,?superclass) if
-- all subclasses of ApplicationModel -subclassOf(?class,?superclass).
SOUL logic
classInHierarchyOf(?class,?superclass) if
reasoning
subclassOf(?interclass,?superclass),
classInHierarchyOf(?class,?interclass).

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

15
SOUL: Prolog-based query language
• Unification:
• powerful pattern matching
• multi-way predicates
• Recursion: detection of...
• ...recursive patterns in code
• ...composite structures in code
• Declarative:
• focuses on ‘what’ instead of ‘how’
• multiple alternative descriptions of same pattern

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

16
Smalltalk vs Soul
result := Collection new.
Smalltalk allClasses do:[:class |
class selectors do:[:selector | | method |
	
	
method := class compiledMethodAt: selector.
	
	
class instVarNames do:[:name |
	
	
	
(method writesField:(class instVarIndexFor: name))
ifTrue:[result add: method]]]].
^result
	

	

	

	

	

	

if methodInClass(?method,?class),
methodWithAssignment(?method, assign(?field,?value)),
instanceVariableInClass(?field,?class)
	

	

	

	

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

	

	

17
SOUL: Reasoning over OO programs
Finding a hard coupling between a class and its subclass:
SuperClass
someMethod

<<references>>

SubClass

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

18
SOUL: Reasoning over OO programs
Finding a hard coupling between a class and its subclass:
SuperClass
someMethod

<<references>>

SubClass

if class(?c),
methodInClass(?method,?c),
methodReferencesClass(?method,?s),
classBelow(?s,?c)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

18
Finding subclass dependencies
SuperClass
someMethod

<<references>>

SubClass

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

19
Finding subclass dependencies
SuperClass
someMethod

<<references>>

SubClass

if class(?c),
methodInClass(?method,?c),
methodReferencesClass(?method,?s),
classBelow(?s,?c)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

19
Finding subclass dependencies
SuperClass
someMethod

<<references>>

SubClass

if class(?c),
methodInClass(?method,?c),
methodReferencesClass(?method,?s),
classBelow(?s,?c)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

19
Finding subclass dependencies
SuperClass
someMethod

<<references>>

SubClass

if class(?c),
methodInClass(?method,?c),
methodReferencesClass(?method,?s),
classBelow(?s,?c)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

19
Finding subclass dependencies
SuperClass
someMethod

<<references>>

SubClass

if class(?c),
methodInClass(?method,?c),
methodReferencesClass(?method,?s),
classBelow(?s,?c)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

19
Finding subclass dependencies

Queries through 7734 classes and 72962 methods
in 44 seconds to find 360 solutions
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

20
SOUL: Reasoning over OO programs
Finding the “Composite” design pattern

compositePattern(?comp,?composite,?msg) if
compositeStructure(?comp,?composite),
compositeAggregation(?comp,?composite,?msg)

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

21
Finding the Composite Pattern
Finding the “Composite” structure

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

22
Finding the Composite Pattern
Finding the “Composite” structure

compositeStructure(?comp,?composite) if
class(?comp),
classInHierarchyOf(?composite,?comp)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

22
Finding the Composite Pattern
Finding the “Aggregation” structure

compositeAggregation(?comp,?composite,?msg) if
classesImplementCommonSelector(?comp,?composite,?msg),
methodWithNameInClass(?method,?msg,?composite),
statementOfMethod(?statement,?method),
iterationStatementWithBlock(?statement,?iterationBlock),
blockIteratesMessage(?iterationBlock,?msg)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

23
Finding the Composite Pattern
Finding the “Aggregation” structure

compositeAggregation(?comp,?composite,?msg) if
classesImplementCommonSelector(?comp,?composite,?msg),
methodWithNameInClass(?method,?msg,?composite),
statementOfMethod(?statement,?method),
iterationStatementWithBlock(?statement,?iterationBlock),
blockIteratesMessage(?iterationBlock,?msg)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

23
Finding the Composite Pattern
Some of the results in the Smalltalk image:

Queries through 7734 classes and 72962 methods
in 13 minutes to find 81 solutions
(Soul is about 10x slower than commercial Prologs)
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

24
Teaser: Visual Query Language

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

25
SOUL: Hybrid Language

if classInHierarchyOf(?class,[ApplicationModel])

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

26
SOUL: Hybrid Language
The SOUL results contain the *real* classes.
i.e.: the Smalltalk class values
Smalltalk expression evaluating
to the class ApplicationModel

if classInHierarchyOf(?class,[ApplicationModel])

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

26
SOUL: Hybrid Language
The SOUL results contain the *real* classes.
i.e.: the Smalltalk class values
Smalltalk expression evaluating
to the class ApplicationModel

if classInHierarchyOf(?class,[ApplicationModel])

className(?class,?name) if
class(?class),
equals(?name,[?class name]).
smallerThan(?x,?y) if
[?x < ?y]
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

26
SOUL: Hybrid Language
The SOUL results contain the *real* classes.
i.e.: the Smalltalk class values
Smalltalk expression evaluating
to the class ApplicationModel

if classInHierarchyOf(?class,[ApplicationModel])

className(?class,?name) if
class(?class),
equals(?name,[?class name]).
smallerThan(?x,?y) if
[?x < ?y]
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Smalltalk expression
- resulting object is
unified with ?name

26
SOUL: Hybrid Language
The SOUL results contain the *real* classes.
i.e.: the Smalltalk class values
Smalltalk expression evaluating
to the class ApplicationModel

if classInHierarchyOf(?class,[ApplicationModel])

className(?class,?name) if
class(?class),
equals(?name,[?class name]).
smallerThan(?x,?y) if
[?x < ?y]
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

Smalltalk expression
- resulting object is
unified with ?name
Smalltalk expression
- evaluates to true or
false

26
SOUL: Reification revisited
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList

Software
structure
Reification

printOn:

class(?class) if
member(?class,[Smalltalk allClasses]).
methodInClass(?method,?class) if
member(?method,[?class selectors
collect:[:sel | ?class compiledMethodAt: sel]])

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

27
SOUL: Reification revisited
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList

Software
structure
Reification

printOn:

class(?class) if
member(?class,[Smalltalk allClasses]).

a collection of all
classes

methodInClass(?method,?class) if
member(?method,[?class selectors
collect:[:sel | ?class compiledMethodAt: sel]])

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

27
SOUL: Reification revisited
Node
name
printOn:

Expression
...
...

Statement
...
...

Composite
Node
children
getChildren
printOn:

VariableRef
var

Message
Send
message

printOn:
printOn:

MethodNode
source
header
printOn:

StatementList

Software
structure
Reification

printOn:

class(?class) if
member(?class,[Smalltalk allClasses]).

a collection of all
classes

methodInClass(?method,?class) if
member(?method,[?class selectors
collect:[:sel | ?class compiledMethodAt: sel]])

a collection of all
methods on a class
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

27
SOUL: hybrid language
• Reification of *real* Smalltalk meta-objects:
• Meta-objects are normal objects
• can reason about ‘structure’ and ‘runtime values’
Advantages:
• Open Unification
• Causal link
• Passing of results to other (Smalltalk) tools
• Full reflection inherited from Smalltalk
• Uniform approach for reasoning about any
object, including runtime values

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

28
SOUL
• Smalltalk Open Unification Language
• Queries on-line over a codebase

• Structural as well as computational reflection
• Supports reasoning over code in a declarative style

SOUL
Inference Engine

Smalltalk
Reflection

Smalltalk
image

Logic Programs

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

29
SOUL
• Smalltalk Open Unification Language
• Queries on-line over a codebase

• Structural as well as computational reflection
• Supports reasoning over code in a declarative style

SOUL
Inference Engine
Logic Programs

Smalltalk
Reflection

Smalltalk
image

JavaConnect
+ JDT

Eclipse
workspace

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

29
SOUL
• Smalltalk Open Unification Language
• Queries on-line over a codebase

• Structural as well as computational reflection
• Supports reasoning over code in a declarative style

SOUL

Logic Programs

Smalltalk
image

JavaConnect
+ JDT

Eclipse
workspace

JavaConnect
+ Soot

Inference Engine

Smalltalk
Reflection

Soot
Analysis

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

29
SOUL: Open Unification
cfr. ‘Open Implementations’
• Define precise unification semantics for entities
• Hiding ‘imperative matching’ process
• For example:
• Unification of objects based on object identity
• Unification of parsetrees via pattern matching
• Unification of variables based on points-to analysis
• etc....

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

30
SOUL: Open Unification
Unification protocol implemented by parsetree items:

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

31
SOUL: Open Unification
Unification protocol implemented by parsetree items:

Objects match based on object identity

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

31
SOUL: Open Unification
Unification protocol implemented by parsetree items:

Objects match based on object identity
Method (objects) match if source code is identical

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

31
SOUL: Open Unification
Unification protocol implemented by parsetree items:

Objects match based on object identity
Method (objects) match if source code is identical
Enhanced parsetree matching:
Expression match based on points-to-analysis
Method bodies match based on control-flow analysis
Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

31
Demonstration

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

32
Active Software Documentation
Software Documentation

Calls to database
- After state change
- Follows DB protocol
(open + (read/write) + close)

• Enforce:
• Adhering to DB protocol
• Using Factory methods
• Coding conventions

Factory Methods
- Must be used to create
objects consistently

Coding Convention
- Classes implementing
events: ‘*Event’
- Must be adhered for
consistency

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

33
Intensional Views
Excerpt from a discussion with a software developer:
I want to be able to express additional semantic constraints on
my programs. An example would be « the GUI module should
never call the database directly ». What I lack is a tool that allows
me to specify such constraints (like « the GUI shouldn't call the DB ») as
queries so that I am able to check to see where in the code base this ad-hoc
rule has been broken. Furthermore, the ability of doing so would allow me to
mould fuzzy or implicit structures in my code base into more concrete
structures. What I mean is that when I'm coding, I often have a
mental concept of my "working set" which might be something
like "The GUI classes and the parts of the back-end which are called directly
from the GUI". I need a tool that allows me to create views
which match my mental model of the program slice which I'm
working on. And these views should preferably be dynamic, so that if my
code changes the views still correspond to the intended slice.

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

34
Conclusions
Declarative Source Code Queries
Specify what to find in source code rather than focusing on how
Extract patterns, metrics, etc...

Hybrid SOUL Language
Mix of imperative and declarative paradigms
Direct manipulation of objects (easy of integration with external tools)
Domain-specific (i.e. language specific) unification of programs

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

35
Links...

SOUL: http://prog.vub.ac.be/SOUL/
Declarative Metaprogramming: http://prog.vub.ac.be/DMP/
Intensional Software Views: http://www.intensional.be

Active Software Documentation using Logic Metaprogramming
Monday 30 March 2009

36

More Related Content

PDF
Application-Specific Models and Pointcuts using a Logic Meta Language
PPT
C# Application program UNIT III
PDF
Writing Swift code with great testability
PDF
ikh331-06-distributed-programming
PDF
Java 7 at SoftShake 2011
PDF
Java 7 JUG Summer Camp
DOCX
PPTX
Hadoop
Application-Specific Models and Pointcuts using a Logic Meta Language
C# Application program UNIT III
Writing Swift code with great testability
ikh331-06-distributed-programming
Java 7 at SoftShake 2011
Java 7 JUG Summer Camp
Hadoop

What's hot (19)

PPT
Thread
PDF
Crawler 2
PPTX
Apache Flink Training: DataStream API Part 2 Advanced
PPTX
Apache Flink Training: DataSet API Basics
PDF
DCN Practical
PDF
FlashAir Android App Development
PDF
Core java pract_sem iii
PDF
4 gouping object
PPT
Socket Programming
PPTX
Java practice programs for beginners
PDF
Spock: A Highly Logical Way To Test
PDF
The Ring programming language version 1.7 book - Part 52 of 196
PDF
PPT
JDK1.7 features
KEY
Non blocking io with netty
PDF
Grails/Groovyによる開発事例紹介
ODP
Java 7 Features and Enhancements
PPTX
разработка серверов и серверных приложений лекция №4
PDF
JJUG CCC 2011 Spring
Thread
Crawler 2
Apache Flink Training: DataStream API Part 2 Advanced
Apache Flink Training: DataSet API Basics
DCN Practical
FlashAir Android App Development
Core java pract_sem iii
4 gouping object
Socket Programming
Java practice programs for beginners
Spock: A Highly Logical Way To Test
The Ring programming language version 1.7 book - Part 52 of 196
JDK1.7 features
Non blocking io with netty
Grails/Groovyによる開発事例紹介
Java 7 Features and Enhancements
разработка серверов и серверных приложений лекция №4
JJUG CCC 2011 Spring
Ad

Viewers also liked (6)

PDF
Mining source code for structural regularities (SATTOSE2010)
PDF
IntensiVE - A Toolsuite For Documenting and Checking Structural Source-Code R...
PPT
From SOA to SCA and FraSCAti
PDF
Bad Code Smells
PDF
Software Reuse and Object-Oriented Programming
PDF
Software Maintenance and Evolution
Mining source code for structural regularities (SATTOSE2010)
IntensiVE - A Toolsuite For Documenting and Checking Structural Source-Code R...
From SOA to SCA and FraSCAti
Bad Code Smells
Software Reuse and Object-Oriented Programming
Software Maintenance and Evolution
Ad

Similar to Active Software Documentation using Soul and IntensiVE (20)

PDF
5. Ввод-вывод, доступ к файловой системе
PDF
Android Studio Assignment HelpCan someone who is familiar with And.pdf
DOCX
JavaExamples
DOCX
Java programming lab_manual_by_rohit_jaiswar
DOC
Inheritance
PPT
Oop lecture9 11
DOCX
I can not get my code to comply it gets mad that I have run declare mo.docx
PPTX
Understanding java streams
DOCX
source code which create file and write into it
PPTX
Anti patterns
DOCX
DOCX
Code red SUM
PPTX
Basic java, java collection Framework and Date Time API
PDF
Refactoring In Tdd The Missing Part
PPT
Java 7
DOCX
Exmaples of file handling
PPT
Oop lecture9 12
PDF
Java Programming Must implement a storage manager that main.pdf
PDF
Create a text file named lnfo.txt. Enter the following informatio.pdf
PPTX
자바스터디 4
5. Ввод-вывод, доступ к файловой системе
Android Studio Assignment HelpCan someone who is familiar with And.pdf
JavaExamples
Java programming lab_manual_by_rohit_jaiswar
Inheritance
Oop lecture9 11
I can not get my code to comply it gets mad that I have run declare mo.docx
Understanding java streams
source code which create file and write into it
Anti patterns
Code red SUM
Basic java, java collection Framework and Date Time API
Refactoring In Tdd The Missing Part
Java 7
Exmaples of file handling
Oop lecture9 12
Java Programming Must implement a storage manager that main.pdf
Create a text file named lnfo.txt. Enter the following informatio.pdf
자바스터디 4

More from kim.mens (20)

PDF
Context-Oriented Programming
PDF
Object-Oriented Design Heuristics
PDF
Software Patterns
PDF
Code Refactoring
PDF
Domain Modelling
PDF
Object-Oriented Application Frameworks
PDF
Towards a Context-Oriented Software Implementation Framework
PDF
Towards a Taxonomy of Context-Aware Software Variabilty Approaches
PDF
Breaking the Walls: A Unified Vision on Context-Oriented Software Engineering
PDF
Context-oriented programming
PDF
Basics of reflection
PDF
Advanced Reflection in Java
PDF
Basics of reflection in java
PDF
Reflection in Ruby
PDF
Introduction to Ruby
PDF
Introduction to Smalltalk
PDF
A gentle introduction to reflection
PDF
Managing the Evolution of Information Systems with Intensional Views and Rela...
PDF
Usage contracts (presented at SATToSE 2014 in L'Aquila, Italy)
PDF
Usage contracts in a nutshell
Context-Oriented Programming
Object-Oriented Design Heuristics
Software Patterns
Code Refactoring
Domain Modelling
Object-Oriented Application Frameworks
Towards a Context-Oriented Software Implementation Framework
Towards a Taxonomy of Context-Aware Software Variabilty Approaches
Breaking the Walls: A Unified Vision on Context-Oriented Software Engineering
Context-oriented programming
Basics of reflection
Advanced Reflection in Java
Basics of reflection in java
Reflection in Ruby
Introduction to Ruby
Introduction to Smalltalk
A gentle introduction to reflection
Managing the Evolution of Information Systems with Intensional Views and Rela...
Usage contracts (presented at SATToSE 2014 in L'Aquila, Italy)
Usage contracts in a nutshell

Recently uploaded (20)

PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPT
Teaching material agriculture food technology
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Machine learning based COVID-19 study performance prediction
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
KodekX | Application Modernization Development
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Encapsulation theory and applications.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPTX
Big Data Technologies - Introduction.pptx
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Encapsulation_ Review paper, used for researhc scholars
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
The Rise and Fall of 3GPP – Time for a Sabbatical?
MIND Revenue Release Quarter 2 2025 Press Release
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Teaching material agriculture food technology
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Unlocking AI with Model Context Protocol (MCP)
Machine learning based COVID-19 study performance prediction
MYSQL Presentation for SQL database connectivity
Network Security Unit 5.pdf for BCA BBA.
KodekX | Application Modernization Development
Diabetes mellitus diagnosis method based random forest with bat algorithm
Encapsulation theory and applications.pdf
The AUB Centre for AI in Media Proposal.docx
Advanced methodologies resolving dimensionality complications for autism neur...
Big Data Technologies - Introduction.pptx
Per capita expenditure prediction using model stacking based on satellite ima...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf

Active Software Documentation using Soul and IntensiVE

  • 1. Active Software Documentation using Soul and IntensiVE Johan Brichau, Kim Mens, Coen De Roover, Andy Kellens, Roel Wuyts Département d’Ingénierie Informatique - Université catholique de Louvain Monday 30 March 2009
  • 2. Documenting Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 2
  • 3. Documenting Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Calls to database 2
  • 4. Documenting Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); (open + (read/write) + close) Factory Methods - Must be used to create objects consistently boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } Calls to database 2
  • 5. Documenting Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } Calls to database Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 2
  • 6. Documenting Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } Discovering and Documenting Regularities Architecture Idioms Patterns Conventions public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); Bugs Bad smells boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } Calls to database Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 2
  • 7. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } Calls to database Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 3
  • 8. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public class HappyNewYear implements Runnable private private private private private public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } */ } public void run() { public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } } - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); */ Calls to database public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency } Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 3
  • 9. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } ? public class HappyNewYear implements Runnable private private private private private public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } */ } public void run() { public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); */ Calls to database Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 3
  • 10. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public class HappyNewYear implements Runnable private private private private private public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } */ } public void run() { public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } } - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); */ Calls to database public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency } Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 4
  • 11. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public class HappyNewYear implements Runnable private private private private private public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } */ } public void run() { public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } } - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); */ Calls to database public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency } Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 4
  • 12. Evolving Software... Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } ? public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } private private private private private */ { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; } public void run() { public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); } } - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } public class HappyNewYear implements Runnable */ Calls to database public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency } Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 4
  • 13. Software Erosion Problem • Architectural drift and design decay • Documentation of source code becomes inconsistent • Original design structure and architecture is eroded import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); source code v1.0 public static Long copyFile(File srcFile, File throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { { checksum.update(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead); } destFile) checksum = if (verify) } */ public class HappyNewYear implements Runnable { private static NumberFormat formatter = NumberFormat.getInstance(); private JFrame frame; private JLabel label; private long newYearMillis; private String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year documentation v1.0 time Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 5
  • 14. Software Erosion Problem • Architectural drift and design decay • Documentation of source code becomes inconsistent • Original design structure and architecture is eroded import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; import java.io.*; import java.util.zip.*; */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; source code v1.0 public static Long copyFile(File srcFile, File throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { { checksum.update(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead); } if (verify) } source code v2.0 public static Long copyFile(File srcFile, File throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { { checksum.update(buffer, 0, bytesRead); out.write(buffer, 0, bytesRead); } destFile) checksum = // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; */ public class HappyNewYear implements Runnable { private static NumberFormat formatter = NumberFormat.getInstance(); private JFrame frame; private JLabel label; private long newYearMillis; private String message; public HappyNewYear(JFrame frame, JLabel label) { destFile) checksum = if (verify) } this.label = label; // compute beginning of next year */ public class HappyNewYear implements Runnable { private static NumberFormat formatter = NumberFormat.getInstance(); private JFrame frame; private JLabel label; private long newYearMillis; private String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements // store argument GUI elements this.frame = frame; */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); */ public class HappyNewYear implements Runnable { private static NumberFormat formatter = NumberFormat.getInstance(); private JFrame frame; private JLabel label; private long newYearMillis; private String message; JLabel label) elements next year GregorianCalendar(); cal.get(Calendar.YEAR) + 1; this.frame = frame; this.label = label; // compute beginning of next year public HappyNewYear(JFrame frame, { // store argument GUI this.frame = frame; this.label = label; // compute beginning of Calendar cal = new int nextYear = } documentation v1.0 documentation v1.0 time Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 5
  • 15. Active Software Documentation Software Documentation import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } Discovering, documenting and enforcing Regularities Architecture Idioms Patterns Conventions public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Bugs Bad smells Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 - After state change - Follows DB protocol (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } Calls to database Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 6
  • 16. Active Software Documentation Code Software Documentation CCallssadatabase au to l Queries Lin state change - Afterk Discovering, import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } documenting and enforcing Regularities Architecture Idioms Patterns Conventions public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Bugs Bad smells Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 (open + (read/write) + close) { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } } - Follows DB protocol Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency 6
  • 17. Small programs vs. Large projects import java.io.*; import java.util.zip.*; /** * Command line program to copy a file to another directory. * @author Marco Schmidt */ public class CopyFile { // constant values for the override option public static final int OVERWRITE_ALWAYS = 1; public static final int OVERWRITE_NEVER = 2; public static final int OVERWRITE_ASK = 3; // program options initialized to default values private static int bufferSize = 4 * 1024; private static boolean clock = true; private static boolean copyOriginalTimestamp = true; private static boolean verify = true; private static int override = OVERWRITE_ASK; public static Long copyFile(File srcFile, File destFile) throws IOException { InputStream in = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile); long millis = System.currentTimeMillis(); CRC32 checksum = null; if (verify) { checksum = new CRC32(); checksum.reset(); } byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { if (verify) { checksum.update(buffer, 0, bytesRead); } out.write(buffer, 0, bytesRead); } out.close(); in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } if (verify) { return new Long(checksum.getValue()); } else { return null; } } public static Long createChecksum(File file) throws IOException { long millis = System.currentTimeMillis(); InputStream in = new FileInputStream(file); CRC32 checksum = new CRC32(); checksum.reset(); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { checksum.update(buffer, 0, bytesRead); } in.close(); if (clock) { millis = System.currentTimeMillis() - millis; System.out.println("Second(s): " + (millis/1000L)); } return new Long(checksum.getValue()); } /** * Determine if data is to be copied to given file. * Take into consideration override option and * ask user in case file exists and override option is ask. * @param file File object for potential destination file * @return true if data is to be copied to file, false if not */ public static boolean doCopy(File file) { boolean exists = file.exists(); if (override == OVERWRITE_ALWAYS || !exists) { return true; } else if (override == OVERWRITE_NEVER) { return false; } else if (override == OVERWRITE_ASK) { return readYesNoFromStandardInput("File exists. " + "Overwrite (y/n)?"); } else { throw new InternalError("Program error. Invalid " + "value for override: " + override); } } public static void main(String[] args) throws IOException { // make sure there are exactly two arguments if (args.length != 2) { System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); System.exit(1); } // make sure the source file is indeed a readable file File srcFile = new File(args[0]); if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Not a readable file: " + srcFile.getName()); System.exit(1); } // make sure the second argument is a directory File destDir = new File(args[1]); if (!destDir.isDirectory()) { System.err.println("Not a directory: " + destDir.getName()); System.exit(1); } // create File object for destination file File destFile = new File(destDir, srcFile.getName()); // check if copying is desired given overwrite option if (!doCopy(destFile)) { return; } // copy file, optionally creating a checksum Long checksumSrc = copyFile(srcFile, destFile); // copy timestamp of last modification if (copyOriginalTimestamp) { if (!destFile.setLastModified(srcFile.lastModified())) { System.err.println("Error: Could not set " + "timestamp of copied file."); } } } // optionally verify file if (verify) { System.out.print("Verifying destination file..."); Long checksumDest = createChecksum(destFile); if (checksumSrc.equals(checksumDest)) { System.out.println(" OK, files are equal."); } else { System.out.println(" Error: Checksums differ."); } } } */ public class FileDownload { public static void download(String address, String localFileName) { OutputStream out = null; URLConnection conn = null; InputStream in = null; try { URL url = new URL(address); out = new BufferedOutputStream( new FileOutputStream(localFileName)); conn = url.openConnection(); in = conn.getInputStream(); byte[] buffer = new byte[1024]; int numRead; long numWritten = 0; while ((numRead = in.read(buffer)) != -1) { out.write(buffer, 0, numRead); numWritten += numRead; } System.out.println(localFileName + "t" + numWritten); } catch (Exception exception) { exception.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ioe) { } } } /** * Print a message to standard output and read lines from * standard input until yes or no (y or n) is entered. * @param message informative text to be answered by user * @return user answer, true for yes, false for no. */ public static boolean readYesNoFromStandardInput(String message) { System.out.println(message); String line; BufferedReader in = new BufferedReader(new InputStreamReader( System.in)); Boolean answer = null; try { while ((line = in.readLine()) != null) { line = line.toLowerCase(); if ("y".equals(line) || "yes".equals(line)) { answer = Boolean.TRUE; break; } else if ("n".equals(line) || "no".equals(line)) { answer = Boolean.FALSE; break; } else { System.out.println("Could not understand answer ("" + line + ""). Please use y for yes or n for no."); } } if (answer == null) { throw new IOException("Unexpected end of input from stdin."); } in.close(); return answer.booleanValue(); } catch (IOException ioe) { throw new InternalError( "Cannot read from stdin or write to stdout."); } } public static void download(String address) { int lastSlashIndex = address.lastIndexOf('/'); if (lastSlashIndex >= 0 && lastSlashIndex < address.length() - 1) { download(address, address.substring(lastSlashIndex + 1)); } else { System.err.println("Could not figure out local file name for " + address); } } public static void main(String[] args) { for (int i = 0; i < args.length; i++) { download(args[i]); } } } public class HappyNewYear implements Runnable private private private private private */ { static NumberFormat formatter = NumberFormat.getInstance(); JFrame frame; JLabel label; long newYearMillis; String message; public HappyNewYear(JFrame frame, JLabel label) { // store argument GUI elements this.frame = frame; this.label = label; // compute beginning of next year Calendar cal = new GregorianCalendar(); int nextYear = cal.get(Calendar.YEAR) + 1; cal.set(Calendar.YEAR, nextYear); cal.set(Calendar.MONTH, Calendar.JANUARY); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); newYearMillis = cal.getTime().getTime(); // prepare a message message = "Happy " + nextYear + "!"; } public static int determineFontSize(JFrame frame, int componentWidth, String fontName, int fontStyle, String text) { int fontSize = componentWidth * 2 / text.length(); Font font = new Font(fontName, fontStyle, fontSize); FontMetrics fontMetrics = frame.getGraphics(). getFontMetrics(font); int stringWidth = fontMetrics.stringWidth(text); return (int)(fontSize * 0.95 * componentWidth / stringWidth); } } public void run() { } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent event) {} public void keyReleased(KeyEvent event) { if (event.getKeyChar() == KeyEvent.VK_ESCAPE) { System.exit(0); } } public void keyTyped(KeyEvent event) {} } ); frame.setUndecorated(true); JLabel label = new JLabel("."); label.setBackground(Color.BLACK); label.setForeground(Color.WHITE); label.setOpaque(true); label.setHorizontalAlignment(SwingConstants.CENTER); frame.getContentPane().add(label); GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(frame); final int fontStyle = Font.BOLD; final String fontName = "SansSerif"; int fontSizeNumber = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, formatter.format(88888888)); int fontSizeText = determineFontSize(frame, Toolkit.getDefaultToolkit().getScreenSize().width, fontName, fontStyle, "Happy 8888!"); label.setFont(new Font(fontName, fontStyle, Math.min(fontSizeNumber, fontSizeText))); new HappyNewYear(frame, label).run(); boolean newYear = false; do { long time = System.currentTimeMillis(); long remaining = (newYearMillis - time) / 1000L; String output; if (remaining < 1) { // new year! newYear = true; output = message; } else { // make a String from the number of seconds output = formatter.format(remaining); } label.setText(output); try { Thread.sleep(1000); Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 7
  • 18. Small programs vs. Large projects ; () ce ar Ye ew yN pp ic bl pu cl s as Ha an impo impo rt ja va rt */ java .io.*; /** .uti public class FileDownload { l.zi /** * Co p.*; * Command line program to copy a file to another directory. ge * @a mmand public static void download(String address, String localFileName) { * @author Marco Schmidt import java.io.*; */ uthor line t. OutputStream out = null; publ Marc prog */ import java.util.zip.*; */ ma ic o Sc ram r public class CopyFile { clas hmid to co public class FileDownload URLConnection conn = null; { s Co t /** // constant values for the override option py Fo pyFi a fi InputStream in = null; * Command linepublic static finalfile to another directory. program to copy a int OVERWRITE_ALWAYS = 1; le er public static void download(String address, String localFileName) { le { to * @author Marco Schmidt import public static final int OVERWRITE_NEVER = 2; java.io.*; try { OutputStream out = null; anot mb // */ import public static final int OVERWRITE_ASK = 3; java.util.zip.*; her */ co Nu dire URL url = new URL(address); publ nsta ; public class CopyFile { conn null; ctor nt = public class FileDownload URLConnection = new=BufferedOutputStream( ce() { publ ic st /** // program options initialized to default values // constant values for the override option va y. out InputStream in = null; publ ic st atic lues fo r * Command linestatic int bufferSize = 4 * another directory. private public static finalfile to 1024; program to ecopy a int OVERWRITE_ALWAYS = 1; an public static void download(String address, String tlocalFileName) { atic fina ic new ) * @author Marco Schmidt t private static boolean final inttrue; public static clock = OVERWRITE_NEVER = 2; stat l r the try { OutputStream out = null;FileOutputStream(localFileName)); ns // ic final int OV over */ private static boolean final int OVERWRITE_ASK= =true; public static copyOriginalTimestamp 3; el pr fina int at conn = url.openConnection(); ri etI ER priv ogra URL conn l in OVER WRIT de op public private staticrm class CopyFile boolean verify = true; ab { */ URLConnection url ==new URL(address); null; t.g m priv ate st opti t OV WRIT E_AL tion in = conn.getInputStream(); l o // constant values for the override option 1; private static int override initialized to default values ; // program options = OVERWRITE_ASK; outin new BufferedOutputStream( = = null; orma ate ERWR E_NE WAYS atic ons f priv InputStream buffer = new byte[1024]; private static int bufferSize = 4 s* public static final int OVERWRITE_ALWAYS ) 1; = st in ITE_ VER = 1; l byte[] F new FileOutputStream(localFileName)); { private static boolean final inttrue;1024; priv ate st atic int bu itia ASK t = public static Long copyFile(File e public static clock = nt File destFile)( 2; r b srcFile,OVERWRITE_NEVER = + try { = 3; 2; ber priv ate st atic boolea ffer lized ); r int numRead; url.openConnection(); e ma private static throwsLacopyOriginalTimestamp d= =true; public booleanIOException { static final int OVERWRITE_ASKa R) 3; Size to ate bo conn = Num at n URL = = RY stat ic bo olean clock = 4 defaul J em true; yea en EA boolean verify or private static InputStream lin = new FileInputStream(srcFile); long numWritten url0; new URL(address); publ ol ic co * = = UA rF private static OutputStream initializedFileOutputStream(destFile); // program options = OVERWRITE_ASK; Y int l default values , ic int ean ve pyOr = true 1024 t valu e */ while inter conn.getInputStream(); != -1) { out stat ; es s; privatelongoverride=out = new =to r.1024; AN ((numRead == new BufferedOutputStream( in.read(buffer)) over rify igin ; e static int System.currentTimeMillis(); xt byte[] buffer = new byte[1024]; me millis bufferSize Ca4 * ; ic ride new FileOutputStream(localFileName)); = alTi Long mat li mb e; public staticraCRC32 GUI private static boolean clockan true; ) destFile) Long copyFile(Filenull; = daFile .J checksum = ne srcFile, out.write(buffer, 0, numRead); = OV true; mestam copy { i en { r for int numRead; url.openConnection(); private static throws IOException ea ar ); = true; boolean f if (verify) { f copyOriginalTimestamp e Nu r m el; Mil ; ERWR p = a conn numWritten += numRead; = File ITE_ true ; bl private static; InputStream in checksum =1new CRC32(); nt e boolean verify = new FileInputStream(srcFile); Y d o gor l true; long numWritten l) 0; = thro (File ASK; ; mat in = conn.getInputStream(); me g a ic f ab ar e e, private e am l; goverride aoutt= en , static OutputStream = checksum.reset(); int OVERWRITE_ASK; new FileOutputStream(destFile); () ); Inpu ws IO srcFil For } while byte[] labe = in.read(buffer)) != -1) { ((numRead n re ( System.currentTimeMillis(); ra um fr be long millisC=nex al TH 0 nn Ex } at ame l Ye sa yl buffer = new byte[1024]; me Outp tStrea cept e, Fi ber System.out.println(localFileName + "t" 0, numWritten); le C ON CRC32 new St a Long checksum = srcFile, rg static buffer G=get , byte[bufferSize]; Ti Ru st Fr bel n w mes (JF public=byte[] nni w copyFile(Filenull;Y, File destFile) e out.write(buffer, + numRead); long utStre m in ion { le de int {el Numame; stFi nab } catch (Exception; exception)abnumRead; s e r a intlbytesRead; if (verify) { , IOException { throws t t M A ; CRC3 mill am ou = new is le) 0) { numWritten n = e i e al. EAR Tin.read(buffer)) >=ge= new "; a is t e J JLa ng ng JL ); e); = H F_ _D = new FileInputStream(srcFile); CRC32(); on Run nts if 2 chec = t = ne File long numWritten = tice fr bel; illexception.printStackTrace(); 0; += numRead; e re ame l whileg ((bytesReadInputStream in )checksumFileOutputStream(destFile); nt f Inpu me = c Y OutputStream out); {) o b if (verify) new checksum.reset(); Y o ta m i e M z va t a } me, while e((numRead = in.read(buffer)) != -1) { "! checksum.update(buffer, th(bytesRead); (verify)ksum System.cw FileOu tStr ON Y_O OF 0 0 = ( . nts ; r l _ E } 0, i el e e s}Fra l l ear ge; em ri va te tr New st .f abe t al = ar. .Mlong millis, = System.currentTimeMillis();nt finally { { = null urre tp ea r ar() + 1; e fra eme System.out.println(localFileName + "t" 0, numWritten); + numRead); ntTi utSt m(srcF ng tS ; i y byte[] bufferU=}new byte[bufferSize]; CRC32 R } DA checksum = null; + vat e J abe newYessa pl p pri iva ate e S d GUI , meMi ream ile) yea out.write(buffer, try e{ mpl nd bytesRead; NUT N , m p // his s.l mpu r c ear eintdar r. H(verify) D{ Ti ar 0, bytesRead); if O out.write(buffer, byte am le on . llis (des ; m priivat e JL ng }g catch (Exception nexception) e{ t alen EAR) im m m numWritten; += numRead; e checksum 0)e{ e, t r i (); tFil ) e t e; int [] bu t i co da }tY al while a r. MI CO= in.read(buffer)) >= a= new CRC32(); t. , f s() ; JFr pr iv vat Hap chec if n x n!= null) { (in C .Y e); en ((bytesRead ff pr ivat e lo rin r exception.printStackTrace(); RY whil bytesR er tY checksum.reset(); ex e c Yea th n out.close(); (verify) { fr Na gum ram } ; x C l nd a r. SE .ge if x chec ksum ar( t) p ri UA l g of oria ndarr); Ain.close(); e (( ead; = ksum = ne ) t l i p c in.close(); } t pr ivat e St } finally {e ar = f abeSystem.out.println(localFileName + "t" + numWritten); New a . al e e by l e // a e ne if ( (Ca ale end dbyte[] buffer =nnew byte[bufferSize]; checksum.update(buffer, 0, bytesRead); tesR new byte wYe i ex g J N .res E CR et t C { l l / ty h me fon Pw ppy [buf } et() C32( or ame try {nnin Gre (Cal tYeadar. ; g a cmillis+ out.write(buffer, 0, bytesRead); ap ead pr ivat t .s e(clock)al en int rbytesRead;= }System.currentTimeMillis() - Smillis; h(t yNe C n b = p ); ferS CA Ha ; i = in t ) } st r (Exception exception) n{null) { catch ra w if nex != sa " C ize] i al .s et( (} al end = ((bytesRead = in.read(buffer)) >=2+ nt Gr pu .rea pr S ; Hap while s System.out.println("Second(s):*" 0) { t // is.f abel beg= ne l.ge ,(out le H, !=; null) { if (in 1) dt ass o (millis/1000L)); ; a T d(bu JFif g n C l s } exception.printStackTrace(); { i if c al } .s et out.close(); me y f e _E cl ff e( ri (verify)h{ i t .l lic th finally e{al = ca YEAR H, CMON , 0 out.close(); out. in.close(); p l s t( {a l c if (verify) C in.close(); 0, ; gW z t , .g checksum.update(buffer, os bytesRead); } (verify) )) >= 0) VK er } his ompu c r h) pub lic in.c cl e( St } t t (clock) { O T ca al. .se if ( Mil e a{Hapreturn newiLong(checksum.getValue());; in c ar. M} N OF_ DAY e() } a id me me ) ar t. { dt if lose ); pub { tS h, } l System.currentTimeMillis() - i millis = out.write(buffer, t tr se t. n c else .{ ar ar " } catch (IOException ioe) ); // lend xtYe lendtry. { AY_ _OF_ (out != tTim {} { ouvewrit ar D tW Na ra n 0, bytesRead); W millis; (clock(); */ if 0 { null) { ca l return n System.out.println("Second(s): " + (millis/1000L)); ) { t e(bu if (in null) { = e p } ) Ca t ne t(Ca lenddar. HOUR TE, 0); ).ge != ;out.close(); en nt f fo .s Fo null; E } ng ff class chec } } e e out.close(); d , } ca ewY pr(verify) { ksum in.close(); nt t) ey public er, 0, by HappyNewYear implements Runnable on fo = ( cs * i ne i in l.se t(Ca len dar. MINU ND, ime( } if in.close(); W } yle .upd if "!" n / sag if (clock)n{ return mp t( cs ics} i 5 str { tesR ve ven = K r 9 new Long(checksum.getValue()); mi e t ate( ca l.se t(Ca len dar. SECO}etT } r + (verif tSt t ead) i mill r n ) } buff / }s public static Long createChecksum(File { IOException on { c millis t System.currentTimeMillis() -} millis; y) t e e = Syst is = ; private static NumberFormat formatter = NumberFormat.getInstance(); r. l.g catch ioe) { e else file)ethrows returno null; r = r e 0. / er, { f n else ca l.se } (Ca len (IOException tifa(out != null)t { o m ;em Sy long millis = System.currentTimeMillis(); et Me tM publ h et po xt Ye 0, t a () { = w FSystem.out.println("Second(s): " + (millis/1000L));nt () byte private JFrame frame; } n a ic en d FileInputStream(file); t on * dt stat InputStream in = } } new m ca l.se t(Ca le{d = c ge ); e, inout.close(); ); 0) .out.pstem.cur ex } r sRea tM te rint re ic } CRC32 checksum t ifc(verify) { ze ne n on f = newo CRC32(); Ev Eve har e i d); th(ize ramame, private JLabel label; public static void download(String address)lis essa + n } t(tu ln(" ntTi ne re ca l.se t(Ca Long } checksum.reset(); return new Long(checksum.getValue()); } Seco meMi rn ey ey yC int lastSlashIndexa = address.lastIndexOf('/'); e f tN in t else file) ithrowsfo etF = Siz W eng tS ng S = t crea e xi t ne { se Mil a m py " private long newYearMillis; } nd(s llis c public static Long createChecksum(File { nt t byte[] buffer = nnew ibyte[bufferSize];IOException {n }r teCh (K am on } () s g return null; t.l fon ): .e return t) w Long(c if (lastSlashIndex al.Year are catch (IOException fioe) { ) ; is long ecksum K Ke d ( th nt ne " + - mi int bytesRead;i long millis = System.currentTimeMillis(); ic t c >= 0 && } "Hap JFr private String message; heck texle, s(). fonew n ric S} (mil llis n null; gs () yL Inpu emiediset e tem while ((bytesRead = in.read(buffer)) >= d0)fo po InputStream in = fo FileInputStream(file); { s ll (Fil sum. i e prep = at lastSlashIndex n< waddress.length() i e(t { ng lis/ ; public static void download(String gaddress) { tS- z1) ri t =t getV fi ; } CRC32 checksum checksum.update(buffer, 0, bytesRead); me Ke CRC32(); )( om CRC3 tStreag = 2 / Sty hic + 1)); 1000 e ve ar es eas . m Syysle) th } gW t c S st checksum.reset();newet alue download(address, address.substring(lastSlashIndex ext) // ss in on tM ch } ; L)); chec in S on th, ()); public HappyNewYear(JFrame frame, }JLabel label) lastSlashIndexa = address.lastIndexOf('/'); * font Grap int ] Pr ecl2 um t ksum = stem.cur rowse IO in inthrows IOException { Fra ew h ) ks t t ybyRe[] en.r public in.close(); createChecksum(File r static Long byte[] buffer = new byte[bufferSize]; g[ file) new F n ic } } else { (lastSlashIndex >= 0 && neFWid te me J (n Exce h(t ( Fi ere { if get me in Wid e, if int long millis = st System.currentTimeMillis(); n ke eyt byv buffeset()= new CRvlen ntTimeMi ption bl (clock) { bytesRead;Fo = in.read(buffer)) >= 0) { e rmi t Inpu i i t System.err.println("Could notnt Nam me. local dfile name for " + ra pu while ((bytesRead tin= rnnew FileInputStream(file); r millis; kwhil( tesR er = ; InputStream System.currentTimeMillis() e E C32(); tStreallis() { ew // store argument GUI elements lastSlashIndex <dete nen address.length() e figure out 1) millis = public static void download(String oaddress) { pon-font {fra nt); ringW tr id ); CRC32 checksumu checksum.update(buffer," 0, nbytesRead); f e ((by ead; new by System.out.println("Second(s): n + (millis/1000L)); ; (f m(fi ; ey t) in et = new CRC32(); mp eaddress); tesR } te[b this.frame = frame; download(address,com t( s = (fo st address.substring(lastSlashIndex + 1)); le); (S = te } } checksum.reset(); vo id ini th) ER int lastSlashIndex = xaddress.lastIndexOf('/'); ow ead (K int co r } = return new Long(checksum.getValue()); in.close(); buffer = new byte[bufferSize]; s byte[] = d in.r ufferSize] in me Li Wid c vo if .c{ se this.label = label; } else { (lastSlashIndex t>= 0 e&& NT Fontric rics ics.* h, lo nd ic int ing e if i t E { ng } if (clock) { int bytesRead; ead( z (clo (); ; } ma } ra ey e t ). Wi ew dt ; // compute beginning of next year System.err.println("Could Metr figure i } yp chec buff ck sta bl c while ((bytesRead = in.read(buffer)) >= 0) { i millis = System.currentTimeMillis() - millis; ) .C } Str lastSlashIndex ntSi = n ontM ntMe ntnot .95 { tr out local file name for " + < address.length() - 1) s f K ksum er)) PE) t( en wi )) /** System.out.println("Second(s): " 0, (millis/1000L)); id checksum.update(buffer, + bytesRead); { eyT pu ubl f Fo ts .upd >= Calendar cal = new GregorianCalendar(); /** d fo nt s address); * 0 h / lic } SCA en re download(address,fo address.substring(lastSlashIndex + 1)); h, ). 88 t ate( 0) n * Determine if data is to be copied to } } given file. k vo me * ad public static void main(String[]1; p retu pub buff { int nextYear = cal.get(Calendar.YEAR)else args) {intnt fotric ge h = Size Widt } + K_E dt ta er, onm lSc e( 88 * Take into consideration override option Long(checksum.getValue()); Determ return new and in.close(); ; ; rn {} { ra e. * newid for (int i } t.V d ic * ask user in case file exists and override option{is ask. F am* Take ine } if (clock) r0, l wi ns iz 88 " ll K FoSystem.err.println("Could not figure out local filet) ; mi) is) ; cal.set(Calendar.YEAR, nextYear); = 0; i < args.length; i++) i{ tfont nent } tMe if as n name for " + ) { ven * @param file File object for potential destination t millis = System.currentTimeMillis() - millis; e) ". stC E) Sy vo Long(check Sy A em.o= st Co a file J r * @p k usinto consdata vi Fu bytesRead) enS (88 download(args[i]);ingWt)( po ). Fon er cal.set(Calendar.MONTH, Calendar.JANUARY); evevent KeyE g u l( m.B * @return true if data is to be copied file, st /** to false if not f System.out.println("Second(s): "c+ (millis/1000L)); L IT ut.p nem.c su ider is to * @r aram in r() , ; e at En et e( tr (in com address); ) t { e } { */ etur file case atio be ico e = */ * Determine if data is to be copied to given file. } ; c re irintlnurs nts H tr e . getV cal.set(Calendar.DAY_OF_MONTH, 1); } public static void main(String[] args) n{ s n me cr m = iz n tr ven nt rgs ten bl publ public static boolean considerationfile) return new Long(checksum.getValue());ue File file} n over pied d( ab or .W alue() l) i("Se. TimeMi * Take into doCopy(File override option and } { lic Sw } ) co i ttur exis ); (); Lis ra S r to L cal.set(Calendar.HOUR_OF_DAY, for (int i = 0; i < args.length; i++) { [] a 0); nS eyE Eve r() l or if objectpu ts ride e gi * ask user in boolean exists = file.exists(); } case file exists andboverride option is ask.ic st } t( be ph e( nd(s):ll"isget .fo data t atic re ); an opti J veo } ame Key (f () r ; d(K Key Cha ee * @param file if (overridefor OVERWRITE_ALWAYS || !exists) { File object == potential destination file cal.set(Calendar.MINUTE, 0); download(args[i]); is for po d a w on (an olle ing pu return true; bool en la ra ic r over Cnd fi to t(0 JFrnew , * @return true if data is to be copied to file, false if not /** "; ze .+ (milmillScr "); )) sse ed( Key ean be tentia ride (C ; . nm d( lG v ; li is Str { co e d e D if Si () te me s/10;00 doCo copi n l n d op ) exi ; cal.set(Calendar.SECOND, 0); } */ * Determine if data } else is to be copied to given file. Pre eas get t) public static void main(String[] args) { ain( ti newner( py de = edou destinat ligis ad oca nD OL er nt kit mat ra get 88! L));ex public static boolean considerationfile) { * Take into doCopy(File override option and if (override == OVERWRITE_NEVER) { bool (Fil em. to n ue on . () key Rel nt. newYearMillis } cal.getTime().getTime(); = m {} for (int i = 0; i < args.length; =iste { i++) fi if ean n e l le r ou file, Aion ) ask. ee B L * ask user in boolean exists returnoverride option is ask. case file exists file.exists(); = and false; r l fa ( file r . sS Fo ol or (f ). 88 , eT yst me L ce } // prepare a message id key (eve id (oveU exis kg { S ) r n ls * @param file if (overridefor OVERWRITE_ALWAYS || !exists) { } else File object == potential destination file nt) vo d vo download(args[i]); etrrbe ts = gfi e(t ta e e et c nt an ne o f ze ( fra ey le iz id c if } el s * @return true if data is to be copied to true; if (override == OVERWRITE_ASK) { return file, false if not ta eve ic voi if ic message = "Happy " + nextYear + "!"; o r OV u .e me addK .se la tBea+== e qERleonxiPan t.g ltSnot "S mi ltT e, Si kit ppy ty tS l s t } a if F t */ } else return readYesNoFromStandardInput("Fileeexists. " o } (ove sts( WR { n } pubblic sta } tIn JFrame. = ter au yl ont ol Ha ntS fon public static boolean doCopy(File file) { if (override == OVERWRITE_NEVER) { "Overwrite ; m el rrid tF pa iz ITE_AL ); u = ven ) (y/n)?");.se e O r ent meWAfa } } ra ase e } else { boolean exists return false; = file.exists(); pu eyE lic fr t tu fif elb l se == OV o t ren De YS e e de Def tSt eF tTo , " fo , ; } } else if (override new OVERWRITE_ALWAYS || !exists)L(o Invalid se+ tHERon ro rn trl || m throw == InternalError("Program error. e l. " { .g } d(K pub WRIT y ueNa !e= st t on in ul le , er ) t ve e xi { J ab rr " . public static int determineFontSize(JFrame frame, if (override == OVERWRITE_ASK) { return "value for override: id + override);i E_NE true; at ype l e exists. b el . e C " re ge tSt t ; r .ges)f{ rm fa ty me mb n( { } } else return readYesNoFromStandardInput("File ==s et nv+ tu VER) n int componentWidth, String fontName, int fontStyle, } rm eyT OV E rn o { e t } el la (y/n)?"); n fa , te e S Na u ru } if (override == OVERWRITE_NEVER) { "Overwrite ab el .g ERWR */ se d k Fo ; String text) ); { f K) umb l ab me ics ITE_ASo g f lse; ki me de etD ont nt zeN ). return false; } else { pu L; voi ER) er public class HappyNewYear implements Runnable ame public static void main(String[] args) throws IOException blic new InternalError("Program error. Invalid "return { N ol Na = } else throw st + g f fo i el l ra ph { } nt n read ri ze 00 { atic ENT } blic mb // make sure there are exactly OVERWRITE_ASK) { if (override == two arguments "value for override: " + ioverride); To nt (fr ; t. , ( tS b f ra { ); void t Si int fontSize = componentWidth * 2 / text.length(); } 10 s.C if (args.length != 2) { return readYesNoFromStandardInput("File exists. " +YesNoFxt ki me nt on la Nu . dow ue) "." K); pu G fo e romS a o f private static NumberFormat formatter = NumberFormat.getInstance(); al thS n ne } System.err.println("Usage: main(S CopyFile SRC-FILE-NAME DEST-DIR-NAME"); "Overwrite (y/n)?"); t ant t() Win = ); / Font font = new Font(fontName, fontStyle, fontSize); */ (trbel( BLAC E); ta T ol tNndarF n( e, l row w tr } else System.exit(1); { in a fo nst private JFrame // ing[] men een Inte e To ted La IT dI r. FontMetrics class HappyNewYear implements Runnable public fontMetrics = frame.getGraphics(). frame; er is( me) h, public static void main(String[] args) throws IOException { new InternalError("Program f in } throw error. Invalid " +iz al on "Oveminput(" rn ew rwri am Fi if make su args) gCo ron lScr ) oraew J Colo r.WH t Er private JLabel label; t f (n // make sure the sure there are exactly a readable file (args. re // make source file is indeed two arguments "valueth for f override: " + override);ror( h. fr l in idt ; le vi ul ec n getFontMetrics(font); { tS s lo at ll ti d( ( leng there rows in t "Progr te (y/n)? exis/ File srcFile = new File(args[0]); if (args.length != 2) { } (Sw l); sEn be ).w 8)) private long newYearMillis; IO Undl NumberFormat.getInstance(); etF on th SRC-FILE-NAME DEST-DIR-NAME"); n "valar am at * "); ts. " nd int stringWidth = fontMetrics.stringWidth(text); private static NumberFormat formatter e= = rounnd(Co; rm Mi } } if (!srcFile.isFile() || !srcFile.canRead()) { System.err.println("Usage: CopyFile ; != are ex Except f } */ set ent abe hic .s ze( 888 la + o 2) Fo M Yeue for error. io ;o 1; creturn (int)(fontSize * 0.95 * private String message; System.err.println("Not a readable file: " ) srcFile.getName());{ System.exit(1); + f ime is // { actly t n h, private JFrame frame; ); ame. lab ackggrou rue) ignmdd(l Grap ce() , nSi 888 over Inva two et public class HappyNewYear implements Runnable e( public static void main(String[] args) throws IOException File ke su } System.exit(1); { ma () se + el ts idt ride lid { t tT ll Syst in ew argu s ame ree (8 fr abel setB ore ue(t alAl ).a ocal Devi componentWidth / stringWidth); private JLabel label; sr file : " "a n if } // make sure the sure there are exactly a readable cF recth // make source file is indeed two arguments n me ); a R) Syst em.err r of r); { l. label) ab en ).w ; e(fr etSc rmat m +e Mi F aq ( tL en l. nts pyN ; +r Y public HappyNewYear(JFrame frame, JL e a d A em.e .pri e (!srcF ile = e sour over r // make sure the if (args.length != 2) directory File srcFile = new File(args[0]); second argument is a { e( private long newYearMillis; JLabel set Op ta ne ont ane ge re BOLD rif" iz .g .fo JL em} p xit( bntln private static NumberFormat l. NumberFormat.getInstance(); r AR sile.isFi w Fi ce fi e E ye lr n Yng a File destDir = new File(args[1]); !srcFile.canRead()) { if (!srcFile.isFile() || System.err.println("Usage: CopyFile SRC-FILE-NAME DEST-DIR-NAME"); S ) Fo rr ride); Siz t . c a e { . lab formatterz = tP ri el private String message; Ha l1); ("Us } if (!destDir.isDirectory()) { System.err.println("Not a n System.exit(1); I readable le() le" + le is file: (arg srcFile.getName()); age: NU a . t be ni e, private JFrame frame;GUIlabe l.seetHo nten ment ultS Font ansSeFont kit(atte e, reen er ;cu Ye s; in s[ // store argument elements o || w Copy } System.err.println("Not a// ma et System.exit(1); directory: " + destDir.getName()); deed publicCmai r); .JA n static void main(String[] args) exnum dar !src 0]); am UI be l.s tC on efa = e ; "S min Tool orm fram etSc "); mb em. new lli File ke private JLabel= label; a n a n G } // make sure the source file is File g a readable file Syst File System.exit(1); indeed su u am l neread la ir D this.frame frame; fr ); re if t. { ri ee a r ; public HappyNewYear(JFrame frame, e e.ge Enlabel) tyle me = ter ult e, f ze( ).g 888! able Syst em.err .canRe // make sure the second new File(args[0]); (! destDi the File srcFile = argument is a directory e; SRC-FIN yst = e( rMi e; t le r } private long newYearMillis;b JLabel v get S ; la m LE-N b o l of ghe a(r Ye da 1)JFrame frame = new JFrame(); t)) em.e .pri ad() e ; n; ; a dest r = a this.label = label; file ls l 8 de fa Si t( File destDir for destination file if (!srcFile.isFile() || cs ; // create File object = new File(args[1]); !srcFile.canRead()) { second { Dir. new ab r! e ee e a xit( ntln ) { ic S f lAME a ) g e, () m private Stringbeginning of fra phiyear font ontNr = etDe tSty Font olki ppy yle, zeTex C am u umg m ; g ret (at xt en , ) isDi File argu File destFile = new File(destDir, srcFile.getName()); if (!destDir.isDirectory()) { System.err.println("Not a readable file: " + srcFile.getName()); 1); ("No // compute message; next ST fa s nn m at = e lng ; YeDE1 a-DIR yea tr ssa fra bel in om rm ne al TH 0 frame.addKeyListener(new KeyListener() or } yl rect " + destDir.getName()); t a me // store argument GUI elements g f be .g fon ne To "Ha St Si me G t System.err.println("Not a directory:or (args[ nt System.exit(1); Fr g Gra -N = C N , read // =u R r e e e st Fe a bni nt w < s y()) 1]); is a Calendar cal = new frame; intrin St rF Ti mi lt nt nt // check if copying is desired given overwrite option } System.exit(1); crea w ge a me this.frame = GregorianCalendar(); m lkit w J AME");r = la inn efr .fo R, , MO AY 0);{ dire e u Syst { public HappyNewYear(JFrame frame, JLabel Nlabel)me, er au le, fo fo r ableefitim mai tpu ng m r( t s J al ; ; nt if (!doCopy(destFile)) { sure the second argument is te directory } // make be File de a File ; et ctor H int {} n l g g ar . A T; _ D 00; ne r e Syst em.err public o void keyPressed(KeyEvent {event)nextYear = cal.get(Calendar.YEAR) ToontNa det tDeftSty me, er, ; this.label = label; fin al SntSiz + 1; y ea nt // create File return;for destination file stFile ob File destDir = new File(args[1]); object at g e le:e L ou g ni g { / eaYea t r= ame l = be in te YE t) OF F_ 10) ; .g () ze) f !" um // ch em.e .pri = tc . uN _ O ( ) ) r J " +nsrin cal.set(Calendar.YEAR, nextYear); yearfo " N = ne in fo } File destFile = new File(destDir, srcFile.getName()); ject fo if (!destDir.isDirectory())if { wY w xi t r me nt a iv n t e l i cFile./ Yew pu o fr be e tr a r p O Y _ p, 0 ( public voidgth Si keyReleased(KeyEvent event) // compute beginning foftGUI elements xt = t.ge fon ntNa Numb un() { t eck w Fi r de // store argument next + + destDir.getName()); lo ng ng ma o = System.err.println("Not a directory: " t(1); ln("None e . r a getN Ne n amt s s. la ut cS lrm = da ut M DA UR eTE , me e le (!doCo if co le(d stin pr iv vat te St in t in eTe == KeyEvent.VK_ESCAPE) frame; Calendar cal = new GregorianCalendar();i py nt en if (event.getKeyChar() cal.set(Calendar.MONTH, Calendar.JANUARY);olk ame, nt(f tSiz el). // copy file, optionally creating r checksum overwrite(d pyin // check if copying is desired given a System.exit(1); option estD atio this.frame = ar } a n p a directpr rlo tri (re py l // h)) ou e(i ;s. mp ra fo r en (o r. HO sNU ND Ti m i a e e, , ar ir, cal.set(Calendar.DAY_OF_MONTH, 1); tSiz To ntN 1; o on ab n )estFilg is de Long checksumSrc } copyFile(srcFile, destFile); if (!doCopy(destFile)) { e = .l fo{ ). int nextYear = cal.get(Calendar.YEAR) + w F f ory: S v at srcF file a n t a t ei ap . } hi co ke = Ye al xt d da r. dMI CO et i p r" f e)) am me this.label = label; f l sire Ye // create Filetreturn;for// object destination file el ile. ); fo n cal.set(Calendar.HOUR_OF_DAY, 0); on xt , s( { d gi + l n t / ma dt xt (C Te e en da er. SE .g co olge System.exit(0); cal.set(Calendar.YEAR, nextYear); year e .min( me, a ar p ri vdest c H fr Na // copy timestamp of last modification } File destFilea = new File(destDir, ab srcFile.getName()); xt Long py fi ve // compute beginning xt retu te le ic Di cal.set(Calendar.MINUTE, 0); int of next ont( th fra C n / a */ rm { / lepu e et et a al en hda r. al e ne b e tName()); i r.getN r ; 1;n ovYo o p if (copyOriginalTimestamp) o{ checl le, e nt E) rn; er cal.set(Calendar.MONTH, Calendar.JANUARY); / Sty aph } (te op new t ame( // copy // check f (!destFile.setLastModified(srcFile.lastModified())) { w dwrite file, optionally creating a checksum Sr tion if if copying is desired l ksum overwrite option() + given s C nt / ut .s .s ( (C al en da c ag + bl // T e; am fo cal.set(Calendar.SECOND, =0); GregorianCalendar(); APCalendar cal etF Ma ear( t )); al N not set " option } 2 nt Gr cal.set(Calendar.DAY_OF_MONTH, s 1); r if { t r L; Long checksumSrc = copyFile(srcFile, pySystem.err.println("Error: Could ) (!doCopy(destFile)) if co be ti { destFile); c = co ly + s i o al el e et (C al en = ss " { Y pu th SC int cal.getTime().getTime(); (cop meen newYearMillis = nextYear = cal.get(Calendar.YEAR) + 1; el. pyFi cra ti a a yO mstam * fo et d R) ppy Y JF ng return; 000 ar le eaA"timestamp of copied file."); public void keyTyped(KeyEvent event) {} cal.set(Calendar.HOUR_OF_DAY, 0); yNew R c al } ab . .s et (C al is me y { id _E ; cal.set(Calendar.YEAR, nextYear); ); / 1 lab JL e lT // copy timestamp of last modification rigina p of ye en (src ngaa A } rm } e( i // prepare a message se pp c al l ry .s et (C ll a pp HN gW th , .g o z}Str VK cal.set(Calendar.MINUTE, 0); Calendar.JANUARY); h) l YE FileU checks imes last } if (copyOriginalTimestamp) { , is( e) ) el cal.set(Calendar.MONTH,w Ha el c t al .s et Mi e Ha rF copy file, optionally creating a checksumCmp r. di s A , dest um ta ( message = "Happy " + nextYear + "!"; id me me ); in Si ); t. dt // xt nna ) mo ); J ill tim s; if (!destFile.setLastModified(srcFile.lastModified())) { fi File s I me ne c { al .s ar ar " cal.set(Calendar.DAY_OF_MONTH, 1); {} { ven cal.set(Calendar.SECOND, 0); ); // optionally be Long file i verify checksumSrc = copyFile(srcFile, destFile);a {r a . tion System.err.println("Error: Could not set " + tW Na ra nt tr ; nt h, Wi a} a nd a de meM } ne iru if (!l r ca ; GU newYearMillis = cal.getTime().getTime(); c al Ye ep = if (verify) u me ; {m fr r "timestamp of copied file."); , ll ) en nt f fo .s ; Fo dt frame.setUndecorated(true); E cal.set(Calendar.HOUR_OF_DAY, 0); ng st e ec a ) tTi lis // t e N ra //l copy timestamp of last modification id l tc nd File.s } destination file..."); c ew pr ge ; JLabel fo s = s( =cs * ri // prepare a message nt nt) Key opti e MiSystem.out.print("Verifying of o o a } iY e , 1 ); et e (copyOriginalTimestamp)mon; ; on ( label i new t JLabel("."); ne tWi n = createChecksum(destFile); () le renrMil e if (v cal.set(Calendar.MINUTE, 0); e e b r Long checksumDest er { ally g v g C ex i l } if Last e n / sa ur a e public vstatic int determineFontSize(JFrame frame, + "!"; se; mp t c ic r 5 s m ty Modi label.setBackground(Color.BLACK); message = "Happy " + ic e f la ea ag (checksumSrc.equals(checksumDest)) l{al TH 0 am ume ra ify) n veri t( nb ab if if (!destFile.setLastModified(srcFile.lastModified())) { e ve = int componentWidth, String nextYear 0); al el i c r rm en cal.set(Calendar.SECOND, int fontStyle, em.cewYe / es fi Y co Fon tri etr Met 0.9 / fontName, { iG e g Ti Syst ed rc tS fy pu C System.err.println("Error:(s F // l nn tat am optionally verify rfile r f } b nnSystem.out.println(" AOK, files are equal.");Could not set " + } e = a ON Y, w ss else te on t) file m ; ) w = f newYearMillis = cal.getTime().getTime();t= (n s ); ; em.e File of n ys = label.setForeground(Color.WHITE); ent nt () Ru s et ; "timestamp .l ocopied file.");) r if e(verify) { }r(J { a = l i ubl l.g AR, H, _M D ; w Me tM nt * th rr.p de mp ex ne me 0)String text) prepare a message label.setOpaque(true); r( ar as ne ond = S g v e _ ) file..."); "!" h( ze b System.out.print("Verifying destination ; Checksums differ."); ri f tM } g System.out.println(" Error: . a // eLong checksumDest a= createChecksum(destFile);g e JF L } g g a s nt od ze ne ont Fon fo ze Wid t co t { E Ev har me l = be =p c .YE ONTSyOFemF 0 ) t( t ln("Er ifiet Si wYe ne sec st y g public static int determineFontSize(JFrame frame, +timeainint; { at te J /*n in } wYe } tor ra (checksumSrc.equals(checksumDest)) 0{ e() Long _O.o, nt f t = Si nt message = "Happy " ne nextYear g "!"; u + 1) Si = label.setHorizontalAlignment(SwingConstants.CENTER); in t ng e Y_ + ror: d())) o xi } r ! in of ; te int {} int fontSize = componentWidth * 2 / text.length(); Ke ey C ; en ont ul { if s . if b e al = r .M DA UR checut, rim String fontName, int utp g Co . me riv va te l*SPr TE ks.p nt nt cs frame.getContentPane().add(label);Key c in tri } * //e optionally f a t ge th nt ne remo fontStyle, yeartrue ge; .e } Font font componentWidth, lean fontStyle,g fontSize); < (che D umi nt(" OK, er ng) on ) ) ; Lis e e, } ) d( d(K t N (verify) s{ verifytfile ar nda ar .System.out.println("Vear files are ,equal."); .l "timesta d not c U ck TDe f ( O l n l m = text) ; le s ) o m in if } pu e N t d o o a p i va te * st intpy fo fo ri GraphicsEnvironment.getLocalGraphicsEnvironment(). String new Font(fontName, ti S ) t w umb ini nt mp ) set // hi . else { Ye le nd ar System.out.println(" eError: gChecksums } p .HelIN O sumS sttY rifyin ra m te an p a lo ring m /** r eFontMetrics fontMetricsboo frame.getGraphics().ain { / ne ar = mess rg tof co " + e( Key ss ase .ge Wi )(f omp * = ta m a message p ri va ate @par dard messaglinesis com dar tSystem.out.print("Verifying destination file..."); differ.");e, ics M createChecksum(destFile); N r .= seEC ge rc.equ=alcreate fdest a ex l e nrema E nt t et s{ d a m t h from x getDefaultScreenDevice().setFullScreenWindow(frame); i h pied s * Print to standard * Ha and in output d ; t y h / wYe x i ev Ch t public static int determineFontSize(JFrame t (re = p yes or no et am readentered. } s( S frame, re e t Ca le n a } r ng nt c x Long checksumDest S { . pu e to ) getFontMetrics(font); Sy AP i on tM file ra w te m t at( e ecks in * standard input until r iv */}@r(y ur messag t unt stan en e if (checksumSrc.equals(checksumDest)) {checks n um ation / t p int (fontSize = componentWidth * 2 if text.length(); put / [] {) p r pu c or n) is e by / lda n ( Ca ale end da r. al ge ne ."); } ne t yP el en i ri final int fontStylee = Font.BOLD; n us F on ic Sym um fi S e ar SC nt JF n * @param message informative text to be answered til user rd et ( } fro orm run int = fontMetrics.stringWidth(text); int fontStyle, c System.out.println("fo Dest(destFil tequal."); th( info / a a stem OK, files 2are le..ra int stringWidth componentWidth, String fontName, fontSize); bl ng ou k yR ev st ( F final String fontName =e "SansSerif"; bl ou am Ye@return user answer, p foriic stat er forswer rmCyeivt .no tput (C Cal len nda = sa + * true bl yes, false an no. at s or s } telse { _E ve w r( ng r.f id Font font = new Font(fontName, fontStyle, String text) e Fr ng .out )) { ne); G ."); d Sy * f E} ri return (int)(fontSize fr 0.95 = * " ic , tr in e l t rn int fontSizeNumber =id ke f ( s System.out.println(".o .printlno et differ."); ); pu /** bo tand ew */ tri tte determineFontSize(frame, c vo FontMetrics fontMetrics * frame.getGraphics(). } VK ne ene ); a te .s(y e ( a e ( (J istem Error: Checksums gWi ol and pu {li (" ey St ut.p h , yN public static boolean readYesNoFromStandardInput(String linesl from{or tn)(C ad l is m * Print a message standard output ean read uemessage)s e to ); in etu h to cfoa xt } be re a line e y componentWidth / stringWidth); .g OK n a Sorma r t = Toolkit.getDefaultToolkit().getScreenSize().width, getFontMetrics(font); t. ; vo oid i { ER { re ow ze tr t nt m p * standard input until yes or no (y or n) is Ye System.out.println(message); ad entered. . s an isCenll s fr pp } (K n( dtrie lne ; ,ifile dt put r l , c yes, a f k pub is 00) {} { {ven NTint . fontSize = componentWidth * 2 / text.length();e Syst sN by a l. se t( i tere nd * @param message } String line; informative text to be answeredoFro user false swered e d.Ha Si , S ed ai ap me yLfontName, fontStyle,}formatter.format(88888888)); font = new th t Erro out intCEstringWidth = fontMetrics.stringWidth(text); { / ma t = Wi Nam ra (" ) trr: s i e eq ic c v c mS . e forM rby " om W ar ( Font Wi Font(fontName, fontStyle, fontSize); p (10 m a H St for no. t { * @return userBufferedReaderfor = newfalse ri em.out a l InputStreamReader( answer, true in yes, BufferedReader(new ual. g ta nt h s r no user f n s / Ch bl i () n t) ) yE . pu } i ); ext int fontSizeText l= determineFontSize(frame, return t(int)(fontSized * "); e Buff ng li .pri and . eep Ty fr dKe */ /** System.in));ne c nt ardIea pa . = s en nt = fo s. FontMetrics fontMetrics = in ecksums Fo dt ypublic static void main(String[] args).w 8) 0.95 * frame.getGraphics(). ut id l np ered pu ub n ee o ln frome ts en nt Ke etT els .sl as public static boolean readYesNoFromStandardInput(String lines es ut(Se * Print a message to = null; output and read c message) { Boolean answer standard diff Re ; Toolkit.getDefaultToolkit().getScreenSize().width, componentWidthh,/ stringWidth); on fo s s( ic * tr ne Wi ke vo me ad ca (mY getFontMetrics(font); Bo (y or p me cr er." an () 88 ev eve == l.s l * standard input until yes or no olea n) aderentered. prgeg tring try System.out.println(message);is in ew sa ); } ead c dt mp t( ic ic tr 95 s ); S messi nt try fontName, fontStyle,{"Happy ; 8888!"); st }) ); d c ra e. = user on lint stringWidth = fontMetrics.stringWidth(text); be * @param message informative text to n answ { String line; be answered n ne sa by / ze 88 t ; Thr la y rmage { co Fon tr etr Me 0. / frame = new JFrame();wi oi * @return userBufferedReaderfor = newfalse=for no. / w s ff != null) {n e) ) answer, true while ((line erin.readLine()) in yes, BufferedReader(new InputStreamReader( ti JF ram . on ic Si 888 label.setFont(new Font(fontName, fontStyle, ); "." ACK E); JFramevir Ful return (int)(fontSize * 0.95 * () en ent r() 0) = nu r e Bu = ered te o xt { t h v a t bl */ System.in)); ; m Syst line.toLowerCase();= w ll line em f frame.addKeyListener(new e() KeyListener() e Me tM on * t gC en ( L T ue ( public static nvoid er Ev v a t( st Math.min(fontSizeNumber, fontSizeText))); t main(String[] args) dRead p e .in) { u public Boolean answer null; = if ("y".equals(line) "yes".equals(line)) { d { static boolean readYesNoFromStandardInput(String message) ); mer(new || e ne nt on f { ic e, re at componentWidth / stringWidth); z in {; sE se tr el .B I {} zanswer en ey yE Ch xi ze W p try System.out.println(message); t co t Inpu fo= Boolean.TRUE; i new HappyNewYear(frame,l label).run(); .WH ic } ub } am Sc orm Si Sw l) ic ). } d( ab or { = tr Si break; tF = Si nt st (K (Ke ey .e { String tS in t ng line; t) p bl fr frame void JFrame(); new ) t( be ph e( JFrame public = een keyPressed(KeyEvent event) {} et f te JL ol or BufferedReader whilenew BufferedReader(new InputStreamReader( eah nt ne in = ((line = while in ri } in.readLine()) != null) { s eamR t nt t c ge de em ); yLi } sed ed etK ; c else en pu ; e( public void keyReleased(KeyEvent System.in));((line = line.toLowerCase(); o o line r ; en la Gra vic gs .g r. r( ra w (C ol frame.addKeyListener(new KeyListener() event) { fo f n r "no".equals(line)) { st )) if ("n".equals(line)o || i St ("y".equals(line) Wid"yes".equals(line)) { me( Ke ti ev { Boolean answer null; = if = in || (f mp es eas t.g ); l e D; if" Siz void , Sc ") ift)(event.getKeyChar() == KeyEvent.VK_ESCAPE) ar co ne nd (C ; nm d( public static() te memain(String[] args) t a Sy .r ) c} o t try Boolean.FALSE; l en t nt nteaMeinanswer ntanswer = Boolean.TRUE;ra ew Prpublic void run() me x dL ng = break; ] de = rou und ue) lig .ad oca{ nD OL er {t kit mat ra get 88! s e n y { a e { i o break; e( i n [ e B S on f T{ JF n JFrame public void keyPressed(KeyEvent event) {} JFrame(); F ont li !=)) (i nu ve while ((line = } in.readLine()) ner null) { ke yR ev fr ; tU el kg ro tr lA () tL re t. ns eF ol or e( (). 88frame = new keyReleased(KeyEvent t = != ll System.exit(0); ng ic } e, ze w r( boolean newYear = false; } else else F line ifs("n line } ) { ri = line.toLowerCase(); yE d ke ( t y public }void w( R) se ab ac eg e( ta ane .ge Sc on Sa in To f izframe.addKeyListener(new KeyListener() event) { bl { yl Si ne e e if ("n".equals(line) || .toLow || "yes".equals(line)) { if ("y".equals(line) t "no".equals(line)) n{ t r y".equal doTE if (event.getKeyChar() == KeyEvent.VK_ESCAPE) Ke ki p oi id if do } ; me. l l etB For aqu zon tP nt ult F " rm ult le, tS l{ ap tSt nt h, pu s(li(S= answer in tu System.out.println("Couldtnot understand answer ("" + answererCase = = Boolean.TRUE; Boolean.FALSE; v } } d( . in ne () ) ra be .s et Op ri en me fa = = te fa ty on oo "H n fo {N E dt ); el e break; ) break;is "").o Please { use y for yeseor n for no."); v public {void keyPressed(KeyEvent event) {} || ; line + c r in keyTyped(KeyEventSystem.exit(0); event) {} p () nW L f La el .s et Ho nt on De le me "yes". i c .C } } } if se wi time ,= System.currentTimeMillis(); me de De tS eF tT , public void public void keyReleased(KeyEvent event) { fo , long 8) ma } equa t}n Ty nt ree h } { else else ("n" b el .s et Co - time) ey ra Kanswer ubl li(l ts ). 88 remaining =J (newYearMillisir et Sty tNa/ =1000L; min ul le e, ber (); y e .equ d long } if (answer == null) { { if ("n".equals(line) || d break; p = Bo ls in { la ab el .s et nv g t n r .ge fo r fa ty m m n b olea dt if (event.getKeyChar() == KeyEvent.VK_ESCAPE) als( an e( 88 i li e f d "no".equals(line)) e) ke nm Sc throw new IOException("Unexpected end ofanswer fromu stdin.");)understand answer ("" + ; input = Boolean.FALSE;{ not e n o e t , ); e S Na u ru } } l ab el .g sE ne p wi st 8 iz String output; vo System.out.println("Could n.TRUE; { ); } am ).a break; line + ""). Please use y for yes or n for no."); ro ll frame.setUndecorated(true); keyTyped(KeyEvent event) {} else fo f mb ki me et etD ont nt zeN ). ; "n id ). ; ") CK ); if (remaining < 1) l ab me ic public void r e ||an o".e on nS 88 } } in.close(); } {} vi Fu System.exit(0); t ng Nu ol Na d JLabelo label = new JLabel("."); 00L ic g f f i el l ra ph qu sw vo e( e) ". LA TE if return answer.booleanValue(); } JF ram brea er = als(li gC , ee t( else t { En t in tri ize To ont t = it. e, t( } tS ab } f ra (a == null) { } if (answer nswe Bool ne)) c iz k; { ta f ru el( .B HI in ); cs .se me Scr rma label.setBackground(Color.BLACK); } n l 10 r == catc i ea from { // new year! ); t b answer l S tS w a } G f ex lk am on fo } catch (IOException ioe) System.out.println("Could not + } s } { nS h (I nullthrow new IOException("Unexpected end of input n.FALS stdin."); understand or .W ("" S { ; / in.c F ( frame.setUndecorated(true); T o N label.setForeground(Color.WHITE); d( Please use y for yeslorhi efor no."); (fr et .fo } E;bl { } line + ""). La l na OExc ( e p n () ) c newYear = true; al on r e, ee lo epti ); () retu se } throw new InternalError( } { li} ze To ont ew min amJLabel label = new JLabel("."); pu fi in te J Co lo Syst label.setOpaque(true); public) void keyTyped(KeyEvent event) {} nt ab ra c ; e .g r , cr ); on in.close(); (); rn output = message; f is ime ioe) em.o )) } return answer.booleanValue(); ro stdin or write to stdout."); } answ "Cannot read th from b w Si } f nt f (n h. fr ra ew d( Co me d(l lG evi ; if" Siz t() tte me tS !" t ut.p er.b == o } } if (answer oo null)u{ new s ll t p } label.setBackground(Color.BLACK); ( D t t label.setHorizontalAlignment(SwingConstants.CENTER); rint nt n ( ; n a e 88 i a lean ex } ); } catch (IOException ioe) throw IOExce new IOException("Unexpected end of input from stdin."); e) ig ad c nD L er nt ki ma r g ln(" ec n ou nd nd frame.getContentPane().add(label); Mi Valu{ on Ma ear O fo f 8 thro } Coul label.setForeground(Color.WHITE); ptio } { eT e(); frame.setUndecorated(true); w ne co nd = r ou ru Al ). Lo ee .B sS Fo ol or e( ). 8 e, zelse d no me s n("U Y GraphicsEnvironment.getLocalGraphicsEnvironment(). tF t throw new w In in.close(); InternalError( nexp { label.setOpaque(true); line t lun kg r se Ti li tU e cders (t al e( et cr nt an ne To f iz t( py yl Si tern JLabel tlabel = new JLabel("."); ew se ecte in g return answer.booleanValue(); getDefaultScreenDevice().setFullScreenWindow(frame); alEr "Cannot read from stdin or write to dstdout."); " e tand // make a String from the .number of seconds label.setBackground(Color.BLACK); of ); label.setHorizontalAlignment(SwingConstants.CENTER); n il se ab+Ba "). Pl e nt Pan t.g ltS Fo "S mi lt e, tS lki ap St nt end yN l } } ror( r uea answ final int fontStyle = Font.BOLD; g re arM } catch (IOException ioe) e. l pp be se }of;input l et tFo paq izo nt ern ("" = = ter fau tyl on oo "H ont fo output = formatter.format(remaining); frame.getContentPane().add(label); e au "Can label.setForeground(Color.WHITE); in } { er n e fr final GraphicsEnvironment.getLocalGraphicsEnvironment(). String cur Ye fontName = "SansSerif"; Ha la not ) ram be omsste tO or te usm y ffo e + me de De tS eF tT , f r, ; } throw new InternalError( ad re r mb i label.setOpaque(true); n l le , e () a l. s din. on e l a w int fontSizeNumber = determineFontSize(frame, m. new getDefaultScreenDevice().setFullScreenWindow(frame); fr read from stdin or write to fstdout.");. se ")H on r tD ty Nyes = et on i u "Cannotom or label.setText(output); ne nu ema elabel.setHorizontalAlignment(SwingConstants.CENTER); JL abe el . et ; C vi ge S t stdi f m a y e b n ( } r g n for r ."f t am m u n or st Toolkit.getDefaultToolkit().getScreenSize().width, e; l ab el .s et En try final =int fontStyle; =e;Font.BOLD;e (r } ; nt on be t. e, tenoDe );tS tN Nu .r e writ th at fontName,r!fontStyle, formatter.format(88888888)); Sy frame.getContentPane().add(label); ls l ab el .g s } e to {L final GraphicsEnvironment.getLocalGraphicsEnvironment(). fo g f um lki am de et fon on ize l) a tru sag 1) stdo l ab me ic 00 = ing ; String fontName = "SansSerif"; fa e g determineFontSize(frame, N f N om rm ut." Thread.sleep(1000); = int fontSizeText = ye = es = determineFontSize(frame, int fontSizeNumber l ra ph < 10 nt rin ze Too nt = t. , t( ntS ab ); fr fo me in ut Toolkit.getDefaultToolkit().getScreenSize().width, ); m getDefaultScreenDevice().setFullScreenWindow(frame); w import java.io.*; import java.util.zip.*; st n tI Manual exploration & documentation do not scale Tool Support Needed i t i e l f ra} i Toolkit.getDefaultToolkit().getScreenSize().width, r ; / finale ear = fontStyle, "Happy; 8888!"); int r. fo ex lk am Fon (fo , ti ema utp ing fontName, fontStyleng= eFont.BOLD; 000 l St tS ea { / n fontName, fontStyle,t)formatter.format(88888888)); () ) } G o i na l on eT oo tN w in me u ri t ng r intn finalY String fontName fontStyle,p(1 wY / w Font(fontName,t = "SansSerif"; label.setFont(new put is me fi ina f iz T fon ne .m fra tp St a lo ong ing ema fontSizeText = determineFontSize(frame, intnefontSizeNumber m= determineFontSize(frame, ne ds e t ll ti Math.min(fontSizeNumber, fontSizeText))); f nt tS ou a or l tr (r n le ou Toolkit.getDefaultToolkit().getScreenSize().width, on t( th r( Mi i new HappyNewYear(frame,Toolkit.getDefaultToolkit().getScreenSize().width, label).run(); .s t( on e = fontStyle, "Happy 8888!"); S f ea } fontName, f ec on Ma ea me is f k fontName, fontStyle, formatter.format(88888888)); s F i ex i ol } ad Y label.setFont(newa ut f ); et int fontSizeText etTdetermineFontSize(frame, = tT ill { / m p Font(fontName, fontStyle, nt bo o re ew o g s n i N Math.min(fontSizeNumber, fontSizeText))); / ut Toolkit.getDefaultToolkit().getScreenSize().width, d Th l. .s label).run(); re arM public void run() er nin py l o fontName, fontStyle, "Happy 8888!"); { new HappyNewYear(frame, p be ur e mb i { } abe Ha } la .c wY nu ma se l ry boolean newYear = false; label.setFont(new Font(fontName, fontStyle, w em ne el e () re t Math.min(fontSizeNumber, fontSizeText))); ; ne do st ( e; { void ! e; ge public th n t( run() new HappyNewYear(frame, label).run(); ru a Sy = { ls ar ru sa { d 1) om rm } ; = ing ; fa ye = t es long time = System.currentTimeMillis(); o. boolean newYear = false; fr i fo n ut < 0) m v = w 0 me i p g ; long remaining = (newYearMillis - time) / 1000L; do r ne ear = ng r 10 ti ema ut in { t) ic e public void run() } String output; t ea { ri t {pu o n p( bl t // ewY pu ng r g ai if (remaining < = System.currentTimeMillis(); wY St a ee long time 1) ut puo m n ut boolean newYear = false; lo ong in em ne a f{ r { sl (o o long remaining = (newYearMillis - time) / 1000L; l tr (r do n e d. } xt S f String // new year! output; ea ak t = { ea Te l newYear = 1) true; i r t if (remaining < = System.currentTimeMillis(); { / m pu long time message; oo Th output = se t b o / u { . long remaining = (newYearMillis - time) / 1000L; d o } el { String // new year! output; } ab else newYear = true; l ry se if (remaining < 1) { t output = message; el { ) { // make a String from the number of seconds } n( // new year! else output = formatter.format(remaining); ru newYear = true; } { output = message; id label.setText(output); from the number of seconds // make a String vo } try c else output = formatter.format(remaining); } { li } b { Thread.sleep(1000); pu label.setText(output); from the number of seconds // make a String } { try output = formatter.format(remaining); } { } Thread.sleep(1000); label.setText(output); } } try } { } Thread.sleep(1000); } } } } Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 7
  • 19. Querying Code - (Simple) name-based searches - Mostly textual matching Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 8
  • 20. Querying Code - (Simple) name-based searches - Mostly textual matching - Name-based searches - Limited use of structural reflection Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 8
  • 21. Querying Code - (Simple) name-based searches - Mostly textual matching - Name-based searches - Limited use of structural reflection PQL ASTLOG ..... ..... JTL Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 ..... - Full query language - Based on reflection, metaprogramming, analysis, etc.. 8
  • 22. SOUL • Smalltalk Open Unification Language • Prolog-based query language • Hybrid (Prolog + Smalltalk) • Inter-language Reflection • Metalanguage over Smalltalk base language • Execute queries over programs • using full structural and computational reflection • using combination of logic and imperative paradigms • Declarative Reasoning about the Structure of Object-Oriented Systems, Roel Wuyts. Proc. TOOLS USA 1998, pp. 112-124. IEEE Computer Society Press, 1998. • Co-evolution of Object-Oriented Software Design and Implementation. Theo D'Hondt, Kris De Volder, Kim Mens & Roel Wuyts, Proc. SACT 2000. Kluwer Academic Publishers, 2000. • A Logic Meta-Programming Approach to Support the Co-Evolution of Object-Oriented Design and Implementation. Roel Wuyts. PhD thesis, Dept. of Computer Science, VUB, 2001. • Supporting software development through declaratively codified programming patterns. Kim Mens, Isabel Michiels & Roel Wuyts. Journal on Expert Systems with Applications, Volume 23, Issue 4, November 2002, Page 405, Elsevier. Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 9
  • 23. Meta Programming “Standard” Program SQL query Queries Expenses Database Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 10
  • 24. Meta Programming “Standard” Program Meta Program SQL query Soul query Queries Queries Expenses Database Software Database Compiler compiles programs JavaDoc generates documentation for programs ... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 10
  • 25. Meta Programming “Standard” Program Meta Program SQL query Soul query Queries Queries Expenses Database Software Database Compiler compiles programs JavaDoc generates documentation for programs ... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 10
  • 26. Meta Programming “Standard” Program Meta Program SQL query Soul query Queries Queries Expenses Database Software Database Compiler compiles programs JavaDoc generates documentation for programs ... Meta Program Programs Works with Program Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Data 10
  • 27. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Family Structure parent of Sophie Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Frank 11
  • 28. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Frank parent of Sophie parentOf(Joe,Mark). parentOf(Joe,Sarah). parentOf(Mark,Kevin). parentOf(Christine,Kevin). parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Family Structure SOUL representation of the family Structure 11
  • 29. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Frank parent of Sophie parentOf(Joe,Mark). parentOf(Joe,Sarah). parentOf(Mark,Kevin). parentOf(Christine,Kevin). parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Family Structure SOUL representation of the family Structure Logic fact 11
  • 30. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Frank parent of Sophie parentOf(Joe,Mark). parentOf(Joe,Sarah). parentOf(Mark,Kevin). parentOf(Christine,Kevin). parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Family Structure Predicate SOUL representation of the family Structure Logic fact 11
  • 31. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Frank Family Structure parent of Sophie SOUL Constant parentOf(Joe,Mark). parentOf(Joe,Sarah). parentOf(Mark,Kevin). parentOf(Christine,Kevin). parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Predicate SOUL representation of the family Structure Logic fact 11
  • 32. SOUL: Prolog-based query language Joe parent of Christine Mark parent of parent of Kevin parent of Sarah parent of Frank Family Structure parent of Sophie SOUL Constant parentOf(Joe,Mark). parentOf(Joe,Sarah). parentOf(Mark,Kevin). parentOf(Christine,Kevin). parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Predicate SOUL representation of the family Structure Logic fact 11
  • 33. SOUL: Prolog-based query language Joe parent of Christine parent of if Mark parentOf(?parent,?child) Family Structure Sarah Frank parent of parent of Kevin parent of parent of Sophie ?child -> Mark ?parent -> Joe SOUL Constant parentOf(Joe,Mark). ?child -> Sarah Predicate parentOf(Joe,Sarah). ?parent -> Joe SOUL representation parentOf(Mark,Kevin). of the ... parentOf(Christine,Kevin). family Structure parentOf(Sarah,Sophie). parentOf(Frank,Sophie). Logic fact Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 11
  • 34. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine Mark grandparent of parent of couple Sarah parent of parent of parent of Kevin Frank parent of Deduced Family Structure Sophie cousin Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 12
  • 35. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine Mark grandparent of parent of couple Sarah parent of parent of parent of Kevin Frank parent of Deduced Family Structure Sophie cousin grandparentOf(?grandparent,?grandchild) if parentOf(?grandparent,?parent), parentOf(?parent,?grandchild). couple(?personA,?personB) if parentOf(?personA,?child), parentOf(?personB,?child) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL deduction rules 12
  • 36. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine Mark grandparent of parent of couple Sarah parent of parent of parent of Kevin Deduced Family Structure Frank parent of Sophie cousin logic rule grandparentOf(?grandparent,?grandchild) if parentOf(?grandparent,?parent), parentOf(?parent,?grandchild). couple(?personA,?personB) if parentOf(?personA,?child), parentOf(?personB,?child) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL deduction rules 12
  • 37. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine Mark grandparent of parent of couple Sarah parent of parent of parent of Kevin Deduced Family Structure Frank parent of Sophie cousin logic rule SOUL variable grandparentOf(?grandparent,?grandchild) if parentOf(?grandparent,?parent), parentOf(?parent,?grandchild). couple(?personA,?personB) if parentOf(?personA,?child), parentOf(?personB,?child) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL deduction rules 12
  • 38. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine Mark grandparent of parent of couple Sarah parent of parent of parent of Kevin Deduced Family Structure Frank parent of Sophie cousin logic rule SOUL variable grandparentOf(?grandparent,?grandchild) if parentOf(?grandparent,?parent), parentOf(?parent,?grandchild). couple(?personA,?personB) if parentOf(?personA,?child), parentOf(?personB,?child) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL deduction rules 12
  • 39. SOUL: Prolog-based query language grandparent of Joe parent of couple Christine grandparent of parent of couple if grandparentOf(?grandparent,?child) Deduced Mark Sarah Frank parent of parent of parent of Kevin parent of Family Structure Sophie logic rule SOUL variable ?grandparent -> Joe ?child -> Kevin grandparentOf(?grandparent,?grandchild) if parentOf(?grandparent,?parent),-> Joe ?grandparent ?child -> Sophie parentOf(?parent,?grandchild). SOUL deduction rules couple(?personA,?personB) if parentOf(?personA,?child), parentOf(?personB,?child) cousin Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 12
  • 40. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Software structure 13
  • 41. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: class(Node). class(StatementList). subclassOf(StatementList,CompositeNode). variableInClass(name,Node). methodInClass(getChildren,CompositeNode). .... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Software structure Reification Representational mapping 13
  • 42. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: class(Node). class(StatementList). subclassOf(StatementList,CompositeNode). variableInClass(name,Node). methodInClass(getChildren,CompositeNode). .... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Software structure Reification Representational mapping 13
  • 43. SOUL: Prolog-based query language Node name printOn: Expression ... ... if class(?x) Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: class(Node). class(StatementList). Smalltalk classes --- all subclassOf(StatementList,CompositeNode). variableInClass(name,Node). methodInClass(getChildren,CompositeNode). .... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Software structure Reification Representational mapping 13
  • 44. SOUL: Reification The SOUL results contain the *real* classes. i.e.: the Smalltalk class values idem for methods, packages, namespaces, etc... Representational mapping is computed using a logic rule that uses Smalltalk reflection Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 14
  • 45. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: classInHierarchyOf(?class,?superclass) if subclassOf(?class,?superclass). classInHierarchyOf(?class,?superclass) if subclassOf(?interclass,?superclass), classInHierarchyOf(?class,?interclass). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Deduced Software structure SOUL logic reasoning 15
  • 46. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Deduced Software structure Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: logic alternatives classInHierarchyOf(?class,?superclass) if subclassOf(?class,?superclass). classInHierarchyOf(?class,?superclass) if subclassOf(?interclass,?superclass), classInHierarchyOf(?class,?interclass). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL logic reasoning 15
  • 47. SOUL: Prolog-based query language Node name printOn: Expression ... ... Statement ... ... Deduced Software structure Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList printOn: logic alternatives classInHierarchyOf(?class,?superclass) if subclassOf(?class,?superclass). classInHierarchyOf(?class,?superclass) if subclassOf(?interclass,?superclass), classInHierarchyOf(?class,?interclass). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 SOUL logic reasoning 15
  • 48. SOUL: Prolog-based query language Node name printOn: if classInHierarchyOf(?class,[ApplicationModel]) Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: Deduced Software structure StatementList printOn: logic alternatives classInHierarchyOf(?class,?superclass) if -- all subclasses of ApplicationModel -subclassOf(?class,?superclass). SOUL logic classInHierarchyOf(?class,?superclass) if reasoning subclassOf(?interclass,?superclass), classInHierarchyOf(?class,?interclass). Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 15
  • 49. SOUL: Prolog-based query language • Unification: • powerful pattern matching • multi-way predicates • Recursion: detection of... • ...recursive patterns in code • ...composite structures in code • Declarative: • focuses on ‘what’ instead of ‘how’ • multiple alternative descriptions of same pattern Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 16
  • 50. Smalltalk vs Soul result := Collection new. Smalltalk allClasses do:[:class | class selectors do:[:selector | | method | method := class compiledMethodAt: selector. class instVarNames do:[:name | (method writesField:(class instVarIndexFor: name)) ifTrue:[result add: method]]]]. ^result if methodInClass(?method,?class), methodWithAssignment(?method, assign(?field,?value)), instanceVariableInClass(?field,?class) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 17
  • 51. SOUL: Reasoning over OO programs Finding a hard coupling between a class and its subclass: SuperClass someMethod <<references>> SubClass Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 18
  • 52. SOUL: Reasoning over OO programs Finding a hard coupling between a class and its subclass: SuperClass someMethod <<references>> SubClass if class(?c), methodInClass(?method,?c), methodReferencesClass(?method,?s), classBelow(?s,?c) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 18
  • 53. Finding subclass dependencies SuperClass someMethod <<references>> SubClass Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 19
  • 54. Finding subclass dependencies SuperClass someMethod <<references>> SubClass if class(?c), methodInClass(?method,?c), methodReferencesClass(?method,?s), classBelow(?s,?c) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 19
  • 55. Finding subclass dependencies SuperClass someMethod <<references>> SubClass if class(?c), methodInClass(?method,?c), methodReferencesClass(?method,?s), classBelow(?s,?c) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 19
  • 56. Finding subclass dependencies SuperClass someMethod <<references>> SubClass if class(?c), methodInClass(?method,?c), methodReferencesClass(?method,?s), classBelow(?s,?c) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 19
  • 57. Finding subclass dependencies SuperClass someMethod <<references>> SubClass if class(?c), methodInClass(?method,?c), methodReferencesClass(?method,?s), classBelow(?s,?c) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 19
  • 58. Finding subclass dependencies Queries through 7734 classes and 72962 methods in 44 seconds to find 360 solutions Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 20
  • 59. SOUL: Reasoning over OO programs Finding the “Composite” design pattern compositePattern(?comp,?composite,?msg) if compositeStructure(?comp,?composite), compositeAggregation(?comp,?composite,?msg) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 21
  • 60. Finding the Composite Pattern Finding the “Composite” structure Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 22
  • 61. Finding the Composite Pattern Finding the “Composite” structure compositeStructure(?comp,?composite) if class(?comp), classInHierarchyOf(?composite,?comp) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 22
  • 62. Finding the Composite Pattern Finding the “Aggregation” structure compositeAggregation(?comp,?composite,?msg) if classesImplementCommonSelector(?comp,?composite,?msg), methodWithNameInClass(?method,?msg,?composite), statementOfMethod(?statement,?method), iterationStatementWithBlock(?statement,?iterationBlock), blockIteratesMessage(?iterationBlock,?msg) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 23
  • 63. Finding the Composite Pattern Finding the “Aggregation” structure compositeAggregation(?comp,?composite,?msg) if classesImplementCommonSelector(?comp,?composite,?msg), methodWithNameInClass(?method,?msg,?composite), statementOfMethod(?statement,?method), iterationStatementWithBlock(?statement,?iterationBlock), blockIteratesMessage(?iterationBlock,?msg) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 23
  • 64. Finding the Composite Pattern Some of the results in the Smalltalk image: Queries through 7734 classes and 72962 methods in 13 minutes to find 81 solutions (Soul is about 10x slower than commercial Prologs) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 24
  • 65. Teaser: Visual Query Language Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 25
  • 66. SOUL: Hybrid Language if classInHierarchyOf(?class,[ApplicationModel]) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 26
  • 67. SOUL: Hybrid Language The SOUL results contain the *real* classes. i.e.: the Smalltalk class values Smalltalk expression evaluating to the class ApplicationModel if classInHierarchyOf(?class,[ApplicationModel]) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 26
  • 68. SOUL: Hybrid Language The SOUL results contain the *real* classes. i.e.: the Smalltalk class values Smalltalk expression evaluating to the class ApplicationModel if classInHierarchyOf(?class,[ApplicationModel]) className(?class,?name) if class(?class), equals(?name,[?class name]). smallerThan(?x,?y) if [?x < ?y] Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 26
  • 69. SOUL: Hybrid Language The SOUL results contain the *real* classes. i.e.: the Smalltalk class values Smalltalk expression evaluating to the class ApplicationModel if classInHierarchyOf(?class,[ApplicationModel]) className(?class,?name) if class(?class), equals(?name,[?class name]). smallerThan(?x,?y) if [?x < ?y] Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Smalltalk expression - resulting object is unified with ?name 26
  • 70. SOUL: Hybrid Language The SOUL results contain the *real* classes. i.e.: the Smalltalk class values Smalltalk expression evaluating to the class ApplicationModel if classInHierarchyOf(?class,[ApplicationModel]) className(?class,?name) if class(?class), equals(?name,[?class name]). smallerThan(?x,?y) if [?x < ?y] Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 Smalltalk expression - resulting object is unified with ?name Smalltalk expression - evaluates to true or false 26
  • 71. SOUL: Reification revisited Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList Software structure Reification printOn: class(?class) if member(?class,[Smalltalk allClasses]). methodInClass(?method,?class) if member(?method,[?class selectors collect:[:sel | ?class compiledMethodAt: sel]]) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 27
  • 72. SOUL: Reification revisited Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList Software structure Reification printOn: class(?class) if member(?class,[Smalltalk allClasses]). a collection of all classes methodInClass(?method,?class) if member(?method,[?class selectors collect:[:sel | ?class compiledMethodAt: sel]]) Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 27
  • 73. SOUL: Reification revisited Node name printOn: Expression ... ... Statement ... ... Composite Node children getChildren printOn: VariableRef var Message Send message printOn: printOn: MethodNode source header printOn: StatementList Software structure Reification printOn: class(?class) if member(?class,[Smalltalk allClasses]). a collection of all classes methodInClass(?method,?class) if member(?method,[?class selectors collect:[:sel | ?class compiledMethodAt: sel]]) a collection of all methods on a class Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 27
  • 74. SOUL: hybrid language • Reification of *real* Smalltalk meta-objects: • Meta-objects are normal objects • can reason about ‘structure’ and ‘runtime values’ Advantages: • Open Unification • Causal link • Passing of results to other (Smalltalk) tools • Full reflection inherited from Smalltalk • Uniform approach for reasoning about any object, including runtime values Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 28
  • 75. SOUL • Smalltalk Open Unification Language • Queries on-line over a codebase • Structural as well as computational reflection • Supports reasoning over code in a declarative style SOUL Inference Engine Smalltalk Reflection Smalltalk image Logic Programs Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 29
  • 76. SOUL • Smalltalk Open Unification Language • Queries on-line over a codebase • Structural as well as computational reflection • Supports reasoning over code in a declarative style SOUL Inference Engine Logic Programs Smalltalk Reflection Smalltalk image JavaConnect + JDT Eclipse workspace Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 29
  • 77. SOUL • Smalltalk Open Unification Language • Queries on-line over a codebase • Structural as well as computational reflection • Supports reasoning over code in a declarative style SOUL Logic Programs Smalltalk image JavaConnect + JDT Eclipse workspace JavaConnect + Soot Inference Engine Smalltalk Reflection Soot Analysis Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 29
  • 78. SOUL: Open Unification cfr. ‘Open Implementations’ • Define precise unification semantics for entities • Hiding ‘imperative matching’ process • For example: • Unification of objects based on object identity • Unification of parsetrees via pattern matching • Unification of variables based on points-to analysis • etc.... Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 30
  • 79. SOUL: Open Unification Unification protocol implemented by parsetree items: Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 31
  • 80. SOUL: Open Unification Unification protocol implemented by parsetree items: Objects match based on object identity Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 31
  • 81. SOUL: Open Unification Unification protocol implemented by parsetree items: Objects match based on object identity Method (objects) match if source code is identical Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 31
  • 82. SOUL: Open Unification Unification protocol implemented by parsetree items: Objects match based on object identity Method (objects) match if source code is identical Enhanced parsetree matching: Expression match based on points-to-analysis Method bodies match based on control-flow analysis Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 31
  • 83. Demonstration Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 32
  • 84. Active Software Documentation Software Documentation Calls to database - After state change - Follows DB protocol (open + (read/write) + close) • Enforce: • Adhering to DB protocol • Using Factory methods • Coding conventions Factory Methods - Must be used to create objects consistently Coding Convention - Classes implementing events: ‘*Event’ - Must be adhered for consistency Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 33
  • 85. Intensional Views Excerpt from a discussion with a software developer: I want to be able to express additional semantic constraints on my programs. An example would be « the GUI module should never call the database directly ». What I lack is a tool that allows me to specify such constraints (like « the GUI shouldn't call the DB ») as queries so that I am able to check to see where in the code base this ad-hoc rule has been broken. Furthermore, the ability of doing so would allow me to mould fuzzy or implicit structures in my code base into more concrete structures. What I mean is that when I'm coding, I often have a mental concept of my "working set" which might be something like "The GUI classes and the parts of the back-end which are called directly from the GUI". I need a tool that allows me to create views which match my mental model of the program slice which I'm working on. And these views should preferably be dynamic, so that if my code changes the views still correspond to the intended slice. Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 34
  • 86. Conclusions Declarative Source Code Queries Specify what to find in source code rather than focusing on how Extract patterns, metrics, etc... Hybrid SOUL Language Mix of imperative and declarative paradigms Direct manipulation of objects (easy of integration with external tools) Domain-specific (i.e. language specific) unification of programs Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 35
  • 87. Links... SOUL: http://prog.vub.ac.be/SOUL/ Declarative Metaprogramming: http://prog.vub.ac.be/DMP/ Intensional Software Views: http://www.intensional.be Active Software Documentation using Logic Metaprogramming Monday 30 March 2009 36