Vous êtes sur la page 1sur 34

Design and Implementation of

the Spreadsheet Application



J.M Rajind Ruparathna
110486D

1 | P a g e


Table of Contents

1. Introduction ................................................................... 2

2. Design of Objects ........................................................... 3

3. Java Implementation ..................................................... 7

4. Application Testing ...................................................... 11

5. Conclusion .................................................................... 17

Appendixes
Appendix A ................................................................ 19
Appendix B ................................................................ 20
Appendix C ................................................................ 22







2 | P a g e



1. Introduction
Step 1:
A cell in the spreadsheet application must be able to hold many different types of values at
different times. Such as a text string, a number or a string giving a function to manipulate
text or number. There must be methods to manipulate text and number inside the cell.
Step 2:
The cell must further more provide the capability to store a simple equation or an address
of another cell. An example of a simple equation is "=A1+10". Each cell must be addressable
using a unique address for each cell within a spreadsheet. It is required to design and
implement manipulation of a collection of cells which is a row or a column.
Step 3:
As the third step, it is required to design and implement manipulation of a region of cells
which is in this step considered as a rectangular collection of cells, preserving the
capabilities of a cell from the earlier steps.
Step 4:
In this step spreadsheet application requires the ability to manipulate a region of cells
within a spreadsheet. A region of cells is a rectangular collection of cells and the required
manipulations are selecting, copying, cutting, pasting and deleting.
Step 5:
As fifth step, spreadsheet application requires the ability to create a new spreadsheet, load
and store a spreadsheet to/from persistent storage.
Step 6:
In the final step, the spreadsheet application requires the Observer-Observable paradigm
used for a cell objects in the spreadsheet.




3 | P a g e



2.Design of Objects
Step 1:
Uploaded Design Document - CS2012-P05-Design-110486D
Cell Object
A cell object was designed as the first step. Cell was designed using an abstract class called
Cell and then inheriting the abstract Cell class into more specific types of Cell classes namely
NumCell, TextCell, and GeneralCell. NumCell class includes a new attribute called number
a double variable, and methods relevant to arithmetic functions which are there to
manipulate the value of variable called number. TextCell includes a new attribute called text
a String variable, and methods to manipulate the variable named text. GeneralCell has
three new attributes namely intNum -an integer variable, doubleNum a double variable,
text a String variable.
However at the end of step 2, this design of Cell was completely altered since I found
difficulties in continuing the design process with keeping the ability to hold many different
types of values at different times in a Cell. The Cell design before altering will be called Cell
Design 1 and after altering will be called Cell Design 2.
Step 2:
Uploaded Design Document - CS2012-P07-Design-110486D
In this step Cell Design 1 was further extended with a few changes. Since the Cell Design 1
was discarded and Cell Design 2 was used after this step, from here on Cell Design 2
(Appendix A) will be discussed.
In this design there is class named Cell and a class named CellSet.
Cell Object
Cell object includes five attributes and four of them are text- a String variable, doubleNum-
a double variable, intNum- an integer variable, dataType- a char variable. dataType variable
is used to keep record of the data type of value currently stored in the Cell object. Above
mentioned other three intNum, doubleNum, text are to provide the capability to hold many
different types of values at different times within a Cell object. The sixth attribute is a
variable named obj which is there to keep a reference of the collection of cells (a row or a
column) of which the Cell object belongs.
4 | P a g e

There are three overloaded constructors for the Cell class to create a Cell object. All three
constructors take two arguments with one always being a CellSet object. Cell object
contains a method called show() which is used to print the value stored in the Cell object .
The value stored in the Cell object can be changed using a method called change( ).
CellSet Object
CellSet object is representing a collection of Cell objects (a row or a column). It has a Cell
object array of which the size is determined using the constructor of the CellSet class.
Step 3:
Uploaded Design Document - CS2012-P11-Design-110486D
CellSet Object
In this step, CellSet class is modified to hold a two dimensional array of Cell objects to
represent a region of cells which is a rectangular collection of cells according to the
application requirements.
Step 4:
Uploaded Design Document - CS2012-P13-Design-110486D
In this step a new class called IndexData was added to the design.
The improvements and modifications done under this step are described in two parts. First
part is improvements and modifications for the purpose of forth step's requirements and
second part is general improvements.
First Part for the purpose of Step 4 requirements
Cell Object
A new method called getValue() is added to the Cell class. It is used in designing the paste
operation.
IndexData
IndexData object is used in designing select range operation.
CellSet
There is a new method called decodeCell() in CellSet object and it is used to determine the
position of a particular Cell in the two dimentional array of cells when a Cell's address is
given as a string. CellSet object has a IndexData object. Starting point and end point of the
selected rectangular region is stored in that IndexData object. CellSet object also has
another CellSet object named as copiedRange. CellSet object has new methods namely
5 | P a g e

