All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ff4j.conf.XmlParser Maven / Gradle / Ivy

package org.ff4j.conf;

/*
 * #%L
 * ff4j-core
 * %%
 * Copyright (C) 2013 Ff4J
 * %%
 * 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.
 * #L%
 */

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set; 
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.ff4j.core.Feature;
import org.ff4j.core.FlippingStrategy;
import org.ff4j.property.Property;
import org.ff4j.property.PropertyString;
import org.ff4j.utils.MappingUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

/**
 * Allow to parse XML files to load {@link Feature}.
 * 
 * @author Cedrick Lunven (@clunven)
 */
public final class XmlParser {

    /** TAG XML. */
    public static final String FEATURES_TAG = "features";

    /** TAG XML. */
    public static final String FEATURE_TAG = "feature";

    /** TAG XML. */
    public static final String FEATURE_ATT_UID = "uid";

    /** TAG XML. */
    public static final String FEATURE_ATT_DESC = "description";

    /** TAG XML. */
    public static final String FEATURE_ATT_ENABLE = "enable";

    /** TAG XML. */
    public static final String FEATUREGROUP_TAG = "feature-group";

    /** TAG XML. */
    public static final String FEATUREGROUP_ATTNAME = "name";

    /** TAG XML. */
    public static final String FLIPSTRATEGY_TAG = "flipstrategy";
    
    /** TAG XML. */
    public static final String PROPERTIES_TAG = "properties";
    
    /** TAG XML. */
    public static final String PROPERTIES_CUSTOM_TAG = "custom-properties";
    
    /** TAG XML. */
    public static final String PROPERTY_TAG = "property";
    
    /** TAG XML. */
    public static final String PROPERTY_PARAMTYPE = "type";
    
    /** TAG XML. */
    public static final String PROPERTY_PARAMNAME = "name";
    
    /** TAG XML. */
    public static final String PROPERTY_PARAMDESCRIPTION = "description";

    /** TAG XML. */
    public static final String PROPERTY_PARAMVALUE = "value";
    
    /** TAG XML. */
    public static final String PROPERTY_PARAMFIXED_VALUES = "fixedValues";
    
    /** TAG XML. */
    public static final String FLIPSTRATEGY_ATTCLASS = "class";

    /** TAG XML. */
    public static final String FLIPSTRATEGY_PARAMTAG = "param";

    /** TAG XML. */
    public static final String FLIPSTRATEGY_PARAMNAME = "name";

    /** TAG XML. */
    public static final String FLIPSTRATEGY_PARAMVALUE = "value";

    /** TAG XML. */
    public static final String SECURITY_TAG = "security";

    /** TAG XML. */
    public static final String SECURITY_ROLE_TAG = "role";

    /** TAG XML. */
    public static final String SECURITY_ROLE_ATTNAME = "name";

    /** TAG XML. */
    public static final String CDATA_START = "";

    /** XML Generation constants. */
    private static final String ENCODING = "UTF-8";

    /** XML Generation constants. */
    private static final String XML_HEADER = 
            "\n"//
            + ""
            + ">\n\n";

    /** XML Generation constants. */
    private static final String XML_FEATURE = "  \n";

    /** XML Generation constants. */
    private static final String XML_AUTH = "      \n";

    /** XML Generation constants. */
    private static final String END_FEATURE = "  \n\n";

    /** XML Generation constants. */
    private static final String BEGIN_FEATURES = " \n\n";
    
    /** XML Generation constants. */
    private static final String END_FEATURES = " \n\n";
    
    /** XML Generation constants. */
    private static final String BEGIN_PROPERTIES = " \n\n";
    
    /** XML Generation constants. */
    private static final String BEGIN_CUSTOMPROPERTIES = "   \n";
    
    /** XML Generation constants. */
    private static final String END_CUSTOMPROPERTIES = "   \n";
    
    /** XML Generation constants. */
    private static final String END_PROPERTIES = " \n\n";
    
    /** XML Generation constants. */
    private static final String END_FF4J = "\n\n";
    public static final String ERROR_SYNTAX_IN_CONFIGURATION_FILE = "Error syntax in configuration file : ";

