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