Initial model implementation
This commit is contained in:
		
							parent
							
								
									7879178b1a
								
							
						
					
					
						commit
						4b47ea8d59
					
				
					 7 changed files with 360 additions and 0 deletions
				
			
		
							
								
								
									
										22
									
								
								app/src/main/java/xl/model/BombCell.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/src/main/java/xl/model/BombCell.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import xl.expr.Environment; | ||||
| import xl.util.XLException; | ||||
| 
 | ||||
| /** | ||||
|  * A class representing a cell with a bomb value. | ||||
|  *  | ||||
|  * This is essentially a placeholder for an invalid cell reference. | ||||
|  */ | ||||
| public class BombCell implements Cell { | ||||
|     public BombCell() {} | ||||
| 
 | ||||
|     public double cellValue(Environment e) { | ||||
|         throw XLException.RECURSIVECELL_ERROR; | ||||
|     } | ||||
| 
 | ||||
|     public String displayValue() { | ||||
|         throw XLException.RECURSIVECELL_ERROR; | ||||
|     } | ||||
|      | ||||
| } | ||||
							
								
								
									
										11
									
								
								app/src/main/java/xl/model/Cell.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/src/main/java/xl/model/Cell.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import xl.expr.Environment; | ||||
| 
 | ||||
| /**  | ||||
|  * Interface for a cell in the spreadsheet. | ||||
|  */ | ||||
| public interface Cell { | ||||
|     public double cellValue(Environment e); | ||||
|     public String displayValue(); | ||||
| } | ||||
							
								
								
									
										21
									
								
								app/src/main/java/xl/model/CommentCell.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/src/main/java/xl/model/CommentCell.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import xl.expr.Environment; | ||||
