001package jmri.jmrit.logixng.util; 002 003import javax.annotation.CheckReturnValue; 004import javax.annotation.Nonnull; 005 006import jmri.InstanceManager; 007import jmri.Memory; 008import jmri.MemoryManager; 009import jmri.jmrit.logixng.*; 010import jmri.util.TypeConversionUtil; 011 012/** 013 * Utility methods to handle references 014 */ 015public class ReferenceUtil { 016 017 // The methods in this class are protected instead of private to let the 018 // test class ReferenceUtilTest access the methods. 019 020 /** 021 * Checks if the parameter is a reference or not. 022 * @param value the string to check 023 * @return true if value has a reference. falsw otherwise 024 */ 025 static public boolean isReference(String value) { 026 if (value == null) return false; 027 // A reference starts with { and ends with } 028 return value.startsWith("{") 029 && value.endsWith("}") 030 && value.length() > 2; 031 } 032 033 static protected String unescapeString(String value, int startIndex, int endIndex) { 034 boolean escaped = false; 035 036 StringBuilder sb = new StringBuilder(); 037 for (int i=startIndex; i < endIndex; i++) { 038 if (value.charAt(i) == '\\') escaped = !escaped; 039 else escaped = false; 040 041 if (! escaped) sb.append(value.charAt(i)); 042 } 043 044 return sb.toString(); 045 } 046 047 /** 048 * Get the value. 049 * The value ends either with end of string, or with any of the characters 050 * comma, left square bracket, right square bracket or right curly bracket. 051 * These characters may be escaped and should then be ignored. 052 * @param reference the reference 053 * @param startIndex where in the string the value starts, since the 054 * reference string may contain several references. 055 * @param endIndex index of the end of the value. This is an output parameter. 056 * @return the value 057 */ 058 static protected String getValue(String reference, int startIndex, IntRef endIndex) { 059 boolean escapeFound = false; 060 boolean escaped = false; 061 int end = startIndex; 062 while (end < reference.length() 063 && (escaped || 064 (reference.charAt(end) != ',' 065 && reference.charAt(end) != '[' 066 && reference.charAt(end) != ']' 067 && reference.charAt(end) != '{' 068 && reference.charAt(end) != '}'))) { 069 if (reference.charAt(end) == '\\') { 070 escaped = !escaped; 071 escapeFound = true; 072 } else { 073 escaped = false; 074 } 075 end++; 076 } 077 endIndex.v = end; 078 079 if (startIndex == end) throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 080 if (escapeFound) return unescapeString(reference, startIndex, end); 081 else return reference.substring(startIndex, end); 082 } 083 084 /** 085 * Get the reference or the value.The value ends either with end of string, 086 * or with any of the characters comma, left square bracket, right square 087 * bracket or right curly bracket. 088 * These characters may be escaped and should then be ignored. 089 * 090 * @param symbolTable the symbol table 091 * @param reference the reference 092 * @param startIndex where in the string the value starts, since the 093 * reference string may contain several references. 094 * @param endIndex index of the end of the value. This is an output parameter. 095 * @return the value 096 */ 097 static protected String getReferenceOrValue(SymbolTable symbolTable, String reference, int startIndex, IntRef endIndex) { 098 099 while ((startIndex < reference.length()-1) 100 && (Character.isSpaceChar(reference.charAt(startIndex)))) { 101 startIndex++; 102 } 103 104 String result; 105 106 // Do we have a new reference? 107 if (reference.charAt(startIndex) == '{') { 108 result = getReference(symbolTable, reference, startIndex, endIndex); 109 } else { 110 result = getValue(reference, startIndex, endIndex); 111 } 112 113 // Skip spaces 114 while ((endIndex.v < reference.length()) && Character.isSpaceChar(reference.charAt(endIndex.v))) { 115 endIndex.v++; 116 } 117 118 return result; 119 } 120 121 /** 122 * Get the value of a reference 123 * @param symbolTable the symbol table 124 * @param reference the reference 125 * @param startIndex where in the string the reference starts, since the 126 * reference string may contain several references. 127 * @param endIndex index of the end of the reference. This is an output parameter. 128 * @return the value of the reference 129 */ 130 static protected String getReference( 131 SymbolTable symbolTable, String reference, int startIndex, IntRef endIndex) { 132 133 // A reference must start with the char { 134 if (reference.charAt(startIndex) != '{') { 135 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 136 } 137 138 String leftValue; 139 String column; 140 String row; 141 142 startIndex++; 143 144 leftValue = getReferenceOrValue(symbolTable, reference, startIndex, endIndex); 145 146 if (endIndex.v == reference.length()) { 147 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 148 } 149 150 if ((reference.charAt(endIndex.v) != '}') && (reference.charAt(endIndex.v) != '[')) { 151 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 152 } 153 154 155 endIndex.v++; 156 157 if ((endIndex.v == reference.length()) || (reference.charAt(endIndex.v-1) == '}')) { 158 159 if ((symbolTable != null) && symbolTable.hasValue(leftValue)) { 160 return TypeConversionUtil.convertToString(symbolTable.getValue(leftValue), false); 161 } 162 MemoryManager memoryManager = InstanceManager.getDefault(MemoryManager.class); 163 Memory m = memoryManager.getNamedBean(leftValue.trim()); 164 if (m != null) { 165 if (m.getValue() != null) return m.getValue().toString(); 166 else throw new IllegalArgumentException("Memory '"+leftValue+"' has no value"); 167 } 168 else throw new IllegalArgumentException("Memory '"+leftValue+"' is not found"); 169 } 170 171 // If we are here, we have a table reference. Find out column and row. 172 if (reference.charAt(endIndex.v-1) != '[') { 173 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 174 } 175 176 177 // If we are here, we have a table reference. Find out column and row. 178 row = getReferenceOrValue(symbolTable, reference, endIndex.v, endIndex); 179 180 endIndex.v++; 181 182 // Skip spaces 183 while ((endIndex.v-2 < reference.length()) && Character.isSpaceChar(reference.charAt(endIndex.v-1))) { 184 endIndex.v++; 185 } 186 187 int lastEndIndV = endIndex.v; 188 189 // Skip spaces 190 while ((endIndex.v < reference.length()) && Character.isSpaceChar(reference.charAt(endIndex.v))) { 191 endIndex.v++; 192 } 193 194 if ((reference.charAt(lastEndIndV-1) == ']') 195 && (reference.charAt(endIndex.v) == '}')) { 196 197 endIndex.v++; 198 199 NamedTableManager tableManager = 200 InstanceManager.getDefault(NamedTableManager.class); 201 202 NamedTable table = tableManager.getNamedBean(leftValue); 203 if (table != null) { 204 Object cell = table.getCell(row.trim()); 205 return cell != null ? cell.toString() : null; 206 } else { 207 throw new IllegalArgumentException("Table '"+leftValue+"' is not found"); 208 } 209 } 210 211 if (endIndex.v == reference.length() || reference.charAt(lastEndIndV-1) != ',') { 212 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 213 } 214 215 column = getReferenceOrValue(symbolTable, reference, endIndex.v, endIndex); 216 217 // Skip spaces 218 while ((endIndex.v < reference.length()) && Character.isSpaceChar(reference.charAt(endIndex.v))) { 219 endIndex.v++; 220 } 221 222 if (endIndex.v == reference.length() || reference.charAt(endIndex.v) != ']') { 223 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 224 } 225 226// if (((reference.charAt(endIndex.v) == ']') 227// && (reference.charAt(endIndex.v+1) == '}'))) { 228 if ((reference.charAt(endIndex.v) == ']')) { 229 230 endIndex.v++; 231 232 NamedTableManager tableManager = 233 InstanceManager.getDefault(NamedTableManager.class); 234 235 NamedTable table = tableManager.getNamedBean(leftValue); 236 if (table != null) { 237 Object cell = table.getCell(row.trim(),column.trim()); 238 // Skip spaces 239 while ((endIndex.v < reference.length()) && Character.isSpaceChar(reference.charAt(endIndex.v))) { 240 endIndex.v++; 241 } 242 endIndex.v++; 243 return cell != null ? cell.toString() : null; 244 } else { 245 throw new IllegalArgumentException("Table '"+leftValue+"' is not found"); 246 } 247 } 248 249 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 250 } 251 252 @CheckReturnValue 253 @Nonnull 254 static public String getReference(SymbolTable symbolTable, String reference) { 255 if (!isReference(reference)) { 256 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 257 } 258 259 IntRef endIndex = new IntRef(); 260 String ref = getReference(symbolTable, reference, 0, endIndex); 261 262 if (endIndex.v != reference.length()) { 263 throw new IllegalArgumentException("Reference '"+reference+"' is not a valid reference"); 264 } 265 266 return ref; 267 } 268 269 270 /** 271 * Reference to an integer. 272 * This class is cheaper to use than AtomicInteger. 273 */ 274 protected static class IntRef { 275 public int v; 276 } 277 278// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ReferenceUtil.class); 279}