selectRange(), copyRange(), cutRange(), deleteRange() and pasteRange() for range
manipulations. The copyRange() method stores copied cells in copiedRange object. The
pasteRange() method uses a an address of a Cell (String variable) to determine where to
paste the selected set of Cells.
Second Part general improvements
CellSet
Three changeValue() methods are added (one for each data type String, integer and
double) in this step apart from the method called change() which was already there.
Methods called add(), substract(), multiply(), divide(), are added to provide capability of
arithmetic operations. A method called concatenate() is added to provide capability to
concatenate two strings. Methods called sumOfRange() and avgOfRange() are included to
calculate sum and average of a range. A method called assign() is added to check and store a
integer as an integer and a double value as a double value after doing a function on the
numeric value inside the Cell object. A method called getOperandCell() is used to identify
the relevant Cells and their indexes in the CellSet array using the provided equation.
Step 5:
Uploaded Design Document - CS2012-P14-Design-110486D
Coming from step four to step five two new classes were included to the design. They are
called Sheet and Application. In this step the classes Cell, CellSet, IndexData, Sheet, were set
to implement Serializable interface.
Sheet Object
Idea of creating class called Sheet was to give an object of Sheet class a similar meaning of a
spreadsheet in a spreadsheet application. The ability to add any property a programmer
wants to a sheet is provided with this improvement. Here an attribute sheet_name is
included in the Sheet object to give the sheet a name. A Sheet object has a CellSet object
which includes the collection of cells.
Application Object
Idea of creating a class called Application was to create an object of Application class an
treat it the same way as a spreadsheet application. An Application object has an ArrayList of
Sheet objects as opened tabs of spreadsheets in a spreadsheet application. There are
methods called createSpreadSheet(), storeSpreadsheet(), loadSpreadsheet() to create, save
and load a spreadsheet. loadSpreadsheet() method takes a String variable( name of the
spreadsheet to be loaded) as an argument and storeSpreadsheet() method takes a String
variable (name which the spreadsheet to be saved) as an argument. Methods called
loadFile() and saveFile() in the Application object are to support storeSpreadsheet(),
6 | P a g e

loadSpreadsheet() methods. Application object has a Sheet object called worksheet which is
used to store working spreadsheet from the ArrayList using the method called
selectWorkSheet(). An attribute called opened_file_index is there in the Application object
to store the index value in the ArrayList of Sheet object which is to be opened as the
working spreadsheet. A method called showData() is there to print out the values of the
spreadsheet.
Step 6:
Uploaded Design Document - CS2012-P14-Design-110486D
Cell class is set to extend Observable class and set to implement Observer interface.
Cell Object
To set the Observer-Observable paradigm to the Cell objects, a String variable callled
equation and an integer variable called maxObservables is added to the attributes of Cell
object. When an equation is entered to a Cell it is stored in the variable called equation.
When values are calculated according to the equation provided relevant Observers are set
using setObservables() method. After that when a value changes, informChange() method is
called. update() method is used with the change() method to make the relevant changes.
There is a ArrayList in the Cell object to keep track of the other Cell objects which the first
Cell object has become an Observer to. A method called clearObservables() in Cell object
removes itself from being an Observer when being an Observer is no longer needed.








7 | P a g e


3. Java Implementation
Spreadsheet application final java implementation Appendix C
The implementation of the cut region operation in a CellSet was done as follows (the
complete code was uploaded as the method <cutRange()>in class <CellSet> in the file
<CS2012-P14-Code-110486D.java>)
public void cutRange(){
copiedRange = new CellSet(s.endRow-s.startRow+1 , s.endCol-s.startCol+1);
for(int i= s.startCol ; i<=s.endCol;i++){
for(int j= s.startRow;j<=s.endRow; j++){
if (this.table[j][i].getValue() instanceof String)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((String)this.table[j][i].getValue() , copiedRange);
else
if (this.table[j][i].getValue() instanceof Double)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Double)this.table[j][i].getValue() , copiedRange);
else
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Integer)this.table[j][i].getValue() , copiedRange);
this.table[j][i] = null;
}
}
}

There are two for loops in this method and those two for loops are used to iterate over the
Cell array between the required ranges. Inside the inner for, two if- else clauses are used to
determine the value of the data type inside the Cell and then they are assigned to the
copiedRange CellSet. Since this is cut operation the line shown in bold in the code fragment
is used to make relevent references null in the Cell array.

The implementation of the store spreadsheet operation in an Application was done as
follows (the complete code was uploaded as the method <storeSpreadSheet (String
name)>in class <Application> in the file <CS2012-P14-Code-110486D.java>)

public void storeSpreadSheet (String name) throws IOException{
String name_with_extention = name +".ser";
File f = new File(name_with_extention);
if(f.exists()){
System.out.printf("File already exists. Press: %n Y to overwrite %n "
+ "C to cancel %n N to save in another file name"
+ "%nEnter Choice:");
Scanner c = new Scanner(System.in);
String choice = c.nextLine();
choice = choice.toUpperCase();
switch (choice){
case "Y": saveFile(f,name);
break;
8 | P a g e

case "N": name = c.nextLine();
f = new File(name+".ser");
saveFile(f,name);
break;
case "C": break;
default : System.out.println("Not a valid choice");
break;
}
}else {
saveFile(f,name);
}
}


