Video

Friday 1 July 2011

lesson 9-Streams and Exceptions

9. Streams and

Exceptions

To be able to read and

write files

To understand the

concepts of text and

binary streams

To learn how to throw

and catch exceptions

To be able to process

the command line

To be able to

read and write

objects using

serialization

Streams, Readers, and

Writers

There are two

different ways to store

data: text or binary

formats.

In text format, data

items are represented

in human-readable

form, as a sequence of

characters. For

example, the interger

12345 is stored as the

sequence of five

characters:

‘1’ ‘2’ ‘3’ ‘4’ ‘5’

In binary form, data

items are represented

in bytes. For example,

the integer 12345 is

stored as a sequence

of four bytes:

0 0 48 57 (because

12345 = 48*256 + 57)

We use the Reader and

Writer classes and

their subclasses to

process text files:

FileReader reader =

new FileReader

(“input.txt”);

int next = reader.read()

; // read() will return int

char c;

if (next != -1) // returns

-1 if the end of input

c = (char) next;

FileWriter writer =

new FileWriter

(“output.txt”);

Text input and output

are more convenient

for humans.

To read text data from

a disk file, we create a

FileReader object:

FileReader reader =

new FileReader

(“input.txt”);

To write text data to a

disk file, we use

FileWriter:

FileWriter writer =

new FileWriter

(“Output.txt”);

The Reader class has a

method read to read a

single character at a

time. However, the

read method returns an

int, thus we have to

cast it to a char.

Reader reader = …;

int next = reader.read()

;

char c;

if (next != -1)

c = (char)next;

We use the

InputStream and

OutputStream classes

and their subclasses to

process binary files:

FileInputStream in = new

FileInputStream

(“input.dat”);

int next = in.read();

byte b;

if (next != -1) // returns -1 if

the end of input

b = (byte) next;

FileOutputStream

output = new

FileOutputStream

(“output.dat”);

Binary storage is more

compact and more

efficient.

To read binary data

from a disk file, we

create a

FileInputStream

object:

FileInputStream

inputStream = new

FileInputStream

(“input.dat”);

To write binary data to

a disk file, we use

FileOutputStream:

FileOutputStream

outputStream = new

FileOutputStream

(“output.dat”);

The InputStream class

has a method read to

read a single byte at a

time. The method also

returns an int, thus we

have to cast it to a

byte.

InputStream in = …;

int next = in.read();

byte b;

if (next != -1)

b = (byte)next;

The Writer and

FileOutputStream

classes have a write

method to write a

single character or

byte.

Reading and Writing

Text Files

We construct a

FileWriter object from

the file name:

FileWriter writer =

new FileWriter

(“output.txt”);

We then send a

character at a time to

the file by calling the

write method.

However, the output

we have is in the form

of numbers and strings

so we need another

class whose task is to

break up numbers and

strings into individual

characters and send

them to a writer.

This class is called

PrintWriter.

PrintWriter out = new

PrintWriter(writer);

Next, we can use the

print and println

methods to print

numbers, objects, and

strings:

out.print(29.95);

out.println(new

Rectangle(5, 10, 15, 25)

);

out.println(“Hello.”);

Reading text files is

less convenient, we

have to use the

BufferedReader class,

which has a readLine

method .

After reading a line of

input, we can convert

the strings to integer

or double as needed.

Reading and Writing

Text Files

To write:

FileWriter writer =

new FileWriter

(“output.txt”);

PrintWriter out = new

PrintWriter(writer);

out.print(29.95);

out.println(new

Rectangle(5, 10, 15, 25)

);

out.println(“Hello,

World!”);

To read:

FileReader reader =

new FileReader

(“input.txt”);

BufferedReader in =

new BufferedReader

(reader);

String inputLine =

in.readLine();

double x =

Double.parseDouble

(inputLine);

When we are done

reading/writing from a

file, we should close

the files:

reader.close();

writer.close();

Before we can put

these input/output

classes to work, we

need to know about

exception handling.

All classes for reading

and writing are defined

in the java.io package.

Exception Handling

An exception is an

indication that a

problem occurred

during the program’s

execution.

When a method

detects a problematic

situation, what should

it do?

The methods should

return an indicator

whether it succeeded

or failed.

The exception-

handling mechanism

has been designed to

solve these problems.

