Bump versions to 4.0.0-SNAPSHOT
[netconf.git] / restconf / restconf-common / src / main / java / org / opendaylight / restconf / common / util / RestUtil.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.restconf.common.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.PushbackInputStream;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Optional;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20 import javax.xml.stream.events.StartElement;
21 import org.opendaylight.restconf.common.util.IdentityValuesDTO.IdentityValue;
22 import org.opendaylight.restconf.common.util.IdentityValuesDTO.Predicate;
23 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
24
25 public final class RestUtil {
26
27     // FIXME: BUG-1275: this is code duplicates data.impl.codec
28
29     public static final String SQUOTE = "'";
30     public static final String DQUOTE = "\"";
31     private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
32
33     private RestUtil() {
34     }
35
36     public static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
37         TypeDefinition<?> superType = type;
38         while (superType.getBaseType() != null) {
39             superType = superType.getBaseType();
40         }
41         return superType;
42     }
43
44     /**
45      * Utility method to find out if is provided {@link InputStream} empty.
46      *
47      * @param  entityStream {@link InputStream} to be checked if it is empty.
48      * @return Empty Optional if provided input stream is empty or Optional
49      *         containing input stream otherwise.
50      *
51      * @throws IOException if an IO error arises during stream inspection.
52      * @throws NullPointerException if provided stream is null.
53      *
54      */
55     public static Optional<InputStream> isInputStreamEmpty(final InputStream entityStream) throws IOException {
56         final PushbackInputStream pushbackInputStream = new PushbackInputStream(requireNonNull(entityStream));
57
58         int firstByte = pushbackInputStream.read();
59         if (firstByte == -1) {
60             return Optional.empty();
61         } else {
62             pushbackInputStream.unread(firstByte);
63             return Optional.of(pushbackInputStream);
64         }
65     }
66
67     public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) {
68         final String valueTrimmed = value.trim();
69         if (!valueTrimmed.startsWith("/")) {
70             return null;
71         }
72         final String[] xPathParts = valueTrimmed.split("/");
73         if (xPathParts.length < 2) { // must be at least "/pr:node"
74             return null;
75         }
76         final IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value);
77         for (int i = 1; i < xPathParts.length; i++) {
78             final String xPathPartTrimmed = xPathParts[i].trim();
79
80             final String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed);
81             final IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap);
82             if (identityValue == null) {
83                 return null;
84             }
85
86             final List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
87             if (predicates == null) {
88                 return null;
89             }
90             identityValue.setPredicates(predicates);
91
92             identityValuesDTO.add(identityValue);
93         }
94         return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
95     }
96
97     private static String getIdAndPrefixAsStr(final String pathPart) {
98         final int predicateStartIndex = pathPart.indexOf("[");
99         return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
100     }
101
102     private static IdentityValue toIdentity(final String xpathPart, final PrefixesMaping prefixMap) {
103         final String xPathPartTrimmed = xpathPart.trim();
104         if (xPathPartTrimmed.isEmpty()) {
105             return null;
106         }
107         final String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
108         // it is not "prefix:value"
109         if (prefixAndIdentifier.length != 2) {
110             return null;
111         }
112         final String prefix = prefixAndIdentifier[0].trim();
113         final String identifier = prefixAndIdentifier[1].trim();
114         if (prefix.isEmpty() || identifier.isEmpty()) {
115             return null;
116         }
117         final String namespace = prefixMap.getNamespace(prefix);
118         return new IdentityValue(namespace, identifier);
119     }
120
121     private static List<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
122         final List<Predicate> result = new ArrayList<>();
123         final List<String> predicates = new ArrayList<>();
124         final Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
125         while (matcher.find()) {
126             predicates.add(matcher.group(1).trim());
127         }
128         for (final String predicate : predicates) {
129             final int indexOfEqualityMark = predicate.indexOf("=");
130             if (indexOfEqualityMark != -1) {
131                 final String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1));
132                 if (predicate.startsWith(".")) { // it is leaf-list
133                     if (predicateValue == null) {
134                         return null;
135                     }
136                     result.add(new Predicate(null, predicateValue));
137                 } else {
138                     final IdentityValue identityValue =
139                             toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap);
140                     if (identityValue == null || predicateValue == null) {
141                         return null;
142                     }
143                     result.add(new Predicate(identityValue, predicateValue));
144                 }
145             }
146         }
147         return result;
148     }
149
150     private static String toPredicateValue(final String predicatedValue) {
151         final String predicatedValueTrimmed = predicatedValue.trim();
152         if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
153                 && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
154             return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
155         }
156         return null;
157     }
158
159     public interface PrefixesMaping {
160         String getNamespace(String prefix);
161     }
162
163     public static class PrefixMapingFromXml implements PrefixesMaping {
164         StartElement startElement = null;
165
166         public PrefixMapingFromXml(final StartElement startElement) {
167             this.startElement = startElement;
168         }
169
170         @Override
171         public String getNamespace(final String prefix) {
172             return startElement.getNamespaceContext().getNamespaceURI(prefix);
173         }
174     }
175
176     public static class PrefixMapingFromJson implements PrefixesMaping {
177
178         @Override
179         public String getNamespace(final String prefix) {
180             return prefix;
181         }
182     }
183
184 }