1111
1212import java .lang .reflect .Field ;
1313import java .util .Arrays ;
14- import java .util .HashMap ;
1514import java .util .Map ;
1615import java .util .Optional ;
1716
@@ -26,49 +25,43 @@ class ParserTest {
2625
2726 void assertInfixParsesTo (String src , int level , MathSymbol expected ) {
2827 // Expects it to be full parse
29- if (!nocache ) assertInfixParsesToInner (src , level , expected );
30- else try (WithSuppressingCache ignored = WithSuppressingCache .start ()) {
31- assertInfixParsesToInner (src , level , expected );
28+ try (var ignored = applyNocacheAttr ()) {
29+ assertInfixParsesTo_inner (src , level , expected );
3230 }
3331 }
34-
3532 void assertInfixParsesTo (ObjStringPair exprPair , int level ) {
3633 assertInfixParsesTo (exprPair .str (), level , exprPair .obj ());
3734 }
38-
39- void assertParsesToInner (String src , MathSymbol expected ) {
35+ void assertInfixParsesTo_inner (String src , int level , MathSymbol expected ) {
4036 // Expects it to be full parse
4137 Parser p = new Parser (src );
42- MathSymbol actual = assertDoesNotThrow (p ::parse );
43- // No need to check EOF as parse() checks that it consumed the whole string
38+ MathSymbol actual = assertDoesNotThrow (() -> p .parseInfixPrecedenceLevel (level ));
39+ assertEquals (src .substring (0 , p .idx ), src , "Didn't fully parse the string" ); // = isEof() but better msg
40+ assertTrue (p .isEof ());
4441 assertEquals (expected , actual );
4542 }
4643
4744 void assertParsesTo (String src , MathSymbol expected ) {
4845 // Expects it to be full parse
49- if (!nocache ) assertParsesToInner (src , expected );
50- else try (WithSuppressingCache ignored = WithSuppressingCache .start ()) {
51- assertParsesToInner (src , expected );
46+ try (var ignored = applyNocacheAttr ()) {
47+ assertParsesTo_inner (src , expected );
5248 }
5349 }
54-
5550 void assertParsesTo (ObjStringPair exprPair ) {
5651 assertParsesTo (exprPair .str (), exprPair .obj ());
5752 }
58-
59- void assertInfixParsesToInner (String src , int level , MathSymbol expected ) {
53+ void assertParsesTo_inner (String src , MathSymbol expected ) {
6054 // Expects it to be full parse
6155 Parser p = new Parser (src );
62- MathSymbol actual = assertDoesNotThrow (() -> p .parseInfixPrecedenceLevel (level ));
63- assertEquals (src .substring (0 , p .idx ), src , "Didn't fully parse the string" ); // = isEof() but better msg
64- assertTrue (p .isEof ());
56+ MathSymbol actual = assertDoesNotThrow (p ::parse );
57+ // No need to check EOF as parse() checks that it consumed the whole string
6558 assertEquals (expected , actual );
6659 }
6760
6861 @ ParameterizedTest
6962 @ ValueSource (booleans ={true , false })
7063 void parseInfixPrecedenceLevel (boolean disableCache ) {
71- try (var ignored = new WithNocache ( this , disableCache ). start ( )) {
64+ try (var ignored = setNocacheAttr ( disableCache )) {
7265 assertInfixParsesTo ("1.0/2.0" , MUL_PREC ,
7366 new DivOperation (new BasicDoubleSymbol (1.0 ), new BasicDoubleSymbol (2.0 )));
7467 assertInfixParsesTo (".3*6." , MUL_PREC ,
@@ -97,7 +90,7 @@ void parseInfixPrecedenceLevel(boolean disableCache) {
9790 @ ParameterizedTest
9891 @ ValueSource (booleans ={true , false })
9992 void parse (boolean disableCache ) {
100- try (var ignored = new WithNocache ( this , disableCache ). start ( )) {
93+ try (var ignored = setNocacheAttr ( disableCache )) {
10194 assertParsesTo ("1.0/2.0" ,
10295 new DivOperation (new BasicDoubleSymbol (1.0 ), new BasicDoubleSymbol (2.0 )));
10396 assertParsesTo (".3*6." ,
@@ -126,7 +119,7 @@ void parse(boolean disableCache) {
126119 @ ParameterizedTest
127120 @ ValueSource (booleans ={true , false })
128121 void parsePrecedenceLevel_pow (boolean disableCache ) {
129- try (var ignored = new WithNocache ( this , disableCache )) {
122+ try (var ignored = setNocacheAttr ( disableCache )) {
130123 assertInfixParsesTo ("1.2**9.1" , POW_PREC ,
131124 new PowOperation (new BasicDoubleSymbol (1.2 ), new BasicDoubleSymbol (9.1 )));
132125 assertInfixParsesTo ("1.2**9.1**.3" , POW_PREC ,
@@ -141,7 +134,7 @@ void parsePrecedenceLevel_pow(boolean disableCache) {
141134 @ ParameterizedTest
142135 @ ValueSource (booleans ={true , false })
143136 void parse_pow (boolean disableCache ) {
144- try (var ignored = new WithNocache ( this , disableCache ). start ( )) {
137+ try (var ignored = setNocacheAttr ( disableCache )) {
145138 assertParsesTo ("1.2**9.1" ,
146139 new PowOperation (new BasicDoubleSymbol (1.2 ), new BasicDoubleSymbol (9.1 )));
147140 assertParsesTo ("1.2**9.1**.3" ,
@@ -153,23 +146,22 @@ void parse_pow(boolean disableCache) {
153146 }
154147 }
155148
149+ protected WithNocache setNocacheAttr (boolean disableCache ) {
150+ return new WithNocache (this , disableCache );
151+ }
152+
156153 static class WithNocache implements AutoCloseable {
157154 boolean origNocache ;
158155 boolean doDisable ;
159156 ParserTest inst ;
160157
161- WithNocache (ParserTest inst_ ) {
158+ public WithNocache (ParserTest inst_ ) {
162159 this (inst_ , true );
163160 }
164- WithNocache (ParserTest inst_ , boolean doDisable_ ) {
161+ public WithNocache (ParserTest inst_ , boolean doDisable_ ) {
165162 inst = inst_ ;
166163 doDisable = doDisable_ ;
167- }
168-
169- @ Contract ("->this" )
170- public WithNocache start () {
171164 _start ();
172- return this ;
173165 }
174166
175167 private void _start () {
@@ -182,33 +174,40 @@ public void close() {
182174 }
183175 }
184176
185- static class WithSuppressingCache implements AutoCloseable {
186- public static WithSuppressingCache getInstance () {
187- return INSTANCE ;
188- }
189- protected static WithSuppressingCache INSTANCE = new WithSuppressingCache ();
190- protected WithSuppressingCache () {}
177+ protected WithSuppressingCache applyNocacheAttr () {
178+ return WithSuppressingCache .start (nocache );
179+ }
191180
192- WithSuppressingCache startInstance () {
193- clearCache ();
194- return this ;
195- }
196- static WithSuppressingCache start () {
197- return getInstance ().startInstance ();
181+ /**
182+ *
183+ * Public API:
184+ * <ul>
185+ * <li>Create an instance and clear cache: {@link #start(boolean doStart)} / {@link #start()}</li>
186+ * <li>Restore the cache: {@link #close()}</li>
187+ * </ul>
188+ */
189+ static class WithSuppressingCache implements AutoCloseable {
190+ @ Contract ("_->new" )
191+ public static @ NotNull WithSuppressingCache start (boolean doStart ) {
192+ WithSuppressingCache self = new WithSuppressingCache ();
193+ if (doStart ) self .clearCache ();
194+ return self ;
198195 }
199- WithSuppressingCache stop () {
200- restoreCache ();
201- return this ;
196+ @ Contract ( " -> new" )
197+ public static @ NotNull WithSuppressingCache start () {
198+ return start ( true ) ;
202199 }
203200
204201 @ Override
205202 public void close () {
206203 restoreCache ();
207204 }
208205
206+ private WithSuppressingCache () {}
207+
208+ private Map <SymbolInfo , Optional <BinOpBiConstructor <?>>> origCache = null ;
209209
210- Map <SymbolInfo , Optional <BinOpBiConstructor <?>>> origCache = new HashMap <>();
211- void clearCache () {
210+ protected void clearCache () {
212211 Field cacheField = getBiConstructorCache ();
213212 origCache = Arrays .stream (SymbolInfo .values ()).map (sym -> {
214213 try {
@@ -221,7 +220,8 @@ void clearCache() {
221220 }).collect (UtilCollectors .entriesToUnmodifiableMap ());
222221 }
223222
224- void restoreCache () {
223+ protected void restoreCache () {
224+ if (origCache == null ) return ; // didn't cache so nothing to restore
225225 Field cacheField = getBiConstructorCache ();
226226 origCache .forEach ((key , value ) -> {
227227 try {
0 commit comments