    /** Document Builder use to parse XML. */
    private static DocumentBuilder builder = null;
   
    /**
     * Parsing of XML Configuration file.
     *
     * @param file
     *      target file
     * @return
     *      features and properties find within file
     */
    public XmlConfig parseConfigurationFile(InputStream in) {
        try {
            
            // Object to be build by parsing
            XmlConfig xmlConf = new XmlConfig();
                
            // Load XML as a Document
            Document ff4jDocument = getDocumentBuilder().parse(in);
            
            // Features Tag
            NodeList fList = ff4jDocument.getElementsByTagName(FEATURES_TAG);
            if (fList.getLength() > 1) {
                throw new IllegalArgumentException("Root Tag is 'features' and must be unique, please check");
            } else if (fList.getLength() == 1) {
                xmlConf.setFeatures(parseFeaturesTag( (Element) fList.item(0)));
            }
            
            // Properties Tag
            NodeList pList = ff4jDocument.getElementsByTagName(PROPERTIES_TAG);
            if (pList.getLength() > 1) {
                throw new IllegalArgumentException("Root Tag is 'properties' and must be unique, please check");
            } else if (pList.getLength() == 1) {
               xmlConf.setProperties(parsePropertiesTag((Element) pList.item(0)));
            }
            
            return xmlConf;
            
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot parse XML data, please check file access ", e);
        }
    }
    
    /**
     * Load map of {@link Feature} from an inpustream (containing xml text).
     * 
     * @param in
     *            inpustream with XML text
     * @return the sorted map of features
     * @throws IOException
     *             exception raised when reading inputstream
     */
    public Map parseFeaturesTag(Element featuresTag) {
        Map xmlFeatures = new LinkedHashMap();
        NodeList firstLevelNodes = featuresTag.getChildNodes();
        for (int i = 0; i < firstLevelNodes.getLength(); i++) {
            if (firstLevelNodes.item(i) instanceof Element) {
                Element currentCore = (Element) firstLevelNodes.item(i);
                if (FEATURE_TAG.equals(currentCore.getNodeName())) {
                    Feature singleFeature = parseFeatureTag(currentCore);
                    xmlFeatures.put(singleFeature.getUid(), singleFeature);
                } else if (FEATUREGROUP_TAG.equals(currentCore.getNodeName())) {
                    xmlFeatures.putAll(parseFeatureGroupTag(currentCore));
                } else {
                    throw new IllegalArgumentException("Invalid XML Format, Features sub nodes are [feature,feature-group]");
                }
            }
        }
        return xmlFeatures;
    }

    /**
     * Parse TAG <feature-group>.
     * 
     * @param featGroupTag
     *            feature group tag
     * @return map of features
     */
    private Map parseFeatureGroupTag(Element featGroupTag) {
        NamedNodeMap nnm = featGroupTag.getAttributes();
        String groupName;
        if (nnm.getNamedItem(FEATUREGROUP_ATTNAME) == null) {
            throw new IllegalArgumentException("Error syntax in configuration featuregroup : must have 'name' attribute");
        }
        groupName = nnm.getNamedItem(FEATUREGROUP_ATTNAME).getNodeValue();

        Map groupFeatures = new HashMap();
        NodeList listOfFeat = featGroupTag.getElementsByTagName(FEATURE_TAG);
        for (int k = 0; k < listOfFeat.getLength(); k++) {
            Feature f = parseFeatureTag((Element) listOfFeat.item(k));
            // Insert feature into group
            f.setGroup(groupName);
            groupFeatures.put(f.getUid(), f);
        }
        return groupFeatures;
    }

