Add junit coverage to RequestInfoBuilder class
[appc.git] / appc-common / src / main / java / org / onap / appc / util / StructuredPropertyHelper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * 
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25
26
27 package org.onap.appc.util;
28
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Properties;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 /**
36  * This class is used to assemble properties that are defined using a structured name into groups, and allow them to be
37  * processed as sets of definitions.
38  * <p>
39  * For example, a structured name uses a dotted-notation, like "provider.name". Further, the nodes of the structured
40  * name may be serialized using a suffix ordinal number (e.g., "provider1.name"). These structured properties form a
41  * hierarchical name space where the names are grouped together and can be retrieved as a set.
42  * </p>
43  * 
44  */
45
46 public class StructuredPropertyHelper {
47
48     /**
49      * This method scans the properties object for all properties that match the root name and constructs a list of
50      * structured property node graphs that represents the namespaces of the properties.
51      * <p>
52      * For example, assume that there are structured properties of the form "provider1.name", "provider2.name",
53      * "provider3.name", and so forth. There may also be other subordinate properties as well (e.g., "provider1.type").
54      * This method would construct a list of graphs of nodes, where each node represents one value of the structured
55      * name. The roots would be the values "provider1", "provider2", "provider3", and so forth. The values of the
56      * subordinate nodes would be the second, third, and so forth name nodes of the compound name. The value of the
57      * property is associated with nodes that are representative of the leaf of the name space.
58      * </p>
59      * 
60      * @param properties
61      *            The properties to be processed
62      * @param prefix
63      *            The prefix of the root structured property name
64      * @return The node graph of the properties
65      */
66     public static List<Node> getStructuredProperties(Properties properties, String prefix) {
67         List<Node> roots = new ArrayList<>();
68
69         for (String name : properties.stringPropertyNames()) {
70             if (name.startsWith(prefix)) {
71                 String value = properties.getProperty(name);
72                 processNamespace(roots, name, value);
73             }
74         }
75
76         return roots;
77     }
78
79     /**
80      * This method recursively walks the name space of the structured property and constructs the node graph to
81      * represent the property
82      * 
83      * @param nodes
84      *            The collection of nodes for the current level of the name space
85      * @param propertyName
86      *            The name of the node
87      * @param value
88      *            The value, if any
89      * @return The node for this level in the namespace
90      */
91     @SuppressWarnings("nls")
92     private static Node processNamespace(List<Node> nodes, String propertyName, String value) {
93         String[] tokens = propertyName.split("\\.", 2);
94         String nodeName = normalizeNodeName(tokens[0]);
95
96         Node namespaceNode = null;
97         for (Node node : nodes) {
98             if (node.getName().equals(nodeName)) {
99                 namespaceNode = node;
100                 break;
101             }
102         }
103         if (namespaceNode == null) {
104             namespaceNode = new Node();
105             namespaceNode.setName(nodeName);
106             nodes.add(namespaceNode);
107         }
108
109         if (tokens.length == 1 || tokens[1] == null || tokens[1].length() == 0) {
110             namespaceNode.setValue(value);
111         } else {
112             processNamespace(namespaceNode.getChildren(), tokens[1], value);
113         }
114
115         return namespaceNode;
116     }
117
118     /**
119      * This method normalizes a node name of the structured property name by removing leading and trailing whitespace,
120      * and by converting any ordinal position to a simple expression without leading zeroes.
121      * 
122      * @param token
123      *            The token to be normalized
124      * @return The normalized name, or null if the token was null;
125      */
126     @SuppressWarnings("nls")
127     private static String normalizeNodeName(String token) {
128         if (token == null) {
129             return null;
130         }
131
132         StringBuffer buffer = new StringBuffer(token.trim());
133         Pattern pattern = Pattern.compile("([^0-9]+)([0-9]*)");
134         Matcher matcher = pattern.matcher(buffer);
135         if (matcher.matches()) {
136             String nameRoot = matcher.group(1);
137             String ordinal = matcher.group(2);
138             if (ordinal != null && ordinal.length() > 0) {
139                 int i = Integer.parseInt(ordinal);
140                 buffer.setLength(0);
141                 buffer.append(nameRoot);
142                 buffer.append(Integer.toString(i));
143             }
144         }
145         return buffer.toString();
146     }
147
148     /**
149      * This class represents a node in the structured property name space
150      *
151      */
152     public static class Node implements Comparable<Node> {
153
154         /**
155          * The name of the structured property node
156          */
157         private String name;
158
159         /**
160          * If the node is a leaf, then the value of the property
161          */
162         private String value;
163
164         /**
165          * If the node is not a leaf, then the sub-nodes of the property
166          */
167         private List<Node> children;
168
169         /**
170          * @return the value of name
171          */
172         public String getName() {
173             return name;
174         }
175
176         /**
177          * @param name
178          *            the value for name
179          */
180         public void setName(String name) {
181             this.name = name;
182         }
183
184         /**
185          * @return the value of value
186          */
187         public String getValue() {
188             return value;
189         }
190
191         /**
192          * @param value
193          *            the value for value
194          */
195         public void setValue(String value) {
196             this.value = value;
197         }
198
199         /**
200          * @return the value of children
201          */
202         public List<Node> getChildren() {
203             if (children == null) {
204                 children = new ArrayList<>();
205             }
206             return children;
207         }
208
209         /**
210          * @see java.lang.Object#hashCode()
211          */
212         @Override
213         public int hashCode() {
214             return name.hashCode() + (value != null ? value.hashCode() : children.hashCode());
215         }
216
217         /**
218          * @see java.lang.Object#equals(java.lang.Object)
219          */
220         @Override
221         public boolean equals(Object obj) {
222             if (obj == null)
223                 return false;
224             if (this.getClass() != obj.getClass())
225                 return false;
226
227             Node other = (Node) obj;
228             boolean result = name.equals(other.name);
229
230             if (value == null) {
231                 result &= other.value == null;
232             } else {
233                 result &= value.equals(other.value);
234             }
235             if (children == null) {
236                 result &= other.children == null;
237             } else {
238                 result &= children.equals(other.children);
239             }
240             return result;
241         }
242
243         /**
244          * @see java.lang.Object#toString()
245          */
246         @SuppressWarnings("nls")
247         @Override
248         public String toString() {
249             if (value != null) {
250                 return String.format("%s = %s", name, value);
251             }
252             return String.format("%s.%s", name, children.toString());
253         }
254
255         @Override
256         public int compareTo(StructuredPropertyHelper.Node o) {
257             return name.compareTo(o.name);
258         }
259     }
260 }