Add MapBodyOrder
[netconf.git] / netconf / netconf-netty-util / src / main / java / org / opendaylight / netconf / nettyutil / handler / exi / EXIParameters.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.nettyutil.handler.exi;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import java.util.Objects;
15 import org.opendaylight.netconf.api.xml.XmlElement;
16 import org.opendaylight.netconf.shaded.exificient.core.CodingMode;
17 import org.opendaylight.netconf.shaded.exificient.core.EXIFactory;
18 import org.opendaylight.netconf.shaded.exificient.core.EncodingOptions;
19 import org.opendaylight.netconf.shaded.exificient.core.FidelityOptions;
20 import org.opendaylight.netconf.shaded.exificient.core.SchemaIdResolver;
21 import org.opendaylight.netconf.shaded.exificient.core.exceptions.EXIException;
22 import org.opendaylight.netconf.shaded.exificient.core.exceptions.UnsupportedOption;
23 import org.opendaylight.netconf.shaded.exificient.core.helpers.DefaultEXIFactory;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.w3c.dom.Element;
27 import org.w3c.dom.NodeList;
28
29 public final class EXIParameters {
30     private static final Logger LOG = LoggerFactory.getLogger(EXIParameters.class);
31
32     static final String EXI_PARAMETER_ALIGNMENT = "alignment";
33     private static final String EXI_PARAMETER_BYTE_ALIGNED = "byte-aligned";
34     private static final String EXI_PARAMETER_BIT_PACKED = "bit-packed";
35     private static final String EXI_PARAMETER_COMPRESSED = "compressed";
36     private static final String EXI_PARAMETER_PRE_COMPRESSION = "pre-compression";
37
38     static final String EXI_PARAMETER_FIDELITY = "fidelity";
39     private static final String EXI_FIDELITY_DTD = "dtd";
40     private static final String EXI_FIDELITY_LEXICAL_VALUES = "lexical-values";
41     private static final String EXI_FIDELITY_COMMENTS = "comments";
42     private static final String EXI_FIDELITY_PIS = "pis";
43     private static final String EXI_FIDELITY_PREFIXES = "prefixes";
44
45     static final String EXI_PARAMETER_SCHEMAS = "schemas";
46
47     private static final SchemaIdResolver SCHEMA_RESOLVER = schemaId -> {
48         if (schemaId == null) {
49             return null;
50         }
51         if (schemaId.isEmpty()) {
52             return EXISchema.BUILTIN.getGrammar();
53         }
54         if (schemaId.equals(EXISchema.BASE_1_1.getOption())) {
55             return EXISchema.BASE_1_1.getGrammar();
56         }
57
58         throw new EXIException("Cannot resolve schema " + schemaId);
59     };
60
61     private static final EncodingOptions ENCODING_OPTIONS;
62
63     static {
64         final EncodingOptions opts = EncodingOptions.createDefault();
65         try {
66             opts.setOption(EncodingOptions.RETAIN_ENTITY_REFERENCE);
67             opts.setOption(EncodingOptions.INCLUDE_OPTIONS);
68
69             /**
70              * NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it
71              * decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head
72              * of the stream. This is really useful, so let's output it now.
73              */
74             opts.setOption(EncodingOptions.INCLUDE_COOKIE);
75         } catch (final UnsupportedOption e) {
76             throw new ExceptionInInitializerError(e);
77         }
78
79         ENCODING_OPTIONS = opts;
80     }
81
82     private final FidelityOptions fidelityOptions;
83     private final CodingMode codingMode;
84     private final EXISchema schema;
85
86     public EXIParameters(final CodingMode codingMode, final FidelityOptions fidelityOptions) {
87         this(codingMode, fidelityOptions, EXISchema.NONE);
88     }
89
90     public EXIParameters(final CodingMode codingMode, final FidelityOptions fidelityOptions, final EXISchema schema) {
91         this.fidelityOptions = requireNonNull(fidelityOptions);
92         this.codingMode = requireNonNull(codingMode);
93         this.schema = requireNonNull(schema);
94     }
95
96     @VisibleForTesting
97     public static EXIParameters empty() {
98         return new EXIParameters(CodingMode.BIT_PACKED, FidelityOptions.createDefault());
99     }
100
101     public static EXIParameters fromXmlElement(final XmlElement root) throws UnsupportedOption {
102         final CodingMode coding;
103         final NodeList alignmentElements = root.getElementsByTagName(EXI_PARAMETER_ALIGNMENT);
104         if (alignmentElements.getLength() > 0) {
105             final Element alignmentElement = (Element) alignmentElements.item(0);
106             final String alignmentTextContent = alignmentElement.getTextContent().trim();
107
108             switch (alignmentTextContent) {
109                 case EXI_PARAMETER_BYTE_ALIGNED:
110                     coding = CodingMode.BYTE_PACKED;
111                     break;
112                 case EXI_PARAMETER_COMPRESSED:
113                     coding = CodingMode.COMPRESSION;
114                     break;
115                 case EXI_PARAMETER_PRE_COMPRESSION:
116                     coding = CodingMode.PRE_COMPRESSION;
117                     break;
118                 case EXI_PARAMETER_BIT_PACKED:
119                     coding = CodingMode.BIT_PACKED;
120                     break;
121                 default:
122                     LOG.warn("Unexpected value in alignmentTextContent: {} , using default value",
123                             alignmentTextContent);
124                     coding = CodingMode.BIT_PACKED;
125                     break;
126             }
127         } else {
128             coding = CodingMode.BIT_PACKED;
129         }
130
131         final FidelityOptions fidelity = FidelityOptions.createDefault();
132         final NodeList fidelityElements = root.getElementsByTagName(EXI_PARAMETER_FIDELITY);
133         if (fidelityElements.getLength() > 0) {
134             final Element fidelityElement = (Element) fidelityElements.item(0);
135
136             fidelity.setFidelity(FidelityOptions.FEATURE_DTD,
137                 fidelityElement.getElementsByTagName(EXI_FIDELITY_DTD).getLength() > 0);
138             fidelity.setFidelity(FidelityOptions.FEATURE_LEXICAL_VALUE,
139                 fidelityElement.getElementsByTagName(EXI_FIDELITY_LEXICAL_VALUES).getLength() > 0);
140             fidelity.setFidelity(FidelityOptions.FEATURE_COMMENT,
141                 fidelityElement.getElementsByTagName(EXI_FIDELITY_COMMENTS).getLength() > 0);
142             fidelity.setFidelity(FidelityOptions.FEATURE_PI,
143                 fidelityElement.getElementsByTagName(EXI_FIDELITY_PIS).getLength() > 0);
144             fidelity.setFidelity(FidelityOptions.FEATURE_PREFIX,
145                 fidelityElement.getElementsByTagName(EXI_FIDELITY_PREFIXES).getLength() > 0);
146         }
147
148         final EXISchema schema;
149         final NodeList schemaElements = root.getElementsByTagName(EXI_PARAMETER_SCHEMAS);
150         if (schemaElements.getLength() > 0) {
151             final Element schemaElement = (Element) schemaElements.item(0);
152             final String schemaName = schemaElement.getTextContent().trim();
153             schema = EXISchema.forOption(schemaName);
154             checkArgument(schema != null, "Unsupported schema name %s", schemaName);
155         } else {
156             schema = EXISchema.NONE;
157         }
158
159         return new EXIParameters(coding, fidelity, schema);
160     }
161
162     public EXIFactory getFactory() {
163         final EXIFactory factory = DefaultEXIFactory.newInstance();
164         factory.setCodingMode(codingMode);
165         factory.setEncodingOptions(ENCODING_OPTIONS);
166         factory.setFidelityOptions(fidelityOptions);
167         factory.setGrammars(schema.getGrammar());
168         factory.setSchemaIdResolver(SCHEMA_RESOLVER);
169         return factory;
170     }
171
172     @Override
173     public int hashCode() {
174         return Objects.hash(fidelityOptions, codingMode, schema);
175     }
176
177     @Override
178     public boolean equals(final Object obj) {
179         if (this == obj) {
180             return true;
181         }
182         if (!(obj instanceof EXIParameters)) {
183             return false;
184         }
185         final EXIParameters other = (EXIParameters) obj;
186         return codingMode == other.codingMode && schema == other.schema
187                 && fidelityOptions.equals(other.fidelityOptions);
188     }
189
190     String getAlignment() {
191         switch (codingMode) {
192             case BIT_PACKED:
193                 return EXI_PARAMETER_BIT_PACKED;
194             case BYTE_PACKED:
195                 return EXI_PARAMETER_BYTE_ALIGNED;
196             case COMPRESSION:
197                 return EXI_PARAMETER_COMPRESSED;
198             case PRE_COMPRESSION:
199                 return EXI_PARAMETER_PRE_COMPRESSION;
200             default:
201                 throw new IllegalStateException("Unhandled coding mode " + codingMode);
202         }
203     }
204
205     private String fidelityString(final String feature, final String string) {
206         return fidelityOptions.isFidelityEnabled(feature) ? string : null;
207     }
208
209     String getPreserveComments() {
210         return fidelityString(FidelityOptions.FEATURE_COMMENT, EXI_FIDELITY_COMMENTS);
211     }
212
213     String getPreserveDTD() {
214         return fidelityString(FidelityOptions.FEATURE_DTD, EXI_FIDELITY_DTD);
215     }
216
217     String getPreserveLexicalValues() {
218         return fidelityString(FidelityOptions.FEATURE_LEXICAL_VALUE, EXI_FIDELITY_LEXICAL_VALUES);
219     }
220
221     String getPreservePIs() {
222         return fidelityString(FidelityOptions.FEATURE_PI, EXI_FIDELITY_PIS);
223     }
224
225     String getPreservePrefixes() {
226         return fidelityString(FidelityOptions.FEATURE_PREFIX, EXI_FIDELITY_PREFIXES);
227     }
228
229     String getSchema() {
230         return schema == EXISchema.NONE ? null : schema.name();
231     }
232 }