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-2024 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 }