In this method first the extension .ser is added to the file name (name which the file is to be saved).
Then it checks whether a file in the same name already exists and if it does them a choice is asked
from the user. Then process is continued according to the choice form the user.
The implementation of function evaluation in a Cell was done as follows (the complete code was
uploaded as the method <change(String t)> in class <Cell> in the file <CS2012-P14-Code-
110486D.java>)
public void change(String t){ //equations are handled by this method
int select =0;
int[] in; //in[1] = col in[0] = row
String operandCellDataType;
//function formats: sum of range "=SUM(A1:B3)"
// average of range "=AVG(A1:B3)"
// sum of a cell value and other value "=A3+other_value"
// assigning another cell's value "=C3"
equation = t;
if(t.length() > 4){
t = t.toUpperCase(Locale.ENGLISH);
if(t.startsWith("=SUM"))
select = 1;

if(t.startsWith("=AVG"))
select = 2;
if(t.charAt(0) == '=' && t.charAt(4) != '('){
select = 4;
maxObservables = 1;
}
}else{
if(t.charAt(0) == '=' && t.length()== 3){
select = 3;
maxObservables = 1;
}
}


switch (select){
case 1:
sumOfRange(t);
break;
case 2:
9 | P a g e

avgOfRange(t);
break;
case 3:
in = getOperandCell(t,1);
operandCellDataType = this.obj.table[in[0]][in[1]].dataType;
setObservables(obj.table[in[0]][in[1]]);
switch (operandCellDataType){
case "Integer":
this.intNum = this.obj.table[in[0]][in[1]].intNum;
this.dataType = "Integer";
break;
case "Double":
this.doubleNum = this.obj.table[in[0]][in[1]].doubleNum;
this.dataType = "Double";
break;
case "String":
this.text = this.obj.table[in[0]][in[1]].text;
this.dataType = "String";
break;
}
break;
case 4:
in = getOperandCell(t,1);
operandCellDataType = this.obj.table[in[0]][in[1]].dataType;
char operation = t.charAt(3); //checks the operation to be done
String newVal = t.substring(4);
setObservables(obj.table[in[0]][in[1]]);
switch (operandCellDataType){
case "String":
concatenate(obj.table[in[0]][in[1]] ,newVal);
break;
case "Integer":
case "Double":
double num = Double.parseDouble(newVal);
switch (operation){
case '+':
add(this.obj.table[in[0]][in[1]] , num);
break;
case '-':
substract(this.obj.table[in[0]][in[1]] , num);
break;
case '*':
multiply(this.obj.table[in[0]][in[1]] , num);
break;
case '/':
divide(this.obj.table[in[0]][in[1]] , num);
break;
}
break;
}
break;
default:
this.text = "#VALUE";
this.dataType = "Error";
break;
}
}

10 | P a g e

If-else condition at the beginning of the method determine the function to be done using the string
of function provided and a suitable value is assigned to the integer variable called select. Then the
select variable is used in a switch case to evaluate the relevant function. If the string of function does
not represent any identifiable function then text value of the cell is set to show that an error has
occurred using default part of the switch case.
The implementation of method called assign() in a Cell was done as follows (the complete code
was uploaded as the method <assign(double num)> in class <Cell> in the file <CS2012-P14-Code-
110486D.java>)
public void assign(double num){
if(Math.ceil(num) == num || Math.floor(num) == num){
this.intNum = (int)num;
this.dataType = "Integer";
}else{
this.doubleNum = num;
this.dataType = "Double";
}
}

When a number is in double format, its value can be an integer or a floating point value. In the Cell
class when evaluating functions a value of a double variable being integer is possible. In such cases
this method checks if the number in double variable is really a floating point value or not. If it is an
integer, then the value is assigned to the intNum variable in the Cell.








11 | P a g e

4. Application Testing
Step 3:
Testing of -SUM function evaluated on a range
-String concatenation
-simple equation
Input:
At the initialization:
12 "Wasted" "=A1-10.7" 56 98 444
3.5 32.5 "=A2" 89 56 "=F1"
"Let me b" 10 "MAD" 23 34 "oooo"

At the editing point:


Test Results:
Spreadsheet:
12 Wasted 1.3 56 98 444
3.5 32.5 3.5 89 56 444
Let me b 10 MAD 23 34 oooo

Spreadsheet:
356 Wasted now 6.9 56 98 555
3.5 32.5 Wasted now 89 56 444
Let me b 10 MADHAWEEE 23 34 oooo





"SUM(D1:E3)" 6.89875 555
"=B1+ now"
"=C3+HAWEEE"
12 | P a g e



Step 4:
Testing of -select range
-copy range
-paste range
-delete range
Input:
At the initialization:


At the editing point:



Test Results:
Spreadsheet:
10.500 Mad 10.560 10.670 10 kadkas
20.500 aloalo 20.340 20.340 20.110 20.110
11 nana 30.560 30 30.778 30.778

Spreadsheet:
No Cell No Cell 10.560 10.670 10.500 Mad
No Cell No Cell 20.340 20.340 20.500 aloalo
No Cell No Cell 30.560 30 11 nana



10.5 "Mad" 10.56 10.67 10 "kadkas"
20.5 "aloalo" 20.34 20.34 20.11 20.11
"=A1+0.5" "nana" 30.56 30 30.778 30.778
"askldlnkjnkj"


13 | P a g e

Step 5:
Testing of -create spreadsheet
-save spreadsheet
-load spreadsheet
Input:
At the initialization:


At the editing point:



Test Results:
Initializing, Editing and Saving:
Spreadsheet "sheet1" created and opened in a tab.
Spreadsheet "sheet2" created and opened in a tab.
Spreadsheet "sheet3" created and opened in a tab.
Spreadsheet "sheet2" selected for work.
Spreadsheet "sheet2":
No Cell No Cell 10.560 10.670 10.500 Mad
No Cell No Cell 20.340 20.340 20.500 aloalo
No Cell No Cell 30.560 30 11 nana

