Make model prefix handling optional
[yangtools.git] / codec / yang-data-codec-xml / src / main / java / org / opendaylight / yangtools / yang / data / codec / xml / PreferredPrefixes.java
index ddf35512f3691bf5b003f02ed030e184d7e358a6..9730e7310fe8219dec6094794dc857e8f333040f 100644 (file)
@@ -10,15 +10,18 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.Maps;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
 
 /**
  * Prefixes preferred by an {@link EffectiveModelContext}. This acts as an advisory to {@link NamespacePrefixes} for
@@ -27,13 +30,11 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
  * with those ambiguities.
  */
 abstract sealed class PreferredPrefixes {
-    private static final class Precomputed extends PreferredPrefixes {
-        static final @NonNull Precomputed EMPTY = new Precomputed(Map.of());
-
-        private final Map<XMLNamespace, String> mappings;
+    static final class Precomputed extends PreferredPrefixes {
+        private final ImmutableBiMap<XMLNamespace, String> mappings;
 
         Precomputed(final Map<XMLNamespace, String> mappings) {
-            this.mappings = requireNonNull(mappings);
+            this.mappings = ImmutableBiMap.copyOf(mappings);
         }
 
         @Override
@@ -41,6 +42,11 @@ abstract sealed class PreferredPrefixes {
             return mappings.get(namespace);
         }
 
+        @Override
+        boolean isUsed(final String prefix) {
+            return mappings.inverse().containsKey(prefix);
+        }
+
         @Override
         Map<XMLNamespace, ?> mappings() {
             return mappings;
@@ -67,6 +73,11 @@ abstract sealed class PreferredPrefixes {
             return modules.hasNext() ? loadPrefix(namespace, modules.next().prefix().argument()) : null;
         }
 
+        @Override
+        boolean isUsed(final String prefix) {
+            return modulesForPrefix(prefix).findAny().isPresent();
+        }
+
         /**
          * Completely populate known mappings and return an optimized version equivalent of this object.
          *
@@ -76,8 +87,8 @@ abstract sealed class PreferredPrefixes {
             for (var module : modelContext.getModuleStatements().values()) {
                 prefixForNamespace(module.namespace().argument());
             }
-            return new Precomputed(Map.copyOf(
-                Maps.transformValues(Maps.filterValues(mappings, Optional::isPresent), Optional::orElseThrow)));
+            return new Precomputed(
+                Maps.transformValues(Maps.filterValues(mappings, Optional::isPresent), Optional::orElseThrow));
         }
 
         @Override
@@ -93,15 +104,13 @@ abstract sealed class PreferredPrefixes {
 
         // Validate that all modules which have the same prefix have also the name namespace
         private boolean isValidMapping(final XMLNamespace namespace, final String prefix) {
-            if (startsWithXml(prefix)) {
-                return false;
-            }
-            for (var module : modelContext.getModuleStatements().values()) {
-                if (prefix.equals(module.prefix().argument()) && !namespace.equals(module.namespace().argument())) {
-                    return false;
-                }
-            }
-            return true;
+            return !startsWithXml(prefix) && modulesForPrefix(prefix)
+                .allMatch(module -> namespace.equals(module.namespace().argument()));
+        }
+
+        private Stream<ModuleEffectiveStatement> modulesForPrefix(final String prefix) {
+            return modelContext.getModuleStatements().values().stream()
+                .filter(module -> prefix.equals(module.prefix().argument()));
         }
 
         // https://www.w3.org/TR/xml-names/#xmlReserved
@@ -126,16 +135,15 @@ abstract sealed class PreferredPrefixes {
         // Hidden on purpose
     }
 
-    static @NonNull PreferredPrefixes empty() {
-        return Precomputed.EMPTY;
-    }
-
     abstract @Nullable String prefixForNamespace(@NonNull XMLNamespace namespace);
 
+    abstract boolean isUsed(String prefix);
+
     @Override
     public final String toString() {
         return MoreObjects.toStringHelper(this).add("mappings", mappings()).toString();
     }
 
     abstract Map<XMLNamespace, ?> mappings();
+
 }