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 */ 147 enum CsvType { 148 149 TABBED(Bundle.getMessage("CsvType_Tabbed")), 150 COMMA(Bundle.getMessage("CsvType_Comma")); 151 152 private final String _text; 153 154 private CsvType(String text) { 155 this._text = text; 156 } 157 158 @Override 159 public String toString() { 160 return _text; 161 } 162 163 } 164 165 default boolean isCsvTypeSupported() { 166 return false; 167 } 168 169 default void setCsvType(CsvType csvType) { 170 throw new UnsupportedOperationException("Not supported"); 171 } 172 173 default CsvType getCsvType() { 174 throw new UnsupportedOperationException("Not supported"); 175 } 176 177 /** 178 * Store the table to a CSV file. 179 * @param file the CSV file 180 * @throws java.io.FileNotFoundException if file not found 181 */ 182 void storeTableAsCSV(@Nonnull File file) 183 throws FileNotFoundException; 184 185 /** 186 * Store the table to a CSV file. 187 * If system name and/or user name is not null, these values are used 188 * instead of the tables own system name and user name. If no system name 189 * and user name is given and the table is anonymous, no system name and 190 * user name is stored in the file. 191 * @param file the CSV file 192 * @param systemName the system name of the table 193 * @param userName the user name of the table 194 * @throws java.io.FileNotFoundException if file not found 195 */ 196 void storeTableAsCSV( 197 @Nonnull File file, 198 @CheckForNull String systemName, @CheckForNull String userName) 199 throws FileNotFoundException; 200 201 202 203 204 class RowNotFoundException extends IllegalArgumentException { 205 206 /** 207 * Constructs a <code>RowNotFoundException</code>. 208 * 209 * @param name the name of the row. 210 */ 211 public RowNotFoundException(String name) { 212 super(Bundle.getMessage("Table_RowNotFound", name)); 213 } 214 215 /** 216 * Constructs a <code>RowNotFoundException</code>. 217 * 218 * <p>Note that the detail message associated with <code>cause</code> is 219 * <i>not</i> automatically incorporated in this exception's detail 220 * message. 221 * 222 * @param name the name of the row. 223 * @param cause the cause (which is saved for later retrieval by the 224 * {@link Throwable#getCause()} method). (A {@code null} value 225 * is permitted, and indicates that the cause is nonexistent or 226 * unknown.) 227 */ 228 public RowNotFoundException(String name, Throwable cause) { 229 super(Bundle.getMessage("Table_RowNotFound", name), cause); 230 } 231 232 } 233 234 235 class ColumnNotFoundException extends IllegalArgumentException { 236 237 /** 238 * Constructs a <code>ColumnNotFoundException</code>. 239 * 240 * @param name the name of the row. 241 */ 242 public ColumnNotFoundException(String name) { 243 super(Bundle.getMessage("Table_ColumnNotFound", name)); 244 } 245 246 /** 247 * Constructs a <code>ColumnNotFoundException</code>. 248 * 249 * <p>Note that the detail message associated with <code>cause</code> is 250 * <i>not</i> automatically incorporated in this exception's detail 251 * message. 252 * 253 * @param name the name of the row. 254 * @param cause the cause (which is saved for later retrieval by the 255 * {@link Throwable#getCause()} method). (A {@code null} value 256 * is permitted, and indicates that the cause is nonexistent or 257 * unknown.) 258 */ 259 public ColumnNotFoundException(String name, Throwable cause) { 260 super(Bundle.getMessage("Table_ColumnNotFound", name), cause); 261 } 262 263 } 264 265}