Spreadsheet "sheet1" selected for work.
Spreadsheet "sheet1":
10.500 Mad 10.560 10.670 10 kadkas
20.500 aloalo 20.340 20.340 20.110 20.110
11 nana 30.560 30 30.778 30.778
10.5 "Mad" 10.56 10.67 10 "kadkas"
20.5 "aloalo" 20.34 20.34 20.11 20.11
"=A1+0.5" "nana" 30.56 30 30.778 30.778
"askldlnkjnkj"


14 | P a g e


File already exists. Press:
Y to overwrite
C to cancel
N to save in another file name
Enter Choice:Y
Spreadsheet "sheet1" selected for work.
Spreadsheet "sheet1" saved to disk successfully.
File already exists. Press:
Y to overwrite
C to cancel
N to save in another file name
Enter Choice:Y
Spreadsheet "sheet2" selected for work.
Spreadsheet "sheet2" saved to disk successfully.
Spreadsheet "sheet3" selected for work.
Spreadsheet "sheet3":
No Cell No Cell No Cell No Cell No Cell No Cell
No Cell No Cell No Cell No Cell No Cell No Cell
No Cell No Cell No Cell No Cell No Cell No Cell

Loading a saved file:
Spreadsheet "sheet2" selected for work.
Spreadsheet "sheet2" loaded form disk successfully.
Spreadsheet "sheet2":
No Cell No Cell 10.560 10.670 10.500 Mad
No Cell No Cell 20.340 20.340 20.500 aloalo
15 | P a g e

No Cell No Cell 30.560 30 11 nana

Spreadsheet "sheet1" selected for work.
Spreadsheet "sheet1" loaded form disk successfully.
Spreadsheet "sheet1":
10.500 Mad 10.560 10.670 10 kadkas
20.500 aloalo 20.340 20.340 20.110 20.110
11 nana 30.560 30 30.778 30.778

Step 6:
Testing of -Observer-Observable paradigm
Input:
At the initialization:


At the editing point :



Test Results:
Spreadsheet "sheet1" created and opened in a tab.
Spreadsheet "sheet1" selected for work.
Spreadsheet "sheet1":
10.500 Mad 10.560 10.670 10 183.358
20.500 aloalo 20.340 20.340 20.110 20.110
11 nana 30.560 30 30.778 30.778

10.5 "Mad" 10.56 10.67 10 "kadkas"
20.5 "aloalo" 20.34 20.34 20.11 20.11
"=A1+0.5" "nana" 30.56 30 30.778 30.778
16.88 1000


16 | P a g e

Spreadsheet "sheet1":
16.880 Mad 1000 10.670 10 1172.798
20.500 aloalo 20.340 20.340 20.110 20.110
17.380 nana 30.560 30 30.778 30.778
















17 | P a g e

5. Conclusion
I believe that I have used many of the object oriented concepts and methodologies
throughout this design process of the spreadsheet application. The foundation of object
orientation is to have a set of well-behaved data objects do the task for the user by passing
messages to one another. Each data object deciding for itself whether to accept the
message and how to interpret what it means. When the project developed step by step, i
understood how to develop the design from one object to another.
Let's consider some examples. The first object set was a Cell. When it came to step 2, it was
clear that i had to step into a new object to continue the process. It is like considering the
required elements as real world objects. In the project I have used a class called CellSet and
a class called Sheet. Both of them are very much close to each other but I felt that coding
looks more organized when you separate the Cell collection and the properties of the
spreadsheet. My goal was to use the CellSet class as the Cell collection and Sheet class as
the real spreadsheet with all the properties and Cell collection. At that point composition is
used.
As moving forward to step five when it was asked to design the capability to create new
spreadsheet, then I moved to a new object called Application. Control of the sheet objects
were given to the Application object.
Up to now discussion was about the foundation of object orientation which is the using a set
of interacting objects. Basic principles of object orientation will be discussed from here on. I
have learned four basic principles of object orientation. They are encapsulation, inheritance,
abstraction and polymorphism. I tried to get the maximum use of them when in need and I
think I succeeded in doing that. I have used encapsulation in almost every class. I
understood that encapsulation is not all about simply using getters and setters to a private
attribute but to use access modifiers properly and using getters and setter when in need
and with suitable conditions. Encapsulation was used to limit the possible occurrences of
errors where the input could corrupt the accuracy of the program.
I have used polymorphism as well. It was used in designing the cut and copy operations.
There is a method called getValue() in Cell class of which the return type is java.lang.Object.
This getValue() method returns data inside the Cell as an object of type java.lang.Object. In
the copyRange() , cutRange() methods in the CellSet class getValue() method is used and the
object returned by getValue() method is cast into the correct type (to a String, Double or an
Integer). This is called dynamic binding or late binding which is a form of polymorphism. I
have used overloaded constructors in the Cell class and I have used method overloading in
the Cell class (changeValue() method) as well. This is also some form of polymorphism.
Inheritance is another important basic object oriented principle. I haven't used it directly
but I have used it in some way when the Observer-Observable concept is added to the Cell
18 | P a g e

class. I have extended the Cell class from the java.util.Observable class. I have implemented
the Cell class with Observer interface and that is using multiple inheritance. Adding this
Observer-Observable concept was easy because of the use of object orientation. All I had to
do was go to the Cell class and modify it inside the Cell class. So I believe that I have
understood and analyzed object oriented concepts and methodologies while completing the
project to a great extent.


















19 | P a g e

Appendixes
Appendix A

