diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Dialect.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Dialect.java
new file mode 100644
index 00000000000..728283aec67
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Dialect.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+
+import static org.apache.maven.api.ExtensibleEnums.dialect;
+
+/**
+ * Dialect.
+ *
+ * This extensible enum has one defined value, {@link #XML},
+ * but can be extended by registering a {@code org.apache.maven.api.spi.DialectProvider}.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Immutable
+@SuppressWarnings("checkstyle:InterfaceIsType")
+public interface Dialect extends ExtensibleEnum {
+
+ /**
+ * The "xml" dialect, the essence of Maven.
+ */
+ Dialect XML = dialect("xml");
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java
index b23e85da75d..67ce68477fe 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java
@@ -26,6 +26,10 @@
abstract class ExtensibleEnums {
+ static Dialect dialect(String id) {
+ return new DefaultDialect(id);
+ }
+
static Language language(String id) {
return new DefaultLanguage(id);
}
@@ -88,6 +92,13 @@ public Set dependencyScopes() {
}
}
+ private static class DefaultDialect extends DefaultExtensibleEnum implements Dialect {
+
+ DefaultDialect(String id) {
+ super(id);
+ }
+ }
+
private static class DefaultProjectScope extends DefaultExtensibleEnum implements ProjectScope {
DefaultProjectScope(String id) {
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DialectRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DialectRegistry.java
new file mode 100644
index 00000000000..b50c33deaa6
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DialectRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * 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.services;
+
+import org.apache.maven.api.Dialect;
+
+public interface DialectRegistry extends ExtensibleEnumRegistry {}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelDialectManager.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelDialectManager.java
new file mode 100644
index 00000000000..3c206efaf9e
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelDialectManager.java
@@ -0,0 +1,68 @@
+/*
+ * 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.services;
+
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.maven.api.Dialect;
+import org.apache.maven.api.Service;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.model.Model;
+
+/**
+ * Dialect manager, that offers passage between various dialects.
+ */
+public interface ModelDialectManager extends Service {
+ /**
+ * Returns the available dialects, never {@code null}. Result set has at least one element, the {@link org.apache.maven.api.Dialect#XML}
+ * which is present in core.
+ */
+ @Nonnull
+ Set getAvailableDialects();
+
+ /**
+ * Reads the model from given directory in given dialect.
+ *
+ * @param dir the directory from where to read, never {@code null}
+ * @param dialect the dialect to use to read, never {@code null}
+ * @param options the options for reading
+ * @return optional with the model that was read or empty
+ * @throws IllegalArgumentException if unknown dialect was asked for
+ */
+ @Nonnull
+ Optional readModel(@Nonnull Path dir, @Nonnull Dialect dialect, @Nullable Map options);
+
+ /**
+ * Write the model to given directory in given dialect.
+ *
+ * @param dir the directory to where to write, never {@code null}
+ * @param dialect the dialect to use to write, never {@code null}
+ * @param model the model to write, never {@code null}
+ * @param options the options for writing
+ * @return optional with file path where write happened or empty
+ * @throws IllegalArgumentException if unknown dialect was asked for
+ */
+ @Nonnull
+ Optional writeModel(
+ @Nonnull Path dir, @Nonnull Dialect dialect, @Nonnull Model model, @Nullable Map options);
+}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/DialectProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/DialectProvider.java
new file mode 100644
index 00000000000..31406bfca4d
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/DialectProvider.java
@@ -0,0 +1,32 @@
+/*
+ * 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.Dialect;
+import org.apache.maven.api.annotations.Consumer;
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.di.Named;
+
+/**
+ * @since 4.0.0
+ */
+@Experimental
+@Consumer
+@Named
+public interface DialectProvider extends ExtensibleEnumProvider {}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelDialectProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelDialectProvider.java
new file mode 100644
index 00000000000..f04401c0692
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelDialectProvider.java
@@ -0,0 +1,51 @@
+/*
+ * 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.Dialect;
+import org.apache.maven.api.annotations.Consumer;
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.di.Named;
+
+/**
+ * @since 4.0.0
+ */
+@Experimental
+@Consumer
+@Named
+public interface ModelDialectProvider {
+ /**
+ * The {@link Dialect} this provider handles.
+ */
+ @Nonnull
+ Dialect getDialect();
+
+ /**
+ * The dialect specific parser.
+ */
+ @Nonnull
+ ModelParser getModelParser();
+
+ /**
+ * The dialect specific writer.
+ */
+ @Nonnull
+ ModelWriter getModelWriter();
+}
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
index 9447e519f57..39c810958ef 100644
--- 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
@@ -33,8 +33,13 @@
/**
* 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.
+ *
+ * Note: to provide Maven model "dialect", that can have models translated from-to, implement {@link ModelDialectProvider}
+ * that compose pairs of parser and writer (make dialect symmetrical).
*
* @since 4.0.0
+ * @see ModelDialectProvider
+ * @see DialectProvider
*/
@Experimental
@Consumer
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriter.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriter.java
new file mode 100644
index 00000000000..6eef1481c4e
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.Consumer;
+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.di.Named;
+import org.apache.maven.api.model.Model;
+
+/**
+ * The {@code ModelWriter} interface is used to write {@link Model}s to the file system.
+ * This allows plugging in additional syntaxes for the main model write.
+ *
+ * Note: to provide Maven model "dialect", that can have models translated from-to, implement {@link ModelDialectProvider}
+ * that compose pairs of parser and writer (make dialect symmetrical).
+ *
+ * @since 4.0.0
+ * @see ModelDialectProvider
+ * @see DialectProvider
+ */
+@Experimental
+@Consumer
+@Named
+public interface ModelWriter extends SpiService {
+
+ /**
+ * Targets the pom in the given directory.
+ *
+ * @param dir the directory to target the pom for, never {@code null}
+ * @return a {@code Path} pointing to the targeted pom file, or an empty {@code Optional} if serializer refuses to target.
+ */
+ @Nonnull
+ Optional target(@Nonnull Path dir);
+
+ /**
+ * Write out given model into given directory. The actual file (within directory) is decided by implementation,
+ * and is returned upon successful write operation.
+ *
+ * @param target the file where to write out model, never {@code null}.
+ * @param model the model to write out. never {@code null}
+ * @param options possible writing options, may be {@code null}
+ * @throws ModelWriterException if the model could not be written
+ */
+ @Nonnull
+ void write(@Nonnull Path target, @Nonnull Model model, @Nullable Map options)
+ throws ModelWriterException;
+
+ /**
+ * Target and write out the model in the specified directory.
+ * This is equivalent to {@code target(dir).map(s -> write(s, model, options))}.
+ *
+ * @param dir the directory to target the pom for, never {@code null}
+ * @param model the model to write out, never {@code null}.
+ * @param options possible parsing options, may be {@code null}
+ * @return an optional with pom file path
+ * @throws ModelWriterException if the target model cannot be written
+ */
+ @Nonnull
+ default Optional targetAndWrite(@Nonnull Path dir, @Nonnull Model model, @Nullable Map options)
+ throws ModelWriterException {
+ return target(dir).map(s -> {
+ write(s, model, options);
+ return s;
+ });
+ }
+}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriterException.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriterException.java
new file mode 100644
index 00000000000..d0f34bc431e
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelWriterException.java
@@ -0,0 +1,37 @@
+/*
+ * 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 ModelWriterException extends MavenException {
+ public ModelWriterException() {
+ this(null, null);
+ }
+
+ public ModelWriterException(String message) {
+ this(message, null);
+ }
+
+ public ModelWriterException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java
index f8bc69e538b..4078f58313b 100644
--- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java
+++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java
@@ -25,6 +25,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.maven.api.Dialect;
import org.apache.maven.api.ExtensibleEnum;
import org.apache.maven.api.Language;
import org.apache.maven.api.PathScope;
@@ -33,10 +34,12 @@
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.SessionScoped;
import org.apache.maven.api.di.Singleton;
+import org.apache.maven.api.services.DialectRegistry;
import org.apache.maven.api.services.ExtensibleEnumRegistry;
import org.apache.maven.api.services.LanguageRegistry;
import org.apache.maven.api.services.PathScopeRegistry;
import org.apache.maven.api.services.ProjectScopeRegistry;
+import org.apache.maven.api.spi.DialectProvider;
import org.apache.maven.api.spi.ExtensibleEnumProvider;
import org.apache.maven.api.spi.LanguageProvider;
import org.apache.maven.api.spi.PathScopeProvider;
@@ -73,6 +76,17 @@ public DefaultProjectScopeRegistry(List providers) {
}
}
+ @Named
+ @Singleton
+ public static class DefaultDialectRegistry extends DefaultExtensibleEnumRegistry
+ implements DialectRegistry {
+
+ @Inject
+ public DefaultDialectRegistry(List providers) {
+ super(providers, Dialect.XML);
+ }
+ }
+
@Named
@Singleton
public static class DefaultLanguageRegistry extends DefaultExtensibleEnumRegistry
diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelDialectManager.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelDialectManager.java
new file mode 100644
index 00000000000..238922d9b1f
--- /dev/null
+++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelDialectManager.java
@@ -0,0 +1,137 @@
+/*
+ * 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.internal.impl.model;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import org.apache.maven.api.Dialect;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Singleton;
+import org.apache.maven.api.model.Model;
+import org.apache.maven.api.services.ModelDialectManager;
+import org.apache.maven.api.services.Source;
+import org.apache.maven.api.services.xml.ModelXmlFactory;
+import org.apache.maven.api.spi.ModelDialectProvider;
+import org.apache.maven.api.spi.ModelParser;
+import org.apache.maven.api.spi.ModelParserException;
+import org.apache.maven.api.spi.ModelWriter;
+import org.apache.maven.api.spi.ModelWriterException;
+
+@Named
+@Singleton
+public class DefaultModelDialectManager implements ModelDialectManager {
+ private final Map dialectProviders;
+
+ @Inject
+ public DefaultModelDialectManager(
+ ModelXmlFactory modelXmlFactory, List modelDialectProviders) {
+ this.dialectProviders = new HashMap<>();
+ // XML
+ dialectProviders.put(Dialect.XML, getXmlDialectProvider(modelXmlFactory));
+ // SPI TODO: (they can override XML dialect as well?)
+ for (ModelDialectProvider modelDialectProvider : modelDialectProviders) {
+ dialectProviders.put(modelDialectProvider.getDialect(), modelDialectProvider);
+ }
+ }
+
+ private ModelDialectProvider getXmlDialectProvider(ModelXmlFactory modelXmlFactory) {
+ ModelParser xmlModelParser = new ModelParser() {
+ @Override
+ public Optional