content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you 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.
  package com.cloud.agent.api.storage;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.StringReader;
  import java.util.Map;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  import org.apache.cloudstack.utils.security.ParserUtils;
  import org.apache.commons.lang3.StringUtils;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  
  public class OVFParser {
      protected Logger logger = LogManager.getLogger(getClass());
  
      private static final String DEFAULT_OVF_SCHEMA = "http://schemas.dmtf.org/ovf/envelope/1";
      private static final String VMW_SCHEMA = "http://www.vmware.com/schema/ovf";
  
      private static final Map<String, String> ATTRIBUTE_SCHEMA_MAP = Map.of(
              "osType", VMW_SCHEMA
      );
  
      private DocumentBuilder documentBuilder;
  
      public OVFParser() {
          try {
              DocumentBuilderFactory documentBuilderFactory = ParserUtils.getSaferDocumentBuilderFactory();
              documentBuilderFactory.setNamespaceAware(true);
              documentBuilder = documentBuilderFactory.newDocumentBuilder();
          } catch (ParserConfigurationException e) {
              logger.error("Cannot start the OVF parser: " + e.getMessage(), e);
          }
      }
  
      public Document parseOVF(String ovfString) throws IOException, SAXException {
          InputSource is = new InputSource(new StringReader(ovfString));
          return documentBuilder.parse(is);
      }
  
      public Document parseOVFFile(String ovfFilePath) {
          if (StringUtils.isBlank(ovfFilePath)) {
              return null;
          }
          try {
              return documentBuilder.parse(new File(ovfFilePath));
          } catch (SAXException | IOException e) {
              logger.error("Error parsing " + ovfFilePath + " " + e.getMessage(), e);
              return null;
          }
      }
  
      /**
       * Retrieve elements with tag name from the document, according to the OVF schema definition
       */
      public NodeList getElementsFromOVFDocument(Document doc, String tagName) {
          return doc != null ? doc.getElementsByTagNameNS(DEFAULT_OVF_SCHEMA, tagName) : null;
      }
  
      /**
       * Retrieve an attribute value from an OVF element
       */
      public String getNodeAttribute(Element element, String attr) {
          return element != null ? element.getAttributeNS(ATTRIBUTE_SCHEMA_MAP.getOrDefault(attr, DEFAULT_OVF_SCHEMA), attr) : null;
      }
  
      /**
       * Get the text value of a node's child with name or suffix "childNodeName", null if not present
       * Example:
       * <Node>
       *    <childNodeName>Text value</childNodeName>
       *    <rasd:childNodeName>Text value</rasd:childNodeName>
       * </Node>
       */
      public String getChildNodeValue(Node node, String childNodeName) {
          if (node == null || !node.hasChildNodes()) {
              return null;
          }
          NodeList childNodes = node.getChildNodes();
          for (int i = 0; i < childNodes.getLength(); i++) {
              Node value = childNodes.item(i);
              // Also match if the child's name has a suffix:
              // Example: <rasd:AllocationUnits>
              if (value != null && (value.getNodeName().equals(childNodeName) || value.getNodeName().endsWith(":" + childNodeName))) {
                  return value.getTextContent();
              }
          }
          return null;
      }
  }
  