| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | #nullable enable |
| | 4 | |
|
| | 5 | | using System.Globalization; |
| | 6 | |
|
| | 7 | | namespace Ice; |
| | 8 | |
|
| | 9 | | /// <summary> |
| | 10 | | /// Utility methods for the Ice run time. |
| | 11 | | /// </summary> |
| | 12 | | public sealed class Util |
| | 13 | | { |
| | 14 | | /// <summary> |
| | 15 | | /// Creates a new empty property set. |
| | 16 | | /// </summary> |
| | 17 | | /// <returns>A new empty property set.</returns> |
| | 18 | | [Obsolete("Use Ice.Properties() constructor instead.")] |
| 0 | 19 | | public static Properties createProperties() => new(); |
| | 20 | |
|
| | 21 | | /// <summary> |
| | 22 | | /// Creates a property set initialized from an argument vector. |
| | 23 | | /// </summary> |
| | 24 | | /// <param name="args">A command-line argument vector, possibly containing |
| | 25 | | /// options to set properties. If the command-line options include |
| | 26 | | /// a --Ice.Config option, the corresponding configuration |
| | 27 | | /// files are parsed. If the same property is set in a configuration |
| | 28 | | /// file and in the argument vector, the argument vector takes precedence. |
| | 29 | | /// This method modifies the argument vector by removing any Ice-related options.</param> |
| | 30 | | /// <returns>A property set initialized with the property settings |
| | 31 | | /// that were removed from args.</returns> |
| | 32 | | [Obsolete("Use Ice.Properties(ref string[], Ice.Properties) constructor instead.")] |
| 0 | 33 | | public static Properties createProperties(ref string[] args) => new(ref args, null); |
| | 34 | |
|
| | 35 | | /// <summary> |
| | 36 | | /// Creates a property set initialized from an argument vector. |
| | 37 | | /// </summary> |
| | 38 | | /// <param name="args">A command-line argument vector, possibly containing |
| | 39 | | /// options to set properties. If the command-line options include |
| | 40 | | /// a --Ice.Config option, the corresponding configuration |
| | 41 | | /// files are parsed. If the same property is set in a configuration |
| | 42 | | /// file and in the argument vector, the argument vector takes precedence. |
| | 43 | | /// This method modifies the argument vector by removing any Ice-related options.</param> |
| | 44 | | /// <param name="defaults">Default values for the property set. Settings in configuration |
| | 45 | | /// files and args override these defaults.</param> |
| | 46 | | /// <returns>A property set initialized with the property settings |
| | 47 | | /// that were removed from args.</returns> |
| | 48 | | [Obsolete("Use Ice.Properties(ref string[], Ice.Properties) constructor instead.")] |
| 0 | 49 | | public static Properties createProperties(ref string[] args, Properties defaults) => new(ref args, defaults); |
| | 50 | |
|
| | 51 | | /// <summary> |
| | 52 | | /// Creates a new communicator, using Ice properties parsed from command-line arguments. |
| | 53 | | /// </summary> |
| | 54 | | /// <param name="args">A command-line argument vector. This method parses arguments starting with `--` and one of |
| | 55 | | /// the reserved prefixes (Ice, IceSSL, etc.) as properties for the new communicator. If there is an argument |
| | 56 | | /// starting with `--Ice.Config`, this method loads the specified configuration file. When the same property is set |
| | 57 | | /// in a configuration file and through a command-line argument, the command-line setting takes precedence.</param> |
| | 58 | | /// <returns>The new communicator.</returns> |
| | 59 | | /// <remarks>This method removes any Ice-related options from <paramref name="args" />.</remarks> |
| | 60 | | public static Communicator initialize(ref string[] args) |
| | 61 | | { |
| 0 | 62 | | var initData = new InitializationData |
| 0 | 63 | | { |
| 0 | 64 | | properties = new Properties(ref args) |
| 0 | 65 | | }; |
| | 66 | |
|
| 0 | 67 | | var result = new Communicator(initData); |
| 0 | 68 | | result.finishSetup(); |
| 0 | 69 | | return result; |
| | 70 | | } |
| | 71 | |
|
| | 72 | | /// <summary> |
| | 73 | | /// Creates a new communicator. |
| | 74 | | /// </summary> |
| | 75 | | /// <param name="initData">Options for the new communicator.</param> |
| | 76 | | /// <returns>The new communicator.</returns> |
| | 77 | | public static Communicator initialize(InitializationData? initData = null) |
| | 78 | | { |
| 1 | 79 | | initData = initData is null ? new InitializationData() : initData with { }; |
| 1 | 80 | | var result = new Communicator(initData); |
| 1 | 81 | | result.finishSetup(); |
| 1 | 82 | | return result; |
| | 83 | | } |
| | 84 | |
|
| | 85 | | /// <summary> |
| | 86 | | /// Converts a string to an object identity. |
| | 87 | | /// </summary> |
| | 88 | | /// <param name="s">The string to convert.</param> |
| | 89 | | /// <returns>The converted object identity.</returns> |
| | 90 | | public static Identity stringToIdentity(string s) |
| | 91 | | { |
| 1 | 92 | | var ident = new Identity(); |
| | 93 | |
|
| | 94 | | // |
| | 95 | | // Find unescaped separator; note that the string may contain an escaped |
| | 96 | | // backslash before the separator. |
| | 97 | | // |
| 1 | 98 | | int slash = -1, pos = 0; |
| 1 | 99 | | while ((pos = s.IndexOf('/', pos)) != -1) |
| | 100 | | { |
| 1 | 101 | | int escapes = 0; |
| 1 | 102 | | while (pos - escapes > 0 && s[pos - escapes - 1] == '\\') |
| | 103 | | { |
| 1 | 104 | | escapes++; |
| | 105 | | } |
| | 106 | |
|
| | 107 | | // |
| | 108 | | // We ignore escaped escapes |
| | 109 | | // |
| 1 | 110 | | if (escapes % 2 == 0) |
| | 111 | | { |
| 1 | 112 | | if (slash == -1) |
| | 113 | | { |
| 1 | 114 | | slash = pos; |
| | 115 | | } |
| | 116 | | else |
| | 117 | | { |
| | 118 | | // |
| | 119 | | // Extra unescaped slash found. |
| | 120 | | // |
| 0 | 121 | | throw new ParseException($"unescaped backslash in identity string '{s}'"); |
| | 122 | | } |
| | 123 | | } |
| 1 | 124 | | pos++; |
| | 125 | | } |
| | 126 | |
|
| 1 | 127 | | if (slash == -1) |
| | 128 | | { |
| 1 | 129 | | ident.category = ""; |
| | 130 | | try |
| | 131 | | { |
| 1 | 132 | | ident.name = Ice.UtilInternal.StringUtil.unescapeString(s, 0, s.Length, "/"); |
| 1 | 133 | | } |
| 1 | 134 | | catch (ArgumentException e) |
| | 135 | | { |
| 1 | 136 | | throw new ParseException($"invalid name in identity string '{s}'", e); |
| | 137 | | } |
| | 138 | | } |
| | 139 | | else |
| | 140 | | { |
| | 141 | | try |
| | 142 | | { |
| 1 | 143 | | ident.category = Ice.UtilInternal.StringUtil.unescapeString(s, 0, slash, "/"); |
| 1 | 144 | | } |
| 0 | 145 | | catch (ArgumentException e) |
| | 146 | | { |
| 0 | 147 | | throw new ParseException($"invalid category in identity string '{s}'", e); |
| | 148 | | } |
| 1 | 149 | | if (slash + 1 < s.Length) |
| | 150 | | { |
| | 151 | | try |
| | 152 | | { |
| 1 | 153 | | ident.name = Ice.UtilInternal.StringUtil.unescapeString(s, slash + 1, s.Length, "/"); |
| 1 | 154 | | } |
| 0 | 155 | | catch (ArgumentException e) |
| | 156 | | { |
| 0 | 157 | | throw new ParseException($"invalid name in identity string '{s}'", e); |
| | 158 | | } |
| | 159 | | } |
| | 160 | | else |
| | 161 | | { |
| 0 | 162 | | ident.name = ""; |
| | 163 | | } |
| | 164 | | } |
| | 165 | |
|
| 1 | 166 | | return ident; |
| | 167 | | } |
| | 168 | |
|
| | 169 | | /// <summary> |
| | 170 | | /// Converts an object identity to a string. |
| | 171 | | /// </summary> |
| | 172 | | /// <param name="ident">The object identity to convert.</param> |
| | 173 | | /// <param name="toStringMode">Specifies if and how non-printable ASCII characters are escaped in the result.</param |
| | 174 | | /// <returns>The string representation of the object identity.</returns> |
| | 175 | | public static string identityToString(Identity ident, ToStringMode toStringMode = ToStringMode.Unicode) |
| | 176 | | { |
| 1 | 177 | | if (ident.category.Length == 0) |
| | 178 | | { |
| 1 | 179 | | return Ice.UtilInternal.StringUtil.escapeString(ident.name, "/", toStringMode); |
| | 180 | | } |
| | 181 | | else |
| | 182 | | { |
| 1 | 183 | | return Ice.UtilInternal.StringUtil.escapeString(ident.category, "/", toStringMode) + '/' + |
| 1 | 184 | | Ice.UtilInternal.StringUtil.escapeString(ident.name, "/", toStringMode); |
| | 185 | | } |
| | 186 | | } |
| | 187 | |
|
| | 188 | | /// <summary> |
| | 189 | | /// This method is deprecated. Use System.Guid instead. |
| | 190 | | /// </summary> |
| | 191 | | /// <returns>A new UUID.</returns> |
| | 192 | | [Obsolete("This method is deprecated. Use System.Guid instead.")] |
| | 193 | | public static string generateUUID() => |
| 0 | 194 | | Guid.NewGuid().ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture); |
| | 195 | |
|
| | 196 | | /// <summary> |
| | 197 | | /// Compares the object identities of two proxies. |
| | 198 | | /// </summary> |
| | 199 | | /// <param name="lhs">The first proxy to compare.</param> |
| | 200 | | /// <param name="rhs">The second proxy to compare.</param> |
| | 201 | | /// <returns>-1 if the identity in lhs compares less than the identity in rhs; 0 if the identities compare equal; |
| | 202 | | /// 1, otherwise.</returns> |
| | 203 | | public static int proxyIdentityCompare(ObjectPrx? lhs, ObjectPrx? rhs) |
| | 204 | | { |
| 1 | 205 | | if (lhs == null && rhs == null) |
| | 206 | | { |
| 0 | 207 | | return 0; |
| | 208 | | } |
| 1 | 209 | | else if (lhs == null && rhs != null) |
| | 210 | | { |
| 0 | 211 | | return -1; |
| | 212 | | } |
| 1 | 213 | | else if (lhs != null && rhs == null) |
| | 214 | | { |
| 0 | 215 | | return 1; |
| | 216 | | } |
| | 217 | | else |
| | 218 | | { |
| 1 | 219 | | Identity lhsIdentity = lhs!.ice_getIdentity(); |
| 1 | 220 | | Identity rhsIdentity = rhs!.ice_getIdentity(); |
| | 221 | | int n; |
| 1 | 222 | | n = string.CompareOrdinal(lhsIdentity.name, rhsIdentity.name); |
| 1 | 223 | | if (n != 0) |
| | 224 | | { |
| 0 | 225 | | return n; |
| | 226 | | } |
| 1 | 227 | | return string.CompareOrdinal(lhsIdentity.category, rhsIdentity.category); |
| | 228 | | } |
| | 229 | | } |
| | 230 | |
|
| | 231 | | /// <summary> |
| | 232 | | /// Compares the object identities and facets of two proxies. |
| | 233 | | /// </summary> |
| | 234 | | /// <param name="lhs">The first proxy to compare.</param> |
| | 235 | | /// <param name="rhs">The second proxy to compare.</param> |
| | 236 | | /// <returns>-1 if the identity and facet in lhs compare |
| | 237 | | /// less than the identity and facet in rhs; 0 if the identities |
| | 238 | | /// and facets compare equal; 1, otherwise.</returns> |
| | 239 | | public static int proxyIdentityAndFacetCompare(ObjectPrx? lhs, ObjectPrx? rhs) |
| | 240 | | { |
| 1 | 241 | | if (lhs == null && rhs == null) |
| | 242 | | { |
| 0 | 243 | | return 0; |
| | 244 | | } |
| 1 | 245 | | else if (lhs == null && rhs != null) |
| | 246 | | { |
| 0 | 247 | | return -1; |
| | 248 | | } |
| 1 | 249 | | else if (lhs != null && rhs == null) |
| | 250 | | { |
| 0 | 251 | | return 1; |
| | 252 | | } |
| | 253 | | else |
| | 254 | | { |
| 1 | 255 | | Identity lhsIdentity = lhs!.ice_getIdentity(); |
| 1 | 256 | | Identity rhsIdentity = rhs!.ice_getIdentity(); |
| | 257 | | int n; |
| 1 | 258 | | n = string.CompareOrdinal(lhsIdentity.name, rhsIdentity.name); |
| 1 | 259 | | if (n != 0) |
| | 260 | | { |
| 1 | 261 | | return n; |
| | 262 | | } |
| 1 | 263 | | n = string.CompareOrdinal(lhsIdentity.category, rhsIdentity.category); |
| 1 | 264 | | if (n != 0) |
| | 265 | | { |
| 0 | 266 | | return n; |
| | 267 | | } |
| | 268 | |
|
| 1 | 269 | | string lhsFacet = lhs.ice_getFacet(); |
| 1 | 270 | | string rhsFacet = rhs.ice_getFacet(); |
| 1 | 271 | | if (lhsFacet == null && rhsFacet == null) |
| | 272 | | { |
| 0 | 273 | | return 0; |
| | 274 | | } |
| 1 | 275 | | else if (lhsFacet == null) |
| | 276 | | { |
| 0 | 277 | | return -1; |
| | 278 | | } |
| 1 | 279 | | else if (rhsFacet == null) |
| | 280 | | { |
| 0 | 281 | | return 1; |
| | 282 | | } |
| | 283 | | else |
| | 284 | | { |
| 1 | 285 | | return string.CompareOrdinal(lhsFacet, rhsFacet); |
| | 286 | | } |
| | 287 | | } |
| | 288 | | } |
| | 289 | |
|
| | 290 | | /// <summary> |
| | 291 | | /// Returns the process-wide logger. |
| | 292 | | /// </summary> |
| | 293 | | /// <returns>The process-wide logger.</returns> |
| | 294 | | public static Logger getProcessLogger() |
| | 295 | | { |
| 1 | 296 | | lock (_processLoggerMutex) |
| | 297 | | { |
| 1 | 298 | | _processLogger ??= new ConsoleLoggerI(AppDomain.CurrentDomain.FriendlyName); |
| 1 | 299 | | return _processLogger; |
| | 300 | | } |
| 1 | 301 | | } |
| | 302 | |
|
| | 303 | | /// <summary> |
| | 304 | | /// Changes the process-wide logger. |
| | 305 | | /// </summary> |
| | 306 | | /// <param name="logger">The new process-wide logger.</param> |
| | 307 | | public static void setProcessLogger(Logger logger) |
| | 308 | | { |
| 0 | 309 | | lock (_processLoggerMutex) |
| | 310 | | { |
| 0 | 311 | | _processLogger = logger; |
| 0 | 312 | | } |
| 0 | 313 | | } |
| | 314 | |
|
| | 315 | | /// <summary> |
| | 316 | | /// Returns the Ice version in the form A.B.C, where A indicates the |
| | 317 | | /// major version, B indicates the minor version, and C indicates the |
| | 318 | | /// patch level. |
| | 319 | | /// </summary> |
| | 320 | | /// <returns>The Ice version.</returns> |
| 0 | 321 | | public static string stringVersion() => "3.8.0-alpha.0"; // "A.B.C", with A=major, B=minor, C=patch |
| | 322 | |
|
| | 323 | | /// <summary> |
| | 324 | | /// Returns the Ice version as an integer in the form AABBCC, where AA |
| | 325 | | /// indicates the major version, BB indicates the minor version, and CC |
| | 326 | | /// indicates the patch level. For example, for Ice 3.8.1, the returned value is 30801. |
| | 327 | | /// </summary> |
| | 328 | | /// <returns>The Ice version.</returns> |
| 1 | 329 | | public static int intVersion() => 30850; // AABBCC, with AA=major, BB=minor, CC=patch |
| | 330 | |
|
| | 331 | | /// <summary> |
| | 332 | | /// Converts a string to a protocol version. |
| | 333 | | /// </summary> |
| | 334 | | /// <param name="version">The string to convert.</param> |
| | 335 | | /// <returns>The converted protocol version.</returns> |
| | 336 | | public static ProtocolVersion stringToProtocolVersion(string version) |
| | 337 | | { |
| 1 | 338 | | stringToMajorMinor(version, out byte major, out byte minor); |
| 1 | 339 | | return new ProtocolVersion(major, minor); |
| | 340 | | } |
| | 341 | |
|
| | 342 | | /// <summary> |
| | 343 | | /// Converts a string to an encoding version. |
| | 344 | | /// </summary> |
| | 345 | | /// <param name="version">The string to convert.</param> |
| | 346 | | /// <returns>The converted encoding version.</returns> |
| | 347 | | public static EncodingVersion stringToEncodingVersion(string version) |
| | 348 | | { |
| 1 | 349 | | stringToMajorMinor(version, out byte major, out byte minor); |
| 1 | 350 | | return new EncodingVersion(major, minor); |
| | 351 | | } |
| | 352 | |
|
| | 353 | | /// <summary> |
| | 354 | | /// Converts a protocol version to a string. |
| | 355 | | /// </summary> |
| | 356 | | /// <param name="v">The protocol version to convert.</param> |
| | 357 | | /// <returns>The converted string.</returns> |
| 1 | 358 | | public static string protocolVersionToString(Ice.ProtocolVersion v) => majorMinorToString(v.major, v.minor); |
| | 359 | |
|
| | 360 | | /// <summary> |
| | 361 | | /// Converts an encoding version to a string. |
| | 362 | | /// </summary> |
| | 363 | | /// <param name="v">The encoding version to convert.</param> |
| | 364 | | /// <returns>The converted string.</returns> |
| 1 | 365 | | public static string encodingVersionToString(Ice.EncodingVersion v) => majorMinorToString(v.major, v.minor); |
| | 366 | |
|
| | 367 | | private static void stringToMajorMinor(string str, out byte major, out byte minor) |
| | 368 | | { |
| 1 | 369 | | int pos = str.IndexOf('.', StringComparison.Ordinal); |
| 1 | 370 | | if (pos == -1) |
| | 371 | | { |
| 0 | 372 | | throw new ParseException($"malformed version value in '{str}'"); |
| | 373 | | } |
| | 374 | |
|
| 1 | 375 | | string majStr = str[..pos]; |
| 1 | 376 | | string minStr = str[(pos + 1)..]; |
| | 377 | | int majVersion; |
| | 378 | | int minVersion; |
| | 379 | | try |
| | 380 | | { |
| 1 | 381 | | majVersion = int.Parse(majStr, CultureInfo.InvariantCulture); |
| 1 | 382 | | minVersion = int.Parse(minStr, CultureInfo.InvariantCulture); |
| 1 | 383 | | } |
| 0 | 384 | | catch (FormatException ex) |
| | 385 | | { |
| 0 | 386 | | throw new ParseException($"invalid version value in '{str}'", ex); |
| | 387 | | } |
| | 388 | |
|
| 1 | 389 | | if (majVersion < 1 || majVersion > 255 || minVersion < 0 || minVersion > 255) |
| | 390 | | { |
| 0 | 391 | | throw new ParseException($"range error in version '{str}'"); |
| | 392 | | } |
| | 393 | |
|
| 1 | 394 | | major = (byte)majVersion; |
| 1 | 395 | | minor = (byte)minVersion; |
| 1 | 396 | | } |
| | 397 | |
|
| 1 | 398 | | private static string majorMinorToString(byte major, byte minor) => $"{major}.{minor}"; |
| | 399 | |
|
| 1 | 400 | | public static readonly ProtocolVersion currentProtocol = |
| 1 | 401 | | new ProtocolVersion(Ice.Internal.Protocol.protocolMajor, Ice.Internal.Protocol.protocolMinor); |
| | 402 | |
|
| 1 | 403 | | public static readonly EncodingVersion currentProtocolEncoding = |
| 1 | 404 | | new EncodingVersion( |
| 1 | 405 | | Ice.Internal.Protocol.protocolEncodingMajor, |
| 1 | 406 | | Ice.Internal.Protocol.protocolEncodingMinor); |
| | 407 | |
|
| 1 | 408 | | public static readonly EncodingVersion currentEncoding = |
| 1 | 409 | | new EncodingVersion(Ice.Internal.Protocol.encodingMajor, Ice.Internal.Protocol.encodingMinor); |
| | 410 | |
|
| 1 | 411 | | public static readonly ProtocolVersion Protocol_1_0 = new ProtocolVersion(1, 0); |
| | 412 | |
|
| 1 | 413 | | public static readonly EncodingVersion Encoding_1_0 = new EncodingVersion(1, 0); |
| 1 | 414 | | public static readonly EncodingVersion Encoding_1_1 = new EncodingVersion(1, 1); |
| | 415 | |
|
| 1 | 416 | | private static readonly object _processLoggerMutex = new object(); |
| | 417 | | private static Logger? _processLogger; |
| | 418 | | } |