001package jmri.jmrit.logixng; 002 003import java.io.File; 004import java.io.FileNotFoundException; 005import javax.annotation.CheckForNull; 006import javax.annotation.CheckReturnValue; 007import javax.annotation.Nonnull; 008 009/** 010 * Represent a Table. 011 * A table is a two dimensional array where the rows and columns may have names. 012 * 013 * @author Daniel Bergqvist Copyright (C) 2019 014 */ 015public interface Table { 016 017 /** 018 * Get the value of a cell. 019 * If the table has both rows and columns, the value of the first column 020 * will be returned. 021 * @param row the row of the cell or null if all rows should be returned 022 * @return the value of the cell 023 */ 024 @CheckReturnValue 025 default Object getCell(int row) { 026 return getCell(row, 1); 027 } 028 029 /** 030 * Get the value of a cell. 031 * @param row the row of the cell 032 * @param column the column of the cell 033 * @return the value of the cell 034 */ 035 @CheckReturnValue 036 Object getCell(int row, int column); 037 038 /** 039 * Get the value of a cell. 040 * If the table has both rows and columns, the value of the first column 041 * will be returned. 042 * @param row the row of the cell or null if all rows should be returned 043 * @return the value of the cell 044 * @throws RowNotFoundException if the row is not found 045 */ 046 @CheckReturnValue 047 default Object getCell(@Nonnull String row) 048 throws RowNotFoundException { 049 return getCell(getRowNumber(row), 1); 050 } 051 052 /** 053 * Get the value of a cell. 054 * @param row the row of the cell. If this string is a name of a row, that 055 * row is used. If it's not a name of a row, but an integer value, that 056 * index is used, where row 0 is the name of the row. 057 * @param column the column of the cell. If this string is a name of a 058 * column, that column is used. If it's not a name of a column, but an 059 * integer value, that index is used, where column 0 is the name of the 060 * column. 061 * @return the value of the cell 062 * @throws RowNotFoundException if the row is not found 063 * @throws ColumnNotFoundException if the column is not found 064 */ 065 default Object getCell(@Nonnull String row, @Nonnull String column) 066 throws RowNotFoundException, ColumnNotFoundException { 067 return getCell(getRowNumber(row), getColumnNumber(column)); 068 } 069 070 /** 071 * Get the value of a cell. 072 * @param value the new value of the cell 073 * @param row the row of the cell 074 * @param column the column of the cell 075 */ 076 @CheckReturnValue 077 void setCell(Object value, int row, int column); 078 079 /** 080 * Set the value of a cell. 081 * If the table has both rows and columns, the value of the first column 082 * will be returned. 083 * @param value the new value of the cell 084 * @param row the row of the cell 085 * @throws RowNotFoundException if the row is not found 086 */ 087 default void setCell(Object value, String row) 088 throws RowNotFoundException { 089 setCell(value, getRowNumber(row), 1); 090 } 091 092 /** 093 * Set the value of a cell. 094 * @param value the new value of the cell 095 * @param row the row of the cell. If this string is a name of a row, that 096 * row is used. If it's not a name of a row, but an integer value, that 097 * index is used, where row 0 is the name of the row. 098 * @param column the column of the cell. If this string is a name of a 099 * column, that column is used. If it's not a name of a column, but an 100 * integer value, that index is used, where column 0 is the name of the column. 101 * @throws RowNotFoundException if the row is not found 102 * @throws ColumnNotFoundException if the column is not found 103 */ 104 default void setCell(Object value, String row, String column) 105 throws RowNotFoundException, ColumnNotFoundException { 106 setCell(value, getRowNumber(row), getColumnNumber(column)); 107 } 108 109 /** 110 * Get the number of rows in the table. 111 * @return the number of rows 112 */ 113 int numRows(); 114 115 /** 116 * Get the number of columns in the table. 117 * @return the number of columns 118 */ 119 int numColumns(); 120 121 /** 122 * Get the row number by name of row. 123 * @param rowName the name of the row. If there is no row with this name, 124 * and rowName is a positive integer, that row number will be returned. 125 * @return the row number 126 * @throws RowNotFoundException if the row is not found 127 */ 128 int getRowNumber(String rowName) throws RowNotFoundException; 129 130 /** 131 * Get the row number by name of row. 132 * @param columnName the name of the column. If there is no column with 133 * this name, and columnName is a positive integer, that column number will 134 * be returned. 135 * @return the column number 136 * @throws ColumnNotFoundException if the column is not found 137 */ 138 int getColumnNumber(String columnName) throws ColumnNotFoundException; 139 140 /** 141 * The available types of CSV from which to load a table 142 * The default is TABBED, as that was previously the only choice 143 * TABBED results in parsing the CSV file with tabs as the delimiters 144 * COMMA uses csvFormat of RFC-4180, which is the standard Comma 145 * Seperated Value format, but does not allow empty lines. 146 * SEMICOLON uses a modified version of RFC-4180 with the semicolon as the field delimiter. 147 */ 148 enum CsvType { 149 150 TABBED(Bundle.getMessage("CsvType_Tabbed")), 151 COMMA(Bundle.getMessage("CsvType_Comma")), 152 SEMICOLON(Bundle.getMessage("CsvType_Semicolon")); 153 154 private final String _text; 155 156 private CsvType(String text) { 157 this._text = text; 158 } 159 160 @Override 161 public String toString() { 162 return _text; 163 } 164 165 } 166 167 default boolean isCsvTypeSupported() { 168 return false; 169 } 170 171 default void setCsvType(CsvType csvType) { 172 throw new UnsupportedOperationException("Not supported"); 173 } 174 175 default CsvType getCsvType() { 176 throw new UnsupportedOperationException("Not supported"); 177 } 178 179 /** 180 * Store the table to a CSV file using the filename given when loading the 181 * CSV file. 182 * @throws java.io.FileNotFoundException if file not found 183 */ 184 void storeTableAsCSV() throws FileNotFoundException; 185 186 /** 187 * Store the table to a CSV file. 188 * @param file the CSV file 189 * @throws java.io.FileNotFoundException if file not found 190 */ 191 void storeTableAsCSV(@Nonnull File file) 192 throws FileNotFoundException; 193 194 /** 195 * Store the table to a CSV file. 196 * @param file the CSV file 197 * @param storeSystemUserName true if to store system name and user name in 198 * the file, false otherwise 199 * @throws java.io.FileNotFoundException if file not found 200 */ 201 void storeTableAsCSV(@Nonnull File file, boolean storeSystemUserName) 202 throws FileNotFoundException; 203 204 /** 205 * Store the table to a CSV file. 206 * If system name and/or user name is not null, these values are used 207 * instead of the tables own system name and user name. If no system name 208 * and user name is given and the table is anonymous, no system name and 209 * user name is stored in the file. 210 * @param file the CSV file 211 * @param systemName the system name of the table 212 * @param userName the user name of the table 213 * @throws java.io.FileNotFoundException if file not found 214 */ 215 void storeTableAsCSV( 216 @Nonnull File file, 217 @CheckForNull String systemName, @CheckForNull String userName) 218 throws FileNotFoundException; 219 220 /** 221 * Store the table to a CSV file. 222 * If system name and/or user name is not null, these values are used 223 * instead of the tables own system name and user name. If no system name 224 * and user name is given and the table is anonymous, no system name and 225 * user name is stored in the file. 226 * @param file the CSV file 227 * @param systemName the system name of the table 228 * @param userName the user name of the table 229 * @param storeSystemUserName true if to store system name and user name in 230 * the file, false otherwise 231 * @throws java.io.FileNotFoundException if file not found 232 */ 233 void storeTableAsCSV( 234 @Nonnull File file, 235 @CheckForNull String systemName, @CheckForNull String userName, 236 boolean storeSystemUserName) 237 throws FileNotFoundException; 238 239 240 241 242 class RowNotFoundException extends IllegalArgumentException { 243 244 /** 245 * Constructs a <code>RowNotFoundException</code>. 246 * 247 * @param name the name of the row. 248 */ 249 public RowNotFoundException(String name) { 250 super(Bundle.getMessage("Table_RowNotFound", name)); 251 } 252 253 /** 254 * Constructs a <code>RowNotFoundException</code>. 255 * 256 * <p>Note that the detail message associated with <code>cause</code> is 257 * <i>not</i> automatically incorporated in this exception's detail 258 * message. 259 * 260 * @param name the name of the row. 261 * @param cause the cause (which is saved for later retrieval by the 262 * {@link Throwable#getCause()} method). (A {@code null} value 263 * is permitted, and indicates that the cause is nonexistent or 264 * unknown.) 265 */ 266 public RowNotFoundException(String name, Throwable cause) { 267 super(Bundle.getMessage("Table_RowNotFound", name), cause); 268 } 269 270 } 271 272 273 class ColumnNotFoundException extends IllegalArgumentException { 274 275 /** 276 * Constructs a <code>ColumnNotFoundException</code>. 277 * 278 * @param name the name of the row. 279 */ 280 public ColumnNotFoundException(String name) { 281 super(Bundle.getMessage("Table_ColumnNotFound", name)); 282 } 283 284 /** 285 * Constructs a <code>ColumnNotFoundException</code>. 286 * 287 * <p>Note that the detail message associated with <code>cause</code> is 288 * <i>not</i> automatically incorporated in this exception's detail 289 * message. 290 * 291 * @param name the name of the row. 292 * @param cause the cause (which is saved for later retrieval by the 293 * {@link Throwable#getCause()} method). (A {@code null} value 294 * is permitted, and indicates that the cause is nonexistent or 295 * unknown.) 296 */ 297 public ColumnNotFoundException(String name, Throwable cause) { 298 super(Bundle.getMessage("Table_ColumnNotFound", name), cause); 299 } 300 301 } 302 303}