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. 181 * @param file the CSV file 182 * @throws java.io.FileNotFoundException if file not found 183 */ 184 void storeTableAsCSV(@Nonnull File file) 185 throws FileNotFoundException; 186 187 /** 188 * Store the table to a CSV file. 189 * If system name and/or user name is not null, these values are used 190 * instead of the tables own system name and user name. If no system name 191 * and user name is given and the table is anonymous, no system name and 192 * user name is stored in the file. 193 * @param file the CSV file 194 * @param systemName the system name of the table 195 * @param userName the user name of the table 196 * @throws java.io.FileNotFoundException if file not found 197 */ 198 void storeTableAsCSV( 199 @Nonnull File file, 200 @CheckForNull String systemName, @CheckForNull String userName) 201 throws FileNotFoundException; 202 203 204 205 206 class RowNotFoundException extends IllegalArgumentException { 207 208 /** 209 * Constructs a <code>RowNotFoundException</code>. 210 * 211 * @param name the name of the row. 212 */ 213 public RowNotFoundException(String name) { 214 super(Bundle.getMessage("Table_RowNotFound", name)); 215 } 216 217 /** 218 * Constructs a <code>RowNotFoundException</code>. 219 * 220 * <p>Note that the detail message associated with <code>cause</code> is 221 * <i>not</i> automatically incorporated in this exception's detail 222 * message. 223 * 224 * @param name the name of the row. 225 * @param cause the cause (which is saved for later retrieval by the 226 * {@link Throwable#getCause()} method). (A {@code null} value 227 * is permitted, and indicates that the cause is nonexistent or 228 * unknown.) 229 */ 230 public RowNotFoundException(String name, Throwable cause) { 231 super(Bundle.getMessage("Table_RowNotFound", name), cause); 232 } 233 234 } 235 236 237 class ColumnNotFoundException extends IllegalArgumentException { 238 239 /** 240 * Constructs a <code>ColumnNotFoundException</code>. 241 * 242 * @param name the name of the row. 243 */ 244 public ColumnNotFoundException(String name) { 245 super(Bundle.getMessage("Table_ColumnNotFound", name)); 246 } 247 248 /** 249 * Constructs a <code>ColumnNotFoundException</code>. 250 * 251 * <p>Note that the detail message associated with <code>cause</code> is 252 * <i>not</i> automatically incorporated in this exception's detail 253 * message. 254 * 255 * @param name the name of the row. 256 * @param cause the cause (which is saved for later retrieval by the 257 * {@link Throwable#getCause()} method). (A {@code null} value 258 * is permitted, and indicates that the cause is nonexistent or 259 * unknown.) 260 */ 261 public ColumnNotFoundException(String name, Throwable cause) { 262 super(Bundle.getMessage("Table_ColumnNotFound", name), cause); 263 } 264 265 } 266 267}