    /**
     * Build a Feature from XML TAG.
     * 
     * @param featXmlTag
     *            xml tag to nuild feature
     * @return current feature
     */
    private Feature parseFeatureTag(Element featXmlTag) {
        NamedNodeMap nnm = featXmlTag.getAttributes();
        // Identifier
        String uid;
        if (nnm.getNamedItem(FEATURE_ATT_UID) == null) {
            throw new IllegalArgumentException(ERROR_SYNTAX_IN_CONFIGURATION_FILE + "'uid' is required for each feature");
        }
        uid = nnm.getNamedItem(FEATURE_ATT_UID).getNodeValue();
        // Enable
        if (nnm.getNamedItem(FEATURE_ATT_ENABLE) == null) {
            throw new IllegalArgumentException(ERROR_SYNTAX_IN_CONFIGURATION_FILE
                    + "'enable' is required for each feature (check " + uid + ")");
        }
        boolean enable = Boolean.parseBoolean(nnm.getNamedItem(FEATURE_ATT_ENABLE).getNodeValue());

        // Create Feature with description
        Feature f = new Feature(uid, enable, parseDescription(nnm));
        
        // Strategy
        NodeList flipStrategies = featXmlTag.getElementsByTagName(FLIPSTRATEGY_TAG);
        if (flipStrategies.getLength() > 0) {
            f.setFlippingStrategy(parseFlipStrategy((Element) flipStrategies.item(0), f.getUid()));
        }
        
        // Security
        NodeList securities = featXmlTag.getElementsByTagName(SECURITY_TAG);
        if (securities.getLength() > 0) {
            f.setPermissions(parseListAuthorizations((Element) securities.item(0)));
        }
        
        // Properties
        NodeList properties = featXmlTag.getElementsByTagName(PROPERTIES_CUSTOM_TAG);
        if (properties.getLength() > 0) {
            f.setCustomProperties(parsePropertiesTag((Element) properties.item(0)));
        }

        return f;
    }
    
    /**
     * Parse Properties.
     *
     * @param properties tag
     * @param uid
     *      current featureid
     * @return
     *      properties map
     */
    private Map < String , Property> parsePropertiesTag(Element propertiesTag) {
        Map< String , Property> properties = new HashMap>(); 
        // 
        NodeList lisOfProperties = propertiesTag.getElementsByTagName(PROPERTY_TAG);
        for (int k = 0; k < lisOfProperties.getLength(); k++) {
            // 
            Element propertyTag = (Element) lisOfProperties.item(k);
            NamedNodeMap attMap = propertyTag.getAttributes();
            if (attMap.getNamedItem(PROPERTY_PARAMNAME) == null) {
                throw new IllegalArgumentException("Invalid XML Syntax, 'name' is a required attribute of 'property' TAG");
            }
            if (attMap.getNamedItem(PROPERTY_PARAMVALUE) == null) {
                throw new IllegalArgumentException("Invalid XML Syntax, 'value' is a required attribute of 'property' TAG");
            }
            String name  = attMap.getNamedItem(PROPERTY_PARAMNAME).getNodeValue();
            String value = attMap.getNamedItem(PROPERTY_PARAMVALUE).getNodeValue();
            Property ap = new PropertyString(name, value);
            
            // If specific type defined ?
            if (null != attMap.getNamedItem(PROPERTY_PARAMTYPE)) {
                String optionalType = attMap.getNamedItem(PROPERTY_PARAMTYPE).getNodeValue();
               
                // Substitution if relevant (e.g. 'int' -> 'org.ff4j.property.PropertyInt')
                optionalType = MappingUtil.mapPropertyType(optionalType);
                
                try {
                    // Constructor (String, String) is mandatory in Property interface
                    Constructor constr = Class.forName(optionalType).getConstructor(String.class, String.class);
                    ap = (Property) constr.newInstance(name, value);
                } catch (Exception e) {
                    throw new IllegalArgumentException("Cannot instantiate '" + optionalType + "' check default constructor", e);
                }
            }
            
            if (null != attMap.getNamedItem(PROPERTY_PARAMDESCRIPTION)) {
                ap.setDescription(attMap.getNamedItem(PROPERTY_PARAMDESCRIPTION).getNodeValue());
            }
            
            // Is there any fixed Value ?
            NodeList listOfFixedValue = propertyTag.getElementsByTagName(PROPERTY_PARAMFIXED_VALUES);
            if (listOfFixedValue.getLength() != 0) {
                Element fixedValueTag = (Element) listOfFixedValue.item(0);
                NodeList listOfValues =  fixedValueTag.getElementsByTagName(PROPERTY_PARAMVALUE);
                for (int l = 0; l < listOfValues.getLength(); l++) {
                    Element valueTag = (Element) listOfValues.item(l);
                    ap.add2FixedValueFromString(valueTag.getTextContent());
                }
            }
            
            // Check fixed value
            if (ap.getFixedValues() != null && !ap.getFixedValues().contains(ap.getValue())) {
                throw new IllegalArgumentException("Cannot create property <" + ap.getName() + 
                        "> invalid value <" + ap.getValue() + 
                        "> expected one of " + ap.getFixedValues());
            }
            
            properties.put(name, ap);
        }
        return properties;
    }

