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 org.apache.camel.kafkaconnector.maven.utils;
  
  import java.io.ByteArrayInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.StringWriter;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.nio.charset.StandardCharsets;
  import java.nio.file.Files;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeSet;
  import java.util.stream.Stream;
  
  import javax.xml.XMLConstants;
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import javax.xml.transform.OutputKeys;
  import javax.xml.transform.Transformer;
  import javax.xml.transform.TransformerFactory;
  import javax.xml.transform.dom.DOMSource;
  import javax.xml.transform.stream.StreamResult;
  import javax.xml.xpath.XPath;
  import javax.xml.xpath.XPathConstants;
  import javax.xml.xpath.XPathExpression;
  import javax.xml.xpath.XPathExpressionException;
  import javax.xml.xpath.XPathFactory;
  
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import org.xml.sax.SAXException;
  
  import freemarker.cache.URLTemplateLoader;
  import freemarker.template.Configuration;
  import freemarker.template.Template;
  import freemarker.template.TemplateException;
  import org.apache.camel.tooling.util.PackageHelper;
  import org.apache.camel.tooling.util.srcgen.JavaClass;
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.io.IOUtils;
  import org.apache.maven.plugin.MojoFailureException;
  import org.apache.maven.plugin.logging.Log;
  
  import static org.apache.camel.maven.packaging.AbstractGeneratorMojo.updateResource;
  
  public final class MavenUtils {
  
      private MavenUtils() {
  
      }
  
      public static void addExclusionsToDependency(Document pom, String targetDependency, Set<String> colonSeparatedDependeciesToExclude, String generatedSectionStartMarker, String generatedSectionStopMarker) throws XPathExpressionException {
          XPath xpath = XPathFactory.newInstance().newXPath();
          Node dependency = ((NodeList) xpath.compile("/project/dependencies/dependency[artifactId/text() = '" + targetDependency + "']").evaluate(pom, XPathConstants.NODESET)).item(0);
  
          Element exclusions = pom.createElement("exclusions");
  
          dependency.appendChild(pom.createComment(generatedSectionStartMarker));
          dependency.appendChild(exclusions);
          dependency.appendChild(pom.createComment(generatedSectionStopMarker));
  
          for (String dep : colonSeparatedDependeciesToExclude) {
              String[] tokens = dep.split("\\:");
              String groupIdStr = tokens[0];
              String artifactIdStr = tokens[1];
  
              Element exclusion = pom.createElement("exclusion");
  
              Element groupId = pom.createElement("groupId");
              groupId.setTextContent(groupIdStr);
              exclusion.appendChild(groupId);
  
              Element artifactId = pom.createElement("artifactId");
              artifactId.setTextContent(artifactIdStr);
              exclusion.appendChild(artifactId);
  
              exclusions.appendChild(exclusion);
          }
      }
  
      public static String removeGeneratedSections(String pom, String generatedSectionStartMarker, String generatedSectionStopMarker, int maxNumberOfOccurrences) {
          if (maxNumberOfOccurrences > 0 && pom.contains(generatedSectionStartMarker)) {
              int start = pom.indexOf(generatedSectionStartMarker);
              int end = pom.indexOf(generatedSectionStopMarker);
              if (end <= start) {
                  throw new IllegalArgumentException("Generated sections inside the xml document are not well-formed");
              }
  
              String newPom = pom.substring(0, start) + pom.substring(end + generatedSectionStopMarker.length());
              return removeGeneratedSections(newPom, generatedSectionStartMarker, generatedSectionStopMarker, maxNumberOfOccurrences - 1);
          }
  
          return pom;
      }
  
      public static void writeFileIfChanged(String newContent, File file, Log log) throws IOException {
          boolean write = true;
          file = file.getCanonicalFile();
          if (file.exists()) {
              try (FileReader fr = new FileReader(file)) {
                  String oldContent = IOUtils.toString(fr);
                  if (!newContent.equals(oldContent)) {
                      log.debug("Writing new file " + file.getAbsolutePath());
                  } else {
                      log.debug("File " + file.getAbsolutePath() + " has been left unchanged");
                      write = false;
                  }
              }
          } else {
              // Create the structure
              File parent = file.getParentFile();
              parent.mkdirs();
          }
  
          if (write) {
              try (FileWriter fw = new FileWriter(file)) {
                  IOUtils.write(newContent, fw);
              }
          }
      }
  
      public static void writeXmlFormatted(Document pom, File destination, Log log) throws Exception {
          XPathExpression xpath = XPathFactory.newInstance().newXPath().compile("//text()[normalize-space(.) = '']");
          NodeList emptyNodes = (NodeList) xpath.evaluate(pom, XPathConstants.NODESET);
  
          // Remove empty text nodes
          for (int i = 0; i < emptyNodes.getLength(); i++) {
              Node emptyNode = emptyNodes.item(i);
              emptyNode.getParentNode().removeChild(emptyNode);
          }
  
          pom.setXmlStandalone(true);
  
          TransformerFactory transformerFactory = TransformerFactory.newInstance();
          transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
          Transformer transformer = transformerFactory.newTransformer();
          transformer.setOutputProperty(OutputKeys.INDENT, "yes");
          transformer.setOutputProperty(OutputKeys.METHOD, "xml");
          transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
  
          DOMSource source = new DOMSource(pom);
  
          String content;
          try (StringWriter out = new StringWriter()) {
              StreamResult result = new StreamResult(out);
              transformer.transform(source, result);
              content = out.toString();
          }
  
          // Fix header formatting problem
          content = content.replaceFirst("-->", "-->\n").replaceFirst("\\?><!--", "\\?>\n<!--");
  
          writeFileIfChanged(content, destination, log);
      }
  
      public static void addDependencies(Document pom, Set<String> deps, String generatedSectionStartMarker, String generatedSectionStopMarker) throws XPathExpressionException {
          XPath xpath = XPathFactory.newInstance().newXPath();
          Node dependencies = ((NodeList) xpath.compile("/project/dependencies").evaluate(pom, XPathConstants.NODESET)).item(0);
  
          if (!deps.isEmpty()) {
              dependencies.appendChild(pom.createComment(generatedSectionStartMarker));
              for (String dep : deps) {
                  Element dependency = pom.createElement("dependency");
                  dependencies.appendChild(dependency);
  
                  String[] comps = dep.split("\\:");
                  String groupIdStr = comps[0];
                  String artifactIdStr = comps[1];
                  String versionStr = comps.length > 2 && !comps[2].isEmpty() ? comps[2] : null;
                  String scopeStr = comps.length > 3 ? comps[3] : null;
  
                  Element groupId = pom.createElement("groupId");
                  groupId.setTextContent(groupIdStr);
                  dependency.appendChild(groupId);
  
                  Element artifactId = pom.createElement("artifactId");
                  artifactId.setTextContent(artifactIdStr);
                  dependency.appendChild(artifactId);
  
                  if (versionStr != null) {
                      Element version = pom.createElement("version");
                      version.setTextContent(versionStr);
                      dependency.appendChild(version);
                  }
  
                  if (scopeStr != null) {
                      Element scope = pom.createElement("scope");
                      scope.setTextContent(scopeStr);
                      dependency.appendChild(scope);
                  }
  
              }
              dependencies.appendChild(pom.createComment(generatedSectionStopMarker));
          }
      }
  
      public static <T> Stream<T> concat(Stream<T> s1, Stream<T> s2, Stream<T> s3) {
          return Stream.concat(s1, Stream.concat(s2, s3));
      }
  
      public static Document createCrateXmlDocumentFromTemplate(Template template, Map<String, String> props) throws IOException, TemplateException, ParserConfigurationException, SAXException {
          StringWriter sw = new StringWriter();
          template.process(props, sw);
  
          String xml = sw.toString();
          ByteArrayInputStream bin = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
  
          DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
          return builder.parse(bin);
      }
  
      public static Template getTemplate(File templateFile) throws IOException {
          Configuration cfg = new Configuration(Configuration.getVersion());
  
          cfg.setTemplateLoader(new URLTemplateLoader() {
              @Override
              protected URL getURL(String name) {
                  try {
                      return new URL(name);
                  } catch (MalformedURLException e) {
                      e.printStackTrace();
                      return null;
                  }
              }
          });
  
          cfg.setDefaultEncoding("UTF-8");
          cfg.setLocalizedLookup(false);
          Template template = cfg.getTemplate(templateFile.toURI().toURL().toExternalForm());
          return template;
      }
  
      public static Set<String> csvToSet(String csv) {
          if (csv == null || csv.trim().length() == 0) {
              return new TreeSet<>();
          }
  
          Set<String> set = new TreeSet<>();
          for (String s : csv.split(",")) {
              set.add(s.trim());
          }
          return set;
      }
  
      public static void writeSourceIfChanged(JavaClass source, String fileName, boolean innerClassesLast, File baseDir, File javaFileHeader) throws MojoFailureException {
          writeSourceIfChanged(source.printClass(innerClassesLast), fileName, baseDir, javaFileHeader);
      }
  
      public static void writeSourceIfChanged(String source, String fileName, File baseDir, File javaFileHeader) throws MojoFailureException {
          File target = new File(new File(baseDir, "src/main/java"), fileName);
          if (target.exists()) {
              try {
                  if (!FileUtils.readFileToString(target).contains("@Generated")) {
                      // Do not write class if a class already exists and has no @Generated annotation
                      return;
                  }
              } catch (IOException ioe) {
                  throw new MojoFailureException("IO error trying to read whether " + target.toString() + " contains @Generated annotation", ioe);
              }
          }
  
          deleteFile(baseDir, target);
  
          try {
              String header;
              try (InputStream is = new FileInputStream(javaFileHeader)) {
                  header = PackageHelper.loadText(is);
              }
              String code = header + source;
  
              updateResource(null, target.toPath(), code);
          } catch (Exception e) {
              throw new MojoFailureException("IOError with file " + target, e);
          }
      }
  
      public static void deleteFile(File baseDir, File targetFile) {
          String relativePath = baseDir.toPath().relativize(targetFile.toPath()).toString();
          File mainArtifactFile = new File(baseDir, relativePath);
          if (mainArtifactFile.exists()) {
              try {
                  Files.delete(mainArtifactFile.toPath());
              } catch (IOException e) {
                  throw new IllegalStateException("Cannot delete file " + mainArtifactFile, e);
              }
          }
      }
  
      public static String sanitizeMavenArtifactId(String toBesanitizedArtifactId) {
          return toBesanitizedArtifactId != null ? toBesanitizedArtifactId.toLowerCase().replaceAll("[^A-Za-z0-9]", "-") : null;
      }
  }
  