From 452649cde3eb8f4c6804b5ecab95a647506e4441 Mon Sep 17 00:00:00 2001 From: Jared Davis Date: Wed, 10 Jun 2026 22:38:48 -0400 Subject: [PATCH 1/4] Eliminate runtime reflection in Operators. Perform reflection in OperatorsTest instead. --- .../org/scijava/parsington/Operators.java | 84 ++++++++++++++----- .../org/scijava/parsington/OperatorsTest.java | 47 +++++++++++ 2 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 src/test/java/org/scijava/parsington/OperatorsTest.java diff --git a/src/main/java/org/scijava/parsington/Operators.java b/src/main/java/org/scijava/parsington/Operators.java index 9da9d77..4e28c18 100644 --- a/src/main/java/org/scijava/parsington/Operators.java +++ b/src/main/java/org/scijava/parsington/Operators.java @@ -32,9 +32,8 @@ import static org.scijava.parsington.Operator.Associativity.LEFT; import static org.scijava.parsington.Operator.Associativity.RIGHT; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.scijava.parsington.Operator.Associativity; @@ -171,6 +170,67 @@ public final class Operators { public static final Operator UNSIGNED_RIGHT_SHIFT_ASSIGN = op(">>>=", 2, RIGHT, 0); + private static final List operatorList = new ArrayList<>( + Arrays.asList( + DOT, + PARENS, + BRACKETS, + BRACES, + TRANSPOSE, + DOT_TRANSPOSE, + POW, + DOT_POW, + POST_INC, + POST_DEC, + PRE_INC, + PRE_DEC, + POS, + NEG, + COMPLEMENT, + NOT, + MUL, + DIV, + MOD, + RIGHT_DIV, + DOT_MUL, + DOT_DIV, + DOT_RIGHT_DIV, + ADD, + SUB, + LEFT_SHIFT, + RIGHT_SHIFT, + UNSIGNED_RIGHT_SHIFT, + LESS_THAN, + GREATER_THAN, + LESS_THAN_OR_EQUAL, + GREATER_THAN_OR_EQUAL, + INSTANCEOF, + EQUAL, + NOT_EQUAL, + BITWISE_AND, + BITWISE_OR, + LOGICAL_AND, + LOGICAL_OR, + QUESTION, + COLON, + ASSIGN, + POW_ASSIGN, + DOT_POW_ASSIGN, + MUL_ASSIGN, + DIV_ASSIGN, + MOD_ASSIGN, + RIGHT_DIV_ASSIGN, + DOT_DIV_ASSIGN, + DOT_RIGHT_DIV_ASSIGN, + ADD_ASSIGN, + SUB_ASSIGN, + AND_ASSIGN, + OR_ASSIGN, + LEFT_SHIFT_ASSIGN, + RIGHT_SHIFT_ASSIGN, + UNSIGNED_RIGHT_SHIFT_ASSIGN)); + + private Operators() { // NB: Prevent instantiation of utility class. } @@ -182,19 +242,7 @@ private Operators() { * {@link Operators} class, in declaration order. */ public static List standardList() { - // Build the standard list from all available Operator constants. - final ArrayList ops = new ArrayList<>(); - for (final Field f : Operators.class.getFields()) { - if (!isOperator(f)) continue; - try { - ops.add((Operator) f.get(null)); - } - catch (final IllegalAccessException exc) { - // This should never happen. - throw new IllegalStateException(exc); - } - } - return ops; + return new ArrayList<>(operatorList); } // -- Helper methods -- @@ -211,10 +259,4 @@ private static Group group(final String leftSymbol, return new Group(leftSymbol, rightSymbol, precedence); } - private static boolean isOperator(final Field f) { - final int mods = f.getModifiers(); - return Modifier.isStatic(mods) && Modifier.isFinal(mods) && - Operator.class.isAssignableFrom(f.getType()); - } - } diff --git a/src/test/java/org/scijava/parsington/OperatorsTest.java b/src/test/java/org/scijava/parsington/OperatorsTest.java new file mode 100644 index 0000000..250bccd --- /dev/null +++ b/src/test/java/org/scijava/parsington/OperatorsTest.java @@ -0,0 +1,47 @@ +package org.scijava.parsington; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OperatorsTest extends AbstractTest { + + @Test + public void testStandardList() { + // ensure the Operators static list matches a list generated by reflection on the Operators class + List fromStatic = Operators.standardList(); + List fromReflection = standardList(); + Collections.sort(fromStatic); + Collections.sort(fromReflection); + + assertEquals(fromStatic, fromReflection); + } + + public List standardList() { + // Build the standard list from all available Operator constants. + final ArrayList ops = new ArrayList<>(); + for (final Field f : Operators.class.getFields()) { + if (!isOperator(f)) continue; + try { + ops.add((Operator) f.get(null)); + } catch (final IllegalAccessException exc) { + // This should never happen. + throw new IllegalStateException(exc); + } + } + return ops; + } + + private boolean isOperator(final Field f) { + final int mods = f.getModifiers(); + return Modifier.isStatic(mods) && Modifier.isFinal(mods) && + Operator.class.isAssignableFrom(f.getType()); + } + +} \ No newline at end of file From 6bbfa69b8057daca5bb53805b3fffcbd57b0f5d7 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Sun, 19 Jul 2026 14:00:56 -0500 Subject: [PATCH 2/4] OperatorsTest: add copyright headers --- .../org/scijava/parsington/OperatorsTest.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/scijava/parsington/OperatorsTest.java b/src/test/java/org/scijava/parsington/OperatorsTest.java index 250bccd..0d34e60 100644 --- a/src/test/java/org/scijava/parsington/OperatorsTest.java +++ b/src/test/java/org/scijava/parsington/OperatorsTest.java @@ -1,3 +1,31 @@ +/*- + * #%L + * Parsington: the SciJava mathematical expression parser. + * %% + * Copyright (C) 2015 - 2026 Parsington developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ package org.scijava.parsington; import org.junit.jupiter.api.Test; @@ -44,4 +72,4 @@ private boolean isOperator(final Field f) { Operator.class.isAssignableFrom(f.getType()); } -} \ No newline at end of file +} From e5e6fa0784da2ac4892dd1f92ce9c3914d6f17ef Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Sun, 19 Jul 2026 14:02:26 -0500 Subject: [PATCH 3/4] Operators: tweak line wrapping --- src/main/java/org/scijava/parsington/Operators.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/scijava/parsington/Operators.java b/src/main/java/org/scijava/parsington/Operators.java index 4e28c18..c3499c6 100644 --- a/src/main/java/org/scijava/parsington/Operators.java +++ b/src/main/java/org/scijava/parsington/Operators.java @@ -167,8 +167,7 @@ public final class Operators { public static final Operator OR_ASSIGN = op("|=", 2, RIGHT, 0); public static final Operator LEFT_SHIFT_ASSIGN = op("<<=", 2, RIGHT, 0); public static final Operator RIGHT_SHIFT_ASSIGN = op(">>=", 2, RIGHT, 0); - public static final Operator UNSIGNED_RIGHT_SHIFT_ASSIGN = op(">>>=", 2, - RIGHT, 0); + public static final Operator UNSIGNED_RIGHT_SHIFT_ASSIGN = op(">>>=", 2, RIGHT, 0); private static final List operatorList = new ArrayList<>( Arrays.asList( From 0d5ae6151742336f4ee0391c79215f469deb2431 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Sun, 19 Jul 2026 15:44:20 -0500 Subject: [PATCH 4/4] Make construction of operators list more DRY We can grow the list as each operator is constructed. --- .../org/scijava/parsington/Operators.java | 72 ++---------------- .../org/scijava/parsington/OperatorsTest.java | 75 ------------------- 2 files changed, 8 insertions(+), 139 deletions(-) delete mode 100644 src/test/java/org/scijava/parsington/OperatorsTest.java diff --git a/src/main/java/org/scijava/parsington/Operators.java b/src/main/java/org/scijava/parsington/Operators.java index c3499c6..7fb2db0 100644 --- a/src/main/java/org/scijava/parsington/Operators.java +++ b/src/main/java/org/scijava/parsington/Operators.java @@ -33,7 +33,6 @@ import static org.scijava.parsington.Operator.Associativity.RIGHT; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.scijava.parsington.Operator.Associativity; @@ -57,6 +56,8 @@ */ public final class Operators { + private static final List operatorList = new ArrayList<>(); + // -- dot -- public static final Operator DOT = op(".", 2, LEFT, 16); @@ -169,67 +170,6 @@ public final class Operators { public static final Operator RIGHT_SHIFT_ASSIGN = op(">>=", 2, RIGHT, 0); public static final Operator UNSIGNED_RIGHT_SHIFT_ASSIGN = op(">>>=", 2, RIGHT, 0); - private static final List operatorList = new ArrayList<>( - Arrays.asList( - DOT, - PARENS, - BRACKETS, - BRACES, - TRANSPOSE, - DOT_TRANSPOSE, - POW, - DOT_POW, - POST_INC, - POST_DEC, - PRE_INC, - PRE_DEC, - POS, - NEG, - COMPLEMENT, - NOT, - MUL, - DIV, - MOD, - RIGHT_DIV, - DOT_MUL, - DOT_DIV, - DOT_RIGHT_DIV, - ADD, - SUB, - LEFT_SHIFT, - RIGHT_SHIFT, - UNSIGNED_RIGHT_SHIFT, - LESS_THAN, - GREATER_THAN, - LESS_THAN_OR_EQUAL, - GREATER_THAN_OR_EQUAL, - INSTANCEOF, - EQUAL, - NOT_EQUAL, - BITWISE_AND, - BITWISE_OR, - LOGICAL_AND, - LOGICAL_OR, - QUESTION, - COLON, - ASSIGN, - POW_ASSIGN, - DOT_POW_ASSIGN, - MUL_ASSIGN, - DIV_ASSIGN, - MOD_ASSIGN, - RIGHT_DIV_ASSIGN, - DOT_DIV_ASSIGN, - DOT_RIGHT_DIV_ASSIGN, - ADD_ASSIGN, - SUB_ASSIGN, - AND_ASSIGN, - OR_ASSIGN, - LEFT_SHIFT_ASSIGN, - RIGHT_SHIFT_ASSIGN, - UNSIGNED_RIGHT_SHIFT_ASSIGN)); - - private Operators() { // NB: Prevent instantiation of utility class. } @@ -249,13 +189,17 @@ public static List standardList() { private static Operator op(final String symbol, final int arity, final Associativity associativity, final double precedence) { - return new Operator(symbol, arity, associativity, precedence); + Operator op = new Operator(symbol, arity, associativity, precedence); + operatorList.add(op); + return op; } private static Group group(final String leftSymbol, final String rightSymbol, final double precedence) { - return new Group(leftSymbol, rightSymbol, precedence); + Group group = new Group(leftSymbol, rightSymbol, precedence); + operatorList.add(group); + return group; } } diff --git a/src/test/java/org/scijava/parsington/OperatorsTest.java b/src/test/java/org/scijava/parsington/OperatorsTest.java deleted file mode 100644 index 0d34e60..0000000 --- a/src/test/java/org/scijava/parsington/OperatorsTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * #%L - * Parsington: the SciJava mathematical expression parser. - * %% - * Copyright (C) 2015 - 2026 Parsington developers. - * %% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * #L% - */ -package org.scijava.parsington; - -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class OperatorsTest extends AbstractTest { - - @Test - public void testStandardList() { - // ensure the Operators static list matches a list generated by reflection on the Operators class - List fromStatic = Operators.standardList(); - List fromReflection = standardList(); - Collections.sort(fromStatic); - Collections.sort(fromReflection); - - assertEquals(fromStatic, fromReflection); - } - - public List standardList() { - // Build the standard list from all available Operator constants. - final ArrayList ops = new ArrayList<>(); - for (final Field f : Operators.class.getFields()) { - if (!isOperator(f)) continue; - try { - ops.add((Operator) f.get(null)); - } catch (final IllegalAccessException exc) { - // This should never happen. - throw new IllegalStateException(exc); - } - } - return ops; - } - - private boolean isOperator(final Field f) { - final int mods = f.getModifiers(); - return Modifier.isStatic(mods) && Modifier.isFinal(mods) && - Operator.class.isAssignableFrom(f.getType()); - } - -}