    /**
     * Parsing strategy TAG.
     * 
     * @param nnm
     *            current parend node
     * @param uid
     *            current feature uid
     * @return flipstrategy related to current feature.
     */
    private FlippingStrategy parseFlipStrategy(Element flipStrategyTag, String uid) {
        NamedNodeMap nnm = flipStrategyTag.getAttributes();
        FlippingStrategy flipStrategy;
        if (nnm.getNamedItem(FLIPSTRATEGY_ATTCLASS) == null) {
            throw new IllegalArgumentException("Error syntax in configuration file : '" + FLIPSTRATEGY_ATTCLASS
                    + "' is required for each flipstrategy (feature=" + uid + ")");
        }

        try {
            // Attribute CLASS
            String clazzName = nnm.getNamedItem(FLIPSTRATEGY_ATTCLASS).getNodeValue();
            flipStrategy = (FlippingStrategy) Class.forName(clazzName).newInstance();

            // LIST OF PARAMS
            Map parameters = new LinkedHashMap();
            NodeList initparamsNodes = flipStrategyTag.getElementsByTagName(FLIPSTRATEGY_PARAMTAG);
            for (int k = 0; k < initparamsNodes.getLength(); k++) {
                Element param = (Element) initparamsNodes.item(k);
                NamedNodeMap nnmap = param.getAttributes();
                // Check for required attribute name
                String currentParamName;
                if (nnmap.getNamedItem(FLIPSTRATEGY_PARAMNAME) == null) {
                    throw new IllegalArgumentException(ERROR_SYNTAX_IN_CONFIGURATION_FILE
                            + "'name' is required for each param in flipstrategy(check " + uid + ")");
                }
                currentParamName = nnmap.getNamedItem(FLIPSTRATEGY_PARAMNAME).getNodeValue();
                // Check for value attribute
                if (nnmap.getNamedItem(FLIPSTRATEGY_PARAMVALUE) != null) {
                    parameters.put(currentParamName, nnmap.getNamedItem(FLIPSTRATEGY_PARAMVALUE).getNodeValue());
                } else if (param.getFirstChild() != null) {
                    parameters.put(currentParamName, param.getFirstChild().getNodeValue());
                } else {
                    throw new IllegalArgumentException("Parameter '" + currentParamName + "' in feature '" + uid
                            + "' has no value, please check XML");
                }
            }

            flipStrategy.init(uid, parameters);
        } catch (Exception e) {
            throw new IllegalArgumentException("An error occurs during flipstrategy parsing TAG" + uid, e);
        }
        return flipStrategy;
    }

    /**
     * Parser target description.
     * 
     * @param nnm
     *            current working tag
     * @return description of the feature
     */
    private static String parseDescription(NamedNodeMap nnm) {
        String desc = null;
        if (nnm.getNamedItem(FEATURE_ATT_DESC) != null) {
            desc = nnm.getNamedItem(FEATURE_ATT_DESC).getNodeValue();
        }
        return desc;
    }

