From 2f95d1f7a89099ea26a6ff7dc0707aa011d20698 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:00:05 +0200 Subject: [PATCH 01/19] Initialize defaultBindings in StaticContext directly instead of static block --- .../org/rumbledb/context/StaticContext.java | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 054fae182d..d6dc17b962 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -75,29 +75,26 @@ public class StaticContext implements Serializable, KryoSerializable { // TODO: should these be transient? private transient SequenceType contextItemStaticType; private transient Map staticallyKnownFunctionSignatures; - private static final Map defaultBindings; + private static final Map defaultBindings = Map.ofEntries( + Map.entry("local", Name.LOCAL_NS), + Map.entry("fn", Name.FN_NS), + Map.entry("math", Name.MATH_NS), + Map.entry("map", Name.MAP_NS), + Map.entry("array", Name.ARRAY_NS), + Map.entry("xs", Name.XS_NS), + Map.entry("xsi", Name.XSI_NS), + Map.entry("xml", Name.XML_NS), + Map.entry("jn", Name.JN_NS), + Map.entry("js", Name.JS_NS), + Map.entry("err", Name.ERROR_NS), + Map.entry("an", Name.JSONIQ_ANNOTATIONS_NS) + ); private DecimalFormatDefinition defaultDecimalFormat; private Map decimalFormats; private int currentMutabilityLevel; - static { - defaultBindings = new HashMap<>(); - defaultBindings.put("local", Name.LOCAL_NS); - defaultBindings.put("fn", Name.FN_NS); - defaultBindings.put("math", Name.MATH_NS); - defaultBindings.put("map", Name.MAP_NS); - defaultBindings.put("array", Name.ARRAY_NS); - defaultBindings.put("xs", Name.XS_NS); - defaultBindings.put("xsi", Name.XSI_NS); - defaultBindings.put("xml", Name.XML_NS); - defaultBindings.put("jn", Name.JN_NS); - defaultBindings.put("js", Name.JS_NS); - defaultBindings.put("err", Name.ERROR_NS); - defaultBindings.put("an", Name.JSONIQ_ANNOTATIONS_NS); - } - private RumbleRuntimeConfiguration configuration; public StaticContext() { From 25a7e14dd0b9a1ee3faa49aa16355b4e1edb025f Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:01:04 +0200 Subject: [PATCH 02/19] Replace trivial getters and setters with Lombok annotations --- .../org/rumbledb/context/StaticContext.java | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index d6dc17b962..692f2d0951 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -47,15 +47,21 @@ import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import lombok.Getter; +import lombok.Setter; + public class StaticContext implements Serializable, KryoSerializable { private static final long serialVersionUID = 1L; + @Getter private transient Map inScopeVariables; private transient Map staticallyKnownNamespaces; private transient UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; private transient InScopeSchemaTypes inScopeSchemaTypes; + @Setter private String queryLanguage; + @Getter private StaticContext parent; private URI staticBaseURI; private boolean emptySequenceOrderLeast; @@ -73,6 +79,8 @@ public class StaticContext implements Serializable, KryoSerializable { private transient String defaultFunctionNamespaceUri; // TODO: should these be transient? + @Getter + @Setter private transient SequenceType contextItemStaticType; private transient Map staticallyKnownFunctionSignatures; private static final Map defaultBindings = Map.ofEntries( @@ -93,6 +101,8 @@ public class StaticContext implements Serializable, KryoSerializable { private DecimalFormatDefinition defaultDecimalFormat; private Map decimalFormats; + @Getter + @Setter private int currentMutabilityLevel; private RumbleRuntimeConfiguration configuration; @@ -174,10 +184,6 @@ private void ensureRootCollationsInitialized() { } } - public StaticContext getParent() { - return this.parent; - } - public RumbleRuntimeConfiguration getRumbleConfiguration() { if (this.configuration != null) { return this.configuration; @@ -188,10 +194,6 @@ public RumbleRuntimeConfiguration getRumbleConfiguration() { throw new OurBadException("Configuration not set."); } - public void setQueryLanguage(String queryLanguage) { - this.queryLanguage = queryLanguage; - } - public String getQueryLanguage() { if (this.queryLanguage != null) { return this.queryLanguage; @@ -322,10 +324,6 @@ public void addFunctionSignature(FunctionIdentifier identifier, FunctionSignatur this.staticallyKnownFunctionSignatures.put(identifier, signature); } - public Map getInScopeVariables() { - return this.inScopeVariables; - } - public void show() { System.err.println(this); } @@ -699,14 +697,6 @@ public StaticContext getModuleContext() { return this; } - public SequenceType getContextItemStaticType() { - return this.contextItemStaticType; - } - - public void setContextItemStaticType(SequenceType contextItemStaticType) { - this.contextItemStaticType = contextItemStaticType; - } - // replace all inScopeVariable in this context and all parents until [stopContext] with name not in [varToExclude] // with same variable with sequence type arity changed from 1 to + and form ? to * // used by groupBy clause @@ -768,14 +758,6 @@ public InScopeSchemaTypes getInScopeSchemaTypes() { throw new OurBadException("In-scope schema types are not set up properly in static context."); } - public int getCurrentMutabilityLevel() { - return this.currentMutabilityLevel; - } - - public void setCurrentMutabilityLevel(int currentMutabilityLevel) { - this.currentMutabilityLevel = currentMutabilityLevel; - } - public boolean getIsAssignable(Name name) { return this.getInScopeVariable(name).isAssignable(); } From d5cd67402aea91d0432ab6317e3184bd70de1a46 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:17:23 +0200 Subject: [PATCH 03/19] Initialize fields in StaticContext constructor and remove null assignments --- .../org/rumbledb/context/StaticContext.java | 68 ++++++------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 692f2d0951..c245745944 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import org.rumbledb.config.RumbleRuntimeConfiguration; @@ -55,17 +56,21 @@ public class StaticContext implements Serializable, KryoSerializable { private static final long serialVersionUID = 1L; @Getter - private transient Map inScopeVariables; + private transient Map inScopeVariables = new HashMap<>(); + private transient Map staticallyKnownNamespaces; private transient UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; private transient InScopeSchemaTypes inScopeSchemaTypes; + @Setter private String queryLanguage; + @Getter private StaticContext parent; + private URI staticBaseURI; - private boolean emptySequenceOrderLeast; - private boolean boundarySpacePreserve; + private boolean emptySequenceOrderLeast = true; + private boolean boundarySpacePreserve = true; private SerializationParameters serializationParameters; private transient Set explicitSerializationParameterNames; private boolean isQuerySideEffecting; @@ -82,7 +87,7 @@ public class StaticContext implements Serializable, KryoSerializable { @Getter @Setter private transient SequenceType contextItemStaticType; - private transient Map staticallyKnownFunctionSignatures; + private transient Map staticallyKnownFunctionSignatures = new HashMap<>(); private static final Map defaultBindings = Map.ofEntries( Map.entry("local", Name.LOCAL_NS), Map.entry("fn", Name.FN_NS), @@ -108,63 +113,30 @@ public class StaticContext implements Serializable, KryoSerializable { private RumbleRuntimeConfiguration configuration; public StaticContext() { - this.parent = null; - this.staticBaseURI = null; - this.queryLanguage = null; - this.inScopeVariables = null; - this.userDefinedFunctionExecutionModes = null; - this.emptySequenceOrderLeast = true; - this.boundarySpacePreserve = true; - this.contextItemStaticType = null; - this.configuration = null; - this.inScopeSchemaTypes = null; - this.currentMutabilityLevel = 0; - this.serializationParameters = null; - this.explicitSerializationParameterNames = null; - this.defaultDecimalFormat = null; - this.decimalFormats = new HashMap<>(); - this.isQuerySideEffecting = false; - initializeRootCollations(); + initializeRoot(null, null); } public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { - this.parent = null; + initializeRoot(staticBaseURI, configuration); + } + + private void initializeRoot(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { this.staticBaseURI = staticBaseURI; - this.queryLanguage = configuration.getQueryLanguage() != null - ? configuration.getQueryLanguage() - : this.queryLanguage; this.configuration = configuration; - this.inScopeVariables = new HashMap<>(); - this.userDefinedFunctionExecutionModes = null; - this.emptySequenceOrderLeast = true; - this.boundarySpacePreserve = true; - this.contextItemStaticType = null; - this.staticallyKnownFunctionSignatures = new HashMap<>(); this.inScopeSchemaTypes = new InScopeSchemaTypes(); - this.currentMutabilityLevel = 0; - this.serializationParameters = SerializationParameters.copy(configuration.getSerializationParameters()); + if (configuration != null) { + this.queryLanguage = configuration.getQueryLanguage(); + this.serializationParameters = SerializationParameters.copy(configuration.getSerializationParameters()); + } this.defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); this.decimalFormats = new HashMap<>(); - this.isQuerySideEffecting = false; initializeRootCollations(); } public StaticContext(StaticContext parent) { - this.parent = parent; - this.queryLanguage = null; - this.inScopeVariables = new HashMap<>(); - this.userDefinedFunctionExecutionModes = null; - this.contextItemStaticType = null; - this.staticallyKnownFunctionSignatures = new HashMap<>(); - this.configuration = null; - this.inScopeSchemaTypes = null; + this.parent = Objects.requireNonNull(parent, "parent"); this.currentMutabilityLevel = parent.currentMutabilityLevel; - this.serializationParameters = null; - this.defaultDecimalFormat = null; - this.decimalFormats = null; - this.isQuerySideEffecting = false; - this.staticallyKnownCollations = null; - this.defaultCollation = null; + // All other nullable fields inherit their values through the parent chain. } private void initializeRootCollations() { From 0d37bebf334a71d95c67ca51847bf1096e6ab798 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:18:55 +0200 Subject: [PATCH 04/19] Remove addStaticallyKnownCollation method from StaticContext class (unused) --- src/main/java/org/rumbledb/context/StaticContext.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index c245745944..7c2e6cf772 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -623,14 +623,6 @@ public boolean isBoundarySpacePreserve() { return this.boundarySpacePreserve; } - public void addStaticallyKnownCollation(String uri) { - if (this.parent != null) { - throw new OurBadException("Statically known collations can only be set in the root static context."); - } - ensureRootCollationsInitialized(); - this.staticallyKnownCollations.add(uri); - } - public boolean isStaticallyKnownCollation(String uri) { return getStaticallyKnownCollations().contains(uri); } From 29ff52fcd77c90a6f29e8ae70e584a1823a3450e Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:19:49 +0200 Subject: [PATCH 05/19] Use Lombok @Setter annotation for serializationParameters and remove the explicit setter method --- src/main/java/org/rumbledb/context/StaticContext.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 7c2e6cf772..857c4ae023 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -71,7 +71,10 @@ public class StaticContext implements Serializable, KryoSerializable { private URI staticBaseURI; private boolean emptySequenceOrderLeast = true; private boolean boundarySpacePreserve = true; + + @Setter private SerializationParameters serializationParameters; + private transient Set explicitSerializationParameterNames; private boolean isQuerySideEffecting; private transient Set staticallyKnownCollations; @@ -459,13 +462,6 @@ public SerializationParameters getSerializationParameters() { return this.serializationParameters; } - /** - * Sets the default serialization parameters at this static context level. - */ - public void setSerializationParameters(SerializationParameters serializationParameters) { - this.serializationParameters = serializationParameters; - } - /** * Override the serialization parameters with the provided parameter name and value. * Throws InvalidSerializationParameterValueException for invalid inputs. From e2a280a97efc4d86d821ece87e377fa79dabeeb7 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:32:09 +0200 Subject: [PATCH 06/19] Simplify StaticContext constructor --- .../java/org/rumbledb/context/StaticContext.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 857c4ae023..f7d054f0c2 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -116,14 +116,10 @@ public class StaticContext implements Serializable, KryoSerializable { private RumbleRuntimeConfiguration configuration; public StaticContext() { - initializeRoot(null, null); + this(null, null); } public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { - initializeRoot(staticBaseURI, configuration); - } - - private void initializeRoot(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { this.staticBaseURI = staticBaseURI; this.configuration = configuration; this.inScopeSchemaTypes = new InScopeSchemaTypes(); @@ -136,10 +132,17 @@ private void initializeRoot(URI staticBaseURI, RumbleRuntimeConfiguration config initializeRootCollations(); } + /** + * Initialize a child static context + * + * @param parent the parent static context + */ public StaticContext(StaticContext parent) { this.parent = Objects.requireNonNull(parent, "parent"); this.currentMutabilityLevel = parent.currentMutabilityLevel; - // All other nullable fields inherit their values through the parent chain. + + // Local maps are initialized at declaration. + // Other fields inherit through parent lookup. } private void initializeRootCollations() { From a72e8a53d20431c4865bf95aa63c65f873789c6d Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:33:52 +0200 Subject: [PATCH 07/19] Remove unused methods from StaticContext class --- .../org/rumbledb/context/StaticContext.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index f7d054f0c2..df8b9ea22b 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -263,10 +263,6 @@ public SequenceType getVariableSequenceType(Name varName) { return getInScopeVariable(varName).getSequenceType(); } - public ExceptionMetadata getVariableMetadata(Name varName) { - return getInScopeVariable(varName).getMetadata(); - } - public ExecutionMode getVariableStorageMode(Name varName) { return getInScopeVariable(varName).getStorageMode(); } @@ -348,16 +344,6 @@ public String toString() { return stringBuilder.toString(); } - public boolean hasVariable(Name variableName) { - if (this.inScopeVariables.containsKey(variableName)) { - return true; - } - if (this.parent != null) { - return this.parent.hasVariable(variableName); - } - return false; - } - public boolean hasVariableInScopeOnly(Name variableName) { return this.inScopeVariables.containsKey(variableName); } @@ -465,17 +451,6 @@ public SerializationParameters getSerializationParameters() { return this.serializationParameters; } - /** - * Override the serialization parameters with the provided parameter name and value. - * Throws InvalidSerializationParameterValueException for invalid inputs. - * - * @param name the name of the parameter to update - * @param value the value of the parameter to update - * @throws org.rumbledb.exceptions.InvalidSerializationParameterValueException if the parameter value is invalid - */ - public void overrideSerializationParameter(String name, String value) { - overrideSerializationParameter(name, value, ExceptionMetadata.EMPTY_METADATA); - } public void overrideSerializationParameter(String name, String value, ExceptionMetadata metadata) { // ensure we have a local copy of the serialization parameters From 9009b50b1820d8ab0fc5993874ab74b2f6136608 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:34:09 +0200 Subject: [PATCH 08/19] Refactor StaticContext to use 'final' for immutable fields and add 'this' keyword for clarity --- .../org/rumbledb/context/StaticContext.java | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index df8b9ea22b..7f74114eb9 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -56,7 +56,7 @@ public class StaticContext implements Serializable, KryoSerializable { private static final long serialVersionUID = 1L; @Getter - private transient Map inScopeVariables = new HashMap<>(); + private final transient Map inScopeVariables = new HashMap<>(); private transient Map staticallyKnownNamespaces; private transient UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; @@ -90,7 +90,8 @@ public class StaticContext implements Serializable, KryoSerializable { @Getter @Setter private transient SequenceType contextItemStaticType; - private transient Map staticallyKnownFunctionSignatures = new HashMap<>(); + private final transient Map staticallyKnownFunctionSignatures = + new HashMap<>(); private static final Map defaultBindings = Map.ofEntries( Map.entry("local", Name.LOCAL_NS), Map.entry("fn", Name.FN_NS), @@ -129,7 +130,7 @@ public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration } this.defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); this.decimalFormats = new HashMap<>(); - initializeRootCollations(); + this.initializeRootCollations(); } /** @@ -156,7 +157,7 @@ private void ensureRootCollationsInitialized() { return; } if (this.staticallyKnownCollations == null) { - initializeRootCollations(); + this.initializeRootCollations(); } else if (this.defaultCollation == null) { this.defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; } @@ -246,7 +247,7 @@ public FunctionSignature getFunctionSignature(FunctionIdentifier identifier) { // replace the sequence type of an existing InScopeVariable, throws an error if the variable does not exists public void replaceVariableSequenceType(Name varName, SequenceType newSequenceType) { - InScopeVariable variable = getInScopeVariable(varName); + InScopeVariable variable = this.getInScopeVariable(varName); this.inScopeVariables.replace( varName, new InScopeVariable( @@ -260,15 +261,15 @@ public void replaceVariableSequenceType(Name varName, SequenceType newSequenceTy } public SequenceType getVariableSequenceType(Name varName) { - return getInScopeVariable(varName).getSequenceType(); + return this.getInScopeVariable(varName).getSequenceType(); } public ExecutionMode getVariableStorageMode(Name varName) { - return getInScopeVariable(varName).getStorageMode(); + return this.getInScopeVariable(varName).getStorageMode(); } public void setVariableStorageMode(Name varName, ExecutionMode mode) { - getInScopeVariable(varName).setStorageMode(mode); + this.getInScopeVariable(varName).setStorageMode(mode); } public void addVariable( @@ -335,11 +336,11 @@ public String toString() { stringBuilder.append("\n"); } if (this.userDefinedFunctionExecutionModes != null) { - stringBuilder.append(this.userDefinedFunctionExecutionModes.toString()); + stringBuilder.append(this.userDefinedFunctionExecutionModes); } if (this.parent != null) { stringBuilder.append("\nParent:"); - stringBuilder.append(this.parent.toString()); + stringBuilder.append(this.parent); } return stringBuilder.toString(); } @@ -352,7 +353,7 @@ public boolean bindNamespace(String prefix, String namespace) { if (this.staticallyKnownNamespaces == null) { this.staticallyKnownNamespaces = new HashMap<>(); } - if (canBindNamespace(prefix)) { + if (this.canBindNamespace(prefix)) { this.staticallyKnownNamespaces.put(prefix, namespace); return true; } @@ -366,7 +367,7 @@ public boolean unbindNamespace(String prefix) { if (this.staticallyKnownNamespaces == null) { this.staticallyKnownNamespaces = new HashMap<>(); } - if (!canBindNamespace(prefix)) { + if (!this.canBindNamespace(prefix)) { return false; } this.staticallyKnownNamespaces.put(prefix, null); @@ -462,15 +463,15 @@ public void overrideSerializationParameter(String name, String value, ExceptionM this.serializationParameters, this, value, - getExplicitSerializationParameterNames(), + this.getExplicitSerializationParameterNames(), metadata ); return; } if ("cdata-section-elements".equals(name) || "suppress-indentation".equals(name)) { - value = expandSerializationQNames(value); + value = this.expandSerializationQNames(value); } - getExplicitSerializationParameterNames().add(name); + this.getExplicitSerializationParameterNames().add(name); // update the local copy of theserialization parameters with the provided parameter name and value SerializationParameterBuilder.update(this.serializationParameters, name, value); } @@ -492,7 +493,7 @@ private String expandSerializationQNames(String value) { if (token.isEmpty()) { continue; } - sb.append(separator).append(expandSerializationQName(token)); + sb.append(separator).append(this.expandSerializationQName(token)); separator = " "; } return sb.toString(); @@ -504,7 +505,7 @@ private String expandSerializationQName(String lexicalQName) { } int colon = lexicalQName.indexOf(':'); if (colon < 0) { - String namespace = getInScopeNamespaceBindings().get(""); + String namespace = this.getInScopeNamespaceBindings().get(""); if (namespace == null || namespace.isEmpty()) { return lexicalQName; } @@ -512,7 +513,7 @@ private String expandSerializationQName(String lexicalQName) { } String prefix = lexicalQName.substring(0, colon); String localName = lexicalQName.substring(colon + 1); - String namespace = getInScopeNamespaceBindings().get(prefix); + String namespace = this.getInScopeNamespaceBindings().get(prefix); if (namespace == null) { return lexicalQName; } @@ -598,14 +599,14 @@ public boolean isBoundarySpacePreserve() { } public boolean isStaticallyKnownCollation(String uri) { - return getStaticallyKnownCollations().contains(uri); + return this.getStaticallyKnownCollations().contains(uri); } public Set getStaticallyKnownCollations() { if (this.parent != null) { return this.parent.getStaticallyKnownCollations(); } - ensureRootCollationsInitialized(); + this.ensureRootCollationsInitialized(); return Collections.unmodifiableSet(this.staticallyKnownCollations); } @@ -613,7 +614,7 @@ public void setDefaultCollation(String uri) { if (this.parent != null) { throw new OurBadException("Default collation can only be set in the root static context."); } - ensureRootCollationsInitialized(); + this.ensureRootCollationsInitialized(); if (!this.staticallyKnownCollations.contains(uri)) { throw new OurBadException("Default collation must be statically known."); } @@ -624,7 +625,7 @@ public String getDefaultCollation() { if (this.parent != null) { return this.parent.getDefaultCollation(); } - ensureRootCollationsInitialized(); + this.ensureRootCollationsInitialized(); return this.defaultCollation; } @@ -644,7 +645,7 @@ public void incrementArities(StaticContext stopContext, Set varToExclude) ? value : new InScopeVariable( value.getName(), - incrementArity(value.getSequenceType()), + this.incrementArity(value.getSequenceType()), value.getMetadata(), value.getStorageMode() ) @@ -657,7 +658,7 @@ public void incrementArities(StaticContext stopContext, Set varToExclude) entry.getKey(), varToExclude.contains(entry.getKey()) ? entry.getValue().getSequenceType() - : incrementArity(entry.getValue().getSequenceType()), + : this.incrementArity(entry.getValue().getSequenceType()), entry.getValue().getMetadata(), entry.getValue().isAssignable() ); @@ -675,7 +676,7 @@ private SequenceType incrementArity(SequenceType sequenceType) { public void bindDefaultNamespaces() { for (String prefix : defaultBindings.keySet()) { - bindNamespace(prefix, defaultBindings.get(prefix)); + this.bindNamespace(prefix, defaultBindings.get(prefix)); } } From c028c33a2c7d72088cf353773b2929b3de997731 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:35:27 +0200 Subject: [PATCH 09/19] Add @NoArgsConstructor to StaticContext for Kryo deserialization --- src/main/java/org/rumbledb/context/StaticContext.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 7f74114eb9..cba044a3d4 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -30,6 +30,7 @@ import java.util.Objects; import java.util.Set; +import lombok.NoArgsConstructor; import org.rumbledb.config.RumbleRuntimeConfiguration; import org.rumbledb.config.SerializationParameterBuilder; import org.rumbledb.exceptions.ExceptionMetadata; @@ -51,6 +52,7 @@ import lombok.Getter; import lombok.Setter; +@NoArgsConstructor // Kryo uses non-arg constructor to deserialize objects public class StaticContext implements Serializable, KryoSerializable { private static final long serialVersionUID = 1L; @@ -116,10 +118,6 @@ public class StaticContext implements Serializable, KryoSerializable { private RumbleRuntimeConfiguration configuration; - public StaticContext() { - this(null, null); - } - public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { this.staticBaseURI = staticBaseURI; this.configuration = configuration; From 38b4c5d30799e7960f498ec8403d2fdf71592141 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:38:58 +0200 Subject: [PATCH 10/19] Add @Serial annotation to StaticContext for serialization compatibility --- src/main/java/org/rumbledb/context/StaticContext.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index cba044a3d4..36f4f2b368 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -20,6 +20,7 @@ package org.rumbledb.context; +import java.io.Serial; import java.io.Serializable; import java.net.URI; import java.util.LinkedHashSet; @@ -55,6 +56,7 @@ @NoArgsConstructor // Kryo uses non-arg constructor to deserialize objects public class StaticContext implements Serializable, KryoSerializable { + @Serial private static final long serialVersionUID = 1L; @Getter @@ -88,7 +90,6 @@ public class StaticContext implements Serializable, KryoSerializable { */ private transient String defaultFunctionNamespaceUri; - // TODO: should these be transient? @Getter @Setter private transient SequenceType contextItemStaticType; @@ -426,7 +427,6 @@ public void read(Kryo kryo, Input input) { /** * Returns the default serialization parameters stored in the static context. - * * Spec references: * *
    @@ -627,13 +627,6 @@ public String getDefaultCollation() { return this.defaultCollation; } - public StaticContext getModuleContext() { - if (this.parent != null) { - return this.parent.getModuleContext(); - } - return this; - } - // replace all inScopeVariable in this context and all parents until [stopContext] with name not in [varToExclude] // with same variable with sequence type arity changed from 1 to + and form ? to * // used by groupBy clause From 985f305a78a0f506516b22e1c24cca5ab975536e Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 18:57:17 +0200 Subject: [PATCH 11/19] Simplify importModuleContext method by using putAll for inScopeVariables and staticallyKnownFunctionSignatures --- src/main/java/org/rumbledb/context/StaticContext.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 36f4f2b368..45592afd67 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -519,14 +519,8 @@ private String expandSerializationQName(String lexicalQName) { } public void importModuleContext(StaticContext moduleContext) { - for (Name name : moduleContext.inScopeVariables.keySet()) { - InScopeVariable variable = moduleContext.inScopeVariables.get(name); - this.inScopeVariables.put(name, variable); - } - for (FunctionIdentifier fi : moduleContext.staticallyKnownFunctionSignatures.keySet()) { - FunctionSignature signature = moduleContext.staticallyKnownFunctionSignatures.get(fi); - this.staticallyKnownFunctionSignatures.put(fi, signature); - } + this.inScopeVariables.putAll(moduleContext.inScopeVariables); + this.staticallyKnownFunctionSignatures.putAll(moduleContext.staticallyKnownFunctionSignatures); } public void setUserDefinedFunctionsExecutionModes( From 626c791ac9e95178239400ef630d66c9f01c5bde Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:00:25 +0200 Subject: [PATCH 12/19] Refactor isInScope and getInScopeVariable methods for improved readability and efficiency --- .../org/rumbledb/context/StaticContext.java | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 45592afd67..fab84a9b8a 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -197,51 +197,42 @@ public void setStaticBaseUri(URI staticBaseURI) { } public boolean isInScope(Name varName) { - boolean found = false; - if (this.inScopeVariables.containsKey(varName)) { - return true; - } else { - StaticContext ancestor = this.parent; - while (ancestor != null) { - found = found || ancestor.getInScopeVariables().containsKey(varName); - ancestor = ancestor.parent; + StaticContext context = this; + while (context != null) { + if (context.inScopeVariables.containsKey(varName)) { + return true; } + context = context.parent; } - return found; + return false; } private InScopeVariable getInScopeVariable(Name varName) { - if (this.inScopeVariables.containsKey(varName)) { - return this.inScopeVariables.get(varName); - } else { - StaticContext ancestor = this.parent; - while (ancestor != null) { - if (ancestor.inScopeVariables.containsKey(varName)) { - return ancestor.inScopeVariables.get(varName); - } - ancestor = ancestor.parent; + StaticContext context = this; + while (context != null) { + InScopeVariable variable = context.inScopeVariables.get(varName); + if (variable != null) { + return variable; } - throw new SemanticException("Variable " + varName + " not in scope", ExceptionMetadata.EMPTY_METADATA); + context = context.parent; } + throw new SemanticException("Variable " + varName + " not in scope", ExceptionMetadata.EMPTY_METADATA); } public FunctionSignature getFunctionSignature(FunctionIdentifier identifier) { - if (this.staticallyKnownFunctionSignatures.containsKey(identifier)) { - return this.staticallyKnownFunctionSignatures.get(identifier); - } else { - StaticContext ancestor = this.parent; - while (ancestor != null) { - if (ancestor.staticallyKnownFunctionSignatures.containsKey(identifier)) { - return ancestor.staticallyKnownFunctionSignatures.get(identifier); - } - ancestor = ancestor.parent; + StaticContext context = this; + while (context != null) { + FunctionSignature signature = context.staticallyKnownFunctionSignatures.get(identifier); + if (signature != null) { + return signature; } - throw new UnknownFunctionCallException( - identifier.getName(), - identifier.getArity(), - ExceptionMetadata.EMPTY_METADATA - ); + context = context.parent; } + throw new UnknownFunctionCallException( + identifier.getName(), + identifier.getArity(), + ExceptionMetadata.EMPTY_METADATA + ); } // replace the sequence type of an existing InScopeVariable, throws an error if the variable does not exists From e4948c95cf8e9c0e6769446a273597184c12c3c5 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:02:23 +0200 Subject: [PATCH 13/19] Rename defaultBindings to DEFAULT_BINDINGS for consistency and clarity --- .../java/org/rumbledb/context/StaticContext.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index fab84a9b8a..b40dabb138 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -95,7 +95,7 @@ public class StaticContext implements Serializable, KryoSerializable { private transient SequenceType contextItemStaticType; private final transient Map staticallyKnownFunctionSignatures = new HashMap<>(); - private static final Map defaultBindings = Map.ofEntries( + private static final Map DEFAULT_BINDINGS = Map.ofEntries( Map.entry("local", Name.LOCAL_NS), Map.entry("fn", Name.FN_NS), Map.entry("math", Name.MATH_NS), @@ -368,8 +368,8 @@ private boolean canBindNamespace(String prefix) { if (!this.staticallyKnownNamespaces.containsKey(prefix)) { return true; } - return defaultBindings.containsKey(prefix) - && defaultBindings.get(prefix).equals(this.staticallyKnownNamespaces.get(prefix)); + return DEFAULT_BINDINGS.containsKey(prefix) + && DEFAULT_BINDINGS.get(prefix).equals(this.staticallyKnownNamespaces.get(prefix)); } public String resolveNamespace(String prefix) { @@ -651,8 +651,8 @@ private SequenceType incrementArity(SequenceType sequenceType) { } public void bindDefaultNamespaces() { - for (String prefix : defaultBindings.keySet()) { - this.bindNamespace(prefix, defaultBindings.get(prefix)); + for (Map.Entry binding : DEFAULT_BINDINGS.entrySet()) { + this.bindNamespace(binding.getKey(), binding.getValue()); } } @@ -660,7 +660,7 @@ public void bindDefaultNamespaces() { * Built-in namespace bindings (fn, xs, map, ...) used when resolving QNames without a full static context. */ public static String getBuiltinNamespaceBinding(String prefix) { - return defaultBindings.get(prefix); + return DEFAULT_BINDINGS.get(prefix); } public InScopeSchemaTypes getInScopeSchemaTypes() { From c4c943864508f91f4721aa02fa3196786e315a26 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:13:25 +0200 Subject: [PATCH 14/19] Remove unused show method from StaticContext for cleaner code --- src/main/java/org/rumbledb/context/StaticContext.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index b40dabb138..70f827b9e9 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -289,10 +289,6 @@ public void addFunctionSignature(FunctionIdentifier identifier, FunctionSignatur this.staticallyKnownFunctionSignatures.put(identifier, signature); } - public void show() { - System.err.println(this); - } - @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); From 9c5ed9e987e6ada8a2b5d8775e66fee59313b300 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:26:15 +0200 Subject: [PATCH 15/19] Make StaticContext no serializable --- .../org/rumbledb/context/StaticContext.java | 91 ++++--------------- 1 file changed, 19 insertions(+), 72 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 70f827b9e9..c2e86d0330 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -20,18 +20,15 @@ package org.rumbledb.context; -import java.io.Serial; -import java.io.Serializable; import java.net.URI; -import java.util.LinkedHashSet; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import lombok.NoArgsConstructor; import org.rumbledb.config.RumbleRuntimeConfiguration; import org.rumbledb.config.SerializationParameterBuilder; import org.rumbledb.exceptions.ExceptionMetadata; @@ -45,26 +42,17 @@ import org.rumbledb.types.ItemType; import org.rumbledb.types.SequenceType; -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.KryoSerializable; -import com.esotericsoftware.kryo.io.Input; -import com.esotericsoftware.kryo.io.Output; - import lombok.Getter; import lombok.Setter; -@NoArgsConstructor // Kryo uses non-arg constructor to deserialize objects -public class StaticContext implements Serializable, KryoSerializable { - - @Serial - private static final long serialVersionUID = 1L; +public class StaticContext { @Getter - private final transient Map inScopeVariables = new HashMap<>(); + private Map inScopeVariables = new HashMap<>(); - private transient Map staticallyKnownNamespaces; - private transient UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; - private transient InScopeSchemaTypes inScopeSchemaTypes; + private Map staticallyKnownNamespaces; + private UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; + private InScopeSchemaTypes inScopeSchemaTypes; @Setter private String queryLanguage; @@ -79,21 +67,21 @@ public class StaticContext implements Serializable, KryoSerializable { @Setter private SerializationParameters serializationParameters; - private transient Set explicitSerializationParameterNames; + private Set explicitSerializationParameterNames; private boolean isQuerySideEffecting; - private transient Set staticallyKnownCollations; - private transient String defaultCollation; + private Set staticallyKnownCollations = CollationCatalogue.defaultStaticallyKnownCollations(); + private String defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; /** * XQuery {@code declare default function namespace}; when null, unprefixed function names use * {@link Name#JSONIQ_DEFAULT_FUNCTION_NS} (Rumble's usual fn/jn/... resolution path). */ - private transient String defaultFunctionNamespaceUri; + private String defaultFunctionNamespaceUri; @Getter @Setter - private transient SequenceType contextItemStaticType; - private final transient Map staticallyKnownFunctionSignatures = + private SequenceType contextItemStaticType; + private Map staticallyKnownFunctionSignatures = new HashMap<>(); private static final Map DEFAULT_BINDINGS = Map.ofEntries( Map.entry("local", Name.LOCAL_NS), @@ -122,14 +110,17 @@ public class StaticContext implements Serializable, KryoSerializable { public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration) { this.staticBaseURI = staticBaseURI; this.configuration = configuration; - this.inScopeSchemaTypes = new InScopeSchemaTypes(); if (configuration != null) { this.queryLanguage = configuration.getQueryLanguage(); this.serializationParameters = SerializationParameters.copy(configuration.getSerializationParameters()); } + this.initializeRootCompilerState(); + } + + private void initializeRootCompilerState() { + this.inScopeSchemaTypes = new InScopeSchemaTypes(); this.defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); this.decimalFormats = new HashMap<>(); - this.initializeRootCollations(); } /** @@ -145,23 +136,6 @@ public StaticContext(StaticContext parent) { // Other fields inherit through parent lookup. } - private void initializeRootCollations() { - this.staticallyKnownCollations = new LinkedHashSet<>(CollationCatalogue.defaultStaticallyKnownCollations()); - this.defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; - } - - private void ensureRootCollationsInitialized() { - if (this.parent != null) { - this.parent.ensureRootCollationsInitialized(); - return; - } - if (this.staticallyKnownCollations == null) { - this.initializeRootCollations(); - } else if (this.defaultCollation == null) { - this.defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; - } - } - public RumbleRuntimeConfiguration getRumbleConfiguration() { if (this.configuration != null) { return this.configuration; @@ -389,29 +363,6 @@ public Map getInScopeNamespaceBindings() { return bindings; } - @Override - public void write(Kryo kryo, Output output) { - kryo.writeObjectOrNull(output, this.parent, StaticContext.class); - kryo.writeObject(output, this.staticBaseURI); - output.writeBoolean(this.emptySequenceOrderLeast); - output.writeBoolean(this.boundarySpacePreserve); - kryo.writeObjectOrNull(output, this.serializationParameters, SerializationParameters.class); - } - - @Override - public void read(Kryo kryo, Input input) { - this.parent = kryo.readObjectOrNull(input, StaticContext.class); - this.staticBaseURI = kryo.readObject(input, URI.class); - this.emptySequenceOrderLeast = input.readBoolean(); - this.boundarySpacePreserve = input.readBoolean(); - // Backward compatibility: older serialized artifacts may not contain the serialization parameters field. - this.serializationParameters = kryo.readObjectOrNull(input, SerializationParameters.class); - // Pointer chain semantics: only root initializes defaults; non-root leaves null to inherit from parent. - if (this.serializationParameters == null && this.parent == null) { - this.serializationParameters = SerializationParameters.defaults(); - } - } - /** * Returns the default serialization parameters stored in the static context. * Spec references: @@ -427,12 +378,11 @@ public SerializationParameters getSerializationParameters() { if (this.serializationParameters != null) { return this.serializationParameters; } - // Backward compatibility: if absent locally (e.g., contexts deserialized from older versions), - // delegate to parent to preserve inheritance instead of creating a shadow copy here. + // A child without a local value inherits its parent's parameters. if (this.parent != null) { return this.parent.getSerializationParameters(); } - // Root context missing the field (e.g., deserialized from an older version): populate defaults once. + // A root context without a local value uses the specification defaults. this.serializationParameters = SerializationParameters.defaults(); return this.serializationParameters; } @@ -585,7 +535,6 @@ public Set getStaticallyKnownCollations() { if (this.parent != null) { return this.parent.getStaticallyKnownCollations(); } - this.ensureRootCollationsInitialized(); return Collections.unmodifiableSet(this.staticallyKnownCollations); } @@ -593,7 +542,6 @@ public void setDefaultCollation(String uri) { if (this.parent != null) { throw new OurBadException("Default collation can only be set in the root static context."); } - this.ensureRootCollationsInitialized(); if (!this.staticallyKnownCollations.contains(uri)) { throw new OurBadException("Default collation must be statically known."); } @@ -604,7 +552,6 @@ public String getDefaultCollation() { if (this.parent != null) { return this.parent.getDefaultCollation(); } - this.ensureRootCollationsInitialized(); return this.defaultCollation; } From e0a1ed24bd35ec6e50176373a34c4728c6e4c0dd Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:30:26 +0200 Subject: [PATCH 16/19] Initialize fields in StaticContext to avoid null checks and improve code clarity --- .../org/rumbledb/context/StaticContext.java | 72 +++++-------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index c2e86d0330..84aeb2aa77 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -50,9 +50,9 @@ public class StaticContext { @Getter private Map inScopeVariables = new HashMap<>(); - private Map staticallyKnownNamespaces; + private Map staticallyKnownNamespaces = new HashMap<>(); private UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; - private InScopeSchemaTypes inScopeSchemaTypes; + private InScopeSchemaTypes inScopeSchemaTypes = new InScopeSchemaTypes(); @Setter private String queryLanguage; @@ -65,9 +65,9 @@ public class StaticContext { private boolean boundarySpacePreserve = true; @Setter - private SerializationParameters serializationParameters; + private SerializationParameters serializationParameters = SerializationParameters.defaults(); - private Set explicitSerializationParameterNames; + private Set explicitSerializationParameterNames = new LinkedHashSet<>(); private boolean isQuerySideEffecting; private Set staticallyKnownCollations = CollationCatalogue.defaultStaticallyKnownCollations(); private String defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; @@ -98,8 +98,8 @@ public class StaticContext { Map.entry("an", Name.JSONIQ_ANNOTATIONS_NS) ); - private DecimalFormatDefinition defaultDecimalFormat; - private Map decimalFormats; + private DecimalFormatDefinition defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); + private Map decimalFormats = new HashMap<>(); @Getter @Setter @@ -114,13 +114,6 @@ public StaticContext(URI staticBaseURI, RumbleRuntimeConfiguration configuration this.queryLanguage = configuration.getQueryLanguage(); this.serializationParameters = SerializationParameters.copy(configuration.getSerializationParameters()); } - this.initializeRootCompilerState(); - } - - private void initializeRootCompilerState() { - this.inScopeSchemaTypes = new InScopeSchemaTypes(); - this.defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); - this.decimalFormats = new HashMap<>(); } /** @@ -131,6 +124,8 @@ private void initializeRootCompilerState() { public StaticContext(StaticContext parent) { this.parent = Objects.requireNonNull(parent, "parent"); this.currentMutabilityLevel = parent.currentMutabilityLevel; + this.inScopeSchemaTypes = parent.inScopeSchemaTypes; + this.serializationParameters = SerializationParameters.copy(parent.serializationParameters); // Local maps are initialized at declaration. // Other fields inherit through parent lookup. @@ -286,15 +281,13 @@ public String toString() { stringBuilder.append(" (namespace " + entry.getKey().getName().getNamespace() + ")"); stringBuilder.append("\n"); } - if (this.inScopeSchemaTypes != null) { - stringBuilder.append("Static context with user-defined types:\n"); - for (ItemType itemType : this.inScopeSchemaTypes.getInScopeSchemaTypes()) { - stringBuilder.append(itemType.getName()); - stringBuilder.append(itemType.isResolved() ? " (resolved)" : " (unresolved)"); - stringBuilder.append("\n"); - } + stringBuilder.append("Static context with user-defined types:\n"); + for (ItemType itemType : this.inScopeSchemaTypes.getInScopeSchemaTypes()) { + stringBuilder.append(itemType.getName()); + stringBuilder.append(itemType.isResolved() ? " (resolved)" : " (unresolved)"); stringBuilder.append("\n"); } + stringBuilder.append("\n"); if (this.userDefinedFunctionExecutionModes != null) { stringBuilder.append(this.userDefinedFunctionExecutionModes); } @@ -310,9 +303,6 @@ public boolean hasVariableInScopeOnly(Name variableName) { } public boolean bindNamespace(String prefix, String namespace) { - if (this.staticallyKnownNamespaces == null) { - this.staticallyKnownNamespaces = new HashMap<>(); - } if (this.canBindNamespace(prefix)) { this.staticallyKnownNamespaces.put(prefix, namespace); return true; @@ -324,9 +314,6 @@ public boolean bindNamespace(String prefix, String namespace) { * Explicitly removes a namespace binding in this context, shadowing any inherited or predeclared binding. */ public boolean unbindNamespace(String prefix) { - if (this.staticallyKnownNamespaces == null) { - this.staticallyKnownNamespaces = new HashMap<>(); - } if (!this.canBindNamespace(prefix)) { return false; } @@ -343,7 +330,7 @@ private boolean canBindNamespace(String prefix) { } public String resolveNamespace(String prefix) { - if (this.staticallyKnownNamespaces != null && this.staticallyKnownNamespaces.containsKey(prefix)) { + if (this.staticallyKnownNamespaces.containsKey(prefix)) { return this.staticallyKnownNamespaces.get(prefix); } if (this.parent != null) { @@ -357,9 +344,7 @@ public Map getInScopeNamespaceBindings() { if (this.parent != null) { bindings.putAll(this.parent.getInScopeNamespaceBindings()); } - if (this.staticallyKnownNamespaces != null) { - bindings.putAll(this.staticallyKnownNamespaces); - } + bindings.putAll(this.staticallyKnownNamespaces); return bindings; } @@ -375,24 +360,11 @@ public Map getInScopeNamespaceBindings() { *
*/ public SerializationParameters getSerializationParameters() { - if (this.serializationParameters != null) { - return this.serializationParameters; - } - // A child without a local value inherits its parent's parameters. - if (this.parent != null) { - return this.parent.getSerializationParameters(); - } - // A root context without a local value uses the specification defaults. - this.serializationParameters = SerializationParameters.defaults(); return this.serializationParameters; } public void overrideSerializationParameter(String name, String value, ExceptionMetadata metadata) { - // ensure we have a local copy of the serialization parameters - if (this.serializationParameters == null) { - this.serializationParameters = SerializationParameters.copy(this.getSerializationParameters()); - } if ("parameter-document".equals(name)) { SerializationParameterUtils.applyParameterDocument( this.serializationParameters, @@ -412,9 +384,6 @@ public void overrideSerializationParameter(String name, String value, ExceptionM } private Set getExplicitSerializationParameterNames() { - if (this.explicitSerializationParameterNames == null) { - this.explicitSerializationParameterNames = new LinkedHashSet<>(); - } return this.explicitSerializationParameterNames; } @@ -607,13 +576,7 @@ public static String getBuiltinNamespaceBinding(String prefix) { } public InScopeSchemaTypes getInScopeSchemaTypes() { - if (this.inScopeSchemaTypes != null) { - return this.inScopeSchemaTypes; - } - if (this.parent != null) { - return this.parent.getInScopeSchemaTypes(); - } - throw new OurBadException("In-scope schema types are not set up properly in static context."); + return this.inScopeSchemaTypes; } public boolean getIsAssignable(Name name) { @@ -653,9 +616,6 @@ public Map getDecimalFormats() { if (this.parent != null) { return this.parent.getDecimalFormats(); } - if (this.decimalFormats == null) { - return Collections.emptyMap(); - } return Collections.unmodifiableMap(this.decimalFormats); } From 5671dc86d699c00abcfaacbf6b4be02b6c21a822 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 19:32:13 +0200 Subject: [PATCH 17/19] Add getter for inScopeSchemaTypes and update serializationParameters handling --- .../org/rumbledb/context/StaticContext.java | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 84aeb2aa77..083dcd1aa5 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -52,6 +52,8 @@ public class StaticContext { private Map staticallyKnownNamespaces = new HashMap<>(); private UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; + + @Getter private InScopeSchemaTypes inScopeSchemaTypes = new InScopeSchemaTypes(); @Setter @@ -64,6 +66,18 @@ public class StaticContext { private boolean emptySequenceOrderLeast = true; private boolean boundarySpacePreserve = true; + /** + * -- GETTER -- + * Returns the default serialization parameters stored in the static context. + * Spec references: + *
    + *
  • XQuery 3.1 Static Context Components (link: + * https://www.w3.org/TR/xquery-31/#id-xq-static-context-components)
  • + *
  • Serialization 3.1 — Serialization Parameters (link: + * https://www.w3.org/TR/xslt-xquery-serialization-31/#serparam)
  • + *
+ */ + @Getter @Setter private SerializationParameters serializationParameters = SerializationParameters.defaults(); @@ -348,21 +362,6 @@ public Map getInScopeNamespaceBindings() { return bindings; } - /** - * Returns the default serialization parameters stored in the static context. - * Spec references: - * - *
    - *
  • XQuery 3.1 Static Context Components (link: - * https://www.w3.org/TR/xquery-31/#id-xq-static-context-components)
  • - *
  • Serialization 3.1 — Serialization Parameters (link: - * https://www.w3.org/TR/xslt-xquery-serialization-31/#serparam)
  • - *
- */ - public SerializationParameters getSerializationParameters() { - return this.serializationParameters; - } - public void overrideSerializationParameter(String name, String value, ExceptionMetadata metadata) { if ("parameter-document".equals(name)) { @@ -575,10 +574,6 @@ public static String getBuiltinNamespaceBinding(String prefix) { return DEFAULT_BINDINGS.get(prefix); } - public InScopeSchemaTypes getInScopeSchemaTypes() { - return this.inScopeSchemaTypes; - } - public boolean getIsAssignable(Name name) { return this.getInScopeVariable(name).isAssignable(); } From 224175c140d461674dd573b46f49541a11cbc2fe Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 20:11:50 +0200 Subject: [PATCH 18/19] Remove setter for serializationParameters in StaticContext to enforce immutability --- src/main/java/org/rumbledb/context/StaticContext.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 083dcd1aa5..5a803d2a57 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -78,7 +78,6 @@ public class StaticContext { * */ @Getter - @Setter private SerializationParameters serializationParameters = SerializationParameters.defaults(); private Set explicitSerializationParameterNames = new LinkedHashSet<>(); From e318d9ea79302471321b54437e2e5f9a5b892f12 Mon Sep 17 00:00:00 2001 From: Jimmy Cai Date: Tue, 21 Jul 2026 20:18:06 +0200 Subject: [PATCH 19/19] Make inScopeVariables, staticallyKnownNamespaces, explicitSerializationParameterNames, staticallyKnownCollations, staticallyKnownFunctionSignatures, and decimalFormats final for improved immutability --- .../java/org/rumbledb/context/StaticContext.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/rumbledb/context/StaticContext.java b/src/main/java/org/rumbledb/context/StaticContext.java index 5a803d2a57..425fc52fbf 100644 --- a/src/main/java/org/rumbledb/context/StaticContext.java +++ b/src/main/java/org/rumbledb/context/StaticContext.java @@ -48,9 +48,9 @@ public class StaticContext { @Getter - private Map inScopeVariables = new HashMap<>(); + private final Map inScopeVariables = new HashMap<>(); - private Map staticallyKnownNamespaces = new HashMap<>(); + private final Map staticallyKnownNamespaces = new HashMap<>(); private UserDefinedFunctionExecutionModes userDefinedFunctionExecutionModes; @Getter @@ -80,9 +80,9 @@ public class StaticContext { @Getter private SerializationParameters serializationParameters = SerializationParameters.defaults(); - private Set explicitSerializationParameterNames = new LinkedHashSet<>(); + private final Set explicitSerializationParameterNames = new LinkedHashSet<>(); private boolean isQuerySideEffecting; - private Set staticallyKnownCollations = CollationCatalogue.defaultStaticallyKnownCollations(); + private final Set staticallyKnownCollations = CollationCatalogue.defaultStaticallyKnownCollations(); private String defaultCollation = CollationCatalogue.CODEPOINT_COLLATION; /** @@ -94,7 +94,7 @@ public class StaticContext { @Getter @Setter private SequenceType contextItemStaticType; - private Map staticallyKnownFunctionSignatures = + private final Map staticallyKnownFunctionSignatures = new HashMap<>(); private static final Map DEFAULT_BINDINGS = Map.ofEntries( Map.entry("local", Name.LOCAL_NS), @@ -112,7 +112,7 @@ public class StaticContext { ); private DecimalFormatDefinition defaultDecimalFormat = DecimalFormatDefinition.defaultInstance(); - private Map decimalFormats = new HashMap<>(); + private final Map decimalFormats = new HashMap<>(); @Getter @Setter