/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.domain.configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.ConfigurationTemplate;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionList;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
/**
* Utility methods for working with {@link Configuration}s.
*
* @author Ian Springer
*/
public class ConfigurationUtility {
/**
* @deprecated do not create instances of this class. It is meant as a static utility class.
*/
@Deprecated
public ConfigurationUtility() {
}
/**
* This will populate the given configuration definition with a default template.
* A default template will only be created if one or more properties are required
* or have default values. If no property definition is required or has a default value,
* the default template will remain <code>null</code> in the given config definition.
*
* Note that if the given configuration definition already has a default template defined
* for it, this method is a no-op and will return immediately.
*
* @param configDef the configuration definition whose default template is to be created and set
*/
public static void initializeDefaultTemplate(ConfigurationDefinition configDef) {
ConfigurationTemplate defaultTemplate = configDef.getDefaultTemplate();
if (defaultTemplate == null) {
Configuration defaultConfig = createDefaultConfiguration(configDef);
// not everything should have a default template - only stuff that has default values
if (!defaultConfig.getProperties().isEmpty()) {
defaultTemplate = new ConfigurationTemplate(ConfigurationTemplate.DEFAULT_TEMPLATE_NAME,
ConfigurationTemplate.DEFAULT_TEMPLATE_NAME);
defaultTemplate.setDefault(true);
defaultTemplate.setConfiguration(defaultConfig);
configDef.putTemplate(defaultTemplate);
}
}
return;
}
/**
* Given a configuration definition, this will build and return a "default configuration" that
* can be validated with the definition. All required properties are set and all properties
* that define a default value are also set. If a required property does not have a default
* value defined in the definition, the property value will be set to <code>null</code>.
*
* Use this to help create the definition's default template.
*
* @param configurationDefinition the configuration definition whose default configuration is to be created
* @return configuration the default configuration
*/
public static Configuration createDefaultConfiguration(ConfigurationDefinition configurationDefinition) {
if (configurationDefinition == null) {
throw new IllegalArgumentException("configurationDefinition == null");
}
Configuration defaultConfig = new Configuration();
Map<String, PropertyDefinition> childPropertyDefinitions = configurationDefinition.getPropertyDefinitions();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
createDefaultProperty(childPropertyDefinition, defaultConfig);
}
return defaultConfig;
}
/**
* "Normalize" the given configuration according to the given configuration definition. That is, for any optional
* properties that are not defined in the top-level configuration Map or any sub-Maps, set them.
* Map properties are set with an empty Map, and List properties with an empty List. By default, simple
* properties that are missing will be created and set with a null value. However, if normalizeRequiredDefaults is true,
* and a simple property is required with a default, this will set the required property to that default value.
* If normalizeOptionalDefaults is true, and a simple property is not required but has a default, this will set the
* optional property to that default. If a simple property does not have a default defined, no matter what those
* "normalize" booleans are, the simple property will still be set to null since this method won't know what value
* to set it to anyway.
*
* @param configuration the configuration to be normalized (must not be null)
* @param configurationDefinition the configuration definition to normalize the configuration against (may be null)
* @param normalizeRequiredDefaults if true, and a property is required, its default will be set as that property's value
* @param normalizeOptionalDefaults if true, and a property is optional, its default will be set as that property's value
*/
public static void normalizeConfiguration(Configuration configuration,
ConfigurationDefinition configurationDefinition, boolean normalizeRequiredDefaults,
boolean normalizeOptionalDefaults) {
if (configuration == null) {
throw new IllegalArgumentException("Configuration parameter is null.");
}
if (configurationDefinition != null) {
Map<String, PropertyDefinition> childPropertyDefinitions = configurationDefinition.getPropertyDefinitions();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
normalizeProperty(childPropertyDefinition, configuration, normalizeRequiredDefaults,
normalizeOptionalDefaults);
}
}
}
/**
* This is a little bit similar to {@link #normalizeConfiguration(Configuration,
* org.rhq.core.domain.configuration.definition.ConfigurationDefinition, boolean, boolean)}
* but can be used in cases where you need a new configuration based on the default template of the provided
* configuration definition yet want to reuse some or all of the corresponding properties from the provided
* "existing" configuration.
* <p/>
* In another words you want to adapt the existing configuration to the new format prescribed by the definition.
*
* @param existingConfiguration the existing configuration to take values from if possible.
* @param definition the definition to which the returned configuration will conform
* @param adaptReadonlyProperties set this to true if also the values of the readonly properties should be taken
* from the existing configuration. If this is false, the default value of the
* readonly properties defined in the definition is used instead.
*
* @return a new configuration object corresponding to the definition, with values reused from the existing
* configuration if possible
*/
public static Configuration adaptConfiguration(Configuration existingConfiguration,
ConfigurationDefinition definition, boolean adaptReadonlyProperties) {
Configuration targetConfig = createDefaultConfiguration(definition);
if (existingConfiguration != null) {
for (Map.Entry<String, PropertyDefinition> e : definition.getPropertyDefinitions().entrySet()) {
String name = e.getKey();
PropertyDefinition def = e.getValue();
Property source = existingConfiguration.get(name);
if (source != null) {
Property target = targetConfig.get(name);
adaptProperty(source, target, def, targetConfig, adaptReadonlyProperties);
}
}
}
return targetConfig;
}
/**
* Validate the given configuration according to the given configuration definition. That is, check that any
* required properties in the top-level configuration Map or any sub-Maps, are defined and, in the case of simple
* properties, check that they have a non-null value. A list of messages describing any errors that were found is
* returned. Additionally, any undefined or null simple properties will be assigned a value of "".
*
* @param configuration the configuration to be validated (must not be null)
* @param configurationDefinition the configuration definition to validate the configuration against (may be null)
*
* @return a list of messages describing any errors that were found - will be empty if there are no messages
*/
public static List<String> validateConfiguration(Configuration configuration,
ConfigurationDefinition configurationDefinition) {
return validateConfiguration(configuration, null, configurationDefinition);
}
/**
* Validate the given configuration according to the given configuration definition. That is, check that any
* required properties in the top-level configuration Map or any sub-Maps, are defined and, in the case of simple
* properties, check that they have a non-null value. Optionally, ensure configuration does not alter readOnly
* properties already defined in an existingConfiguration. A list of messages describing any errors that were found
* is returned. Additionally, any undefined or null simple properties will be assigned a value of "".
*
* @param configuration the configuration to be validated (must not be null)
* @param currentConfiguration if supplied, validate that readOnly properties do not differ between
* configuration and existingConfiguration. Ignored if null.
* @param configurationDefinition the configuration definition to validate the configuration against (may be null)
*
* @return a list of messages describing any errors that were found - will be empty if there are no messages
*/
public static List<String> validateConfiguration(Configuration configuration, Configuration currentConfiguration,
ConfigurationDefinition configurationDefinition) {
List<String> errorMessages = new ArrayList<String>();
if (configurationDefinition != null) {
Map<String, PropertyDefinition> childPropertyDefinitions = configurationDefinition.getPropertyDefinitions();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
validateProperty(childPropertyDefinition, configuration, currentConfiguration, errorMessages);
}
}
return errorMessages;
}
private static void createDefaultProperty(PropertyDefinition propertyDefinition,
AbstractPropertyMap parentPropertyMap) {
Property property = instantiateDefaultProperty(propertyDefinition);
if (property != null) {
parentPropertyMap.put(property);
}
}
private static Property instantiateDefaultProperty(PropertyDefinition propertyDefinition) {
Property property = null;
if (propertyDefinition instanceof PropertyDefinitionSimple) {
String defaultValue = ((PropertyDefinitionSimple) propertyDefinition).getDefaultValue();
if (defaultValue != null || propertyDefinition.isRequired()) {
property = new PropertySimple(propertyDefinition.getName(), defaultValue);
}
} else if (propertyDefinition.isRequired()) {
if (propertyDefinition instanceof PropertyDefinitionMap) {
property = new PropertyMap(propertyDefinition.getName());
Map<String, PropertyDefinition> childPropertyDefinitions = ((PropertyDefinitionMap) propertyDefinition)
.getMap();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
createDefaultProperty(childPropertyDefinition, (PropertyMap) property);
}
} else if (propertyDefinition instanceof PropertyDefinitionList) {
property = new PropertyList(propertyDefinition.getName());
PropertyDefinition listMemberPropertyDefinition = ((PropertyDefinitionList) propertyDefinition)
.getMemberDefinition();
if (listMemberPropertyDefinition.isRequired()) {
if (listMemberPropertyDefinition instanceof PropertyDefinitionMap) {
// member property is a list-o-maps, create a default child map if appropriate
PropertyDefinitionMap listMemberDefinitionMap = (PropertyDefinitionMap) listMemberPropertyDefinition;
PropertyMap listMap = new PropertyMap(listMemberDefinitionMap.getName());
createDefaultProperty(listMemberDefinitionMap, listMap);
((PropertyList) property).add(listMap);
} else if (listMemberPropertyDefinition instanceof PropertyDefinitionSimple) {
// member property is a simple, create a single list entry as its default if appropriate
PropertyDefinitionSimple listMemberDefinitionSimple = (PropertyDefinitionSimple) listMemberPropertyDefinition;
String defaultValue = listMemberDefinitionSimple.getDefaultValue();
if (defaultValue != null || listMemberDefinitionSimple.isRequired()) {
PropertySimple listSimple = new PropertySimple(listMemberDefinitionSimple.getName(),
defaultValue);
((PropertyList) property).add(listSimple);
}
}
}
} else if (propertyDefinition instanceof PropertyDefinitionDynamic) {
// Dynamic property values should simply be stored as simple
property = new PropertySimple(propertyDefinition.getName(), null);
} else {
throw new IllegalStateException("Unsupported PropertyDefinition subclass: "
+ propertyDefinition.getClass().getName());
}
}
return property;
}
private static void normalizeProperty(PropertyDefinition propertyDefinition, AbstractPropertyMap parentPropertyMap,
boolean normalizeRequiredDefaults, boolean normalizeOptionalDefaults) {
if (parentPropertyMap.getMap().keySet().contains(propertyDefinition.getName())) // property is already set
{
if (propertyDefinition instanceof PropertyDefinitionSimple) {
PropertySimple propertySimple = parentPropertyMap.getSimple(propertyDefinition.getName());
String value = propertySimple.getStringValue();
if (value != null) {
if (value.equals("")) {
// Normalize "" to null, since Oracle will do the same upon persistence.
propertySimple.setStringValue(null);
} else if (value.length() > PropertySimple.MAX_VALUE_LENGTH) {
// Truncate the value to the max length allowed by the DB schema.
propertySimple.setStringValue(value.substring(0, PropertySimple.MAX_VALUE_LENGTH));
}
}
}
// If property is a Map, recurse into it and normalize its child properties.
else if (propertyDefinition instanceof PropertyDefinitionMap) {
PropertyMap propertyMap = parentPropertyMap.getMap(propertyDefinition.getName());
PropertyDefinitionMap propertyDefinitionMap = (PropertyDefinitionMap) propertyDefinition;
normalizePropertyMap(propertyMap, propertyDefinitionMap, false,
false); // TODO do we want to pass normalizeRequired/OptionalDefaults?
} else if (propertyDefinition instanceof PropertyDefinitionList) {
PropertyDefinitionList propertyDefinitionList = (PropertyDefinitionList) propertyDefinition;
PropertyDefinition listMemberPropertyDefinition = propertyDefinitionList.getMemberDefinition();
// If property is a List of Maps, iterate the list and recurse into each Map and normalize its child properties.
if (listMemberPropertyDefinition instanceof PropertyDefinitionMap) {
PropertyDefinitionMap propertyDefinitionMap = (PropertyDefinitionMap) listMemberPropertyDefinition;
PropertyList propertyList = parentPropertyMap.getList(propertyDefinition.getName());
for (Property property : propertyList.getList()) {
PropertyMap propertyMap = (PropertyMap) property;
normalizePropertyMap(propertyMap, propertyDefinitionMap, false,
false); // TODO do we want to pass normalizeRequired/OptionalDefaults?
}
}
}
} else // property is not set yet
{
Property property;
if (propertyDefinition instanceof PropertyDefinitionSimple) {
String value = null;
if (normalizeRequiredDefaults || normalizeOptionalDefaults) {
if (propertyDefinition.isRequired()) {
if (normalizeRequiredDefaults) {
value = ((PropertyDefinitionSimple) propertyDefinition).getDefaultValue();
}
} else {
if (normalizeOptionalDefaults) {
value = ((PropertyDefinitionSimple) propertyDefinition).getDefaultValue();
}
}
}
property = new PropertySimple(propertyDefinition.getName(), value);
} else if (propertyDefinition instanceof PropertyDefinitionMap) {
property = new PropertyMap(propertyDefinition.getName());
} else if (propertyDefinition instanceof PropertyDefinitionList) {
property = new PropertyList(propertyDefinition.getName());
} else if (propertyDefinition instanceof PropertyDefinitionDynamic) {
// Dynamic property values should simply be stored as simple
property = new PropertySimple(propertyDefinition.getName(), null);
} else {
throw new IllegalStateException("Unsupported PropertyDefinition subclass: "
+ propertyDefinition.getClass().getName());
}
parentPropertyMap.put(property);
}
}
private static void normalizePropertyMap(AbstractPropertyMap propertyMap,
PropertyDefinitionMap propertyDefinitionMap, boolean normalizeRequiredDefaults,
boolean normalizeOptionalDefaults) {
Map<String, PropertyDefinition> childPropertyDefinitions = propertyDefinitionMap.getMap();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
normalizeProperty(childPropertyDefinition, propertyMap, normalizeRequiredDefaults,
normalizeOptionalDefaults);
}
}
private static void validateProperty(PropertyDefinition propertyDefinition, AbstractPropertyMap parentPropertyMap,
AbstractPropertyMap currentParentPropertyMap, List<String> errorMessages) {
if (parentPropertyMap.getMap().keySet().contains(propertyDefinition.getName())) // property is already set
{
if (propertyDefinition instanceof PropertyDefinitionSimple) {
PropertySimple propertySimple = parentPropertyMap.getSimple(propertyDefinition.getName());
PropertySimple currentPropertySimple = (null == currentParentPropertyMap) ? null
: currentParentPropertyMap.getSimple(propertyDefinition.getName());
validatePropertySimple(propertyDefinition, propertySimple, currentPropertySimple, errorMessages);
}
// If the property is a Map, validate it and recurse into it, validating its child properties.
else if (propertyDefinition instanceof PropertyDefinitionMap) {
PropertyMap propertyMap = parentPropertyMap.getMap(propertyDefinition.getName());
PropertyMap currentPropertyMap = (null == currentParentPropertyMap) ? null : currentParentPropertyMap
.getMap(propertyDefinition.getName());
PropertyDefinitionMap propertyDefinitionMap = (PropertyDefinitionMap) propertyDefinition;
validatePropertyMap(propertyMap, currentPropertyMap, propertyDefinitionMap, errorMessages);
// If the property is a List, validate each list member
} else if (propertyDefinition instanceof PropertyDefinitionList) {
PropertyDefinitionList propertyDefinitionList = (PropertyDefinitionList) propertyDefinition;
PropertyList propertyList = parentPropertyMap.getList(propertyDefinition.getName());
PropertyList currentPropertyList = (null == currentParentPropertyMap) ? null : currentParentPropertyMap
.getList(propertyDefinition.getName());
if (propertyDefinitionList.isReadOnly()) {
if (null != currentPropertyList && !currentPropertyList.getList().isEmpty()) {
if (!currentPropertyList.equals(propertyList)) {
errorMessages.add("ReadOnly property '" + propertyDefinitionList.getName()
+ "' has a value " + propertyList.getList() + " different than the current value "
+ currentPropertyList.getList() + "]. It is not allowed to change.");
}
}
}
validatePropertyListSize(propertyList, propertyDefinitionList, errorMessages);
PropertyDefinition listMemberPropertyDefinition = propertyDefinitionList.getMemberDefinition();
for (Property property : propertyList.getList()) {
if (listMemberPropertyDefinition instanceof PropertyDefinitionSimple) {
validatePropertySimple(listMemberPropertyDefinition, (PropertySimple) property, null,
errorMessages);
} else if (listMemberPropertyDefinition instanceof PropertyDefinitionMap) {
validatePropertyMap((PropertyMap) property, null,
(PropertyDefinitionMap) listMemberPropertyDefinition, errorMessages);
}
}
}
} else // property is not set yet
{
if (propertyDefinition.isRequired()) {
errorMessages.add("Required property '" + propertyDefinition.getName() + "' was not set in "
+ parentPropertyMap + ".");
if (propertyDefinition instanceof PropertyDefinitionSimple) {
PropertySimple propertySimple = new PropertySimple(propertyDefinition.getName(), "");
parentPropertyMap.put(propertySimple);
}
}
}
}
private static void validatePropertySimple(PropertyDefinition propertyDefinition, PropertySimple propertySimple,
PropertySimple currentPropertySimple, List<String> errorMessages) {
// make sure required properties have a value
if (propertyDefinition.isRequired() && (propertySimple.getStringValue() == null)) {
errorMessages.add("Required property '" + propertyDefinition.getName() + "' has a null value.");
propertySimple.setStringValue("");
}
// make sure readOnly properties are not being changed
if (propertyDefinition.isReadOnly() && null != currentPropertySimple) {
String currentValue = currentPropertySimple.getStringValue();
// if there is no current value allow an initial value to be set for the readOnly property.
if (!(null == currentValue || currentValue.trim().isEmpty() || propertySimple.getStringValue().equals(
currentValue))) {
errorMessages.add("ReadOnly property '" + propertyDefinition.getName() + "' has a value ["
+ propertySimple.getStringValue() + "] different than the current value [" + currentValue
+ "]. It is not allowed to change.");
}
}
}
private static void validatePropertyMap(AbstractPropertyMap propertyMap, AbstractPropertyMap currentPropertyMap,
PropertyDefinitionMap propertyDefinitionMap, List<String> errorMessages) {
// if the entire map is read-only then the new map must match the current map if the current map is non-empty
if (propertyDefinitionMap.isReadOnly() && null != currentPropertyMap && !currentPropertyMap.getMap().isEmpty()) {
if (!propertyMap.getMap().equals(currentPropertyMap.getMap())) {
errorMessages.add("ReadOnly property '" + propertyDefinitionMap.getName() + "' has a value "
+ propertyMap.getMap() + " different than the current value " + currentPropertyMap.getMap()
+ "]. It is not allowed to change.");
return;
}
}
Map<String, PropertyDefinition> childPropertyDefinitions = propertyDefinitionMap.getMap();
for (PropertyDefinition childPropertyDefinition : childPropertyDefinitions.values()) {
validateProperty(childPropertyDefinition, propertyMap, currentPropertyMap, errorMessages);
}
}
private static void validatePropertyListSize(PropertyList propertyList,
PropertyDefinitionList propertyDefinitionList, List<String> errorMessages) {
int listMin = propertyDefinitionList.getMin();
int listMax = propertyDefinitionList.getMax();
if (listMin == 0 && listMax == Integer.MAX_VALUE) {
return;
}
int listSize = propertyList.getList().size();
if (listMin == 0 && listMax < Integer.MAX_VALUE && listSize > listMax) {
errorMessages.add("The list property '" + propertyDefinitionList.getName() + "' should contain " + listMax
+ " row(s) at most");
} else if (listMin > 0 && listMax == Integer.MAX_VALUE && listSize < listMin) {
errorMessages.add("The list property '" + propertyDefinitionList.getName() + "' should contain at least "
+ listMin + " row(s)");
} else if (listSize < listMin || listSize > listMax) {
errorMessages.add("The list property '%s' should contain a minimum of " + listMin + " and a maximum of "
+ listMax + " row(s)");
}
}
private static void adaptPropertyMap(AbstractPropertyMap source, AbstractPropertyMap target,
PropertyDefinitionMap definition, Object parent, boolean adaptReadonlyProperties) {
if ((adaptReadonlyProperties || !definition.isReadOnly()) && target == null) {
target = new PropertyMap(definition.getName());
add(parent, (PropertyMap) target);
}
for (Map.Entry<String, PropertyDefinition> e : definition.getPropertyDefinitions().entrySet()) {
String name = e.getKey();
PropertyDefinition def = e.getValue();
Property sourceChild = source.get(name);
// only bother if we have something to adapt from
if (sourceChild != null) {
Property targetChild = target.get(name);
adaptProperty(sourceChild, targetChild, def, target, adaptReadonlyProperties);
}
}
}
private static void adaptPropertyList(PropertyList source, PropertyList target, PropertyDefinitionList definition,
Object parent, boolean adaptReadonlyProperties) {
if ((adaptReadonlyProperties || !definition.isReadOnly())) {
if (target == null) {
target = new PropertyList(definition.getName());
add(parent, target);
}
if (target.getList().isEmpty()) {
PropertyDefinition memberDef = definition.getMemberDefinition();
for (Property p : source.getList()) {
PropertyType type = conforms(p, memberDef);
if (type != null && type != PropertyType.UNKNOWN) {
Property targetMember = instantiateDefaultProperty(memberDef);
target.add(targetMember);
adaptProperty(p, targetMember, memberDef, target, adaptReadonlyProperties);
}
}
}
}
}
private static void adaptPropertySimple(PropertySimple source, PropertySimple target,
PropertyDefinitionSimple definition, Object parent, boolean adaptReadonlyProperties) {
if (adaptReadonlyProperties || !definition.isReadOnly()) {
if (target == null) {
target = new PropertySimple();
target.setName(definition.getName());
add(parent, target);
}
target.setStringValue(source.getStringValue());
}
}
private static void adaptProperty(Property source, Property target, PropertyDefinition def, Object parent,
boolean adaptReadonlyProperties) {
PropertyType sourceType = conforms(source, def);
PropertyType targetType = conforms(target, def);
if (sourceType != null && sourceType != PropertyType.UNKNOWN &&
(sourceType == targetType || targetType == PropertyType.UNKNOWN)) {
switch (sourceType) {
case MAP:
adaptPropertyMap((AbstractPropertyMap) source, (AbstractPropertyMap) target,
(PropertyDefinitionMap) def, parent, adaptReadonlyProperties);
break;
case LIST:
adaptPropertyList((PropertyList) source, (PropertyList) target, (PropertyDefinitionList) def, parent,
adaptReadonlyProperties);
break;
case SIMPLE:
adaptPropertySimple((PropertySimple) source, (PropertySimple) target, (PropertyDefinitionSimple) def,
parent, adaptReadonlyProperties);
break;
case DYNAMIC:
//TODO
}
}
// the types of the properties don't match... let's just leave target as it is because it comes
// from the config definition we want...
}
private static PropertyType conforms(Property p, PropertyDefinition d) {
if (p == null) {
return PropertyType.UNKNOWN;
}
if (!p.getName().equals(d.getName())) {
return null;
}
if (p instanceof PropertySimple && d instanceof PropertyDefinitionSimple) {
return PropertyType.SIMPLE;
} else if (p instanceof PropertyList && d instanceof PropertyDefinitionList) {
return PropertyType.LIST;
} else if (p instanceof PropertyMap && d instanceof PropertyDefinitionMap) {
return PropertyType.MAP;
} else if (p instanceof PropertySimple && d instanceof PropertyDefinitionDynamic) {
return PropertyType.DYNAMIC;
} else {
return null;
}
}
private static void add(Object parent, Property prop) {
if (parent instanceof AbstractPropertyMap) {
((AbstractPropertyMap) parent).put(prop);
} else if (parent instanceof PropertyList) {
((PropertyList) parent).add(prop);
}
}
private enum PropertyType {
UNKNOWN, SIMPLE, LIST, MAP, DYNAMIC
}
}