ContextUtil.java

  1. package nl.tudelft.simulation.naming.context.util;

  2. import java.rmi.RemoteException;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.List;
  6. import java.util.Map.Entry;

  7. import javax.naming.NameNotFoundException;
  8. import javax.naming.NamingException;

  9. import nl.tudelft.simulation.naming.context.ContextInterface;

  10. /**
  11.  * ContextUtil contains a few helper methods to deal with an InitialEventContext.
  12.  * <p>
  13.  * Copyright (c) 2020-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
  14.  * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
  15.  * project is distributed under a three-clause BSD-style license, which can be found at
  16.  * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
  17.  * </p>
  18.  * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
  19.  */
  20. public class ContextUtil
  21. {
  22.     /**
  23.      * Lookup or create a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
  24.      * terminating part of the name will be used as the key under which the created subcontext will be registered.
  25.      * @param parentContext ContextInterface; the parent context
  26.      * @param name String; the name to register the new subcontext
  27.      * @return ContextInterface; the newly created subcontext
  28.      * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
  29.      * @throws RemoteException on a network error when the Context is used over RMI
  30.      */
  31.     public static ContextInterface lookupOrCreateSubContext(final ContextInterface parentContext, final String name)
  32.             throws NamingException, RemoteException
  33.     {
  34.         try
  35.         {
  36.             if (parentContext.exists(name))
  37.             {
  38.                 Object result = parentContext.get(name);
  39.                 if (result instanceof ContextInterface)
  40.                     return (ContextInterface) result;
  41.                 throw new NamingException(
  42.                         "lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
  43.             }
  44.         }
  45.         catch (NameNotFoundException nnfe)
  46.         {
  47.             // ignore -- create the context path
  48.         }
  49.         return parentContext.createSubcontext(name);
  50.     }

  51.     /**
  52.      * Lookup a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
  53.      * terminating part of the name will be used as the key under which the created subcontext will be registered.
  54.      * @param parentContext ContextInterface; the parent context
  55.      * @param name String; the name to register the new subcontext
  56.      * @return ContextInterface; the newly created subcontext
  57.      * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
  58.      * @throws RemoteException on a network error when the Context is used over RMI
  59.      */
  60.     public static ContextInterface lookupSubContext(final ContextInterface parentContext, final String name)
  61.             throws NamingException, RemoteException
  62.     {
  63.         if (parentContext.exists(name))
  64.         {
  65.             Object result = parentContext.get(name);
  66.             if (result instanceof ContextInterface)
  67.                 return (ContextInterface) result;
  68.             throw new NamingException(
  69.                     "lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
  70.         }
  71.         throw new NamingException("Context " + name + " not found in parentContext " + parentContext);
  72.     }

  73.     /**
  74.      * Destroy a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
  75.      * terminating part of the name will be used as the key under for the subcontext to be removed.
  76.      * @param parentContext ContextInterface; the parent context
  77.      * @param name String; the name to use to find the subcontext to remove
  78.      * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
  79.      * @throws RemoteException on a network error when the Context is used over RMI
  80.      */
  81.     public static void destroySubContext(final ContextInterface parentContext, final String name)
  82.             throws NamingException, RemoteException
  83.     {
  84.         parentContext.destroySubcontext(name);
  85.     }

  86.     /**
  87.      * Resolve the key(s) for an object for a given context. This can be an expensive operation if the context is large. An
  88.      * object can be registered zero or more times in the context, so a List of keys under which the object is registered will
  89.      * be returned. The keys are relative to the startContext. The method starts with the given context. It is possible to look
  90.      * up null objects in the Context.
  91.      * @param startContext ContextInterface; the context to start the search
  92.      * @param object Object; the object to look up in the tree under the startContext
  93.      * @return List&lt;String&gt;; the list of keys that are bound to the object, or an empty list if no bindings for the object
  94.      *         were found
  95.      * @throws NamingException when an error occurs during searching
  96.      * @throws RemoteException on a network error when the Context is used over RMI
  97.      */
  98.     public static List<String> resolveKeys(final ContextInterface startContext, final Object object)
  99.             throws NamingException, RemoteException
  100.     {
  101.         List<String> result = new ArrayList<>();
  102.         resolveKeys(startContext, object, result, "");
  103.         return result;
  104.     }

  105.     /**
  106.      * Resolve the key(s) for an object for a given context. This can be an expensive operation if the context is large. An
  107.      * object can be registered zero or more times in the context, so a List of keys under which the object is registered will
  108.      * be returned. The keys are relative to the startContext. The method starts with the given context. It is possible to look
  109.      * up null objects in the Context.
  110.      * @param context ContextInterface; the context to start the search
  111.      * @param object Object; the object to look up in the tree under the startContext
  112.      * @param result List&lt;String&gt;; the current list of keys that are bound to the object, or an empty list if no bindings
  113.      *            for the object were found yet
  114.      * @param partialKey the key of the current level in the tree, relative to the original start context of the search
  115.      * @throws NamingException when an error occurs during searching
  116.      * @throws RemoteException on a network error when the Context is used over RMI
  117.      */
  118.     private static void resolveKeys(final ContextInterface context, final Object object, final List<String> result,
  119.             final String partialKey) throws NamingException, RemoteException
  120.     {
  121.         for (Entry<String, Object> binding : context.bindings().entrySet())
  122.         {
  123.             Object value = binding.getValue();
  124.             if (value instanceof ContextInterface)
  125.             {
  126.                 resolveKeys((ContextInterface) value, object, result,
  127.                         partialKey + ContextInterface.SEPARATOR + binding.getKey());
  128.             }
  129.             // no else; we might be looking for an object that implements ContextInterface
  130.             if ((value == null && object == null) || (value != null && value.equals(object)))
  131.             {
  132.                 result.add(partialKey + ContextInterface.SEPARATOR + binding.getKey());
  133.             }
  134.         }
  135.     }

  136.     /**
  137.      * recursively print the context in human-readable format to a String.
  138.      * @param ctx the context to print
  139.      * @return a human-readable String with the context tree
  140.      */
  141.     public static String toText(final ContextInterface ctx)
  142.     {
  143.         return toText(ctx, new StringBuffer(), 0);
  144.     }

  145.     /**
  146.      * recursively print the context in human-readable format to a String.
  147.      * @param ctx the context to print
  148.      * @param sb the StringBuffer to add the information to
  149.      * @param indent the indentation on the screen
  150.      * @return a human-readable String with the context tree
  151.      */
  152.     private static String toText(final ContextInterface ctx, final StringBuffer sb, final int indent)
  153.     {
  154.         try
  155.         {
  156.             if (indent > 0)
  157.             {
  158.                 sb.append(String.join("", Collections.nCopies(indent - 1, "| ")));
  159.                 sb.append("+ ");
  160.             }
  161.             sb.append("CTX ");
  162.             sb.append(ctx.getAtomicName());
  163.             sb.append("\n");
  164.             for (String key : ctx.keySet())
  165.             {
  166.                 Object obj = ctx.getObject(key);
  167.                 if (obj instanceof ContextInterface)
  168.                 {
  169.                     toText((ContextInterface) obj, sb, indent + 1);
  170.                 }
  171.                 else
  172.                 {
  173.                     sb.append(String.join("", Collections.nCopies(indent, "| ")));
  174.                     sb.append("+ ");
  175.                     sb.append(key);
  176.                     sb.append("=");
  177.                     sb.append(obj);
  178.                     sb.append("\n");
  179.                 }
  180.             }
  181.         }
  182.         catch (NamingException | RemoteException exception)
  183.         {
  184.             sb.append("ERR " + exception.getMessage() + "\n");
  185.         }
  186.         return sb.toString();
  187.     }

  188. }