This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new f24266eb64 [MNG-7836] Support alternative syntaxes for POMs (#1197)
f24266eb64 is described below

commit f24266eb64fbb4f23655fbc7d76dbf4be61045c2
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Fri Sep 22 09:25:10 2023 +0200

    [MNG-7836] Support alternative syntaxes for POMs (#1197)
    
    The IT associated with this PR is using the Maven model to generate a hocon 
POM parser.  This requires the maven-api-model module to attach the POM as an 
artifact, and the maven.yml change so that the model is present in the local 
repository.
---
 .github/workflows/maven.yml                        | 16 ++---
 api/maven-api-model/pom.xml                        | 20 ++++++
 .../java/org/apache/maven/api/spi/ModelParser.java | 71 ++++++++++++++++++++++
 .../apache/maven/api/spi/ModelParserException.java | 66 ++++++++++++++++++++
 .../model/building/DefaultModelBuilderFactory.java |  7 ++-
 .../model/building/DefaultModelProcessor.java      | 59 ++++++++++++++++--
 6 files changed, 226 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index ce78f7b5a7..26492a05c1 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -106,12 +106,6 @@ jobs:
           ref: ${{ env.REPO_BRANCH }}
           persist-credentials: false
 
-      - name: Download built Maven
-        uses: actions/download-artifact@v3
-        with:
-          name: built-maven
-          path: built-maven/
-
       - name: Set up JDK
         uses: actions/setup-java@v3
         with:
@@ -119,6 +113,14 @@ jobs:
           distribution: 'temurin'
 #          cache: 'maven' - don't use cache for integration tests
 
+      - uses: actions/checkout@v3
+        with:
+          path: maven/
+          persist-credentials: false
+
+      - name: Build Maven
+        run: mvn install -e -B -V -DdistributionFileName=apache-maven 
-DskipTests -f maven/pom.xml
+
       - name: Running integration tests
         shell: bash
-        run: mvn install -e -B -V -Prun-its,embedded 
-DmavenDistro="$GITHUB_WORKSPACE/built-maven/apache-maven-bin.zip" -f 
maven-integration-testing/pom.xml
+        run: mvn install -e -B -V -Prun-its,embedded 
-DmavenDistro="$GITHUB_WORKSPACE/maven/apache-maven/target/apache-maven-bin.zip"
 -f maven-integration-testing/pom.xml
diff --git a/api/maven-api-model/pom.xml b/api/maven-api-model/pom.xml
index 8c989d2988..47e5e2758a 100644
--- a/api/maven-api-model/pom.xml
+++ b/api/maven-api-model/pom.xml
@@ -76,6 +76,26 @@ under the License.
           </excludes>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>3.4.0</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${basedir}/src/main/mdo/maven.mdo</file>
+                  <type>mdo</type>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
diff --git 
a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java 
b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java
new file mode 100644
index 0000000000..68d8097ba1
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParser.java
@@ -0,0 +1,71 @@
+/*
+ * 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.maven.api.spi;
+
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.services.Source;
+
+/**
+ * The {@code ModelParser} interface is used to locate and read {@link Model}s 
from the file system.
+ * This allows plugging in additional syntaxes for the main model read by 
Maven when building a project.
+ */
+@Experimental
+public interface ModelParser {
+
+    /**
+     * Locates the pom in the given directory.
+     *
+     * @param dir the directory to locate the pom for, never {@code null}
+     * @return a {@code Source} pointing to the located pom or an empty {@code 
Optional} if none was found by this parser
+     */
+    @Nonnull
+    Optional<Source> locate(@Nonnull Path dir);
+
+    /**
+     * Parse the model obtained previously by a previous call to {@link 
#locate(Path)}.
+     *
+     * @param source the source to parse, never {@code null}
+     * @param options possible parsing options, may be {@code null}
+     * @return the parsed {@link Model}, never {@code null}
+     * @throws ModelParserException if the model cannot be parsed
+     */
+    @Nonnull
+    Model parse(@Nonnull Source source, @Nullable Map<String, ?> options) 
throws ModelParserException;
+
+    /**
+     * Locate and parse the model in the specified directory.
+     * This is equivalent to {@code locate(dir).map(s -> parse(s, options))}.
+     *
+     * @param dir the directory to locate the pom for, never {@code null}
+     * @param options possible parsing options, may be {@code null}
+     * @return an optional parsed {@link Model} or {@code null} if none could 
be found
+     * @throws ModelParserException if the located model cannot be parsed
+     */
+    default Optional<Model> locateAndParse(@Nonnull Path dir, @Nullable 
Map<String, ?> options)
+            throws ModelParserException {
+        return locate(dir).map(s -> parse(s, options));
+    }
+}
diff --git 
a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java
 
b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java
new file mode 100644
index 0000000000..4520f0650b
--- /dev/null
+++ 
b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelParserException.java
@@ -0,0 +1,66 @@
+/*
+ * 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.maven.api.spi;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.services.MavenException;
+
+@Experimental
+public class ModelParserException extends MavenException {
+
+    /**
+     * The one-based index of the line containing the error.
+     */
+    private final int lineNumber;
+
+    /**
+     * The one-based index of the column containing the error.
+     */
+    private final int columnNumber;
+
+    public ModelParserException() {
+        this(null, null);
+    }
+
+    public ModelParserException(String message) {
+        this(message, null);
+    }
+
+    public ModelParserException(String message, Throwable cause) {
+        this(message, -1, -1, cause);
+    }
+
+    public ModelParserException(String message, int lineNumber, int 
columnNumber, Throwable cause) {
+        super(message, cause);
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+    }
+
+    public ModelParserException(Throwable cause) {
+        this(null, cause);
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public int getColumnNumber() {
+        return columnNumber;
+    }
+}
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
index a4bdaaab57..dc474cef22 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
@@ -20,6 +20,7 @@ package org.apache.maven.model.building;
 
 import java.util.Arrays;
 
+import org.apache.maven.api.spi.ModelParser;
 import org.apache.maven.model.Model;
 import org.apache.maven.model.composition.DefaultDependencyManagementImporter;
 import org.apache.maven.model.composition.DependencyManagementImporter;
@@ -215,7 +216,11 @@ public class DefaultModelBuilderFactory {
     }
 
     protected ModelProcessor newModelProcessor() {
-        return new DefaultModelProcessor(newModelLocator(), newModelReader());
+        return new DefaultModelProcessor(Arrays.asList(newModelParsers()), 
newModelLocator(), newModelReader());
+    }
+
+    protected ModelParser[] newModelParsers() {
+        return new ModelParser[0];
     }
 
     protected ModelLocator newModelLocator() {
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
index 0b4935318a..b3c459c0d0 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProcessor.java
@@ -27,10 +27,18 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.spi.ModelParser;
+import org.apache.maven.api.spi.ModelParserException;
 import org.apache.maven.building.Source;
+import org.apache.maven.model.io.ModelParseException;
 import org.apache.maven.model.io.ModelReader;
 import org.apache.maven.model.locator.ModelLocator;
 import org.eclipse.sisu.Typed;
@@ -65,11 +73,14 @@ import org.eclipse.sisu.Typed;
 @Typed(ModelProcessor.class)
 public class DefaultModelProcessor implements ModelProcessor {
 
+    private final Collection<ModelParser> modelParsers;
     private final ModelLocator modelLocator;
     private final ModelReader modelReader;
 
     @Inject
-    public DefaultModelProcessor(ModelLocator modelLocator, ModelReader 
modelReader) {
+    public DefaultModelProcessor(
+            Collection<ModelParser> modelParsers, ModelLocator modelLocator, 
ModelReader modelReader) {
+        this.modelParsers = modelParsers;
         this.modelLocator = modelLocator;
         this.modelReader = modelReader;
     }
@@ -81,7 +92,15 @@ public class DefaultModelProcessor implements ModelProcessor 
{
 
     public Path locatePom(Path projectDirectory) {
         // Note that the ModelProcessor#locatePom never returns null
-        Path pom = modelLocator.locatePom(projectDirectory.toFile()).toPath();
+        // while the ModelParser#locatePom needs to return an existing path!
+        Path pom = modelParsers.stream()
+                .map(m -> m.locate(projectDirectory)
+                        .map(org.apache.maven.api.services.Source::getPath)
+                        .orElse(null))
+                .filter(Objects::nonNull)
+                .findFirst()
+                .orElseGet(
+                        () -> 
modelLocator.locatePom(projectDirectory.toFile()).toPath());
         if (!pom.equals(projectDirectory) && 
!pom.getParent().equals(projectDirectory)) {
             throw new IllegalArgumentException("The POM found does not belong 
to the given directory: " + pom);
         }
@@ -95,8 +114,15 @@ public class DefaultModelProcessor implements 
ModelProcessor {
 
     public Path locateExistingPom(Path projectDirectory) {
         // Note that the ModelProcessor#locatePom never returns null
-        File f = modelLocator.locateExistingPom(projectDirectory.toFile());
-        Path pom = f != null ? f.toPath() : null;
+        // while the ModelParser#locatePom needs to return an existing path!
+        Path pom = modelParsers.stream()
+                .map(m -> m.locate(projectDirectory).map(s -> 
s.getPath()).orElse(null))
+                .filter(Objects::nonNull)
+                .findFirst()
+                .orElseGet(() -> {
+                    File f = 
modelLocator.locateExistingPom(projectDirectory.toFile());
+                    return f != null ? f.toPath() : null;
+                });
         if (pom != null && !pom.equals(projectDirectory) && 
!pom.getParent().equals(projectDirectory)) {
             throw new IllegalArgumentException("The POM found does not belong 
to the given directory: " + pom);
         }
@@ -109,7 +135,28 @@ public class DefaultModelProcessor implements 
ModelProcessor {
         if (pomFile == null && source instanceof 
org.apache.maven.building.FileSource) {
             pomFile = ((org.apache.maven.building.FileSource) 
source).getFile().toPath();
         }
-        return readXmlModel(pomFile, input, reader, options);
+        if (pomFile != null) {
+            Path projectDirectory = pomFile.getParent();
+            List<ModelParserException> exceptions = new ArrayList<>();
+            for (ModelParser parser : modelParsers) {
+                try {
+                    Optional<Model> model = 
parser.locateAndParse(projectDirectory, options);
+                    if (model.isPresent()) {
+                        return model.get().withPomFile(pomFile);
+                    }
+                } catch (ModelParserException e) {
+                    exceptions.add(e);
+                }
+            }
+            try {
+                return readXmlModel(pomFile, null, null, options);
+            } catch (IOException e) {
+                exceptions.forEach(e::addSuppressed);
+                throw e;
+            }
+        } else {
+            return readXmlModel(pomFile, input, reader, options);
+        }
     }
 
     private org.apache.maven.api.model.Model readXmlModel(
@@ -137,6 +184,8 @@ public class DefaultModelProcessor implements 
ModelProcessor {
         try (InputStream in = input) {
             org.apache.maven.api.model.Model model = read(null, in, null, 
options);
             return new org.apache.maven.model.Model(model);
+        } catch (ModelParserException e) {
+            throw new ModelParseException("Unable to read model: " + e, 
e.getLineNumber(), e.getColumnNumber(), e);
         }
     }
 

Reply via email to