When we detect an

error, we only need to

throw an appropriate

exception object.

The programmer

encloses in a try block

the code that may

generate an exception.

The try block is

followed by zero or

more catch blocks.

Each catch block

specifies the type of

exception it can catch

and contains an

exception handler.

An optional finally

block provides code

that always executes

regardless of whether

or not an exception

occurs.

If there is no catch

blocks following a try

block, the finally block

is required.

When an exception is

thrown, program

control leaves the try

block and the catch

blocks are searched.

If the type of the

thrown exception

matches the

parameter type in one

of the catch blocks,

the code for that catch

block is executed.

If a finally block

appears after the last

catch block, it is

executed regardless

of whether or not an

exception is thrown.

An exception can be

thrown from

statements in the

method, or from a

method called directly

or indirectly from the

try block.

The point at which the

throw is executed is

called the throw point.

The throw statement

is executed to indicate

that an exception has

occurred (i.e., a

method could not

complete

successfully).

This is called throwing

an exception.

Throwing an exception

is simple, the Java

library provides many

classes to signal all

sorts of exceptional

conditions.

For example, to signal

an unexpected end of

file, you can use the

EOFException, a

subclass of the

IOException class,

which denotes input/

output errors.

We make an object of

the exception class,

and then throw it.

For example, suppose

we write a method

that reads a product in

from a reader:

public class Product

{ private String name;

private double price;

private int score;

public void read

(BufferedReader in)

{ // read product name

name = in.readLine();

// read product price

String inputLine =

in.readLine();

price =

Double.parseDouble

(inputLine);

// read product score

inputLine = in.readLine

();

score =

Integer.parseInt

(inputLine);

}

}

What happens if we

reach the end of input?

String inputLine =

in.readLine();

if (inputline == null)

// do something

We can throw an

exception as follows:

String inputLine =

in.readLine();

if (inputline == null)

{ EOFException

exception = new

EOFException(“End of

file when reading

price.”);

throw exception;

}

Or we can just throw

the object that the

new operator returns:

String inputLine =

in.readLine();

if (inputline == null)

throw new

EOFException(“End of

file when reading

price.”);

Throwing a new

exception like an

example above is used

when you want to

generate an exception

under your control

rather than letting Java

generate it for you.

Throwing Exceptions

Throwing Exceptions

throw

exceptionObject;

throw new

IllegalArgumentExcept

ion();

...

String input =

in.readLine();

if (input == null)

throw new

EOFException(“EOF

when reading”);

When we throw an

exception, the method

exits immediately.

Execution does not

continue with the

method’s caller but

with an exception

handler.

Catching Exceptions

When an exception is

thrown, an exception

handler needs to catch

it.

We must install

exception handler for

all exceptions that our

program might throw.

We install an

exception handler with

the try statement.

Each try block contains

one or more method

calls that may cause an

exception, and catch

clauses for all

exception types.

Catching Exceptions

try

{ statement

}

catch(ExceptionClass

exceptionObject)

{ statement

}

catch(ExceptionClass

exceptionObject)

{ statement

}

finally

{ ….

}

if use "try block" you

must have whichever

"catch" or "finally"

If a try block has a

corresponding finally

block, the finally block

will be executed even

if the try block is

exited with return,

break or continue; then

the effect of the

return, break or

continue will occur.

When the catch

(IOException e) block

is executed, then

some method in the

try block has failed

with an IOException,

and that exception

object is stored in the

variable e.

try

{ BufferedReader in =

new BufferedReader(

new

InputStreamReader

(System.in));

System.out.println

(“How old are you?”);

String input =

in.readLine();

int age =

Integer.parseInt(input)

;

age++;

System.out.println

(“Next year, you’ll be ”

+ age);

}

catch(IOException e)

{ System.out.println

(“Input/Output error ” +

e);

}

catch(

NumberFormatExcepti

on e)

{ System.out.println

(“Input was not a

number”);

}

The try block contains

six statements with

two potential

exceptions that can be

thrown in this code.

The readLine method

can throw an

IOException, and

Integer.parseInt can

throw a

NumberFormatExcepti

on.

If either of these

exceptions is thrown,

the rest of the

instructions in the try

block are skipped.

Finally

Suppose a method

open a file, calls one

or more methods, and

then close the file:

BufferedReader in;

