0ad2fc213438c39e23fc2eb530dd1d06984c918c
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / InstanceIdentifierForXmlCodec.java
1 /*
2  * Copyright (c) 2014 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.yangtools.yang.data.impl.codec.xml;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
12
13 import java.net.URI;
14 import java.net.URISyntaxException;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.regex.Matcher;
22 import java.util.regex.Pattern;
23
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.model.api.Module;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 import org.w3c.dom.Element;
30
31 public final class InstanceIdentifierForXmlCodec {
32     private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
33     private static final Splitter SLASH_SPLITTER = Splitter.on('/');
34     private static final Splitter COLON_SPLITTER = Splitter.on(':');
35
36     private InstanceIdentifierForXmlCodec() {
37         throw new UnsupportedOperationException("Utility class");
38     }
39
40     public static YangInstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
41         Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
42         Preconditions.checkNotNull(schemaContext,
43                 "Schema context for deserialization of instance identifier type can't be null");
44
45         final String valueTrimmed = element.getTextContent().trim();
46         final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
47
48         // must be at least "/pr:node"
49         if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
50             return null;
51         }
52
53         List<PathArgument> result = new ArrayList<>();
54         while (xPathParts.hasNext()) {
55             String xPathPartTrimmed = xPathParts.next().trim();
56
57             PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
58             if (pathArgument != null) {
59                 result.add(pathArgument);
60             }
61         }
62         return YangInstanceIdentifier.create(result);
63     }
64
65     public static Element serialize(final YangInstanceIdentifier id, final Element element) {
66         Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
67         Preconditions.checkNotNull(element, "DOM element can't be null");
68
69         final RandomPrefix prefixes = new RandomPrefix();
70         final String str = XmlUtils.encodeIdentifier(prefixes, id);
71
72         for (Entry<URI, String> e: prefixes.getPrefixes()) {
73             element.setAttribute("xmlns:" + e.getValue(), e.getKey().toString());
74         }
75         element.setTextContent(str);
76         return element;
77     }
78
79     private static String getIdAndPrefixAsStr(final String pathPart) {
80         int predicateStartIndex = pathPart.indexOf('[');
81         return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
82     }
83
84     private static PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
85         final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
86
87         // predicates
88         final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
89         final Map<QName, Object> predicates = new HashMap<>();
90         QName currentQName = mainQName;
91
92         while (matcher.find()) {
93             final String predicateStr = matcher.group(1).trim();
94             final int indexOfEqualityMark = predicateStr.indexOf('=');
95             if (indexOfEqualityMark != -1) {
96                 final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
97                 if (predicateValue == null) {
98                     return null;
99                 }
100
101                 if (predicateStr.charAt(0) != '.') {
102                     // target is not a leaf-list
103                     currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
104                     if (currentQName == null) {
105                         return null;
106                     }
107                 }
108                 predicates.put(currentQName, predicateValue);
109             }
110         }
111
112         if (predicates.isEmpty()) {
113             return new YangInstanceIdentifier.NodeIdentifier(mainQName);
114         } else {
115             return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
116         }
117
118     }
119
120     public static QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
121         final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
122         final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
123
124         // Empty string
125         if (!it.hasNext()) {
126             return null;
127         }
128
129         final String prefix = it.next().trim();
130         if (prefix.isEmpty()) {
131             return null;
132         }
133
134         // it is not "prefix:value"
135         if (!it.hasNext()) {
136             return null;
137         }
138
139         final String identifier = it.next().trim();
140         if (identifier.isEmpty()) {
141             return null;
142         }
143
144         URI namespace = null;
145         String namespaceStr = null;
146         try {
147             namespaceStr = element.lookupNamespaceURI(prefix);
148             namespace = new URI(namespaceStr);
149         } catch (URISyntaxException e) {
150             throw new IllegalArgumentException("It wasn't possible to convert " + namespaceStr + " to URI object.");
151         } catch (NullPointerException e) {
152             throw new IllegalArgumentException("It wasn't possible to get namespace for prefix " + prefix);
153         }
154
155         Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
156         return QName.create(module.getQNameModule(), identifier);
157     }
158
159     private static String trimIfEndIs(final String str, final char end) {
160         final int l = str.length() - 1;
161         if (str.charAt(l) != end) {
162             return null;
163         }
164
165         return str.substring(1, l);
166     }
167
168     private static String toPredicateValue(final String predicatedValue) {
169         final String predicatedValueTrimmed = predicatedValue.trim();
170         if (predicatedValue.isEmpty()) {
171             return null;
172         }
173
174         switch (predicatedValueTrimmed.charAt(0)) {
175         case '"':
176             return trimIfEndIs(predicatedValueTrimmed, '"');
177         case '\'':
178             return trimIfEndIs(predicatedValueTrimmed, '\'');
179         default:
180             return null;
181         }
182     }
183 }