+ public static @NonNull NetconfSessionPreferences fromNetconfSession(final NetconfClientSession session) {
+ return fromStrings(session.getServerCapabilities());
+ }
+
+ public static @NonNull NetconfSessionPreferences fromStrings(final Collection<String> capabilities) {
+ // we do not know origin of capabilities from only Strings, so we set it to default value
+ return fromStrings(capabilities, CapabilityOrigin.DeviceAdvertised);
+ }
+
+ public static @NonNull NetconfSessionPreferences fromStrings(final Collection<String> capabilities,
+ final CapabilityOrigin capabilityOrigin) {
+ final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
+ final var nonModuleCaps = new HashMap<String, CapabilityOrigin>();
+
+ for (final String capability : capabilities) {
+ nonModuleCaps.put(capability, capabilityOrigin);
+ final int qmark = capability.indexOf('?');
+ if (qmark == -1) {
+ continue;
+ }
+
+ final String namespace = capability.substring(0, qmark);
+ final Iterable<String> queryParams = AMP_SPLITTER.split(capability.substring(qmark + 1));
+ final String moduleName = MODULE_PARAM.from(queryParams);
+ if (Strings.isNullOrEmpty(moduleName)) {
+ continue;
+ }
+
+ String revision = REVISION_PARAM.from(queryParams);
+ if (!Strings.isNullOrEmpty(revision)) {
+ addModuleQName(moduleBasedCaps, nonModuleCaps, capability, cachedQName(namespace, revision, moduleName),
+ capabilityOrigin);
+ continue;
+ }
+
+ /*
+ * We have seen devices which mis-escape revision, but the revision may not
+ * even be there. First check if there is a substring that matches revision.
+ */
+ if (Iterables.any(queryParams, input -> input.contains("revision="))) {
+ LOG.debug("Netconf device was not reporting revision correctly, trying to get amp;revision=");
+ revision = BROKEN_REVISON_PARAM.from(queryParams);
+ if (Strings.isNullOrEmpty(revision)) {
+ LOG.warn("Netconf device returned revision incorrectly escaped for {}, ignoring it", capability);
+ addModuleQName(moduleBasedCaps, nonModuleCaps, capability,
+ cachedQName(namespace, moduleName), capabilityOrigin);
+ } else {
+ addModuleQName(moduleBasedCaps, nonModuleCaps, capability,
+ cachedQName(namespace, revision, moduleName), capabilityOrigin);
+ }
+ continue;
+ }
+
+ // Fallback, no revision provided for module
+ addModuleQName(moduleBasedCaps, nonModuleCaps, capability,
+ cachedQName(namespace, moduleName), capabilityOrigin);
+ }
+
+ return new NetconfSessionPreferences(ImmutableMap.copyOf(nonModuleCaps), ImmutableMap.copyOf(moduleBasedCaps));
+ }
+