Cell
-text : String
-doubleNum : double
-intNum : int
-dataType : char
-obj : CellSet
<<constructor>> Cell( n: double , o : CellSet)
<<constructor>> Cell( n: int , o : CellSet)
<<constructor>> Cell( t: String , o : CellSet)
+ change( t: String , o : CellSet) : void
+ show() : void


















TestClass
-a :CellSet
- A1,A2,A3 : Cell

+ createCell() : void
+ showCell() : void
+ editCell() : void
CellSet
#table : Cell[]
<<constructor>> CellSet(Num :int)
TestClass
CellSet
Cell (Cell objects
A1,A2,A3,B1,B2,B3)
20 | P a g e

Appendix B (Uploaded Design Document - CS2012-P14-Design-110486D)










Cell :: Serializable
- text : String
- doubleNum : double
- intNum : int
- maxObservables : int
- dataType : String
- equation : String
- obj : CellSet
- observables : ArrayList<Cell>
-<<constructor>> Cell( n: double , o : CellSet)
-<<constructor>> Cell( n: int , o : CellSet)
-<<constructor>> Cell( t: String , o : CellSet)
+changeValue(n : int) : void
+changeValue(n : double) : void
+changeValue(t : String) : void
+change( t: String) : void
-clearObservables() : void
-setObservables(obs : Cell) : void
+informChange( Observable o, Object arg) : void
+update(Observable o, Object arg) : void
+getOperandCell(s : String, ad : int) : int[]
+assign( num : double) : void
+sumOfRange( t : String) : int
+avgOfRange(t : String) : void
+add(cell : Cell , num: double) : void
+substract(cell : Cell , num: double) : void
+multiply(cell : Cell , num: double) : void
+divide(cell : Cell , num: double) : void
+concatenate(cell : Cell, s : String) : void
-clear() : void
+clean() : void
+getValue() : Object
+show() : void

CellSet :: Serializable
#table : Cell[][]
#copiedRange : CellSet
-s : IndexData
-<<constructor>> CellSet(r :int ,c: int)
+selectRange(st : String , en :String) : IndexData
+cutRange() : void
+copyRange() : void
+pasteRange(st : String) : void
+deleteRange() : void
+cellPrsent( row : int, col : int) : void
+decodeCell(code : String) : int[]
+addData(cell : String , n : int) : void
+addData(cell : String , n : double) : void

+addData(cell : String , t : String) : void


IndexData :: Serializable
startRow : int
startCol : int
endRow : int
endCol : int

Sheet :: Serializable
-sheet_name : String
-cell_set : CellSet
<<constructor>> Sheet(name : String)
Application
-sheet_array : ArrayList<Sheet>
-opened_file_index : int
#worksheet : Sheet
+createSpreadSheet(name : String) : void
+isAlreadyOpened(name :String) : boolean
+selectWorkSheet(name : String) : void
+showdata(numberOfRows :int, numberOfColumns :int) : void
+saveFile(f : File, name :String) : void
+storeSpreadSheet(name : String) : void
+laodFile(f : File , name: String) : void
+loadSpreadSheet()name : String) : void
21 | P a g e

Serializable
CellSet Cell IndexData
Sheet




























IndexData
CellSet
//copiedRange
Sheet
CellSet
Cell (Cell objects
A1,A2,A3,B1,B2,B3)
Application
TestApp
Cell (Cell objects
A1,A2,A3,B1,B2,B3)
Observable Observer
22 | P a g e

Appendix C (Uploaded Code - CS2012-P14-Code-110486D.java)
Spreadsheet application final java code
package Week14;
import java.io.Serializable;
import java.util.Observable;
import java.util.Observer;
import java.util.ArrayList;
import java.util.Locale;

