/** * 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.Map; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; /** * The {@link ConverterAttributeMapValidator} class reads any attributes of a node, validates * if they appear or not, and returns the validated key-value pair map. * * @author Michael Grammling - Initial Contribution */ public class ConverterAttributeMapValidator { private Map<String, Boolean> validationMaskTemplate; /** * Creates a new instance of this class with the specified parameter. * <p> * The validation mask template is a two-dimensional key-required ({@code String, [true|false]}) list, defining all * attributes the node could have and which are required and which not. The structure of the array is the following: * <br> * <code><pre> * String[] validationMaskTemplate = new String[][] { * { "uri", "false" }, * { "attribute", "true" }}; * </pre></code> * <p> * If the validation mask template is {@code null} the validation is skipped. If it's empty, no attributes are * allowed for this node. * * @param validationMaskTemplate the two-dimensional key-required list (could be null or empty) */ public ConverterAttributeMapValidator(String[][] validationMaskTemplate) { if (validationMaskTemplate != null) { this.validationMaskTemplate = new HashMap<>(validationMaskTemplate.length); for (String[] validationProperty : validationMaskTemplate) { this.validationMaskTemplate.put(validationProperty[0], Boolean.parseBoolean(validationProperty[1])); } } } /** * Creates a new instance of this class with the specified parameter. * <p> * The validation mask template is a key-required ({@code String, [true|false]}) map, defining all attributes the * node could have, and which are required and which not. * <p> * If the validation mask template is {@code null} the validation is skipped. If it's empty, no attributes are * allowed for this node. * * @param validationMaskTemplate the key-required map (could be null or empty) */ public ConverterAttributeMapValidator(Map<String, Boolean> validationMaskTemplate) { this.validationMaskTemplate = validationMaskTemplate; } /** * Reads, validates and returns all attributes of a node associated with the specified * reader as key-value map. * * @param reader the reader to be used to read-in all attributes of the node (must not be null) * @return the key-value map (not null, could be empty) * @throws ConversionException if the validation check fails */ public Map<String, String> readValidatedAttributes(HierarchicalStreamReader reader) throws ConversionException { return readValidatedAttributes(reader, this.validationMaskTemplate); } /** * Reads, validates and returns all attributes of a node associated with the specified * reader as key-value map. * <p> * The validation mask template is a key-required ({@code String, [true|false]}) map, defining all attributes the * node could have, and which are required and which not. * * @param reader the reader to be used to read-in all attributes of the node (must not be null) * @param validationMaskTemplate the key-required map (could be null or empty) * * @return the key-value map (not null, could be empty) * * @throws ConversionException if the validation check fails */ public static Map<String, String> readValidatedAttributes(HierarchicalStreamReader reader, Map<String, Boolean> validationMaskTemplate) throws ConversionException { Map<String, String> attributeMap = new HashMap<>(reader.getAttributeCount()); Map<String, Boolean> validationMask = null; if (validationMaskTemplate != null) { // create a new one, because entries are removed during validation validationMask = new HashMap<>(validationMaskTemplate); } for (int index = 0; index < reader.getAttributeCount(); index++) { String attributeName = reader.getAttributeName(index); if ((validationMask == null) || validationMask.containsKey(attributeName)) { attributeMap.put(attributeName, reader.getAttribute(index)); if (validationMask != null) { validationMask.remove(attributeName); // no duplicates are allowed } } else { throw new ConversionException("The attribute '" + attributeName + "' of the node '" + reader.getNodeName() + "' is not supported or exists multiple times!"); } } // there are still attributes in the validation mask left -> check if they are required if ((validationMask != null) && (validationMask.size() > 0)) { for (Map.Entry<String, Boolean> entry : validationMask.entrySet()) { String attributeName = entry.getKey(); boolean attributeRequired = entry.getValue(); if (attributeRequired) { throw new ConversionException("The attribute '" + attributeName + "' of the node '" + reader.getNodeName() + "' is missing!"); } } } return attributeMap; } }