in = new

BufferedReader(new

FileReader(“input.txt”))

;

readProducts(in);

in.close();

Next, suppose that one

of the methods throws

an exception, then the

call to close is never

executed.

We solve this problem

by placing the call to

close inside a finally

clause:

BufferedReader in =

null;

try

{ in = new

BufferedReader(

new FileReader

(“input.txt”));

readProducts(in);

}

// optional catch

clauses here

finally

{ if (in != null)

in.close();

}

Checked Exception

Exceptions fall into

two categories, called

checked and

unchecked exceptions.

When you call a

method that throws a

checked exception,

you must tell the

compiler what we are

going to do about the

exception if it is

thrown.

We do not need to

keep track of

unchecked exceptions.

Exceptions that belong

to subclasses of

RuntimeException are

unchecked.

Checked Exceptions:

For example: All

subclasses of

IOException;

InterruptedException;

invent yourself by

subclassing Exception.

Unchecked Exceptions:

For example:

RuntimeException and

all subclasses;

ArithmeticException;

IllegalArgumentExcept

ion;

NumberFormatExcetpi

on;

IndexOutOfBoundsExce

ption;

NullPointerException;

invent yourself by

subclassing

RuntimeException

Checked exceptions

must be listed in

method's throws

clause, if we do not

provide catch clause.

Exception Class

Hierarchy

Suppose we want to

throw an IOException

in a method that call

BufferedReader.

readLine.

The IOException is a

checked exception, so

we need to tell the

compiler what we are

going to do about it.

We have two choices:

We can place the call

to the readLine inside a

try block and supply a

catch clause.

We can simply tell the

compiler by tagging

the method with a

throws clause.

Throws Clauses

If we want the

exception to go

uncaught and rely on

the propagation

mechanisms to catch

the exception

elsewhere, we use a

throws clause in the

method header.

Use a throws clause

only if a method will

throw one of the

checked exceptions.

public class Product

{ public void read

(BufferedReader in)

throws IOException

{ ... }

}

The throws clause

signals the caller that

it may encounter an

IOException.

If a method can throw

multiple checked

exceptions, we

separate them by

commas:

public void read

(BufferedReader in)

throws IOException,

FileNotFoundException

Error do not need to be

listed, nor do

RuntimeException

Examples: an out-of-

bounds array subscript,

arithmetic overflow,

division by zero,

invalid method

parameters, memory

exhaustion.

Example

In the next example,

we pass along all

exceptions of type

IOException that

readLine may throw.

When we encounter an

unexpected end of file,

we report it as an

EOFException.

For an expected end of

file, if the end of file

has been reached

before the start of a

record, the method

simply returns false.

If the file ends in the

middle of a record (an

unexpected end of file)

, then we throw an

exception.

public class Product

{ public boolean read

(BufferedReader in)

throws IOException

{ // read product name

name = in.readLine();

if (name == null) return

false;

String inputLine =

in.readLine();

if (inputLine == null)

throw new

EOFException (“EOF

when reading price.”);

price =

Double.parseDouble

(inputLine);

inputLIne = in.readLine

();

if (inputLine == null)

throw new

EOFException (“EOF

when reading score.”);

score =

Integer.parseInt

(inputLine);

return;

}

private String name;

private double price;

private int score;

}

Example

The following method

reads product records

and puts them into a

panel.

It is unconcerned with

any exceptions.

If there is a problem

with the input file, it

simply passes the

exception to its caller.

public void

readProducts

(BufferedReader in)

throws IOException

{ boolean done = false;

while (!done)

{

Product p = new

Product();

if (p.read(in))

panel.addProduct(p);

else

done = true;

}

}

Next, we implement

the user interaction.

We ask the user to

enter a file name and

read the product file.

If there is a problem,

we report the problem.

The program is not

terminated, and the

user has a chance to

open a different file.

public void openFile()

{ BufferedReader in =

null;

try

{ // select file name

. . .

in = new

BufferedReader(new

FileReader(selectFile))

;

readProducts(in);

}

catch(

FileNotFoundException

e)

{ JOptionPane.

showMessageDialog

(null, “Bad filename.

Try again.”);

}

catch(IOException e)

{ JOptionPane.

showMessageDialog

(null, “Corrupted file.

Try again.”);

}

finally

{ if (in != null)

try

{ in.close();

}

catch(IOException e)

{ JOptionPane.

showMessageDialog

(null, “Error closing

file.”);

}

}

}

