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