ContextUtil.java
package nl.tudelft.simulation.naming.context.util;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import nl.tudelft.simulation.naming.context.ContextInterface;
/**
* ContextUtil contains a few helper methods to deal with an InitialEventContext.
* <p>
* Copyright (c) 2020-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
* https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
* </p>
* @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
*/
public class ContextUtil
{
/**
* Lookup or create a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
* terminating part of the name will be used as the key under which the created subcontext will be registered.
* @param parentContext ContextInterface; the parent context
* @param name String; the name to register the new subcontext
* @return ContextInterface; the newly created subcontext
* @throws NamingException when terminating key in the name is blank or contains "/" character(s)
* @throws RemoteException on a network error when the Context is used over RMI
*/
public static ContextInterface lookupOrCreateSubContext(final ContextInterface parentContext, final String name)
throws NamingException, RemoteException
{
try
{
if (parentContext.exists(name))
{
Object result = parentContext.get(name);
if (result instanceof ContextInterface)
return (ContextInterface) result;
throw new NamingException(
"lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
}
}
catch (NameNotFoundException nnfe)
{
// ignore -- create the context path
}
return parentContext.createSubcontext(name);
}
/**
* Lookup a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
* terminating part of the name will be used as the key under which the created subcontext will be registered.
* @param parentContext ContextInterface; the parent context
* @param name String; the name to register the new subcontext
* @return ContextInterface; the newly created subcontext
* @throws NamingException when terminating key in the name is blank or contains "/" character(s)
* @throws RemoteException on a network error when the Context is used over RMI
*/
public static ContextInterface lookupSubContext(final ContextInterface parentContext, final String name)
throws NamingException, RemoteException
{
if (parentContext.exists(name))
{
Object result = parentContext.get(name);
if (result instanceof ContextInterface)
return (ContextInterface) result;
throw new NamingException(
"lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
}
throw new NamingException("Context " + name + " not found in parentContext " + parentContext);
}
/**
* Destroy a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
* terminating part of the name will be used as the key under for the subcontext to be removed.
* @param parentContext ContextInterface; the parent context
* @param name String; the name to use to find the subcontext to remove
* @throws NamingException when terminating key in the name is blank or contains "/" character(s)
* @throws RemoteException on a network error when the Context is used over RMI
*/
public static void destroySubContext(final ContextInterface parentContext, final String name)
throws NamingException, RemoteException
{
parentContext.destroySubcontext(name);
}
/**
* Resolve the key(s) for an object for a given context. This can be an expensive operation if the context is large. An
* object can be registered zero or more times in the context, so a List of keys under which the object is registered will
* be returned. The keys are relative to the startContext. The method starts with the given context. It is possible to look
* up null objects in the Context.
* @param startContext ContextInterface; the context to start the search
* @param object Object; the object to look up in the tree under the startContext
* @return List<String>; the list of keys that are bound to the object, or an empty list if no bindings for the object
* were found
* @throws NamingException when an error occurs during searching
* @throws RemoteException on a network error when the Context is used over RMI
*/
public static List<String> resolveKeys(final ContextInterface startContext, final Object object)
throws NamingException, RemoteException
{
List<String> result = new ArrayList<>();
resolveKeys(startContext, object, result, "");
return result;
}
/**
* Resolve the key(s) for an object for a given context. This can be an expensive operation if the context is large. An
* object can be registered zero or more times in the context, so a List of keys under which the object is registered will
* be returned. The keys are relative to the startContext. The method starts with the given context. It is possible to look
* up null objects in the Context.
* @param context ContextInterface; the context to start the search
* @param object Object; the object to look up in the tree under the startContext
* @param result List<String>; the current list of keys that are bound to the object, or an empty list if no bindings
* for the object were found yet
* @param partialKey the key of the current level in the tree, relative to the original start context of the search
* @throws NamingException when an error occurs during searching
* @throws RemoteException on a network error when the Context is used over RMI
*/
private static void resolveKeys(final ContextInterface context, final Object object, final List<String> result,
final String partialKey) throws NamingException, RemoteException
{
for (Entry<String, Object> binding : context.bindings().entrySet())
{
Object value = binding.getValue();
if (value instanceof ContextInterface)
{
resolveKeys((ContextInterface) value, object, result,
partialKey + ContextInterface.SEPARATOR + binding.getKey());
}
// no else; we might be looking for an object that implements ContextInterface
if ((value == null && object == null) || (value != null && value.equals(object)))
{
result.add(partialKey + ContextInterface.SEPARATOR + binding.getKey());
}
}
}
/**
* recursively print the context in human-readable format to a String.
* @param ctx the context to print
* @return a human-readable String with the context tree
*/
public static String toText(final ContextInterface ctx)
{
return toText(ctx, new StringBuffer(), 0);
}
/**
* recursively print the context in human-readable format to a String.
* @param ctx the context to print
* @param sb the StringBuffer to add the information to
* @param indent the indentation on the screen
* @return a human-readable String with the context tree
*/
private static String toText(final ContextInterface ctx, final StringBuffer sb, final int indent)
{
try
{
if (indent > 0)
{
sb.append(String.join("", Collections.nCopies(indent - 1, "| ")));
sb.append("+ ");
}
sb.append("CTX ");
sb.append(ctx.getAtomicName());
sb.append("\n");
for (String key : ctx.keySet())
{
Object obj = ctx.getObject(key);
if (obj instanceof ContextInterface)
{
toText((ContextInterface) obj, sb, indent + 1);
}
else
{
sb.append(String.join("", Collections.nCopies(indent, "| ")));
sb.append("+ ");
sb.append(key);
sb.append("=");
sb.append(obj);
sb.append("\n");
}
}
}
catch (NamingException | RemoteException exception)
{
sb.append("ERR " + exception.getMessage() + "\n");
}
return sb.toString();
}
}