public class Cell extends Observable implements Observer ,Serializable{
private String text;
private double doubleNum;
private int intNum;
private String dataType;
private CellSet obj;
private ArrayList<Cell> observables = new ArrayList();
private int maxObservables;
private String equation;

public Cell(int n ,CellSet o ){ //constructor for integers
this.obj = o;
clear();
this.intNum = n;
this.dataType = "Integer";
}

public Cell(double n , CellSet o){ //constructor for double values
this.obj = o;
clear();
this.doubleNum = n;
this.dataType = "Double";
}

public Cell(String t,CellSet o){ //constructor for integers & equations
this.obj = o;
clear();
this.changeValue(t);
}

public void changeValue(int n){ //method to edit integer value
this.dataType = "Integer";
this.intNum = n;
equation = null;
clearObservables();
informChange("A");
}

public void changeValue(double n){ //method to edit double value
this.dataType = "Double";
this.doubleNum = n;
equation = null;
clearObservables();
informChange("A");
}

23 | P a g e

public void changeValue(String t){
if(t.charAt(0) != '='){
equation = null;
clearObservables();
this.text = t;
this.dataType = "String";
}else{
clearObservables();
this.change(t); //once the equation changes change() method his has to be called
}
informChange("A");
}

public void change(String t){ //equations are handled by this method

int select =0;
int[] in; //in[1] = col in[0] = row
String operandCellDataType;
//function formats: sum of range "=SUM(A1:B3)"
// average of range "=AVG(A1:B3)"
// sum of a cell value and other value "=A3+other_value"
// assigning another cell's value "=C3"
equation = t;
if(t.length() > 4){
t = t.toUpperCase(Locale.ENGLISH);
if(t.startsWith("=SUM"))
select = 1;

if(t.startsWith("=AVG"))
select = 2;
if(t.charAt(0) == '=' && t.charAt(4) != '('){
select = 4;
maxObservables = 1;
}
}else{
if(t.charAt(0) == '=' && t.length()== 3){
select = 3;
maxObservables = 1;
}
}


switch (select){
case 1:
sumOfRange(t);
break;
case 2:
avgOfRange(t);
break;
case 3:
in = getOperandCell(t,1);
operandCellDataType = this.obj.table[in[0]][in[1]].dataType;
setObservables(obj.table[in[0]][in[1]]);
switch (operandCellDataType){
case "Integer":
this.intNum = this.obj.table[in[0]][in[1]].intNum;
this.dataType = "Integer";
break;
24 | P a g e

case "Double":
this.doubleNum = this.obj.table[in[0]][in[1]].doubleNum;
this.dataType = "Double";
break;
case "String":
this.text = this.obj.table[in[0]][in[1]].text;
this.dataType = "String";
break;
}
break;
case 4:
in = getOperandCell(t,1);
operandCellDataType = this.obj.table[in[0]][in[1]].dataType;
char operation = t.charAt(3); //checks the operation to be done
String newVal = t.substring(4);
setObservables(obj.table[in[0]][in[1]]);
switch (operandCellDataType){
case "String":
concatenate(obj.table[in[0]][in[1]] ,newVal);
break;
case "Integer":
case "Double":
double num = Double.parseDouble(newVal);
switch (operation){
case '+':
add(this.obj.table[in[0]][in[1]] , num);
break;
case '-':
substract(this.obj.table[in[0]][in[1]] , num);
break;
case '*':
multiply(this.obj.table[in[0]][in[1]] , num);
break;
case '/':
divide(this.obj.table[in[0]][in[1]] , num);
break;
}
break;
}
break;
default:
this.text = "#VALUE";
this.dataType = "Error";
break;
}
}

private void clearObservables(){ //to stop from being an observer
if(observables.size() > 0){
for(Cell temp : observables){
temp.deleteObserver(this);
observables.remove(obj);
}
maxObservables = 0;
}
}

private void setObservables(Cell obs){
25 | P a g e

if(!observables.contains(obs)){
System.out.println("maxObservers value "+maxObservables);
obs.addObserver(this);
observables.add(obs);
}
}

public void informChange(String type){ //informs the change to observers
if(this.countObservers()>0){ //if cell has any observers
this.setChanged();
this.notifyObservers(type);
}
}

@Override
public void update(Observable o, Object arg) {
if( equation != null){
change(equation);
}
}

public int[] getOperandCell(String s , int ad){ //ad = address strating point
int[] index = new int[2];
index[0] = Integer.parseInt(s.substring((ad+1),(ad+2)));
index[1] = (int)s.charAt(ad) - 64;
return index;
}

public void assign(double num){
if(Math.ceil(num) == num || Math.floor(num) == num){
this.intNum = (int)num;
this.dataType = "Integer";
}else{
this.doubleNum = num;
this.dataType = "Double";
}
}
public int sumOfRange(String t){ // fprmat is SUM(A1:B3)
int[] start = getOperandCell(t,5); //[0] - row [1] - col
int[] end = getOperandCell(t,8);
int numOfCells = (end[0]-start[0]+1)*(end[1]-start[1]+1);
maxObservables = numOfCells;
double sum=0;
for(int i=start[1];i<=end[1];i++){
for(int j=start[0]; j<=end[0];j++){
String operandCellDataType = this.obj.table[j][i].dataType;
setObservables(obj.table[j][i]);
switch(operandCellDataType){
case "Integer":
sum = sum + (double)this.obj.table[j][i].intNum;
break;
case "Double":
sum = sum + this.obj.table[j][i].doubleNum;
break;
}
}
}
assign(sum);
26 | P a g e

return numOfCells;
}

public void avgOfRange(String t){
int numOfCells = sumOfRange(t);
if(this.dataType.equalsIgnoreCase("Integer"))
assign(this.intNum/numOfCells);
else
assign(this.doubleNum/numOfCells);
}

public void add(Cell cell , double num){
if(cell.dataType.equalsIgnoreCase("Integer"))
num = (double)cell.intNum + (double)num;
else
num = cell.doubleNum + (double)num;
assign(num);
}
public void substract(Cell cell , double num){
if(cell.dataType.equalsIgnoreCase("Integer"))
num = (double)cell.intNum - (double)num;
else
num = cell.doubleNum - (double)num;
assign(num);
}
public void multiply(Cell cell , double num){
if(cell.dataType.equalsIgnoreCase("Integer"))
num = (double)cell.intNum*(double)num;
else
num = cell.doubleNum*(double)num;
assign(num);
}
public void divide(Cell cell , double num){
if(cell.dataType.equalsIgnoreCase("Integer"))
num = (double)cell.intNum/(double)num;
else
num = cell.doubleNum/(double)num;
assign(num);
}
public void concatenate(Cell cell , String s){
this.text = cell.text + s;
this.dataType = "String";
}

private void clear(){
this.dataType = "Empty";
this.doubleNum = 0;
this.intNum = 0;
this.text = null;
this.equation = null;
clearObservables();
}

public void clean(){
switch(dataType){
case "String":
this.doubleNum = 0;
this.intNum = 0;
27 | P a g e

break;
case "Double":
this.intNum = 0;
this.text = null;
break;
case "Integer":
this.doubleNum = 0;
this.text = null;
break;
}
}
public Object getValue(){
switch (dataType){
case "Integer":
return this.intNum;
case "Double":
return this.doubleNum;
case "String":
return this.text;
case "Error":
return this.text;
default:
return "Empty";
}
}
public void show(){
switch (dataType){
case "Integer":
System.out.format("%-15d",intNum);
break;
case "Double":
System.out.format("%-15.3f",doubleNum);
break;
case "String":
System.out.format("%-15s",text);
break;
case "Error":
System.out.printf("%-15s",text);
break;
default:
System.out.format("%-15s","Cell is empty");
break;
}
}
}
//////////////////////////////////////////////////////////////
package Week14;
import java.io.Serializable;