Example

import java.io.*;

public class StringEx2 {

public static void main

(String args[]) throws

IOException {

FileWriter writer = null;

try {

String input2;

InputStreamReader

reader = new

InputStreamReader

(System.in);

BufferedReader

console = new

BufferedReader

(reader);

System.out.print("Enter

filename ");

input2 =

console.readLine();

writer = new

FileWriter(input2);

PrintWriter out = new

PrintWriter(writer);

double gpa;

System.out.print("Enter

Your GPA ");

input2 =

console.readLine();

gpa =

Double.parseDouble

(input2);

System.out.println

("Your GPA is " + gpa);

out.println(gpa);

}

catch(

NumberFormatExcepti

on e)

{ System.out.println

("Input was not a

number.");

}

catch(

FileNotFoundException

e)

{ System.out.println

("Bad filename. Try

again.");

}

finally {

writer.close();

}

}

}

Example: Reading Input

Data in a Graphical

Program

Recall the BestProduct

program that reads

product data from the

keyboard and then

marks the best buys.

In this example, we

will use a text editor

to write the data

values into a file and

then specify the name

of the file when the

data are to be used.

This program will

display its result in a

graph.

Prices grow in the x-

direction, performance

grows in the y-

direction.

// PlotProducts.java

/**

Reads products and

adds them to the

product panel.

@param in the buffered

reader to read from

*/

public void

readProducts

(BufferedReader in)

throws IOException

{ ...

boolean done = false;

while (!done)

{ Product p = new

Product();

if (p.read(in))

panel.addProduct(p);

else // last product

read

done = true;

}

}

// PlotProducts.java

/**

Handles the open file

menu. Prompts user

for file

name and reads

products from file.

*/

public void openFile()

{ BufferedReader in =

null;

try

{ // show file chooser

dialog

JFileChooser chooser

= new JFileChooser();

if (chooser.

showOpenDialog(null)

==

JFileChooser.APPROVE_

OPTION)

{ // construct reader

and read products

File selectedFile =

chooser.

getSelectedFile();

in = new

BufferedReader(new

FileReader

(selectedFile));

readProducts(in);

}

}

catch(

FileNotFoundException

e)

{ JOptionPane.

showMessageDialog

(null, "Bad filename.

Try again.");

}

catch(IOException e)

{ JOptionPane.

showMessageDialog

(null, "Corrupted file.

Try again.");

}

finally

{ if (in != null)

try

{ in.close();

}

catch(IOException e)

{ JOptionPane.

showMessageDialog

(null, "Error closing

file.");

}

}

}

Command Line

Arguments

There are several

methods of starting a

program:

By selecting “Run” in

the compilation

environment

By clicking on an icon

By typing the name of

the program at a

prompt in a terminal or

shell window

The latter method is

called “invoking the

program from the

command line”.

For the latter method,

we can also type in

additional information

that the program can

use.

These additional

strings are called

command line

arguments.

For example, if we

start a program with

the command line

java Prog –v input.txt

then the program

receives two

command line

arguments: the strings

“-v” and “input.txt”.

Java usually interprets

strings with a – as

options and other

strings as file names.

Command line

arguments are placed

in the args parameter

of the main method:

public static void main

(String[] args)

{ …

}

Thus args contains

two strings

args[0] = “-v”

args[1] = “input.txt”

Encryption

Encryption is a method

used for scrambling a

file so that it is

unreadable except to

those who know the

decryption method and

the secret keyword.

The person performing

any encryption

chooses an encryption

key; here we use a

number between 1 and

25 as the key.

This key indicates the

shift to be used in

encrypting each letter.

For example, if the key

is 3, replace A with a

D, B with an E, and so

on.

Encryption

To encrypt/decrpt data

java Crypt input.txt

encrypt.txt

java Crypt -d -k11

encrypt.txt output.txt

The program takes to

following command

line arguments:

- An optional -d flag to

indicate decryption

- An optional

encryption key, -k flag

- input file name

- output file name

If no key is specified,

then 3 is used.

import java.io.*;

public class Crypt

{ public static void

main(String[] args)

{ boolean decrypt =

false;

int key = DEFAULT_KEY;

FileReader infile = null;

FileWriter outfile =

null;

if (args.length < 2 ||
args.length > 4) usage

();

// gather command line

arguments and open

files

try

{ for (int i = 0; i <
args.length; i++)
{ if (args[i].substring(0,
1).equals("-"))
// it is a command line
option
{ String option = args[i]
.substring(1, 2);
if (option.equals("d"))
decrypt = true;
else if (option.equals
("k"))
{ key =
Integer.parseInt
(args[i].substring(2));
if (key < 1 || key >=

NLETTERS)

usage();

}

}

else

{ if (infile == null)

infile = new FileReader

(args[i]);

else if (outfile == null)

outfile = new

FileWriter(args[i]);

}

}

}

catch(IOException e)

{ System.out.println

("Error opening file");

System.exit(0);

}

if(infile == null || outfile

== null) usage();

// encrypt or decrypt

the input

if (decrypt) key =

NLETTERS - key;

try

{ encryptFile(infile,

outfile, key);

infile.close();

outfile.close();

}

catch(IOException e)

{ System.out.println

("Error processing

file");

System.exit(0);

}

}

public static void

usage()

{ System.out.println

("Usage: java Crypt [-

d] [-kn] infile outfile");

System.exit(1);

}

public static char

encrypt(char c, int k)

{ if ('a' <= c && c <= 'z')

return (char)('a' + (c - 'a'

+ k) % NLETTERS);

if ('A' <= c && c <= 'Z')

return (char)('A' + (c -

'A' + k) % NLETTERS);

return c;

}

public static void

encryptFile(FileReader

in, FileWriter out, int k)

throws IOException

{ while (true)

{ int next = in.read();

if (next == -1) return; //

end of file

char c = (char)next;

out.write(encrypt(c, k))

;

}

}

public static final int

DEFAULT_KEY = 3;

public static final int

NLETTERS = 'z' - 'a' + 1;

}

Random Access

Suppose we want to

change the prices of

some products that we

stored in a file, the

simplest technique is:

read data into an array

Update data

Save data back to a file

What happens if the

data is very large?

There will be a lot of

much reading and

writing.

Up til now, we read to

a file and write to a file

one item at a time, this

access pattern is

called sequential

access.

Next, we will learn

how to access specific

locations in a file and

change just those

locations. This access

pattern is called

random access.

Only disk files support

random access. Each

disk file has a special

file pointer position.

Normally, the file

pointer is at the end of

the file, and any output

is appended to the end.

If we move the file

pointer to the middle

of the file, the output

overwrite what is

already there.

Then the next read

command starts

reading at the file point

location.

In Java, we use a

RandomAccessFile

object a access a file

and move a file

pointer.

We can open a file

either for reading only

(“r”) or for reading and

writing (“rw”).

To open a random-

access file, we supply

a file name and a string

to specify the open

mode.

Random Access File

To open the file

product “product.dat”

for both reading and

writing, we write:

RandomAccessFile f =

new

RandomAccessFile

(“product.dat”, "rw");

To move file pointer

to byte n from the

beginning of the file:

f.seek(n);

To find out the current

position of the file

pointer (counted from

the beginning of the

file):

n = f.getFilePointer();

To find out the number

of bytes in a file:

long filelength =

f.length();

When the new data is

longer than the current

data, the update will

overwrite the old data.

For example, if the

price is increased by

50, the new price has

more digits so it

overwrites the

newline symbol that

separates the fields.

We must give each

field a fixed size that is

sufficiently large.

This also makes it

easy to access the nth

data in a file, without

having to read in the

first n-1 data.

When storing numbers

in a file with fixed

record sizes, it is

easier to store them in

binary format, not in

text format.

The readInt and

writeInt methods read

and write integers as

four-byte quantities.

The readDouble and

writeDouble methods

process double-

precision floating-point

numbers as eight-byte

quantities.

To read:

int n = f.readInt();

double x =

f.readDouble();

char c = f.readChar();

To write:

f.writeInt(n);

f.writeDouble(x);

f.writeChar(c);

Example: See

Database.java

- Name: 30 characters

at two bytes each (60

bytes)

- Price: one double (8

bytes)

- Score: one int (4

bytes)

import

java.io.IOException;

import java.io.

RandomAccessFile;

public class Database

{ public static void

main(String[] args)

{ ConsoleReader

console = new

ConsoleReader

(System.in);

System.out.println

("Please enter the data

file name:");

String filename =

console.readLine();

try

{ RandomAccessFile

file

= new

RandomAccessFile

(filename, "rw");

long nrecord =

file.length() / RECORD_

SIZE;

boolean done = false;

while (!done)

{ System.out.println

("Please enter the

record to update (1 - "

+ nrecord + "), new

record (0), quit (-1)");

int pos =

console.readInt();

if (1 <= pos && pos <=

nrecord) // update

record

{ file.seek((pos - 1) *

RECORD_SIZE);

Product p =

readProduct(file);

System.out.println

("Found " + p.getName

()

+ " " + p.getPrice() + " "

+ p.getScore());

System.out.println

("Enter the new

price:");

double newPrice =

console.readDouble();

p.setPrice(newPrice);

file.seek((pos - 1) *

RECORD_SIZE);

writeProduct(file, p);

}

else if (pos == 0) // add

record

{ System.out.println

("Enter new product:");

String name =

console.readLine();

System.out.println

("Enter price:");

double price =

console.readDouble();

System.out.println

("Enter score:");

int score =

console.readInt();

Product p = new

Product(name, price,

score);

file.seek(nrecord *

RECORD_SIZE);

writeProduct(file, p);

nrecord++;

}

else if (pos == -1)

done = true;

}

file.close();

}

catch(IOException e)

{ System.out.println

("Input/Output

Exception " + e); }

}

public static String

readFixedString

(RandomAccessFile f,

int size) throws

IOException

{ String b = "";

for (int i = 0; i < size; i+

+)

b += f.readChar();

return b.trim();

}

public static void

writeFixedString

(RandomAccessFile f,

String s, int size)

throws IOException

{ if (s.length() <= size)

{ f.writeChars(s);

for (int i = s.length(); i <

size; i++)

f.writeChar(' ');

}

else

f.writeChars

(s.substring(0, size));

}

public static Product

readProduct

(RandomAccessFile f)

throws IOException

{ String name =

readFixedString(f,

NAME_SIZE);

double price =

f.readDouble();

int score = f.readInt();

return new Product

(name, price, score);

}

public static void

writeProduct

(RandomAccessFile f,

Product p) throws

IOException

{ writeFixedString(f,

p.getName(), NAME_

SIZE);

f.writeDouble

(p.getPrice());

f.writeInt(p.getScore()

);

}

public static final int

NAME_SIZE = 30;

public static final int

CHAR_SIZE = 2;

public static final int

INT_SIZE = 4;

public static final int

DOUBLE_SIZE = 8;

public static final int

RECORD_SIZE

= CHAR_SIZE * NAME_

SIZE + DOUBLE_SIZE +

INT_SIZE;

}

Object Streams

We can read and write

complete objects with

the ObjectInputStream

and

ObjectOutputStream

Objects are saved in

binary format; hence

we use streams, not

writers.

For example, we write

a Product object to a

file as follows:

Product p = … ;

ObjectOutputStream out

= new

ObjectOutputStream

(new FileOutputStream

(“products.dat”));

out.writeObject(p);

To read objects:

ObjectInputStream in =

new ObjectInputStream

(new FileInputStream

(“products.dat”));

Product p = (Product)

in.readObject( );

We can store a whole

bunch of objects in an

array or vector, or

inside another object,

and then save that

object:

Vector v = new Vector

();

// now add many

Product objects into v

out.writeObject(v);

We can read all of

objects:

Vector v = (Vector)

in.readObject( );

The objects must

belong to classes that

implement Serializable

interface:

class Product

implements

Serializable

{ … }

To save data to disk, it

is best to put them all

into one object and

save that object.

class Catalog

implements

Serializable

{ public void

addProduct(Product p)

{… }

private Vector

products;

}

class ProductEditor

{ public void saveFile()

{ ObjectOutputStream

out = …;

out.writeObject

(productCatalog);

out.close();

}

public void loadFile()

{ ObjectInputStream in

= … ;

productCatalog =

(Catalog)in.readObject

();

in.close();

}

private Catalog

productCatalog;

}

0 comments:

Post a Comment

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Sweet Tomatoes Printable Coupons