From 02c44758abfb5eb59eca9fe012c197395e1e52e4 Mon Sep 17 00:00:00 2001 From: Remi Date: Tue, 18 Jun 2019 15:23:44 +0200 Subject: [PATCH] finish ihm --- essai.ser | Bin 1164 -> 0 bytes grid.data | Bin 0 -> 1326 bytes src/app/ApplicationDeserialization.java | 38 -------------------------------------- src/ihm/TablooProto.java | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/kernel/Cell.java | 7 +++++++ src/kernel/Grid.java | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- src/kernel/exception/CannotDeleteCellException.java | 8 ++++++++ src/kernel/exception/CellNotFoundException.java | 4 ++++ 8 files changed, 329 insertions(+), 235 deletions(-) delete mode 100644 essai.ser create mode 100644 grid.data delete mode 100644 src/app/ApplicationDeserialization.java create mode 100644 src/kernel/exception/CannotDeleteCellException.java diff --git a/essai.ser b/essai.ser deleted file mode 100644 index 249ca48..0000000 Binary files a/essai.ser and /dev/null differ diff --git a/grid.data b/grid.data new file mode 100644 index 0000000..f47ffca Binary files /dev/null and b/grid.data differ diff --git a/src/app/ApplicationDeserialization.java b/src/app/ApplicationDeserialization.java deleted file mode 100644 index f8455ba..0000000 --- a/src/app/ApplicationDeserialization.java +++ /dev/null @@ -1,38 +0,0 @@ -package app; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.List; - -import kernel.Cell; -import kernel.Grid; - -public class ApplicationDeserialization { - public static void main(String[] args) throws IOException, ClassNotFoundException { - File fichier = new File("essai.ser") ; - - // ouverture d'un flux sur un fichier - ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fichier)) ; - - // désérialization de l'objet - Grid grid = (Grid)ois.readObject() ; - // Affichage - List cells = grid.getCells(); - - System.out.println("Affichage des valeurs :"); - for (Cell cell : cells) - System.out.println(cell.getId() + ": " + cell.getValue()); - - System.out.println("Affichage des formules :"); - for (Cell cell : cells) - System.out.println(cell.getId() + ": " + cell.toString()); - - System.out.println("Affichage des formules développées :"); - for (Cell cell : cells) - System.out.println(cell.getId() + ": " + cell.getDevelopedFormula()); - ois.close(); - } - -} diff --git a/src/ihm/TablooProto.java b/src/ihm/TablooProto.java index 4a66491..70d9de7 100644 --- a/src/ihm/TablooProto.java +++ b/src/ihm/TablooProto.java @@ -24,53 +24,69 @@ import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import kernel.Grid; -import kernel.exception.CellNotFoundException; -import kernel.exception.InvalidIntervalException; +import kernel.exception.*; +import kernel.function.Average; +import kernel.function.Sum; +import kernel.operation.Addition; +import kernel.operation.Division; +import kernel.operation.Multiplication; +import kernel.operation.Subtraction; + +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public class TablooProto extends JPanel { - - /** - * - */ - private static final long serialVersionUID = 1L; - + + static Grid grid; + // Fourni: ne rien changer. - public TablooProto() throws FileNotFoundException, ClassNotFoundException, IOException { - super(new GridLayout(1, 0)); - - // modele de donnees - // cf. plus loin la inner classe MyTableModel a modifier... - MyTableModel tableModel = new MyTableModel(); - - // la JTable et ses parametres - JTable table = new JTable(tableModel); - table.setPreferredScrollableViewportSize(new Dimension(1000, 500)); - table.setGridColor(Color.BLACK); - table.setShowGrid(true); - - // on ajoute un scroll - JScrollPane scrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - add(scrollPane); - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - - // parametrage de la 1ere ligne = noms des colonnes - ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(JLabel.CENTER); - - // parametrage de la 1ere colonne consacree a la numerotation des lignes - TableColumn tm = table.getColumnModel().getColumn(0); - tm.setPreferredWidth(tm.getPreferredWidth() * 2 / 3); - tm.setCellRenderer(new PremiereColonneSpecificRenderer(Color.LIGHT_GRAY)); - - } - - // Inner class pour changer l'aspect de la premiere colonne consacree a la numerotation des lignes - // Fourni: ne rien changer. - class PremiereColonneSpecificRenderer extends DefaultTableCellRenderer { - - /** - * + public TablooProto(Grid grid) throws ClassNotFoundException, IOException { + super(new GridLayout(1, 0)); + + // modele de donnees + // cf. plus loin la inner classe MyTableModel a modifier... + MyTableModel tableModel = new MyTableModel(grid); + + // la JTable et ses parametres + JTable table = new JTable(tableModel); + table.setPreferredScrollableViewportSize(new Dimension(1000, 500)); + table.setGridColor(Color.BLACK); + table.setShowGrid(true); + + // on ajoute un scroll + JScrollPane scrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + add(scrollPane); + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // parametrage de la 1ere ligne = noms des colonnes + ((DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer()).setHorizontalAlignment(JLabel.CENTER); + + // parametrage de la 1ere colonne consacree a la numerotation des lignes + TableColumn tm = table.getColumnModel().getColumn(0); + tm.setPreferredWidth(tm.getPreferredWidth() * 2 / 3); + tm.setCellRenderer(new PremiereColonneSpecificRenderer(Color.LIGHT_GRAY)); + + } + + // Inner class pour changer l'aspect de la premiere colonne consacree a la numerotation des lignes + // Fourni: ne rien changer. + class PremiereColonneSpecificRenderer extends DefaultTableCellRenderer { + + /** + * */ - private static final long serialVersionUID = 1L; Color couleur; public PremiereColonneSpecificRenderer(Color couleur) { @@ -98,145 +114,212 @@ public class TablooProto extends JPanel { * */ private static final long serialVersionUID = 1L; - // TODO - // remplacer ce tableau en dur du prototype par la grille serialisee: - kernel.Grid calc; - //String[][] calc; - - MyTableModel() throws FileNotFoundException, IOException, ClassNotFoundException { - // TODO: remplacer cette initialisation par le chargement de la grille serialisee - File fichier = new File("grille.ser") ; - ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fichier)) ; - calc = (Grid)ois.readObject() ; - ois.close(); - //calc = new String[this.getRowCount()][this.getColumnCount()]; - //for (int ligne =0; ligne < calc.length; ligne++) - //for (int colonne=0; colonne < calc[ligne].length; colonne++) - //calc[ligne][colonne] = ""; - } - - @Override - // Standard: doit retourner le nbre de colonnes de la JTable - public int getColumnCount() { - // TODO: remplacer par le nbre de colonnes de la grille - // + 1 pour la colonne 0 consacrée aux numeros de ligne) - return calc.getTotalColumn()+1; - } - - @Override - // Standard: doit retourner le nbre de lignes de la JTable - public int getRowCount() { - // TODO: remplacer par le nbre de lignes de la grille - return calc.getTotalLine(); - } - - // Standard: doit renvoyer le nom de la colonne a afficher en tete - // Fourni: ne rien changer. - @Override - public String getColumnName(int col) { - if (col == 0) { - return ""; // colonne consacrée aux numeros de ligne - } else { - return "" + (char) ((int) ('A') + col - 1); - } - } - - // Utilitaire interne fourni (ne rien changer) - // Retourne le nom d'une case a partir de ses coordonnees dans la JTable. - String getNomCase(int row, int col) { - return this.getColumnName(col) + String.valueOf(row + 1); // row commence a 0 - } - - @Override - // Standard: doit renvoyer le contenu a afficher de la case correspondante - public Object getValueAt(int row, int col) { - if (col == 0) { - // Fourni: ne rien changer. - // en colonne 0 : numeros de lignes - return "" + String.valueOf(row + 1); - } else { - // TODO: remplacer par le contenu + la valeur - // de la case de nom getNomCase(row, col) - // dans la grille (comme dans la figure 1 du sujet). - try { - return "" + calc.getDevelopedFormula(this.getColumnName(col), row+1)+"="+calc.getValue(this.getColumnName(col), row+1); + private Grid grid; + + MyTableModel(Grid grid) { + this.grid = grid; + } + + @Override + // Standard: doit retourner le nbre de colonnes de la JTable + public int getColumnCount() { + return grid.getTotalColumn() + 1; + } + + @Override + // Standard: doit retourner le nbre de lignes de la JTable + public int getRowCount() { + return grid.getTotalRow(); + } + + // Standard: doit renvoyer le nom de la colonne a afficher en tete + // Fourni: ne rien changer. + @Override + public String getColumnName(int col) { + if (col == 0) { + return ""; // colonne consacrée aux numeros de ligne + } else { + return "" + (char) ((int) ('A') + col - 1); + } + } + + // Utilitaire interne fourni (ne rien changer) + // Retourne le nom d'une case a partir de ses coordonnees dans la JTable. + private String getNomCase(int row, int col) { + return this.getColumnName(col) + (row + 1); // row commence a 0 + } + + @Override + // Standard: doit renvoyer le contenu a afficher de la case correspondante + public Object getValueAt(int row, int col) { + if (col == 0) { + // Fourni: ne rien changer. + // en colonne 0 : numeros de lignes + return "" + (row + 1); + } else { + try { + return grid.getDevelopedFormula(this.getColumnName(col), row + 1) + "=" + grid.getValue(this.getColumnName(col), row + 1); } catch (CellNotFoundException e) { // TODO Auto-generated catch block } } return ""; - } - - // Standard. - // Fourni: ne rien changer. - @Override - public Class getColumnClass(int c) { - return getValueAt(0, c).getClass(); - } - - // Standard: determine si une case est editable ou non. - // Fourni: ne rien changer. - // Seules les cases de la 1er colonne ne le sont pas - // (consacrees a la numerotation des lignes) - @Override - public boolean isCellEditable(int row, int col) { - if (col < 1) { - return false; // col 0 consacree a la numerotation des lignes (non editable) - } else { - return true; - } - } - - - // Standard: l'utilisateur a entré une valeur dans une case, - // mettre a jour le modèle de donnees connecte. - // L'utilisateur a modifie une case. - // Si c'est une valeur numerique (sinon ne rien faire) - // - modifier la case correspondante dans la grille si cette case existe - // - ajouter la case correspondante dans la grille - @Override - public void setValueAt(Object value, int row, int col) { - - // TODO remplacer par le code correspondant - if (value instanceof String) { - if (calc.getCellsId().contains(this.getNomCase(row, col))) + } + + // Standard. + // Fourni: ne rien changer. + @Override + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + // Standard: determine si une case est editable ou non. + // Fourni: ne rien changer. + // Seules les cases de la 1er colonne ne le sont pas + // (consacrees a la numerotation des lignes) + @Override + public boolean isCellEditable(int row, int col) { + return col > 0; + } + + // Standard: l'utilisateur a entré une valeur dans une case, + // mettre a jour le modèle de donnees connecte. + // L'utilisateur a modifie une case. + // Si c'est une valeur numerique + // - modifier la case correspondante dans la grille si cette case existe + // - ajouter la case correspondante dans la grille + @Override + public void setValueAt(Object input, int row, int col) { + Formula formula = new Addition(null, null); + double value = 0; + int cellType; + + if (input instanceof String && !((String) input).isEmpty()) { + String text = (String) input; + try { try { - calc.setValue(this.getColumnName(col), row+1, Double.parseDouble((String)value)); - } catch (CellNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // Récupération de la valeur + value = Double.parseDouble(text); + cellType = 0; + } catch (NumberFormatException exception) { + // Récupération de la formule + formula = this.generateFormulaWithString(text); + cellType = 1; } - else - try { - calc.createCell(this.getColumnName(col), row+1, Double.parseDouble((String)value)); - } catch (InvalidIntervalException e) { - // TODO Auto-generated catch block - + + String cellName = this.getNomCase(row, col); + switch (cellType) { + case 0: + if (this.grid.cellExist(cellName)) + this.grid.setValue(this.getColumnName(col), row + 1, value); + else + this.grid.createCell(this.getColumnName(col), row + 1, value); + break; + case 1: + if (this.grid.cellExist(cellName)) + this.grid.setFormula(this.getColumnName(col), row + 1, formula); + else + this.grid.createCell(this.getColumnName(col), row + 1, formula); + break; } - } - // Ne pas modifier : - // mise a jour automatique de l'affichage suite a la modification - fireTableCellUpdated(row, col); - } - } - // Fin de la inner class MyTableModel - - // Exécution de l'interface graphique a partir d'un terminal. - // TODO: parametrer le tout par un fichier de grille serialisee. - public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException, IOException { - // TODO: parametrer le tableur par un fichier de grille serialisee - // a charger comme modele de donnees. - TablooProto tableur = new TablooProto(); - - // Creation de l'application et lancement - // Fourni: ne rien changer. - JFrame frame = new JFrame("TABLO"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - tableur.setOpaque(true); - frame.setContentPane(tableur); - frame.pack(); - frame.setVisible(true); - - } + } catch (CellNotFoundException | BadSyntaxException | CreateCycleException | InvalidIntervalException e) { + JOptionPane.showMessageDialog(null, e.getMessage(), "Attention", JOptionPane.INFORMATION_MESSAGE); + } + } else { + try { + this.grid.deleteCell(this.getColumnName(col), row + 1); + } catch (CannotDeleteCellException e) { + JOptionPane.showMessageDialog(null, e.getMessage(), "Attention", JOptionPane.INFORMATION_MESSAGE); + } + } + + // mise a jour automatique de l'affichage de toute la table suite a la modification + fireTableDataChanged(); + } + + private Formula generateFormulaWithString(String input) throws CellNotFoundException, BadSyntaxException { + Pattern functionPattern = Pattern.compile("=([A-Z]+)\\(([A-Z0-9,]+)\\)"); + Matcher functionMatcher = functionPattern.matcher(input); + + if (functionMatcher.matches()) { + List cells = Arrays.stream(functionMatcher.group(2).split(",")) + .map(c -> grid.getCell(c)) + .collect(Collectors.toList()); + + if (cells.contains(null)) + throw new CellNotFoundException(); + + cells = cells.stream().filter(Objects::nonNull).collect(Collectors.toList()); + + switch (functionMatcher.group(1)) { + case "SUM": + case "SOMME": + return new Sum(cells); + case "AVERAGE": + case "MOYENNE": + return new Average(cells); + } + } else { + Pattern binaryOperationPattern = Pattern.compile("=([A-Z]+[0-9]+)([+\\-*/])([A-Z]+[0-9]+)"); + Matcher binaryOperationMatcher = binaryOperationPattern.matcher(input); + + if (!binaryOperationMatcher.matches()) + throw new BadSyntaxException(); + + Cell leftCell = grid.getCell(binaryOperationMatcher.group(1)); + Cell rightCell = grid.getCell(binaryOperationMatcher.group(3)); + + if (leftCell == null || rightCell == null) + throw new CellNotFoundException(); + + switch (binaryOperationMatcher.group(2)) { + case "+": + return new Addition(leftCell, rightCell); + case "-": + return new Subtraction(leftCell, rightCell); + case "*": + return new Multiplication(leftCell, rightCell); + case "/": + return new Division(leftCell, rightCell); + } + } + + return null; + } + } + // Fin de la inner class MyTableModel + + // Exécution de l'interface graphique a partir d'un terminal. + public static void main(String[] args) throws ClassNotFoundException, IOException { + try { + grid = Grid.load(); + } catch (IOException | ClassNotFoundException e) { + grid = new Grid(); + } + // a charger comme modele de donnees. + TablooProto tableur = new TablooProto(grid); + + // Creation de l'application et lancement + // Fourni: ne rien changer. + JFrame frame = new JFrame("TABLO"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + tableur.setOpaque(true); + frame.setContentPane(tableur); + frame.pack(); + frame.setVisible(true); + + // Sauvegarde de la grille lors de la fermeture de l'application + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent we) { + try { + grid.save(); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + System.exit(0); + } + }); + } } diff --git a/src/kernel/Cell.java b/src/kernel/Cell.java index 27503a4..61037dc 100644 --- a/src/kernel/Cell.java +++ b/src/kernel/Cell.java @@ -65,6 +65,13 @@ public class Cell implements Serializable { this.value = this.formula.eval(); } + public void updateUsedIn() { + if (this.containFormula()) { + for (Cell cell : this.formula.getUtilisedCells()) + cell.getUsedIn().remove(this); + } + } + public boolean containFormula() { return this.formula != null; } diff --git a/src/kernel/Grid.java b/src/kernel/Grid.java index 7e3f39a..419a5e1 100644 --- a/src/kernel/Grid.java +++ b/src/kernel/Grid.java @@ -1,10 +1,11 @@ package kernel; +import kernel.exception.CannotDeleteCellException; import kernel.exception.CellNotFoundException; import kernel.exception.CreateCycleException; import kernel.exception.InvalidIntervalException; -import java.io.Serializable; +import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -13,32 +14,32 @@ import java.util.Map; public class Grid implements Serializable { private static final long serialVersionUID = 1L; - private static final int MAX_LIGNES = 20; - private static final String MAX_COLONNES = "Z"; + private static final int MAX_ROWS = 20; + private static final String MAX_COLUMNS = "Z"; private Map cells = new HashMap<>(); public static LanguageEnum language = LanguageEnum.FR; - + public void createCell(String column, int line, double value) throws InvalidIntervalException { column = column.toUpperCase(); - + if (!validateInterval(column, line)) throw new InvalidIntervalException(); String id = this.getCellId(column, line); Cell cell = new Cell(column, line, value); this.cells.put(id, cell); - + } public void createCell(String column, int line, Formula formula) throws CreateCycleException, InvalidIntervalException { column = column.toUpperCase(); - + if (!validateInterval(column, line)) throw new InvalidIntervalException(); String id = this.getCellId(column, line); Cell cell = new Cell(column, line, formula); this.cells.put(id, cell); - + } public void setValue(String column, int line, double value) throws CellNotFoundException { @@ -63,10 +64,6 @@ public class Grid implements Serializable { return new ArrayList<>(this.cells.values()); } - public List getCellsId() { - return new ArrayList<>(this.cells.keySet()); - } - public double getValue(String column, int line) throws CellNotFoundException { return this.getCell(column, line).getValue(); } @@ -85,26 +82,59 @@ public class Grid implements Serializable { public int getTotalColumn() { - return MAX_COLONNES.charAt(0) - (int) 'A' + 1; - //return convertStringToInt(MAX_COLONNES); + return MAX_COLUMNS.charAt(0) - (int) 'A' + 1; } - public int getTotalLine() { - return MAX_LIGNES; + public int getTotalRow() { + return MAX_ROWS; } private boolean validateInterval(String column, int line) { - return line >= 1 && line <= MAX_LIGNES && convertStringToInt(column)>=convertStringToInt("A") && convertStringToInt(column)<=convertStringToInt(MAX_COLONNES); + return line >= 1 && line <= MAX_ROWS && convertStringToInt(column) >= convertStringToInt("A") && convertStringToInt(column) <= convertStringToInt(MAX_COLUMNS); + } + + public boolean cellExist(String key) { + return this.cells.containsKey(key); } - private int convertStringToInt(String str){ - int ascii=0; - for(int i = 0; i < str.length(); i++){ // while counting characters if less than the length add one + public void deleteCell(String column, int line) throws CannotDeleteCellException { + String id = this.getCellId(column, line); + + if (this.cellExist(id)) { + Cell cell = this.getCell(id); + + if (!cell.getUsedIn().isEmpty()) + throw new CannotDeleteCellException(); + + cell.updateUsedIn(); + this.cells.remove(this.getCellId(column, line)); + } + } + + private int convertStringToInt(String str) { + int ascii = 0; + for (int i = 0; i < str.length(); i++) { // while counting characters if less than the length add one char character = str.charAt(i); // start on the first character - ascii = ascii+(int) character; - + ascii = ascii + (int) character; + } return ascii; } + public void save() throws IOException { + File file = new File("grid.data"); + + ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file)); + stream.writeObject(this); + stream.close(); + } + + public static Grid load() throws IOException, ClassNotFoundException { + File file = new File("grid.data"); + + ObjectInputStream stream = new ObjectInputStream(new FileInputStream(file)); + + return (Grid) stream.readObject(); + } + } diff --git a/src/kernel/exception/CannotDeleteCellException.java b/src/kernel/exception/CannotDeleteCellException.java new file mode 100644 index 0000000..e1d18f2 --- /dev/null +++ b/src/kernel/exception/CannotDeleteCellException.java @@ -0,0 +1,8 @@ +package kernel.exception; + +public class CannotDeleteCellException extends Exception { + + public CannotDeleteCellException() { + super("Cette cellule est utilisée dans une autre cellule."); + } +} diff --git a/src/kernel/exception/CellNotFoundException.java b/src/kernel/exception/CellNotFoundException.java index 3b059e4..44fe037 100644 --- a/src/kernel/exception/CellNotFoundException.java +++ b/src/kernel/exception/CellNotFoundException.java @@ -1,4 +1,8 @@ package kernel.exception; public class CellNotFoundException extends Exception { + + public CellNotFoundException() { + super("Vous voulez utiliser une cellule qui n'est pas encore créée."); + } } -- libgit2 0.21.2