public class CellSet implements Serializable{
protected Cell[][] table; //2D array of cells
private IndexData s = new IndexData();
protected CellSet copiedRange;

public class IndexData implements Serializable{
int startRow;
int startCol;
int endRow;
28 | P a g e

int endCol;
}

public CellSet(int r, int c){
table = new Cell[r][c];
}

public IndexData selectRange(String st, String en){
int []one;
int []two;
one = decodeCell(st);
two = decodeCell(en);
s.startRow = one[0];
s.startCol = one[1];
s.endRow = two[0];
s.endCol = two[1];
return s;
}

public void cutRange(){
copiedRange = new CellSet(s.endRow-s.startRow+1 , s.endCol-s.startCol+1);
for(int i= s.startCol ; i<=s.endCol;i++){
for(int j= s.startRow;j<=s.endRow; j++){
if (this.table[j][i].getValue() instanceof String)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((String)this.table[j][i].getValue() , copiedRange);
else
if (this.table[j][i].getValue() instanceof Double)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Double)this.table[j][i].getValue() , copiedRange);
else
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Integer)this.table[j][i].getValue() , copiedRange);
this.table[j][i] = null;
}
}
}
public void copyRange(){
copiedRange = new CellSet(s.endRow-s.startRow+1 , s.endCol-s.startCol+1);
for(int i= s.startCol ; i<=s.endCol;i++){
for(int j= s.startRow;j<=s.endRow; j++){
if (this.table[j][i].getValue() instanceof String)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((String)this.table[j][i].getValue() , copiedRange);
else
if (this.table[j][i].getValue() instanceof Double)
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Double)this.table[j][i].getValue() , copiedRange);
else
copiedRange.table[j-s.startRow][i-s.startCol] = new Cell((Integer)this.table[j][i].getValue() , copiedRange);
//this.table[j][i] = null;
}
}
}
public void deleteRange(){
for(int i= s.startCol ; i<=s.endCol;i++){
for(int j= s.startRow;j<=s.endRow; j++){
this.table[j][i] = null;
}
}
}
public void pasteRange(String st){
int []start;
29 | P a g e

start = decodeCell(st);
for(int i=start[1];i<start[1]+copiedRange.table[0].length;i++){
for(int j=start[0];j<start[0]+copiedRange.table.length;j++){
this.table[j][i] = copiedRange.table[j-start[0]][i-start[1]];
}
}
}
public boolean cellPresent(int row, int col){
if(table[row][col] != null)
return true;
else
return false;
}

public int[] decodeCell(String code){ //method to get the row and col numbers
int[] index = new int[2];
//1,2,3.... like that rows are named
//A,B,C..... like that columns are named
code = code.toUpperCase();
index[1] = (int)code.charAt(0) - 64; //column
index[0] = Integer.parseInt(code.substring(1)); //row
return index;
}
//editing data and adding data are done using same set of functions.
public void addData(String cell , String t){
int []index = decodeCell(cell);
if(cellPresent(index[0],index[1]))
this.table[index[0]][index[1]].changeValue(t);
else
this.table[index[0]][index[1]] = new Cell(t, this);
}

public void addData(String cell , int n){
int []index = decodeCell(cell);
if(cellPresent(index[0],index[1]))
this.table[index[0]][index[1]].changeValue(n);
else
this.table[index[0]][index[1]] = new Cell(n, this);
}
public void addData(String cell , double n){
int []index = decodeCell(cell);
if(cellPresent(index[0],index[1]))
this.table[index[0]][index[1]].changeValue(n);
else
this.table[index[0]][index[1]] = new Cell(n, this);
}
}
////////////////////////////////////////////////////////////
package Week14;

import java.io.Serializable;

public class Sheet implements Serializable{
//include a CellSet and sheet properties
protected String sheet_name = null;
protected CellSet cell_set = null;

Sheet( String name){
30 | P a g e

this.sheet_name = name;
this.cell_set = new CellSet(26,100);
}
}
////////////////////////////////////////////////////////////
package Week14;

import java.io.*;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Scanner;

