View Javadoc
1   package nl.tudelft.simulation.naming.context.util;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.List;
6   import java.util.Map.Entry;
7   
8   import javax.naming.NameNotFoundException;
9   import javax.naming.NamingException;
10  
11  import nl.tudelft.simulation.naming.context.ContextInterface;
12  
13  /**
14   * ContextUtil contains a few helper methods to deal with an InitialEventContext.
15   * <p>
16   * Copyright (c) 2020-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
17   * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
18   * project is distributed under a three-clause BSD-style license, which can be found at
19   * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
20   * </p>
21   * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
22   */
23  public class ContextUtil
24  {
25      /**
26       * Lookup or create a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
27       * terminating part of the name will be used as the key under which the created subcontext will be registered.
28       * @param parentContext the parent context
29       * @param name the name to register the new subcontext
30       * @return the newly created subcontext
31       * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
32       */
33      public static ContextInterface lookupOrCreateSubContext(final ContextInterface parentContext, final String name)
34              throws NamingException
35      {
36          try
37          {
38              if (parentContext.exists(name))
39              {
40                  Object result = parentContext.get(name);
41                  if (result instanceof ContextInterface)
42                      return (ContextInterface) result;
43                  throw new NamingException(
44                          "lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
45              }
46          }
47          catch (NameNotFoundException nnfe)
48          {
49              // ignore -- create the context path
50          }
51          return parentContext.createSubcontext(name);
52      }
53  
54      /**
55       * Lookup a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
56       * terminating part of the name will be used as the key under which the created subcontext will be registered.
57       * @param parentContext the parent context
58       * @param name the name to register the new subcontext
59       * @return the newly created subcontext
60       * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
61       */
62      public static ContextInterface lookupSubContext(final ContextInterface parentContext, final String name)
63              throws NamingException
64      {
65          if (parentContext.exists(name))
66          {
67              Object result = parentContext.get(name);
68              if (result instanceof ContextInterface)
69                  return (ContextInterface) result;
70              throw new NamingException(
71                      "lookup for " + name + " in context " + parentContext + " returned object that is not a Context");
72          }
73          throw new NamingException("Context " + name + " not found in parentContext " + parentContext);
74      }
75  
76      /**
77       * Destroy a sub-context in the parentContext with the name as its path. The path can be absolute or relative. The
78       * terminating part of the name will be used as the key under for the subcontext to be removed.
79       * @param parentContext the parent context
80       * @param name the name to use to find the subcontext to remove
81       * @throws NamingException when terminating key in the name is blank or contains "/" character(s)
82       */
83      public static void destroySubContext(final ContextInterface parentContext, final String name) throws NamingException
84      {
85          parentContext.destroySubcontext(name);
86      }
87  
88      /**
89       * Resolve the key(s) for an object for a given context. This can be an expensive operation if the context is large. An
90       * object can be registered zero or more times in the context, so a List of keys under which the object is registered will
91       * be returned. The keys are relative to the startContext. The method starts with the given context. It is possible to look
92       * up null objects in the Context.
93       * @param startContext the context to start the search
94       * @param object the object to look up in the tree under the startContext
95       * @return the list of keys that are bound to the object, or an empty list if no bindings for the object were found
96       * @throws NamingException when an error occurs during searching
97       */
98      public static List<String> resolveKeys(final ContextInterface startContext, final Object object) throws NamingException
99      {
100         List<String> result = new ArrayList<>();
101         resolveKeys(startContext, object, result, "");
102         return result;
103     }
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 the context to start the search
111      * @param object the object to look up in the tree under the startContext
112      * @param result the current list of keys that are bound to the object, or an empty list if no bindings for the object were
113      *            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      */
117     private static void resolveKeys(final ContextInterface context, final Object object, final List<String> result,
118             final String partialKey) throws NamingException
119     {
120         for (Entry<String, Object> binding : context.bindings().entrySet())
121         {
122             Object value = binding.getValue();
123             if (value instanceof ContextInterface)
124             {
125                 resolveKeys((ContextInterface) value, object, result,
126                         partialKey + ContextInterface.SEPARATOR + binding.getKey());
127             }
128             // no else; we might be looking for an object that implements ContextInterface
129             if ((value == null && object == null) || (value != null && value.equals(object)))
130             {
131                 result.add(partialKey + ContextInterface.SEPARATOR + binding.getKey());
132             }
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     /**
147      * recursively print the context in human-readable format to a String.
148      * @param ctx the context to print
149      * @param sb the StringBuffer to add the information to
150      * @param indent the indentation on the screen
151      * @return a human-readable String with the context tree
152      */
153     private static String toText(final ContextInterface ctx, final StringBuffer sb, final int indent)
154     {
155         try
156         {
157             if (indent > 0)
158             {
159                 sb.append(String.join("", Collections.nCopies(indent - 1, "| ")));
160                 sb.append("+ ");
161             }
162             sb.append("CTX ");
163             sb.append(ctx.getAtomicName());
164             sb.append("\n");
165             for (String key : ctx.keySet())
166             {
167                 Object obj = ctx.getObject(key);
168                 if (obj instanceof ContextInterface)
169                 {
170                     toText((ContextInterface) obj, sb, indent + 1);
171                 }
172                 else
173                 {
174                     sb.append(String.join("", Collections.nCopies(indent, "| ")));
175                     sb.append("+ ");
176                     sb.append(key);
177                     sb.append("=");
178                     sb.append(obj);
179                     sb.append("\n");
180                 }
181             }
182         }
183         catch (NamingException exception)
184         {
185             sb.append("ERR " + exception.getMessage() + "\n");
186         }
187         return sb.toString();
188     }
189 
190 }