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 }