*/
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();
}
}