code: public ExecutionContext with( String key,
                                    String value ) {
          Map<String, String> newData = data;
          if (value == null) {
              // Remove the value with the key ...
              if (this.data.isEmpty() || !this.data.containsKey(key)) {
                  // nothing to remove
                  return this;
              }
              newData = new HashMap<String, String>(data);
              newData.remove(key);
              newData = Collections.unmodifiableMap(newData);
          } else {
              // We are to add the value ...
              newData = new HashMap<String, String>(data);
              newData.put(key, value);
              newData = Collections.unmodifiableMap(newData);
          }
          return new ExecutionContext(securityContext, namespaceRegistry, propertyFactory, threadPools, binaryStore, newData,
                                      processId, decoder, encoder, stringFactory, binaryFactory, booleanFactory, dateFactory,
                                      decimalFactory, doubleFactory, longFactory, nameFactory, pathFactory, referenceFactory,
                                      weakReferenceFactory, simpleReferenceFactory, uriFactory, objectFactory, locale);
      }
docs: Create a new execution context that mirrors this context but that contains the supplied key-value pair in the new context's
  data.
  
  @param key the key for the new data that is to be affiliated with the resulting context
  @param value the data value to be affiliated with the supplied key in the resulting context, or null if an existing data
  affiliated with the key should be removed in the resulting context
  @return the execution context that is identical with this execution context, but which uses the supplied data; never null
  @since 2.0