Fix xml->CompositeNode transformation for rpc replies for rpcs with no output
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / AbstractStringInstanceIdentifierCodec.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.util;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Splitter;
13
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
27 import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
28
29 /**
30  * Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a
31  * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name).
32  */
33 @Beta
34 public abstract class AbstractStringInstanceIdentifierCodec extends AbstractNamespaceCodec implements InstanceIdentifierCodec<String> {
35     private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
36     private static final Splitter SLASH_SPLITTER = Splitter.on('/');
37
38     @Override
39     public final String serialize(final YangInstanceIdentifier data) {
40         StringBuilder sb = new StringBuilder();
41         for (PathArgument arg : data.getPathArguments()) {
42             sb.append('/');
43             appendQName(sb, arg.getNodeType());
44
45             if (arg instanceof NodeIdentifierWithPredicates) {
46                 for (Map.Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) arg).getKeyValues().entrySet()) {
47                     sb.append('[');
48                     appendQName(sb, entry.getKey());
49                     sb.append("='");
50                     sb.append(String.valueOf(entry.getValue()));
51                     sb.append("']");
52                 }
53             } else if (arg instanceof NodeWithValue) {
54                 sb.append("[.='");
55                 sb.append(((NodeWithValue) arg).getValue());
56                 sb.append("']");
57             }
58         }
59
60         return sb.toString();
61     }
62
63     @Override
64     public final YangInstanceIdentifier deserialize(final String data) {
65         Preconditions.checkNotNull(data, "Data may not be null");
66
67         final Iterator<String> xPathParts = SLASH_SPLITTER.split(data).iterator();
68
69         // must be at least "/pr:node"
70         if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
71             return null;
72         }
73
74         List<PathArgument> result = new ArrayList<>();
75         while (xPathParts.hasNext()) {
76             String xPathPartTrimmed = xPathParts.next().trim();
77
78             PathArgument pathArgument = toPathArgument(xPathPartTrimmed);
79             if (pathArgument != null) {
80                 result.add(pathArgument);
81             }
82         }
83         return YangInstanceIdentifier.create(result);
84     }
85
86     private PathArgument toPathArgument(final String xPathArgument) {
87         final QName mainQName = parseQName(xPathArgument);
88
89         // predicates
90         final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
91         final Map<QName, Object> predicates = new HashMap<>();
92         QName currentQName = mainQName;
93
94         while (matcher.find()) {
95             final String predicateStr = matcher.group(1).trim();
96             final int indexOfEqualityMark = predicateStr.indexOf('=');
97             if (indexOfEqualityMark != -1) {
98                 final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
99                 if (predicateValue == null) {
100                     return null;
101                 }
102
103                 if (predicateStr.charAt(0) != '.') {
104                     // target is not a leaf-list
105                     currentQName = parseQName(predicateStr.substring(0, indexOfEqualityMark));
106                     if (currentQName == null) {
107                         return null;
108                     }
109                 }
110                 predicates.put(currentQName, predicateValue);
111             }
112         }
113
114         if (predicates.isEmpty()) {
115             return new YangInstanceIdentifier.NodeIdentifier(mainQName);
116         } else {
117             return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates);
118         }
119     }
120
121     private static String toPredicateValue(final String predicatedValue) {
122         final String predicatedValueTrimmed = predicatedValue.trim();
123         if (predicatedValue.isEmpty()) {
124             return null;
125         }
126
127         switch (predicatedValueTrimmed.charAt(0)) {
128         case '"':
129             return trimIfEndIs(predicatedValueTrimmed, '"');
130         case '\'':
131             return trimIfEndIs(predicatedValueTrimmed, '\'');
132         default:
133             return null;
134         }
135     }
136
137     private static String trimIfEndIs(final String str, final char end) {
138         final int l = str.length() - 1;
139         if (str.charAt(l) != end) {
140             return null;
141         }
142
143         return str.substring(1, l);
144     }
145 }