Add 'protocol-framework/' from commit '5d015c2c5f800d136406c15fcb64fd531d4ffc26'
[netconf.git] / netconf / tools / netconf-testtool / src / main / java / org / opendaylight / netconf / test / tool / customrpc / RpcMapping.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.netconf.test.tool.customrpc;
10
11 import com.google.common.collect.ArrayListMultimap;
12 import com.google.common.collect.Multimap;
13 import java.io.File;
14 import java.util.Collection;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.stream.Stream;
20 import javax.xml.bind.JAXB;
21 import org.opendaylight.controller.config.util.xml.DocumentedException;
22 import org.opendaylight.controller.config.util.xml.XmlElement;
23 import org.opendaylight.controller.config.util.xml.XmlUtil;
24 import org.w3c.dom.Attr;
25 import org.w3c.dom.Document;
26
27 /**
28  * Mapping between RPCs and responses.
29  */
30 class RpcMapping {
31
32     private final Multimap<Request, Document> requestToResponseMap = ArrayListMultimap.create();
33
34     /**
35      * Creates new mapping from file.
36      *
37      * @param config config file
38      */
39     RpcMapping(final File config) {
40         final Rpcs rpcs = JAXB.unmarshal(config, Rpcs.class);
41         for (final Rpc rpc : rpcs.getRpcs()) {
42             final Stream<Document> stream = rpc.getOutput().stream()
43                     .map(XmlData::getData);
44             final XmlElement element = XmlElement.fromDomDocument(rpc.getInput().getData());
45             requestToResponseMap.putAll(new Request(element), stream::iterator);
46         }
47     }
48
49     /**
50      * Returns response matching given input. If multiple responses are configured for the same
51      * request, every invocation of this method returns next response as they are defined in the config file.
52      * Last configured response is used, when number of invocations is higher than number of configured responses
53      * for rpc.
54      *
55      * @param input request
56      * @return response document, or absent if mapping is not defined
57      */
58     Optional<Document> getResponse(final XmlElement input) {
59         final Collection<Document> responses = requestToResponseMap.get(new Request(input));
60         final Iterator<Document> iterator = responses.iterator();
61         if (iterator.hasNext()) {
62             final Document response = iterator.next();
63             if (iterator.hasNext()) {
64                 iterator.remove();
65             }
66             return Optional.of(response);
67         }
68         return Optional.empty();
69     }
70
71     /**
72      * Rpc input wrapper. Needed because of custom {@link Request#equals(Object)}
73      * and {@link Request#hashCode()} implementations.
74      */
75     private static class Request {
76         private final XmlElement xmlElement;
77         private final int hashCode;
78
79         private Request(final XmlElement element) {
80             this.xmlElement = element;
81             hashCode = XmlUtil.toString(element)
82                     .replaceAll("message-id=.*(>| )", "") //message id is variable, remove it
83                     .replaceAll("\\s+", "") //remove whitespaces
84                     .hashCode();
85         }
86
87         @Override
88         public boolean equals(final Object obj) {
89             if (this == obj) {
90                 return true;
91             }
92             if (obj == null || getClass() != obj.getClass()) {
93                 return false;
94             }
95             final Request request = (Request) obj;
96             return documentEquals(this.xmlElement, request.xmlElement);
97         }
98
99         @Override
100         public int hashCode() {
101             return hashCode;
102         }
103
104         private static boolean documentEquals(final XmlElement e1, final XmlElement e2) {
105             if (!e1.getNamespaceOptionally().equals(e2.getNamespaceOptionally())) {
106                 return false;
107             }
108             if (!e1.getName().equals(e2.getName())) {
109                 return false;
110             }
111             if (attributesNotEquals(e1, e2)) {
112                 return false;
113             }
114
115             final List<XmlElement> e1Children = e1.getChildElements();
116             final List<XmlElement> e2Children = e2.getChildElements();
117             if (e1Children.size() != e2Children.size()) {
118                 return false;
119             }
120             final Iterator<XmlElement> e1Iterator = e1Children.iterator();
121             final Iterator<XmlElement> e2Iterator = e2Children.iterator();
122             while (e1Iterator.hasNext() && e2Iterator.hasNext()) {
123                 if (!documentEquals(e1Iterator.next(), e2Iterator.next())) {
124                     return false;
125                 }
126             }
127
128             if (e1Children.isEmpty() && e1Children.isEmpty()) {
129                 try {
130                     return e1.getTextContent().equals(e2.getTextContent());
131                 } catch (final DocumentedException e) {
132                     return false;
133                 }
134             }
135             return true;
136         }
137
138         private static boolean attributesNotEquals(final XmlElement e1, final XmlElement e2) {
139             final Map<String, Attr> e1Attrs = e1.getAttributes();
140             final Map<String, Attr> e2Attrs = e2.getAttributes();
141             final Iterator<Map.Entry<String, Attr>> e1AttrIt = e1Attrs.entrySet().iterator();
142             final Iterator<Map.Entry<String, Attr>> e2AttrIt = e2Attrs.entrySet().iterator();
143             while (e1AttrIt.hasNext() && e2AttrIt.hasNext()) {
144                 final Map.Entry<String, Attr> e1Next = e1AttrIt.next();
145                 final Map.Entry<String, Attr> e2Next = e2AttrIt.next();
146                 if (e1Next.getKey().equals("message-id") && e2Next.getKey().equals("message-id")) {
147                     continue;
148                 }
149                 if (e1Next.getKey().equals("xmlns") && e2Next.getKey().equals("xmlns")) {
150                     continue;
151                 }
152                 if (!e1Next.getKey().equals(e2Next.getKey())) {
153                     return true;
154                 }
155                 if (!e1Next.getValue().getValue().equals(e2Next.getValue().getValue())) {
156                     return true;
157                 }
158             }
159             return false;
160         }
161
162     }
163 }