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