Fix instance-identifier key being not recognized properly
[netconf.git] / netconf / netconf-netty-util / src / main / java / org / opendaylight / netconf / nettyutil / handler / exi / EXIParameters.java
index 8ba28731481cf0fc8dee499e6e876054871e0def..d15f441c6960e92f9c8888591e9c62e091366fdb 100644 (file)
  */
 package org.opendaylight.netconf.nettyutil.handler.exi;
 
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.util.xml.XmlElement;
-import org.openexi.proc.common.AlignmentType;
-import org.openexi.proc.common.EXIOptions;
-import org.openexi.proc.common.EXIOptionsException;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Objects;
+import org.opendaylight.netconf.api.xml.XmlElement;
+import org.opendaylight.netconf.shaded.exificient.core.CodingMode;
+import org.opendaylight.netconf.shaded.exificient.core.EXIFactory;
+import org.opendaylight.netconf.shaded.exificient.core.EncodingOptions;
+import org.opendaylight.netconf.shaded.exificient.core.FidelityOptions;
+import org.opendaylight.netconf.shaded.exificient.core.SchemaIdResolver;
+import org.opendaylight.netconf.shaded.exificient.core.exceptions.EXIException;
+import org.opendaylight.netconf.shaded.exificient.core.exceptions.UnsupportedOption;
+import org.opendaylight.netconf.shaded.exificient.core.helpers.DefaultEXIFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
 public final class EXIParameters {
-    private static final String EXI_PARAMETER_ALIGNMENT = "alignment";
-    static final String EXI_PARAMETER_BYTE_ALIGNED = "byte-aligned";
-    static final String EXI_PARAMETER_BIT_PACKED = "bit-packed";
-    static final String EXI_PARAMETER_COMPRESSED = "compressed";
-    static final String EXI_PARAMETER_PRE_COMPRESSION = "pre-compression";
+    private static final Logger LOG = LoggerFactory.getLogger(EXIParameters.class);
 
-    private static final String EXI_PARAMETER_FIDELITY = "fidelity";
+    static final String EXI_PARAMETER_ALIGNMENT = "alignment";
+    private static final String EXI_PARAMETER_BYTE_ALIGNED = "byte-aligned";
+    private static final String EXI_PARAMETER_BIT_PACKED = "bit-packed";
+    private static final String EXI_PARAMETER_COMPRESSED = "compressed";
+    private static final String EXI_PARAMETER_PRE_COMPRESSION = "pre-compression";
+
+    static final String EXI_PARAMETER_FIDELITY = "fidelity";
     private static final String EXI_FIDELITY_DTD = "dtd";
     private static final String EXI_FIDELITY_LEXICAL_VALUES = "lexical-values";
     private static final String EXI_FIDELITY_COMMENTS = "comments";
     private static final String EXI_FIDELITY_PIS = "pis";
     private static final String EXI_FIDELITY_PREFIXES = "prefixes";
 
-    private final EXIOptions options;
-    private static final Logger LOG = LoggerFactory.getLogger(EXIParameters.class);
+    static final String EXI_PARAMETER_SCHEMAS = "schemas";
+
+    private static final SchemaIdResolver SCHEMA_RESOLVER = schemaId -> {
+        if (schemaId == null) {
+            return null;
+        }
+        if (schemaId.isEmpty()) {
+            return EXISchema.BUILTIN.getGrammar();
+        }
+        if (schemaId.equals(EXISchema.BASE_1_1.getOption())) {
+            return EXISchema.BASE_1_1.getGrammar();
+        }
+
+        throw new EXIException("Cannot resolve schema " + schemaId);
+    };
+
+    private static final EncodingOptions ENCODING_OPTIONS;
+
+    static {
+        final EncodingOptions opts = EncodingOptions.createDefault();
+        try {
+            opts.setOption(EncodingOptions.RETAIN_ENTITY_REFERENCE);
+            opts.setOption(EncodingOptions.INCLUDE_OPTIONS);
 
-    private EXIParameters(final EXIOptions options) {
-        this.options = Preconditions.checkNotNull(options);
+            /**
+             * NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it
+             * decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head
+             * of the stream. This is really useful, so let's output it now.
+             */
+            opts.setOption(EncodingOptions.INCLUDE_COOKIE);
+        } catch (final UnsupportedOption e) {
+            throw new ExceptionInInitializerError(e);
+        }
+
+        ENCODING_OPTIONS = opts;
     }
 
+    private final FidelityOptions fidelityOptions;
+    private final CodingMode codingMode;
+    private final EXISchema schema;
 
-    public static EXIParameters fromXmlElement(final XmlElement root) throws EXIOptionsException {
-        final EXIOptions options =  new EXIOptions();
+    public EXIParameters(final CodingMode codingMode, final FidelityOptions fidelityOptions) {
+        this(codingMode, fidelityOptions, EXISchema.NONE);
+    }
+
+    public EXIParameters(final CodingMode codingMode, final FidelityOptions fidelityOptions, final EXISchema schema) {
+        this.fidelityOptions = requireNonNull(fidelityOptions);
+        this.codingMode = requireNonNull(codingMode);
+        this.schema = requireNonNull(schema);
+    }
+
+    @VisibleForTesting
+    public static EXIParameters empty() {
+        return new EXIParameters(CodingMode.BIT_PACKED, FidelityOptions.createDefault());
+    }
+
+    public static EXIParameters fromXmlElement(final XmlElement root) throws UnsupportedOption {
+        final CodingMode coding;
         final NodeList alignmentElements = root.getElementsByTagName(EXI_PARAMETER_ALIGNMENT);
         if (alignmentElements.getLength() > 0) {
             final Element alignmentElement = (Element) alignmentElements.item(0);
             final String alignmentTextContent = alignmentElement.getTextContent().trim();
 
             switch (alignmentTextContent) {
-            case EXI_PARAMETER_BYTE_ALIGNED:
-                options.setAlignmentType(AlignmentType.byteAligned);
-                break;
-            case EXI_PARAMETER_COMPRESSED:
-                options.setAlignmentType(AlignmentType.compress);
-                break;
-            case EXI_PARAMETER_PRE_COMPRESSION:
-                options.setAlignmentType(AlignmentType.preCompress);
-                break;
-            default:
-                LOG.warn("Unexpected value in alignmentTextContent: {} , using default value", alignmentTextContent);
-            case EXI_PARAMETER_BIT_PACKED:
-                options.setAlignmentType(AlignmentType.bitPacked);
-                break;
+                case EXI_PARAMETER_BYTE_ALIGNED:
+                    coding = CodingMode.BYTE_PACKED;
+                    break;
+                case EXI_PARAMETER_COMPRESSED:
+                    coding = CodingMode.COMPRESSION;
+                    break;
+                case EXI_PARAMETER_PRE_COMPRESSION:
+                    coding = CodingMode.PRE_COMPRESSION;
+                    break;
+                case EXI_PARAMETER_BIT_PACKED:
+                    coding = CodingMode.BIT_PACKED;
+                    break;
+                default:
+                    LOG.warn("Unexpected value in alignmentTextContent: {} , using default value",
+                            alignmentTextContent);
+                    coding = CodingMode.BIT_PACKED;
+                    break;
             }
         } else {
-            options.setAlignmentType(AlignmentType.bitPacked);
+            coding = CodingMode.BIT_PACKED;
         }
 
+        final FidelityOptions fidelity = FidelityOptions.createDefault();
         final NodeList fidelityElements = root.getElementsByTagName(EXI_PARAMETER_FIDELITY);
         if (fidelityElements.getLength() > 0) {
             final Element fidelityElement = (Element) fidelityElements.item(0);
-            if (fidelityElement.getElementsByTagName(EXI_FIDELITY_DTD).getLength() > 0) {
-                options.setPreserveDTD(true);
-            }
-            if (fidelityElement.getElementsByTagName(EXI_FIDELITY_LEXICAL_VALUES).getLength() > 0) {
-                options.setPreserveLexicalValues(true);
-            }
-            if (fidelityElement.getElementsByTagName(EXI_FIDELITY_COMMENTS).getLength() > 0) {
-                options.setPreserveComments(true);
-            }
-            if (fidelityElement.getElementsByTagName(EXI_FIDELITY_PIS).getLength() > 0) {
-                options.setPreservePIs(true);
-            }
-            if (fidelityElement.getElementsByTagName(EXI_FIDELITY_PREFIXES).getLength() > 0) {
-                options.setPreserveNS(true);
-            }
+
+            fidelity.setFidelity(FidelityOptions.FEATURE_DTD,
+                fidelityElement.getElementsByTagName(EXI_FIDELITY_DTD).getLength() > 0);
+            fidelity.setFidelity(FidelityOptions.FEATURE_LEXICAL_VALUE,
+                fidelityElement.getElementsByTagName(EXI_FIDELITY_LEXICAL_VALUES).getLength() > 0);
+            fidelity.setFidelity(FidelityOptions.FEATURE_COMMENT,
+                fidelityElement.getElementsByTagName(EXI_FIDELITY_COMMENTS).getLength() > 0);
+            fidelity.setFidelity(FidelityOptions.FEATURE_PI,
+                fidelityElement.getElementsByTagName(EXI_FIDELITY_PIS).getLength() > 0);
+            fidelity.setFidelity(FidelityOptions.FEATURE_PREFIX,
+                fidelityElement.getElementsByTagName(EXI_FIDELITY_PREFIXES).getLength() > 0);
+        }
+
+        final EXISchema schema;
+        final NodeList schemaElements = root.getElementsByTagName(EXI_PARAMETER_SCHEMAS);
+        if (schemaElements.getLength() > 0) {
+            final Element schemaElement = (Element) schemaElements.item(0);
+            final String schemaName = schemaElement.getTextContent().trim();
+            schema = EXISchema.forOption(schemaName);
+            checkArgument(schema != null, "Unsupported schema name %s", schemaName);
+        } else {
+            schema = EXISchema.NONE;
+        }
+
+        return new EXIParameters(coding, fidelity, schema);
+    }
+
+    public EXIFactory getFactory() {
+        final EXIFactory factory = DefaultEXIFactory.newInstance();
+        factory.setCodingMode(codingMode);
+        factory.setEncodingOptions(ENCODING_OPTIONS);
+        factory.setFidelityOptions(fidelityOptions);
+        factory.setGrammars(schema.getGrammar());
+        factory.setSchemaIdResolver(SCHEMA_RESOLVER);
+        return factory;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fidelityOptions, codingMode, schema);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof EXIParameters)) {
+            return false;
         }
-        return new EXIParameters(options);
+        final EXIParameters other = (EXIParameters) obj;
+        return codingMode == other.codingMode && schema == other.schema
+                && fidelityOptions.equals(other.fidelityOptions);
+    }
+
+    String getAlignment() {
+        switch (codingMode) {
+            case BIT_PACKED:
+                return EXI_PARAMETER_BIT_PACKED;
+            case BYTE_PACKED:
+                return EXI_PARAMETER_BYTE_ALIGNED;
+            case COMPRESSION:
+                return EXI_PARAMETER_COMPRESSED;
+            case PRE_COMPRESSION:
+                return EXI_PARAMETER_PRE_COMPRESSION;
+            default:
+                throw new IllegalStateException("Unhandled coding mode " + codingMode);
+        }
+    }
+
+    private String fidelityString(final String feature, final String string) {
+        return fidelityOptions.isFidelityEnabled(feature) ? string : null;
+    }
+
+    String getPreserveComments() {
+        return fidelityString(FidelityOptions.FEATURE_COMMENT, EXI_FIDELITY_COMMENTS);
+    }
+
+    String getPreserveDTD() {
+        return fidelityString(FidelityOptions.FEATURE_DTD, EXI_FIDELITY_DTD);
+    }
+
+    String getPreserveLexicalValues() {
+        return fidelityString(FidelityOptions.FEATURE_LEXICAL_VALUE, EXI_FIDELITY_LEXICAL_VALUES);
+    }
+
+    String getPreservePIs() {
+        return fidelityString(FidelityOptions.FEATURE_PI, EXI_FIDELITY_PIS);
+    }
+
+    String getPreservePrefixes() {
+        return fidelityString(FidelityOptions.FEATURE_PREFIX, EXI_FIDELITY_PREFIXES);
     }
 
-    public EXIOptions getOptions() {
-        return options;
+    String getSchema() {
+        return schema == EXISchema.NONE ? null : schema.name();
     }
 }