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;
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 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(':');
38 private static final String SQUOTE = "'";
39 private static final String DQUOTE = "\"";
41 public static final InstanceIdentifierForXmlCodec INSTANCE_IDENTIFIER_FOR_XML_CODEC = new InstanceIdentifierForXmlCodec();
43 protected InstanceIdentifierForXmlCodec() {
46 public InstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
47 Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
48 Preconditions.checkNotNull(schemaContext,
49 "Schema context for deserialization of instance identifier type can't be null");
51 final String valueTrimmed = element.getTextContent().trim();
52 final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
54 // must be at least "/pr:node"
55 if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
59 List<PathArgument> result = new ArrayList<>();
60 while (xPathParts.hasNext()) {
61 String xPathPartTrimmed = xPathParts.next().trim();
63 PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
64 if (pathArgument != null) {
65 result.add(pathArgument);
68 return InstanceIdentifier.create(result);
71 public Element serialize(final InstanceIdentifier data, final Element element) {
72 Preconditions.checkNotNull(data, "Variable should contain instance of instance identifier and can't be null");
73 Preconditions.checkNotNull(element, "DOM element can't be null");
74 Map<String, String> prefixes = new HashMap<>();
75 StringBuilder textContent = new StringBuilder();
76 for (PathArgument pathArgument : data.getPathArguments()) {
77 textContent.append('/');
78 writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
79 if (pathArgument instanceof NodeIdentifierWithPredicates) {
80 Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
82 for (QName keyValue : predicates.keySet()) {
83 String predicateValue = String.valueOf(predicates.get(keyValue));
84 textContent.append('[');
85 writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
86 textContent.append("='");
87 textContent.append(predicateValue);
88 textContent.append("']");
90 } else if (pathArgument instanceof NodeWithValue) {
91 textContent.append("[.='");
92 textContent.append(((NodeWithValue) pathArgument).getValue());
93 textContent.append("']");
96 element.setTextContent(textContent.toString());
100 private static String getIdAndPrefixAsStr(final String pathPart) {
101 int predicateStartIndex = pathPart.indexOf('[');
102 return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
105 private PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
106 final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
109 final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
110 final Map<QName, Object> predicates = new HashMap<>();
111 QName currentQName = mainQName;
113 while (matcher.find()) {
114 final String predicateStr = matcher.group(1).trim();
115 final int indexOfEqualityMark = predicateStr.indexOf('=');
116 if (indexOfEqualityMark != -1) {
117 final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
118 if (predicateValue == null) {
122 if (predicateStr.charAt(0) != '.') {
123 // target is not a leaf-list
124 currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
125 if (currentQName == null) {
129 predicates.put(currentQName, predicateValue);
133 if (predicates.isEmpty()) {
134 return new InstanceIdentifier.NodeIdentifier(mainQName);
136 return new InstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
141 public QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
142 final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
143 final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
150 final String prefix = it.next().trim();
151 if (prefix.isEmpty()) {
155 // it is not "prefix:value"
160 final String identifier = it.next().trim();
161 if (identifier.isEmpty()) {
165 URI namespace = null;
166 String namespaceStr = null;
168 namespaceStr = element.lookupNamespaceURI(prefix);
169 namespace = new URI(namespaceStr);
170 } catch (URISyntaxException e) {
171 throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
172 } catch (NullPointerException e) {
173 throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
176 Module youngestModule = findYoungestModuleByNamespace(schemaContext, namespace);
177 return QName.create(namespace, youngestModule.getRevision(), identifier);
180 // FIXME: this method should be provided by SchemaContext
181 private static Module findYoungestModuleByNamespace(final SchemaContext schemaContext, final URI namespace) {
182 Module result = null;
183 for (Module module : schemaContext.findModuleByNamespace(namespace)) {
184 if (result != null) {
185 if (module.getRevision().after(result.getRevision())) {
195 private static String toPredicateValue(final String predicatedValue) {
196 String predicatedValueTrimmed = predicatedValue.trim();
197 if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
198 && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
199 return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
204 private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qName,
205 final Map<String, String> prefixes) {
206 String namespace = qName.getNamespace().toString();
207 String prefix = prefixes.get(namespace);
208 if (prefix == null) {
209 prefix = qName.getPrefix();
210 if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
211 prefix = generateNewPrefix(prefixes.values());
215 element.setAttribute("xmlns:" + prefix, namespace.toString());
216 textContent.append(prefix);
217 prefixes.put(namespace, prefix);
219 textContent.append(':');
220 textContent.append(qName.getLocalName());
223 private static String generateNewPrefix(final Collection<String> prefixes) {
226 final ThreadLocalRandom random = ThreadLocalRandom.current();
228 StringBuilder sb = new StringBuilder();
229 for (int i = 0; i < 4; i++) {
230 sb.append('a' + random.nextInt(25));
233 result = sb.toString();
234 } while (prefixes.contains(result));