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