public class Application implements Serializable{
protected Sheet worksheet = null;
private ArrayList<Sheet> sheet_array = new ArrayList();
private int opened_file_index = -1;

public void createSpreadSheet(String name){
Sheet temp = new Sheet(name);
sheet_array.add(temp);
System.out.format("Spreadsheet \"%s\" created and opened in a tab."
+ "%n",name);
}

public boolean isAlreadyOpened(String name){
opened_file_index = -1;
for(Sheet temp : sheet_array){
if(temp.sheet_name.equals(name)){
opened_file_index = sheet_array.indexOf(temp);
return true;
}
}
return false;
}

public void selectWorkSheet(String name){ //method to select currently-
if(isAlreadyOpened(name)){ //working sheet
worksheet = sheet_array.get(opened_file_index);
System.out.format("Spreadsheet \"%s\" selected for work."
+ "%n",name);
}else {
System.out.println("No spreadsheet in that name found");
}
}

public void showData(int numberOfRows , int numberOfColumns){
System.out.format("Spreadsheet \"%s\": %n",worksheet.sheet_name);
for(int i=1;i<=numberOfRows;i++){
for(int j=1;j<=numberOfColumns;j++){
if(this.worksheet.cell_set.table[i][j] != null)
this.worksheet.cell_set.table[i][j].show();
Else
System.out.format("%-15s","No Cell");
}
System.out.print("\n");
}
System.out.println("");
}
31 | P a g e


public void saveFile(File f, String name) throws IOException{
FileOutputStream file = null;
ObjectOutputStream output = null;
try{
file = new FileOutputStream(f);
output = new ObjectOutputStream(file);
this.selectWorkSheet(name);
output.writeObject(this.worksheet);
System.out.format("Spreadsheet \"%s\" saved to disk successfully."
+ "%n",name);
} finally{
if(output !=null)
output.close();
if(file != null)
file.close();
}
}

public void storeSpreadSheet (String name) throws IOException{
String name_with_extention = name +".ser";
File f = new File(name_with_extention);
if(f.exists()){
System.out.printf("File already exists. Press: %n Y to overwrite %n "
+ "C to cancel %n N to save in another file name"
+ "%nEnter Choice:");
Scanner c = new Scanner(System.in);
String choice = c.nextLine();
choice = choice.toUpperCase();
switch (choice){
case "Y": saveFile(f,name);
break;
case "N": name = c.nextLine();
f = new File(name+".ser");
saveFile(f,name);
break;
case "C": break;
default : System.out.println("Not a valid choice");
break;
}
}else {
saveFile(f,name);
}
}

public void loadFile(File f, String name) throws IOException{
FileInputStream file = null;
ObjectInputStream input = null;
try{
file = new FileInputStream(f);
input = new ObjectInputStream(file);
sheet_array.add((Sheet)input.readObject());
this.selectWorkSheet(name);
System.out.format("Spreadsheet \"%s\" loaded form disk "
+ "successfully.%n",name);
}catch ( FileNotFoundException | ClassNotFoundException e){
e.printStackTrace();
}finally{
32 | P a g e

if(input != null)
input.close();

if(file !=null)
file.close();
}
}

public void loadSpreadSheet(String name) throws IOException {
String name_with_extention = name +".ser";
File f = new File(name_with_extention);
Sheet temp;

if(f.exists()){
if(!isAlreadyOpened(name)){
loadFile(f,name);
}else{
System.out.printf("A file with the same name is already open. "
+ "Press:%n O to open above it %n Enter C to cancel"
+ "%n Enter Choice: ");
Scanner c = new Scanner(System.in);
String choice = c.nextLine();
choice = choice.toUpperCase();

switch (choice){
case "O": sheet_array.remove(opened_file_index);
loadFile(f,name);
break;

case "C": break;

default : System.out.println("Not a valid choice");
}
}
}else {
System.out.println("Incorrect File name. File doesn't exist!");
}
}
}
////////////////////////////////////////////////////////////////////
package Week14;

import java.io.IOException;

public class TestApp {
Application app = new Application();

public void test_enter_data(){
app.worksheet.cell_set.addData("A1" , 10.5);app.worksheet.cell_set.addData("A2" ,
20.5);app.worksheet.cell_set.addData("A3", "=A1+0.5");
app.worksheet.cell_set.addData("B1" , "Mad");app.worksheet.cell_set.addData("B2" ,
"aloalo");app.worksheet.cell_set.addData("B3" , "nana");
app.worksheet.cell_set.addData("C1" , 10.56);app.worksheet.cell_set.addData("C2" ,
20.34);app.worksheet.cell_set.addData("C3" , 30.56);
app.worksheet.cell_set.addData("D1" , 10.67);app.worksheet.cell_set.addData("D2" ,
20.34);app.worksheet.cell_set.addData("D3" , 30);
app.worksheet.cell_set.addData("E1" , 10);app.worksheet.cell_set.addData("E2" ,
20.11);app.worksheet.cell_set.addData("E3" , 30.778);
33 | P a g e

app.worksheet.cell_set.addData("F1", "=SUM(C1:E3)");app.worksheet.cell_set.addData("F2" ,
20.11);app.worksheet.cell_set.addData("F3" , 30.778);
}

public void test_edit_data(){
app.worksheet.cell_set.addData("A1", 16.88);
app.worksheet.cell_set.addData("C1", 1000);
/*app.worksheet.cell_set.selectRange("C1", "B3");
app.worksheet.cell_set.copyRange();
app.worksheet.cell_set.pasteRange("E1");
app.worksheet.cell_set.addData("B1", "askldlnkjnkj");
app.worksheet.cell_set.deleteRange();*/
}
public static void main(String args[]) throws IOException {
TestApp test = new TestApp();
test.app.createSpreadSheet("sheet1");
test.app.selectWorkSheet("sheet1");

test.test_enter_data();
test.app.showData(3, 6);

test.test_edit_data();
test.app.showData(3, 6);
}
}

Vous aimerez peut-être aussi