| /** | ||||
|  * A class representing a cell with a comment value. | ||||
|  */ | ||||
| public class CommentCell implements Cell { | ||||
|     private String commentText; | ||||
| 
 | ||||
|     public CommentCell(String commentText) { | ||||
|         this.commentText = commentText; | ||||
|     } | ||||
| 
 | ||||
|     public double cellValue(Environment e) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     public String displayValue() { | ||||
|         return commentText; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								app/src/main/java/xl/model/ExpressionCell.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/src/main/java/xl/model/ExpressionCell.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import xl.expr.Environment; | ||||
| import xl.expr.Expr; | ||||
| 
 | ||||
| /** | ||||
|  * A class representing a cell with an expression value. | ||||
|  */ | ||||
| public class ExpressionCell implements Cell { | ||||
|     private Expr expr; | ||||
| 
 | ||||
|     public ExpressionCell(Expr expr) { | ||||
|         this.expr = expr; | ||||
|     } | ||||
| 
 | ||||
|     public double cellValue(Environment env) { | ||||
|         return expr.value(env); | ||||
|     } | ||||
| 
 | ||||
|     public String displayValue() { | ||||
|         return expr.toString(); | ||||
|     } | ||||
|      | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										41
									
								
								app/src/main/java/xl/model/XLBufferedReader.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/src/main/java/xl/model/XLBufferedReader.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.FileReader; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import xl.expr.Expr; | ||||
| import xl.expr.ExprParser; | ||||
| import xl.util.XLException; | ||||
| 
 | ||||
| /** | ||||
|  * Specialized BufferedReader for reading XL files. | ||||
|  */ | ||||
| public class XLBufferedReader extends BufferedReader { | ||||
| 
 | ||||
|     public XLBufferedReader(String name) throws FileNotFoundException { | ||||
|         super(new FileReader(name)); | ||||
|     } | ||||
| 
 | ||||
|     public void load(Map<String, Cell> map) { | ||||
|         try { | ||||
|             while (ready()) { | ||||
|                 String string = readLine(); | ||||
|                 int i = string.indexOf('='); | ||||
|                 String address = string.substring(0, i); | ||||
|                 String content = string.substring(i + 1); | ||||
| 
 | ||||
|                 if(content.startsWith("#")){ | ||||
|                     map.put(address, new CommentCell(content.substring(1))); | ||||
|                 } else { | ||||
|                     ExprParser parser = new ExprParser(); | ||||
|                     Expr expression = parser.build(content); | ||||
|                     map.put(address, new ExpressionCell(expression)); | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
|             throw new XLException(e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										208
									
								
								app/src/main/java/xl/model/XLModel.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								app/src/main/java/xl/model/XLModel.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,208 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
| 
 | ||||
| import xl.expr.Environment; | ||||
| import xl.expr.ExprParser; | ||||
| import xl.util.XLException; | ||||
| 
 | ||||
| public class XLModel extends Observable implements Environment { | ||||
|     private Map<String, Cell> map; | ||||
|     private String status; | ||||
|     private String currentAddress; | ||||
| 
 | ||||
|     // The observers that are registered to be notified | ||||
|     private List<Observer> observers = new ArrayList<>(); | ||||
| 
 | ||||
|     public XLModel() { | ||||
|         status = ""; | ||||
|         currentAddress = "A1"; | ||||
|         map = new java.util.HashMap<String, Cell>(); | ||||
|     } | ||||
| 
 | ||||
|     public String getCurrentAddress() { | ||||
|         return currentAddress; | ||||
|     } | ||||
| 
 | ||||
|     public void setCurrentAddress(String currentAddress) { | ||||
|         this.currentAddress = currentAddress; | ||||
|     } | ||||
| 
 | ||||
|     public Optional<Cell> getCell(String address) { | ||||
|         return Optional.ofNullable(map.get(address)); | ||||
|     } | ||||
| 
 | ||||
|     public boolean remove(String address) { | ||||
|         // Save the existing cell at the specified address | ||||
|         Cell oldCell = map.get(address); | ||||
| 
 | ||||
|         if (oldCell != null) { | ||||
|             map.remove(address); | ||||
| 
 | ||||
|             // If the output is invalid, restore the old cell | ||||
|             if (hasWrongOutputs()) { | ||||
|                 map.put(address, oldCell); | ||||
|                 status = "Cannot remove cell"; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public void add(String address, String text) { | ||||
|         try { | ||||
|             createCell(address, text); | ||||
|         } catch (XLException e) { | ||||
|             status = "Error creating cell"; | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         // Tell observers that the model has changed | ||||
|         setChanged(); | ||||
|         notifyObservers(); | ||||
|     } | ||||
| 
 | ||||
|     public String getStatus() { | ||||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     public void clearStatus() { | ||||
|         status = ""; | ||||
|     } | ||||
| 
 | ||||
|     public void clearCell() { | ||||
|         if (remove(currentAddress)) { | ||||
|             notifyObservers(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void clearAll() { | ||||
|         map.clear(); | ||||
|         clearStatus(); | ||||
|         setChanged(); | ||||
|         notifyObservers(); | ||||
|     } | ||||
| 
 | ||||
|     public void load(String path) { | ||||
|         try (XLBufferedReader reader = new XLBufferedReader(path)) { | ||||
|             map.clear(); | ||||
|             reader.load(map); | ||||
|             status = ""; | ||||
|         } catch (IOException e) { | ||||
|             status = "Error loading file"; | ||||
|         } | ||||
|         setChanged(); | ||||
|         notifyObservers(); | ||||
|     } | ||||
| 
 | ||||
|     public void save(String path) { | ||||
|         try (XLPrintStream xlPrintStream = new XLPrintStream(path)) { | ||||
|             xlPrintStream.save(map.entrySet()); | ||||
|         } catch (IOException e) { | ||||
|             status = "Error saving file"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public String getAddressValue(String address) { | ||||
|         if (map.containsKey(address)) { | ||||
|             Cell cell = map.get(address); | ||||
|             if (cell instanceof CommentCell) { | ||||
|                 return cell.displayValue(); | ||||
|             } | ||||
|             return Double.toString(cell.cellValue(this)); | ||||
|         } | ||||
|         return ""; | ||||
|     } | ||||
| 
 | ||||
|     public String getAddressContent(String address) { | ||||
|         if (map.containsKey(address)) { | ||||
|             Cell cell = map.get(address); | ||||
|             if (cell instanceof CommentCell) { | ||||
|                 return "#" + map.get(address).displayValue(); | ||||
|             } | ||||
|             return map.get(address).displayValue(); | ||||
|         } else { | ||||
|             return ""; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean containsKey(String address) { | ||||
|         return map.containsKey(address); | ||||
|     } | ||||
| 
 | ||||
|     private void createCell(String address, String text) { | ||||
|         if (text.length() > 0 && text.charAt(0) == '#') { | ||||
|             map.put(address, new CommentCell(text.substring(1))); | ||||
|         } else { | ||||
|             ExprParser parser = new ExprParser(); | ||||
|             try { | ||||
|                 Cell newCell = new ExpressionCell(parser.build(text)); | ||||
|                 Cell oldCell = map.get(address); | ||||
|                 BombCell bomb = new BombCell(); | ||||
|                 map.put(address, bomb); | ||||
|                 if (hasCircularDependancy(newCell) || hasWrongOutputs()) { | ||||
|                     if (oldCell != null) { | ||||
|                         map.put(address, oldCell); | ||||
|                     } else { | ||||
|                         map.remove(address); | ||||
|                     } | ||||
|                 } | ||||
|             } catch (IOException e) { | ||||
|                 throw XLException.CREATECELL_ERROR; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private boolean hasCircularDependancy(Cell cell) { | ||||
|         try { | ||||
|             cell.cellValue(this); | ||||
|             map.put(currentAddress, cell); | ||||
|             status = ""; | ||||
|         } catch (XLException e) { | ||||
|             status = e.getMessage(); | ||||
|             return true; | ||||
|         } catch (NullPointerException e) { | ||||
|             status = e.toString(); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private boolean hasWrongOutputs() { | ||||
|         for (Map.Entry<String, Cell> entry : map.entrySet()) { | ||||
|             try { | ||||
|                 entry.getValue().cellValue(this); | ||||
|             } catch (Exception e) { | ||||
|                 status = "cannot update"; | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public double value(String name) { | ||||
|         // Check if the cell is referencing itself | ||||
|         if (currentAddress.equals(name)) { | ||||
|             throw XLException.RECURSIVECELL_ERROR; | ||||
|         } | ||||
|         // Check if the cell is empty | ||||
|         if (map.containsKey(name)) { | ||||
|             return map.get(name).cellValue(this); | ||||
|         } else { | ||||
|             throw XLException.EMPTY_ERROR; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void addObserver(Observer o) { | ||||
|         observers.add(o); | ||||
|     } | ||||
| 
 | ||||
|     public void notifyObservers() { | ||||
|         for (Observer o : observers) { | ||||
|             o.update(this, null); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								app/src/main/java/xl/model/XLPrintStream.java
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/src/main/java/xl/model/XLPrintStream.java
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| package xl.model; | ||||
| 
 | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.PrintStream; | ||||
| import java.util.Map.Entry; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| /** | ||||
|  * Specialized PrintStream for writing XL files. | ||||
|  */ | ||||
| public class XLPrintStream extends PrintStream { | ||||
| 
 | ||||
|     public XLPrintStream(String fileName) throws FileNotFoundException { | ||||
|         super(fileName); | ||||
|     } | ||||
| 
 | ||||
|     public void save(Set<Entry<String, Cell>> set) { | ||||
|         for (Entry<String, Cell> entry : set) { | ||||
|             print(entry.getKey()); | ||||
|             print('='); | ||||
|             Cell cell = entry.getValue(); | ||||
|             if (cell instanceof CommentCell) { | ||||
|                 println("#" + cell.displayValue()); | ||||
|             } else { | ||||
|                 println(cell.displayValue()); | ||||
|             } | ||||
|         } | ||||
|         flush(); | ||||
|         close(); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Imbus
						Imbus