2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
14 import java.net.URISyntaxException;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.concurrent.ThreadLocalRandom;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
30 import org.opendaylight.yangtools.yang.model.api.Module;
31 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
32 import org.w3c.dom.Element;
34 public final class InstanceIdentifierForXmlCodec {
35 private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
36 private static final Splitter SLASH_SPLITTER = Splitter.on('/');
37 private static final Splitter COLON_SPLITTER = Splitter.on(':');
39 private InstanceIdentifierForXmlCodec() {
40 throw new UnsupportedOperationException("Utility class");
43 public static InstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
44 Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
45 Preconditions.checkNotNull(schemaContext,
46 "Schema context for deserialization of instance identifier type can't be null");
48 final String valueTrimmed = element.getTextContent().trim();
49 final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
51 // must be at least "/pr:node"
52 if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
56 List<PathArgument> result = new ArrayList<>();
57 while (xPathParts.hasNext()) {
58 String xPathPartTrimmed = xPathParts.next().trim();
60 PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
61 if (pathArgument != null) {
62 result.add(pathArgument);
65 return InstanceIdentifier.create(result);
68 public static Element serialize(final InstanceIdentifier data, final Element element) {
69 Preconditions.checkNotNull(data, "Variable should contain instance of instance identifier and can't be null");
70 Preconditions.checkNotNull(element, "DOM element can't be null");
71 Map<String, String> prefixes = new HashMap<>();
72 StringBuilder textContent = new StringBuilder();
73 for (PathArgument pathArgument : data.getPathArguments()) {
74 textContent.append('/');
75 writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
76 if (pathArgument instanceof NodeIdentifierWithPredicates) {
77 Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
79 for (QName keyValue : predicates.keySet()) {
80 String predicateValue = String.valueOf(predicates.get(keyValue));
81 textContent.append('[');
82 writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
83 textContent.append("='");
84 textContent.append(predicateValue);
85 textContent.append("']");
87 } else if (pathArgument instanceof NodeWithValue) {
88 textContent.append("[.='");
89 textContent.append(((NodeWithValue) pathArgument).getValue());
90 textContent.append("']");
93 element.setTextContent(textContent.toString());
97 private static String getIdAndPrefixAsStr(final String pathPart) {
98 int predicateStartIndex = pathPart.indexOf('[');
99 return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
102 private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
103 final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
106 final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
107 final Map<QName, Object> predicates = new HashMap<>();
108 QName currentQName = mainQName;
110 while (matcher.find()) {
111 final String predicateStr = matcher.group(1).trim();
112 final int indexOfEqualityMark = predicateStr.indexOf('=');
113 if (indexOfEqualityMark != -1) {
114 final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
115 if (predicateValue == null) {
119 if (predicateStr.charAt(0) != '.') {
120 // target is not a leaf-list
121 currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
122 if (currentQName == null) {
126 predicates.put(currentQName, predicateValue);
130 if (predicates.isEmpty()) {
131 return new InstanceIdentifier.NodeIdentifier(mainQName);
133 return new InstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
138 public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
139 final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
140 final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
147 final String prefix = it.next().trim();
148 if (prefix.isEmpty()) {
152 // it is not "prefix:value"
157 final String identifier = it.next().trim();
158 if (identifier.isEmpty()) {
162 URI namespace = null;
163 String namespaceStr = null;
165 namespaceStr = element.lookupNamespaceURI(prefix);
166 namespace = new URI(namespaceStr);
167 } catch (URISyntaxException e) {
168 throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
169 } catch (NullPointerException e) {
170 throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
173 Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
174 return QName.create(module.getQNameModule(), identifier);
177 private static String trimIfEndIs(final String str, final char end) {
178 final int l = str.length() - 1;
179 if (str.charAt(l) != end) {
183 return str.substring(1, l);
186 private static String toPredicateValue(final String predicatedValue) {
187 final String predicatedValueTrimmed = predicatedValue.trim();
188 if (predicatedValue.isEmpty()) {
192 switch (predicatedValueTrimmed.charAt(0)) {
194 return trimIfEndIs(predicatedValueTrimmed, '"');
196 return trimIfEndIs(predicatedValueTrimmed, '\'');
202 private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qName,
203 final Map<String, String> prefixes) {
204 String namespace = qName.getNamespace().toString();
205 String prefix = prefixes.get(namespace);
206 if (prefix == null) {
207 prefix = qName.getPrefix();
208 if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
209 prefix = generateNewPrefix(prefixes.values());
213 element.setAttribute("xmlns:" + prefix, namespace.toString());
214 textContent.append(prefix);
215 prefixes.put(namespace, prefix);
217 textContent.append(':');
218 textContent.append(qName.getLocalName());
221 private static String generateNewPrefix(final Collection<String> prefixes) {
224 final ThreadLocalRandom random = ThreadLocalRandom.current();
226 StringBuilder sb = new StringBuilder();
227 for (int i = 0; i < 4; i++) {
228 sb.append('a' + random.nextInt(25));
231 result = sb.toString();
232 } while (prefixes.contains(result));