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