    /**
     * Parsing autorization tag.
     * 
     * @param featXmlTag
     *            current TAG
     * @return list of authorizations.
     */
    private static Set parseListAuthorizations(Element securityTag) {
        Set authorizations = new TreeSet();
        NodeList lisOfAuth = securityTag.getElementsByTagName(SECURITY_ROLE_TAG);
        for (int k = 0; k < lisOfAuth.getLength(); k++) {
            Element role = (Element) lisOfAuth.item(k);
            authorizations.add(role.getAttributes().getNamedItem(SECURITY_ROLE_ATTNAME).getNodeValue());
        }
        return authorizations;
    }

    /**
     * Build {@link DocumentBuilder} to parse XML.
     * 
     * @return current document builder.
     * @throws ParserConfigurationException
     *             error during initialization
     */
    public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
        if (builder == null) {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            // -- Prevent against XXE @see https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            // If you can't completely disable DTDs, then at least do the following:
            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
            // JDK7+ - http://xml.org/sax/features/external-general-entities    
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
            // JDK7+ - http://xml.org/sax/features/external-parameter-entities    
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            // Disable external DTDs as well
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" (see reference below)
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);
            builder = dbf.newDocumentBuilder();
            builder.setErrorHandler(new XmlParserErrorHandler());
        }
        return builder;
    }

    /**
     * Create XML output stream from a map of {@link Feature}.
     * 
     * @param mapOfFeatures
     *            map of features
     * @return streams
     * @throws IOException
     *             error occurs when generating output
     */
    public InputStream exportFeatures(Map mapOfFeatures) throws IOException {    
        return new ByteArrayInputStream(exportFeaturesPart(mapOfFeatures).getBytes(ENCODING));
    }
    
    /**
     * Create XML output stream from a map of {@link PropertyString}.
     * 
     * @param mapOfProperties
     *            map of properties
     * @return streams
     * @throws IOException
     *             error occurs when generating output
     */
    public InputStream exportProperties(Map < String, Property> mapOfProperties) throws IOException {   
        return new ByteArrayInputStream(exportPropertiesPart(mapOfProperties).getBytes(ENCODING));
    }
    
    /**
     * Create XML output stream with both {@link Feature} and {@link PropertyString}.
     * 
     * @param f
     *            map of features
     * @return streams
     * @throws IOException
     *             error occurs when generating output
     */
    public InputStream exportAll(Map mapOfFeatures, Map < String, Property> mapOfProperties) throws IOException {   
        // Create output
        StringBuilder sb = new StringBuilder(XML_HEADER);
        sb.append(exportFeaturesPart(mapOfFeatures));
        sb.append(exportPropertiesPart(mapOfProperties));
        sb.append(END_FF4J);
        return new ByteArrayInputStream(sb.toString().getBytes(ENCODING));
    }
    
    /**
     * Utility method to export from configuration.
     *
     * @param conf
     *      target configuration
     * @return
     *      target stream
     * @throws IOException
     *      error during marshalling
     */
    public InputStream exportAll(XmlConfig conf) throws IOException {
        return exportAll(conf.getFeatures(), conf.getProperties());
    }
    
    
    
    /**
     * Create dedicated output for Properties.
     *
     * @param mapOfProperties
     *      target properties
     * @return
     *      XML Flow     
     */
    private String exportPropertiesPart(Map < String, Property> mapOfProperties) {
        // Create 
        StringBuilder sb = new StringBuilder(BEGIN_PROPERTIES);
        if (mapOfProperties != null && !mapOfProperties.isEmpty()) {
            sb.append(buildPropertiesPart(mapOfProperties));
        }
        sb.append(END_PROPERTIES);
        return sb.toString();
    }
    
    /**
     * Export Features part of the XML.
     *
     * @param mapOfFeatures
     *      current map of feaures.
     * 
     * @return
     *      all XML
     */
    private String exportFeaturesPart(Map mapOfFeatures) {
        // Create 
        StringBuilder sb = new StringBuilder(BEGIN_FEATURES);
        
        // Recreate Groups
        Map> featuresPerGroup = new HashMap>();
        if (mapOfFeatures != null && !mapOfFeatures.isEmpty()) {
            for (Feature feat : mapOfFeatures.values()) {
                String groupName = feat.getGroup();
                if (!featuresPerGroup.containsKey(groupName)) {
                    featuresPerGroup.put(groupName, new ArrayList());
                }
                featuresPerGroup.get(groupName).add(feat);
            }
        }
            
        for (Map.Entry> groupName : featuresPerGroup.entrySet()) {
            /// Building featureGroup
            if (null != groupName.getKey() && !groupName.getKey().isEmpty()) {
                sb.append(" <" + FEATUREGROUP_TAG + " " + FEATUREGROUP_ATTNAME + "=\"" + groupName.getKey() + "\" >\n\n");
            }
            // Loop on feature
            for (Feature feat : groupName.getValue()) {
                sb.append(MessageFormat.format(XML_FEATURE, feat.getUid(), feat.getDescription(), feat.isEnable()));
                // 
                if (null != feat.getPermissions() && !feat.getPermissions().isEmpty()) {
                    sb.append("   <" + SECURITY_TAG + ">\n");
                    for (String auth : feat.getPermissions()) {
                        sb.append(MessageFormat.format(XML_AUTH, auth));
                    }
                    sb.append("   \n");
                }
                // 
                FlippingStrategy fs = feat.getFlippingStrategy();
                if (null != fs) {
                    sb.append("   <" + FLIPSTRATEGY_TAG + " class=\"" + fs.getClass().getName() + "\" >\n");
                    for (String p : fs.getInitParams().keySet()) {
                        sb.append("     <" + FLIPSTRATEGY_PARAMTAG + " " + FLIPSTRATEGY_PARAMNAME + "=\"");
                        sb.append(p);
                        sb.append("\" " + FLIPSTRATEGY_PARAMVALUE + "=\"");
                        // Escape special characters to build XML
                        // https://github.com/clun/ff4j/issues/63
                        String paramValue = fs.getInitParams().get(p);
                        sb.append(escapeXML(paramValue));
                        sb.append("\" />\n");
                    }
                    sb.append("   \n");
                }
                // 
                Map < String, Property> props = feat.getCustomProperties();
                if (props != null && !props.isEmpty()) {
                    sb.append(BEGIN_CUSTOMPROPERTIES);
                    sb.append(buildPropertiesPart(feat.getCustomProperties()));
                    sb.append(END_CUSTOMPROPERTIES);
                }
                sb.append(END_FEATURE);
            }
            
            if (null != groupName.getKey() && !groupName.getKey().isEmpty()) {
                sb.append(" \n\n");
            }
        }
        sb.append(END_FEATURES);
        return sb.toString();
    }
    
    /**
     * Create XML content of the properties or custom properties elements.
     *
     * @param props
     *      properties elements.
     * @return
     */
    private String buildPropertiesPart(Map < String, Property> props) {
        StringBuilder sb = new StringBuilder();
        if (props != null && !props.isEmpty()) {
            // Loop over property
            for (Property property : props.values()) {
                sb.append("    <" + PROPERTY_TAG + " " + PROPERTY_PARAMNAME + "=\"" + property.getName() + "\" ");
                sb.append(PROPERTY_PARAMVALUE + "=\"" + property.asString() + "\" ");
                if (!(property instanceof PropertyString)) {
                    sb.append(PROPERTY_PARAMTYPE  + "=\"" + property.getClass().getName()  + "\"");
                }
                // Processing fixedValue is present
                if (property.getFixedValues() != null && !property.getFixedValues().isEmpty()) {
                    sb.append(">\n");
                    sb.append("     \n");
                    for (Object o : property.getFixedValues()) {
                        sb.append("      " + o.toString() + "\n");
                    }
                    sb.append("     \n");
                    sb.append("    \n");
                } else {
                    sb.append("/>\n");
                }
            }
        }
        return sb.toString();
    }
   
    /**
     * Substitution to create XML.
     *
     * @param value
     *      target XML
     * @return
     */
    public String escapeXML(String value) {
        if (value == null) {
            return null;
        }
        return value.replaceAll("&", "&")
                    .replaceAll(">", ">")
                    .replaceAll("<", "<");
    }
    

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy