aboutsummaryrefslogtreecommitdiffstats
path: root/utils/src/main/java/org/onap/policy/common/utils/properties
diff options
context:
space:
mode:
Diffstat (limited to 'utils/src/main/java/org/onap/policy/common/utils/properties')
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/BeanConfigurator.java8
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/PropertyObjectUtils.java242
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java15
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java43
4 files changed, 265 insertions, 43 deletions
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/BeanConfigurator.java b/utils/src/main/java/org/onap/policy/common/utils/properties/BeanConfigurator.java
index 2ef91911..f8c52091 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/properties/BeanConfigurator.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/BeanConfigurator.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP - Common Modules
* ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
package org.onap.policy.common.utils.properties;
+import com.google.re2j.Pattern;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -45,6 +46,7 @@ import org.onap.policy.common.utils.properties.exception.PropertyMissingExceptio
* <i>accept</i> includes the "empty" option.
*/
public class BeanConfigurator {
+ private static final Pattern COMMA_PAT = Pattern.compile(",");
/**
* The "empty" option that may appear within the {@link Property}'s <i>accept</i>
@@ -414,7 +416,7 @@ public class BeanConfigurator {
* @return {@code true} if the <i>accept</i> attribute includes "empty"
*/
protected boolean isEmptyOk(Property prop) {
- for (String option : prop.accept().split(",")) {
+ for (String option : COMMA_PAT.split(prop.accept())) {
if (ACCEPT_EMPTY.equals(option)) {
return true;
}
@@ -495,7 +497,7 @@ public class BeanConfigurator {
* @throws PropertyAccessException if a "get" method cannot be identified
*/
private Method getGetter(Field field, Property prop) throws PropertyAccessException {
- String capnm = StringUtils.capitalize(field.getName());
+ var capnm = StringUtils.capitalize(field.getName());
try {
return getGetter(field, "get" + capnm);
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyObjectUtils.java b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyObjectUtils.java
new file mode 100644
index 00000000..2b6e514f
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyObjectUtils.java
@@ -0,0 +1,242 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.properties;
+
+import com.google.re2j.Pattern;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utilities for generating POJOs from Properties.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class PropertyObjectUtils {
+
+ public static final Logger logger = LoggerFactory.getLogger(PropertyObjectUtils.class);
+ private static final Pattern NAME_PAT = Pattern.compile("\\[(\\d{1,3})\\]$");
+ private static final Pattern DOT_PAT = Pattern.compile("[.]");
+
+ /**
+ * Converts a set of properties to a Map. Supports json-path style property names with
+ * "." separating components, where components may have an optional subscript.
+ *
+ * @param properties properties to be converted
+ * @param prefix properties whose names begin with this prefix are included. The
+ * prefix is stripped from the name before adding the value to the map
+ * @return a hierarchical map representing the properties
+ */
+ public static Map<String, Object> toObject(Properties properties, String prefix) {
+ String dottedPrefix = prefix + (prefix.isEmpty() || prefix.endsWith(".") ? "" : ".");
+ int pfxlen = dottedPrefix.length();
+
+ Map<String, Object> map = new LinkedHashMap<>();
+
+ for (String name : properties.stringPropertyNames()) {
+ if (name.startsWith(dottedPrefix)) {
+ String[] components = DOT_PAT.split(name.substring(pfxlen));
+ setProperty(map, components, properties.getProperty(name));
+ }
+ }
+
+ return map;
+ }
+
+ /**
+ * Sets a property within a hierarchical map.
+ *
+ * @param map map into which the value should be placed
+ * @param names property name components
+ * @param value value to be placed into the map
+ */
+ private static void setProperty(Map<String, Object> map, String[] names, String value) {
+ Map<String, Object> node = map;
+
+ final int lastComp = names.length - 1;
+
+ // process all but the final component
+ for (var comp = 0; comp < lastComp; ++comp) {
+ node = getNode(node, names[comp]);
+ }
+
+ // process the final component
+ String name = names[lastComp];
+ var matcher = NAME_PAT.matcher(name);
+
+ if (!matcher.find()) {
+ // no subscript
+ node.put(name, value);
+ return;
+ }
+
+ // subscripted
+ List<Object> array = getArray(node, name.substring(0, matcher.start()));
+ var index = Integer.parseInt(matcher.group(1));
+ expand(array, index);
+ array.set(index, value);
+ }
+
+ /**
+ * Gets a node.
+ *
+ * @param map map from which to get the object
+ * @param name name of the element to get from the map, with an optional subscript
+ * @return a Map
+ */
+ @SuppressWarnings("unchecked")
+ private static Map<String, Object> getNode(Map<String, Object> map, String name) {
+ var matcher = NAME_PAT.matcher(name);
+
+ if (!matcher.find()) {
+ // no subscript
+ return getObject(map, name);
+ }
+
+ // subscripted
+ List<Object> array = getArray(map, name.substring(0, matcher.start()));
+ var index = Integer.parseInt(matcher.group(1));
+ expand(array, index);
+
+ Object item = array.get(index);
+ if (item instanceof Map) {
+ return (Map<String, Object>) item;
+
+ } else {
+ LinkedHashMap<String, Object> result = new LinkedHashMap<>();
+ array.set(index, result);
+ return result;
+ }
+ }
+
+ /**
+ * Ensures that an array's size is large enough to hold the specified element.
+ *
+ * @param array array to be expanded
+ * @param index index of the desired element
+ */
+ private static void expand(List<Object> array, int index) {
+ while (array.size() <= index) {
+ array.add(null);
+ }
+ }
+
+ /**
+ * Gets an object (i.e., Map) from a map. If the particular element is not a Map, then
+ * it is replaced with an empty Map.
+ *
+ * @param map map from which to get the object
+ * @param name name of the element to get from the map, without any subscript
+ * @return a Map
+ */
+ private static Map<String, Object> getObject(Map<String, Object> map, String name) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> result = (Map<String, Object>) map.compute(name, (key, value) -> {
+ if (value instanceof Map) {
+ return value;
+ } else {
+ return new LinkedHashMap<>();
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Gets an array from a map. If the particular element is not an array, then it is
+ * replaced with an empty array.
+ *
+ * @param map map from which to get the array
+ * @param name name of the element to get from the map, without any subscript
+ * @return an array
+ */
+ private static List<Object> getArray(Map<String, Object> map, String name) {
+ @SuppressWarnings("unchecked")
+ List<Object> result = (List<Object>) map.compute(name, (key, value) -> {
+ if (value instanceof List) {
+ return value;
+ } else {
+ return new ArrayList<>();
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Compresses lists contained within a generic object, removing all {@code null}
+ * items.
+ *
+ * @param object object to be compressed
+ * @return the original object, modified in place
+ */
+ public static Object compressLists(Object object) {
+ if (object instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> asMap = (Map<String, Object>) object;
+ compressMapValues(asMap);
+
+ } else if (object instanceof List) {
+ @SuppressWarnings("unchecked")
+ List<Object> asList = (List<Object>) object;
+ compressListItems(asList);
+ }
+
+ // else: ignore anything else
+
+ return object;
+ }
+
+ /**
+ * Walks a hierarchical map and removes {@code null} items found in any Lists.
+ *
+ * @param map map whose lists are to be compressed
+ */
+ private static void compressMapValues(Map<String, Object> map) {
+ for (Object value : map.values()) {
+ compressLists(value);
+ }
+ }
+
+ /**
+ * Removes {@code null} items from the list. In addition, it walks the items within
+ * the list, compressing them, as well.
+ *
+ * @param list the list to be compressed
+ */
+ private static void compressListItems(List<Object> list) {
+ Iterator<Object> iter = list.iterator();
+ while (iter.hasNext()) {
+ Object item = iter.next();
+ if (item == null) {
+ // null item - remove it
+ iter.remove();
+
+ } else {
+ compressLists(item);
+ }
+ }
+ }
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java
index ec7157d3..2ea94959 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2018, 2020 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2018, 2020-2021 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,13 @@
package org.onap.policy.common.utils.properties;
import java.util.Properties;
+import lombok.AccessLevel;
+import lombok.Getter;
/**
* Properties with an optional specialization (e.g., session name, controller name).
*/
+@Getter(AccessLevel.PROTECTED)
public class SpecProperties extends Properties {
private static final long serialVersionUID = 1L;
@@ -89,7 +92,7 @@ public class SpecProperties extends Properties {
return super.getProperty(key);
}
- String suffix = key.substring(prefix.length());
+ var suffix = key.substring(prefix.length());
String val = super.getProperty(specPrefix + suffix);
if (val != null) {
@@ -99,14 +102,6 @@ public class SpecProperties extends Properties {
return super.getProperty(key);
}
- protected String getPrefix() {
- return prefix;
- }
-
- protected String getSpecPrefix() {
- return specPrefix;
- }
-
@Override
public final synchronized int hashCode() {
throw new UnsupportedOperationException("SpecProperties cannot be hashed");
diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java
index 44edd428..3c03f38d 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/properties/exception/PropertyException.java
@@ -2,14 +2,14 @@
* ============LICENSE_START=======================================================
* ONAP Policy Engine - Common Modules
* ================================================================================
- * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2018, 2021 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,9 +20,12 @@
package org.onap.policy.common.utils.properties.exception;
+import lombok.Getter;
+
/**
* Exception associated with a Property.
*/
+@Getter
public class PropertyException extends Exception {
private static final long serialVersionUID = 1L;
@@ -38,7 +41,7 @@ public class PropertyException extends Exception {
/**
* Constructor.
- *
+ *
* @param propName name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
*/
@@ -51,7 +54,7 @@ public class PropertyException extends Exception {
/**
* Constructor.
- *
+ *
* @param propnm name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
* @param message error message
@@ -65,7 +68,7 @@ public class PropertyException extends Exception {
/**
* Constructor.
- *
+ *
* @param propnm name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
* @param cause cause of the exception
@@ -79,7 +82,7 @@ public class PropertyException extends Exception {
/**
* Constructor.
- *
+ *
* @param propnm name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
* @param message error message
@@ -93,28 +96,8 @@ public class PropertyException extends Exception {
}
/**
- * Get the property name.
- *
- * @return name of the property for which the exception was thrown, or {@code null} if
- * no name was provided
- */
- public String getPropertyName() {
- return propertyName;
- }
-
- /**
- * Get the field name.
- *
- * @return name of the field for which the exception was thrown, or {@code null} if no
- * field was provided
- */
- public String getFieldName() {
- return fieldName;
- }
-
- /**
* Make the message.
- *
+ *
* @param propnm name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
* @param message error message, never {@code null}
@@ -126,13 +109,13 @@ public class PropertyException extends Exception {
/**
* Make the message.
- *
+ *
* @param propnm name of the property causing the exception, or {@code null}
* @param fieldName name of the field causing the exception, or {@code null}
* @return an error message composed of the two items
*/
private static String makeMessage(String propnm, String fieldName) {
- StringBuilder bldr = new StringBuilder(50);
+ var bldr = new StringBuilder(50);
if (propnm == null) {
bldr.append("property exception");