Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
/**
* Storage layer abstraction for tool execution context.
*
* <p>This interface defines the storage contract for context objects. It supports two retrieval
* <p>This interface defines the storage contract for context objects. It supports three retrieval
* modes:
* <ol>
* <li><b>By type only</b>: {@code get(Class<T>)} - suitable for singleton scenarios</li>
* <li><b>By key + type</b>: {@code get(String, Class<T>)} - suitable for multi-instance
* scenarios</li>
* scenarios requiring type safety</li>
* </ol>
*
* <p>This design allows handling both simple cases (one UserContext) and complex cases
* (multiple UserContext instances for different users).
* <p>This design allows handling both simple cases (one UserContext), complex cases
* (multiple UserContext instances for different users), and dynamic attributes (like a simple
* string tenantId).
*
* <p>Implementations can be:
* <ul>
Expand All @@ -43,6 +44,9 @@
* // Multiple instances of same type
* UserContext admin = store.get("admin", UserContext.class);
* UserContext guest = store.get("guest", UserContext.class);
*
* // Dynamic property by key only
* Object tenantId = store.get("tenantId");
* }</pre>
*
* @see ToolExecutionContext
Expand Down Expand Up @@ -73,6 +77,19 @@ public interface ContextStore {
*/
<T> T get(String key, Class<T> type);

/**
* Retrieves an object by key without requiring the caller to know its registered type.
*
* <p>This is useful when the key is the public contract and the runtime type may be any
* application object.
*
* @param key The key identifying the specific instance
* @return The object instance, or null if not found
*/
default Object get(String key) {
return null;
}

/**
* Retrieves an object by type only (without key).
*
Expand Down Expand Up @@ -105,6 +122,16 @@ public interface ContextStore {
*/
boolean contains(String key, Class<?> type);

/**
* Checks whether an object with the specified key exists regardless of type.
*
* @param key The key identifying the instance
* @return true if any object exists with this key, false otherwise
*/
default boolean contains(String key) {
return get(key) != null;
}

/**
* Checks whether any object of the specified type exists (regardless of key).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class DefaultContextStore implements ContextStore {
// Two-level map: Class -> (Key -> Object)
private final Map<Class<?>, Map<String, Object>> objectMap;

// Direct key index for type-agnostic lookup
private final Map<String, Object> keyMap;

private DefaultContextStore(Builder builder) {
if (builder.objectMap != null) {
Map<Class<?>, Map<String, Object>> copy = new HashMap<>();
Expand All @@ -45,6 +48,10 @@ private DefaultContextStore(Builder builder) {
} else {
this.objectMap = Collections.emptyMap();
}
this.keyMap =
builder.keyMap != null
? Collections.unmodifiableMap(new HashMap<>(builder.keyMap))
: Collections.emptyMap();
}

/**
Expand All @@ -70,6 +77,17 @@ public <T> T get(String key, Class<T> type) {
return null;
}

/**
* Retrieves an object by key regardless of its registered runtime type.
*
* @param key The key identifying the instance
* @return The object instance, or null if not found
*/
@Override
public Object get(String key) {
return keyMap.get(key);
}

/**
* Retrieves an object by type only (singleton case).
*
Expand Down Expand Up @@ -98,6 +116,17 @@ public boolean contains(String key, Class<?> type) {
return keyMap != null && keyMap.containsKey(key);
}

/**
* Checks whether an object with the specified key exists regardless of its registered type.
*
* @param key The key identifying the instance
* @return true if the object exists, false otherwise
*/
@Override
public boolean contains(String key) {
return keyMap.containsKey(key);
}

/**
* Checks whether any object of the specified type exists.
*
Expand Down Expand Up @@ -146,22 +175,23 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DefaultContextStore that = (DefaultContextStore) o;
return Objects.equals(objectMap, that.objectMap);
return Objects.equals(objectMap, that.objectMap) && Objects.equals(keyMap, that.keyMap);
}

@Override
public int hashCode() {
return Objects.hash(objectMap);
return Objects.hash(objectMap, keyMap);
}

@Override
public String toString() {
return "DefaultContextStore{" + "objectMap=" + objectMap + '}';
return "DefaultContextStore{" + "objectMap=" + objectMap + ", keyMap=" + keyMap + '}';
}

/** Builder for DefaultContextStore. */
public static class Builder {
private Map<Class<?>, Map<String, Object>> objectMap;
private Map<String, Object> keyMap;

private Builder() {}

Expand Down Expand Up @@ -243,9 +273,14 @@ public <T> Builder register(String key, Class<T> type, T object) {
if (this.objectMap == null) {
this.objectMap = new HashMap<>();
}

Map<String, Object> keyMap = this.objectMap.computeIfAbsent(type, k -> new HashMap<>());
keyMap.put(key, object);

if (this.keyMap == null) {
this.keyMap = new HashMap<>();
}
this.keyMap.put(key, object);

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ public <T> T get(String key, Class<T> type) {
return null;
}

/**
* Retrieves an object by key without requiring the caller to know its registered type.
*
* @param key The key identifying the instance
* @return The object instance, or null if not found
*/
public Object get(String key) {
for (ContextStore store : stores) {
Object obj = store.get(key);
if (obj != null) {
return obj;
}
}
return null;
}

/**
* Retrieves an object by type only (for singleton scenarios).
*
Expand Down Expand Up @@ -109,6 +125,21 @@ public boolean contains(String key, Class<?> type) {
return false;
}

/**
* Checks whether an object with the specified key exists in any store, regardless of type.
*
* @param key The key identifying the instance
* @return true if any store contains the object, false otherwise
*/
public boolean contains(String key) {
for (ContextStore store : stores) {
if (store.contains(key)) {
return true;
}
}
return false;
}

/**
* Checks whether an object of the specified type exists in any store (regardless of key).
*
Expand Down
Loading
Loading