/* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.props; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Parses the configuration data associated with classes that contain setter properties. The format of the property * string should be like: * * <pre> MyClass{{propertyOne=foo}{propertyTwo=bar}} * </pre> * * <p>If the class name is supplied to the constructor, the property string need not contain the class declaration.</p> * * @author Middleware Services */ public class PropertyValueParser { /** Property string containing configuration. */ protected static final Pattern CONFIG_PATTERN = Pattern.compile("([^\\{]+)\\s*\\{(.*)\\}\\s*"); /** Property string for configuring a config where the class is known. */ protected static final Pattern PARAMS_ONLY_CONFIG_PATTERN = Pattern.compile("\\s*\\{\\s*(.*)\\s*\\}\\s*"); /** Pattern for finding properties. */ protected static final Pattern PROPERTY_PATTERN = Pattern.compile("([^\\}\\{])+"); /** Logger for this class. */ protected final Logger logger = LoggerFactory.getLogger(getClass()); /** Class found in the config. */ private String className; /** Properties found in the config to set on the class. */ private final Map<String, String> properties = new HashMap<>(); /** Default constructor. */ protected PropertyValueParser() {} /** * Creates a new config parser. * * @param config containing configuration data */ public PropertyValueParser(final String config) { final Matcher matcher = CONFIG_PATTERN.matcher(config); if (matcher.matches()) { initialize(matcher.group(1).trim(), matcher.group(2).trim()); } } /** * Creates a new config parser. * * @param config containing configuration data * @param clazz fully qualified class name */ public PropertyValueParser(final String config, final String clazz) { final Matcher matcher = PARAMS_ONLY_CONFIG_PATTERN.matcher(config); if (matcher.matches()) { initialize(clazz, matcher.group(1).trim()); } } /** * Invokes {@link #setClassName(String)} and {@link #initializeProperties(Matcher)}. * * @param clazz type to create and initialize * @param props to set on the class */ protected void initialize(final String clazz, final String props) { setClassName(clazz); if (!"".equals(props)) { initializeProperties(PROPERTY_PATTERN.matcher(props)); } } /** * Finds all the matches in the supplied matcher puts them into the properties map. Properties are split on '='. * * @param matcher to find matches */ protected void initializeProperties(final Matcher matcher) { while (matcher.find()) { final String input = matcher.group().trim(); if (input != null && !"".equals(input)) { final String[] s = input.split("=", 2); if (s.length < 2) { throw new IllegalArgumentException("Invalid property syntax: " + input); } properties.put(s[0].trim(), s[1].trim()); } } } /** * Returns the class name of the object to initialize. * * @return class name */ public String getClassName() { return className; } /** * Sets the class name of the object to initialize. * * @param name of the object class type */ protected void setClassName(final String name) { className = name; } /** * Returns the properties from the configuration. * * @return map of property name to value */ public Map<String, String> getProperties() { return properties; } /** * Returns whether the supplied configuration data contains a config. * * @param config containing configuration data * * @return whether the supplied configuration data contains a config */ public static boolean isConfig(final String config) { return CONFIG_PATTERN.matcher(config).matches(); } /** * Returns whether the supplied configuration data contains a params only config. * * @param config containing configuration data * * @return whether the supplied configuration data contains a params only config */ public static boolean isParamsOnlyConfig(final String config) { return PARAMS_ONLY_CONFIG_PATTERN.matcher(config).matches(); } /** * Initialize an instance of the class type with the properties contained in this config. * * @return object of the type the config parsed */ public Object initializeType() { final Class<?> c = SimplePropertyInvoker.createClass(getClassName()); final Object o = SimplePropertyInvoker.instantiateType(c, getClassName()); setProperties(c, o); return o; } /** * Sets the properties on the supplied object. * * @param c type of the supplied object * @param o to invoke properties on */ protected void setProperties(final Class<?> c, final Object o) { final SimplePropertyInvoker invoker = new SimplePropertyInvoker(c); for (Map.Entry<String, String> entry : getProperties().entrySet()) { invoker.setProperty(o, entry.getKey(), entry.getValue()); } if (invoker.getProperties().contains("initialize")) { try { invoker.setProperty(o, "initialize", null); } catch (Throwable t) { logger.debug("Error invoking initialize method", t); } } } }