/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.config.xml.util; import java.util.HashMap; import java.util.List; import java.util.Map; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; /** * The {@link ConverterValueMap} reads all children elements of a node and provides * them as key-value pair map. * <p> * This class should be used for nodes whose children elements <i>only</i> contain simple values (without children) and * whose order is unpredictable. There must be only one children with the same name. * * @author Michael Grammling - Initial Contribution * @author Alex Tugarev - Extended for options and filter criteria */ public class ConverterValueMap { private HierarchicalStreamReader reader; private Map<String, Object> valueMap; private UnmarshallingContext context; /** * Creates a new instance of this class with the specified parameter. * * @param reader the reader to be used to read-in all children (must not be null) * @param context */ public ConverterValueMap(HierarchicalStreamReader reader, UnmarshallingContext context) { this(reader, -1, context); } /** * Creates a new instance of this class with the specified parameters. * * @param reader the reader to be used to read-in all children (must not be null) * @param numberOfValues the number of children to be read-in (< 0 = until end of section) * @param context * * @throws ConversionException if not all children could be read-in */ public ConverterValueMap(HierarchicalStreamReader reader, int numberOfValues, UnmarshallingContext context) throws ConversionException { if (numberOfValues < -1) { numberOfValues = -1; } this.reader = reader; this.context = context; this.valueMap = readValueMap(this.reader, numberOfValues, this.context); } /** * Returns the key-value map containing all read-in children. * * @return the key-value map containing all read-in children (not null, could be empty) */ public Map<String, Object> getValueMap() { return this.valueMap; } /** * Reads-in {@code N} children in a key-value map and returns it. * * @param reader the reader to be used to read-in the children (must not be null) * @param numberOfValues the number of children to be read in (< 0 = until end of section) * @param context * * @return the key-value map containing the read-in children (not null, could be empty) * * @throws ConversionException if not all children could be read-in */ public static Map<String, Object> readValueMap(HierarchicalStreamReader reader, int numberOfValues, UnmarshallingContext context) throws ConversionException { Map<String, Object> valueMap = new HashMap<>((numberOfValues >= 0) ? numberOfValues : 10); int counter = 0; while (reader.hasMoreChildren() && ((counter < numberOfValues) || (numberOfValues == -1))) { reader.moveDown(); if (reader.hasMoreChildren()) { List<?> list = (List<?>) context.convertAnother(context, List.class); valueMap.put(reader.getNodeName(), list); } else { valueMap.put(reader.getNodeName(), reader.getValue()); } reader.moveUp(); counter++; } if ((counter < numberOfValues) && (numberOfValues > 0)) { throw new ConversionException("Not all children could be read-in!"); } return valueMap; } /** * Returns the object associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @return the object associated with the specified name of the child's node (could be null) */ public Object getObject(String nodeName) { return this.valueMap.get(nodeName); } /** * Returns the object associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @param defaultValue the value to be returned if the node could not be found (could be null) * * @return the object associated with the specified name of the child's node (could be null) */ public Object getObject(String nodeName, Object defaultValue) { Object value = this.valueMap.get(nodeName); if (value != null) { return value; } return defaultValue; } /** * Returns the text associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @return the text associated with the specified name of the child's node (could be null) */ public String getString(String nodeName) { return getString(nodeName, null); } /** * Returns the text associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @param defaultValue the text to be returned if the node could not be found (could be null) * * @return the text associated with the specified name of the child's node (could be null) */ public String getString(String nodeName, String defaultValue) { Object value = this.valueMap.get(nodeName); if (value instanceof String) { // fixes a formatting problem with line breaks in text return ((String) value).replaceAll("\\n\\s*", " ").trim(); } return defaultValue; } /** * Returns the boolean associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @return the boolean associated with the specified name of the child's node (could be null) */ public Boolean getBoolean(String nodeName) { return getBoolean(nodeName, null); } /** * Returns the boolean associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @param defaultValue the boolean to be returned if the node could not be found (could be null) * * @return the boolean associated with the specified name of the child's node (could be null) */ public Boolean getBoolean(String nodeName, Boolean defaultValue) { Object value = this.valueMap.get(nodeName); if (value != null) { return Boolean.parseBoolean(value.toString()); } return defaultValue; } /** * Returns the numeric value associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * * @return the numeric value associated with the specified name of the child's node * (could be null) * * @throws ConversionException if the value could not be converted to a numeric value */ public Integer getInteger(String nodeName) throws ConversionException { return getInteger(nodeName, null); } /** * Returns the numeric value associated with the specified name of the child's node. * * @param nodeName the name of the child's node (must not be null) * @param defaultValue the numeric value to be returned if the node could not be found * (could be null) * * @return the numeric value associated with the specified name of the child's node * (could be null) * * @throws ConversionException if the value could not be converted to a numeric value */ public Integer getInteger(String nodeName, Integer defaultValue) throws ConversionException { Object value = this.valueMap.get(nodeName); if (value != null) { try { return Integer.parseInt(value.toString()); } catch (NumberFormatException nfe) { throw new ConversionException("The value '" + value + "' cannot be converted to a numeric value!", nfe); } } return defaultValue; } }