This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/overwrite-source-directory in repository https://gitbox.apache.org/repos/asf/maven-doxia-sitetools.git
commit 3baee06f2a7cd31bb61ade60c7771c37e38c3f6f Author: Konrad Windszus <[email protected]> AuthorDate: Wed Mar 11 14:45:38 2026 +0100 Allow to set an alternative source directory (for editing) Always check if a file is existing before returning true for isEditable and non-null for getDoxiaSourcePath()/getDoxiaSourcePath(String) This closes #277 --- .../doxia/siterenderer/DefaultSiteRenderer.java | 25 ++-- .../siterenderer/DocumentRenderingContext.java | 127 ++++++++++++++++++--- .../doxia/siterenderer/SiteRenderingContext.java | 34 ++++++ .../siterenderer/DefaultSiteRendererTest.java | 6 +- .../siterenderer/DocumentRenderingContextTest.java | 51 +++++++++ .../doxia/siterenderer/RenderingContextTest.java | 9 +- 6 files changed, 213 insertions(+), 39 deletions(-) diff --git a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java index 82ed93e..8cfe368 100644 --- a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java +++ b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java @@ -198,12 +198,11 @@ public class DefaultSiteRenderer implements Renderer { addModuleFiles( siteRenderingContext.getRootDirectory(), + siteDirectory, moduleBasedir, module, excludes, - files, - siteDirectory.isEditable(), - siteDirectory.isSkipDuplicates()); + files); } } } @@ -229,31 +228,26 @@ public class DefaultSiteRenderer implements Renderer { * taking care of duplicates if needed. * * @param rootDir + * @param siteDirectory * @param moduleBasedir * @param module * @param excludes * @param files - * @param editable - * @param skipDuplicates * @throws IOException * @throws RendererException */ private void addModuleFiles( File rootDir, + SiteDirectory siteDirectory, File moduleBasedir, ParserModule module, String excludes, - Map<String, DocumentRenderer> files, - boolean editable, - boolean skipDuplicates) + Map<String, DocumentRenderer> files) throws IOException, RendererException { if (!moduleBasedir.exists() || ArrayUtils.isEmpty(module.getExtensions())) { return; } - String moduleRelativePath = - PathTool.getRelativeFilePath(rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath()); - List<String> allFiles = FileUtils.getFileNames(moduleBasedir, "**/*", excludes, false); for (String extension : module.getExtensions()) { @@ -268,14 +262,19 @@ public class DefaultSiteRenderer implements Renderer { for (String doc : docs) { DocumentRenderingContext docRenderingContext = new DocumentRenderingContext( - moduleBasedir, moduleRelativePath, doc, module.getParserId(), extension, editable); + moduleBasedir, + doc, + module.getParserId(), + extension, + rootDir, + siteDirectory.getSourceDirectories()); // TODO: DOXIA-111: we need a general filter here that knows how to alter the context if (endsWithIgnoreCase(doc, ".vm")) { docRenderingContext.setAttribute("velocity", "true"); } - if (!checkForDuplicate(docRenderingContext, files, skipDuplicates)) { + if (!checkForDuplicate(docRenderingContext, files, siteDirectory.isSkipDuplicates())) { String key = docRenderingContext.getOutputName(); files.put(key, new DoxiaDocumentRenderer(docRenderingContext)); } diff --git a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContext.java b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContext.java index b176c83..97893f0 100644 --- a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContext.java +++ b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContext.java @@ -19,6 +19,8 @@ package org.apache.maven.doxia.siterenderer; import java.io.File; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -32,26 +34,55 @@ import org.codehaus.plexus.util.PathTool; * @since 1.5 (was since 1.1 in o.a.m.d.sink.render) */ public class DocumentRenderingContext { - private final File basedir; - private final String basedirRelativePath; + /** absolute path to the source base directory (not null, pseudo value when not a Doxia source), this is parser format specific */ + private final File basedir; + /** {@link #basedir} relative path to the document's source including {@link #extension}. May be {@code null} if not based by Doxia source */ private final String inputPath; + /** same as {@link #inputPath} but with extension replaced with {@code .html}, this is parser format specific */ private final String outputPath; + /** the Doxia module parser id associated to this document, may be null if document not rendered from a Doxia source. */ private final String parserId; - private final String relativePath; - + /** the source document filename extension, may be null if document not rendered from a Doxia source. */ private final String extension; private Map<String, String> attributes; - private final boolean editable; + /** The absolute paths of directories which may contain the original editable source. + * If empty document is not editable. + */ + private final Collection<File> sourceDirectories; + + /** The project's build directory (never null) */ + private final File rootDirectory; + /** optional descriptive text of the plugin which generated the output (usually Maven coordinates). Only set when document is not based on a Doxia source. */ private final String generator; + static File stripSuffixFromPath(File file, String suffix) { + File relevantFile = file; + if (suffix == null || suffix.isEmpty()) { + return relevantFile; + } + File currentSuffixPart = new File(suffix); + // compare elements from end, suffix should be a suffix of file + do { + if (currentSuffixPart.getName().equals(relevantFile.getName())) { + relevantFile = relevantFile.getParentFile(); + if (relevantFile == null) { + throw new IllegalArgumentException("Suffix " + suffix + " has more elements than file " + file); + } + } else { + throw new IllegalArgumentException("Suffix " + suffix + " is not a suffix of file " + file); + } + } while ((currentSuffixPart = currentSuffixPart.getParentFile()) != null); + return relevantFile; + } + /** * <p> * Constructor for rendering context when document is not rendered from a Doxia markup source. @@ -64,9 +95,10 @@ public class DocumentRenderingContext { * @since 1.8 */ public DocumentRenderingContext(File basedir, String document, String generator) { - this(basedir, null, document, null, null, false, generator); + this(basedir, document, null, null, basedir, Collections.emptySet(), generator); } + @Deprecated public DocumentRenderingContext( File basedir, String basedirRelativePath, @@ -77,6 +109,16 @@ public class DocumentRenderingContext { this(basedir, basedirRelativePath, document, parserId, extension, editable, null); } + public DocumentRenderingContext( + File basedir, + String document, + String parserId, + String extension, + File rootDirectory, + Collection<File> sourceDirectories) { + this(basedir, document, parserId, extension, rootDirectory, sourceDirectories, null); + } + /** * <p> * Constructor for document rendering context. @@ -101,6 +143,42 @@ public class DocumentRenderingContext { String extension, boolean editable, String generator) { + this( + basedir, + document, + parserId, + extension, + stripSuffixFromPath(basedir, basedirRelativePath), + editable ? Collections.singleton(basedir) : Collections.emptySet(), + generator); + } + + /** + * <p> + * Constructor for document rendering context. + * </p> + * + * @param basedir the source base directory (not null, pseudo value when not a Doxia source). + * @param basedirRelativePath the relative path from root (null if not Doxia source) + * @param document the source document path. + * @param parserId the Doxia module parser id associated to this document, may be null if document not rendered from + * a Doxia source. + * @param extension the source document filename extension, may be null if document not rendered from + * a Doxia source. + * @param rootDirectory the project's root directory (not null) + * @param sourceDirectories the absolute paths of directories which may contain the original editable source. + * @param editable is the document editable as source, i.e. not generated? + * @param generator the generator (in general a reporting goal: <code>groupId:artifactId:version:goal</code>) + * @since 2.1 + */ + public DocumentRenderingContext( + File basedir, + String document, + String parserId, + String extension, + File rootDirectory, + Collection<File> sourceDirectories, + String generator) { this.basedir = basedir; this.parserId = parserId; this.extension = extension; @@ -110,10 +188,10 @@ public class DocumentRenderingContext { document = document.replace('\\', '/'); this.inputPath = document; + this.rootDirectory = rootDirectory; if (extension != null && !extension.isEmpty()) { - this.basedirRelativePath = basedirRelativePath.replace('\\', '/'); // document comes from a Doxia source: see DoxiaDocumentRenderer - this.editable = editable; + this.sourceDirectories = sourceDirectories; // here we know the parserId and extension, we can play with this to get output name from document: // - index.xml -> index.html @@ -126,13 +204,9 @@ public class DocumentRenderingContext { this.outputPath = filePathWithoutExt + ".html"; } else { // document does not come from a Doxia source but direct Sink API, so no file extension to strip - this.basedirRelativePath = null; - this.editable = false; + this.sourceDirectories = Collections.emptySet(); this.outputPath = document + ".html"; } - - this.relativePath = PathTool.getRelativePath(basedir.getPath(), new File(basedir, inputPath).getPath()) - .replace('\\', '/'); } /** @@ -189,12 +263,12 @@ public class DocumentRenderingContext { } /** - * Get the relative path to site root. + * Get the relative path of the parent folder to site root. * * @return the relative path to site root */ public String getRelativePath() { - return relativePath; + return PathTool.getRelativePath(basedir.getPath(), new File(basedir, inputPath).getPath()); } /** @@ -233,7 +307,7 @@ public class DocumentRenderingContext { * @since 1.8 */ public boolean isEditable() { - return editable; + return getDoxiaSourcePath() != null; } /** @@ -263,7 +337,10 @@ public class DocumentRenderingContext { * @since 1.8 */ public String getBasedirRelativePath() { - return basedirRelativePath; + if (!isDoxiaSource()) { + return null; + } + return PathTool.getRelativeFilePath(rootDirectory.getPath(), basedir.getPath()); } /** @@ -273,11 +350,23 @@ public class DocumentRenderingContext { * @since 1.8 */ public String getDoxiaSourcePath() { - return isDoxiaSource() ? (basedirRelativePath + '/' + inputPath) : null; + if (!isDoxiaSource()) { + return null; + } else { + // legacy fuzzy logic if no source directories are specified + for (File sourceDirectory : sourceDirectories) { + File sourceFile = new File(sourceDirectory, getBasedirRelativePath() + '/' + inputPath); + if (sourceFile.exists()) { + return PathTool.getRelativePath(rootDirectory.getPath(), sourceFile.getPath()); + } + } + } + return null; } /** - * Get url of the Doxia source calculate from given base url. + * Get absolute url of the Doxia source calculate from given base url. + * Used from Skins to render an edit button. * * @param base the base url to use * @return the resulting url diff --git a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java index 64db4c7..20c584f 100644 --- a/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java +++ b/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java @@ -20,8 +20,10 @@ package org.apache.maven.doxia.siterenderer; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -44,6 +46,7 @@ public class SiteRenderingContext { private File path; private boolean editable; private boolean skipDuplicates; + private final Collection<File> sourceDirectories = new HashSet<>(); public SiteDirectory(File path, boolean editable) { this(path, editable, false); @@ -73,6 +76,37 @@ public class SiteRenderingContext { public boolean isSkipDuplicates() { return skipDuplicates; } + + /** + * Add a source directory to this site directory. + * This will implicitly turn it into an editable site directory. Multiple source directories can be used, the first one containing a file with a given name will be used for that file, + * the others will be ignored. This allows to have a main source directory and an optional overlay directory with custom files. + * Only necessary to call if the source directory is different from the site directory, otherwise the site directory will be used as source directory. + * @param sourceDirectory + * @since 2.1 + */ + public void addAlternativeSourceDirectory(File sourceDirectory) { + sourceDirectories.add(sourceDirectory); + editable = true; + } + + /** + * Get the source directories for this site directory. If no alternative source directory has been added via {@link #addAlternativeSourceDirectory(File)} + * the site directory itself will be returned as the only source directory. + * If the site directory is not editable, an empty collection will be returned. + * @return the source directories for this site directory + * @since 2.1 + */ + public Collection<File> getSourceDirectories() { + if (!editable) { + return Collections.emptyList(); + } + if (sourceDirectories.isEmpty()) { + return Collections.singleton(path); + } else { + return sourceDirectories; + } + } } private String inputEncoding = ReaderFactory.FILE_ENCODING; diff --git a/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java index d41ed81..62c0978 100644 --- a/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java +++ b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java @@ -181,8 +181,8 @@ public class DefaultSiteRendererTest { SiteRenderer siteRenderer = container.lookup(SiteRenderer.class); ReflectionUtils.setVariableValueInObject(siteRenderer, "doxia", doxiaSpy); - DocumentRenderingContext docRenderingContext = - new DocumentRenderingContext(testBasedir, "", testDocument, "xdoc", "", false); + DocumentRenderingContext docRenderingContext = new DocumentRenderingContext( + testBasedir, "src/test/resources/site/xdoc", testDocument, "xdoc", "", false); try { siteRenderer.renderDocument(null, docRenderingContext, new SiteRenderingContext()); @@ -335,7 +335,7 @@ public class DefaultSiteRendererTest { siteRenderingContext.setTemplateName("org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm"); DocumentRenderingContext docRenderingContext = - new DocumentRenderingContext(new File(""), "document.html", "generator"); + new DocumentRenderingContext(new File("."), "document.html", "generator"); SiteRendererSink sink = new SiteRendererSink(docRenderingContext); siteRenderer.mergeDocumentIntoSite(writer, sink, siteRenderingContext); diff --git a/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContextTest.java b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContextTest.java new file mode 100644 index 0000000..085d01a --- /dev/null +++ b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DocumentRenderingContextTest.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.doxia.siterenderer; + +import java.io.File; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class DocumentRenderingContextTest { + + @Test + void stripSuffixFromPath() { + assertEquals( + new File("test/base/some/suffix"), + DocumentRenderingContext.stripSuffixFromPath(new File("test/base/some/suffix"), null)); + assertEquals( + new File("test/base/some/suffix"), + DocumentRenderingContext.stripSuffixFromPath(new File("test/base/some/suffix"), "")); + assertEquals( + new File("test/base/some"), + DocumentRenderingContext.stripSuffixFromPath(new File("test/base/some/suffix"), "suffix")); + assertEquals( + new File("test/base"), + DocumentRenderingContext.stripSuffixFromPath(new File("test/base/some/suffix"), "some/suffix")); + assertThrows( + IllegalArgumentException.class, + () -> DocumentRenderingContext.stripSuffixFromPath(new File("test/base/some/suffix"), "other/suffix")); + assertThrows( + IllegalArgumentException.class, + () -> DocumentRenderingContext.stripSuffixFromPath(new File("some/suffix"), "test/base/some/suffix")); + } +} diff --git a/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java index 5e8fae5..4b53899 100644 --- a/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java +++ b/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java @@ -43,8 +43,9 @@ class RenderingContextTest { File baseDir = new File(getBasedir() + File.separatorChar + "test" + File.separatorChar + "resources"); String document = "file.with.dot.in.name.xml"; + String baseDirRelativePath = "test" + File.separatorChar + "resources"; DocumentRenderingContext docRenderingContext = - new DocumentRenderingContext(baseDir, "test", document, "", "xml", false); + new DocumentRenderingContext(baseDir, baseDirRelativePath, document, "", "xml", false); assertEquals("file.with.dot.in.name.html", docRenderingContext.getOutputPath()); assertEquals(".", docRenderingContext.getRelativePath()); @@ -54,17 +55,17 @@ class RenderingContextTest { assertEquals(".", docRenderingContext.getRelativePath()); document = "index.xml.vm"; - docRenderingContext = new DocumentRenderingContext(baseDir, "test", document, "", "xml", false); + docRenderingContext = new DocumentRenderingContext(baseDir, baseDirRelativePath, document, "", "xml", false); assertEquals("index.html", docRenderingContext.getOutputPath()); assertEquals(".", docRenderingContext.getRelativePath()); document = "download.apt.vm"; - docRenderingContext = new DocumentRenderingContext(baseDir, "test", document, "", "apt", false); + docRenderingContext = new DocumentRenderingContext(baseDir, baseDirRelativePath, document, "", "apt", false); assertEquals("download.html", docRenderingContext.getOutputPath()); assertEquals(".", docRenderingContext.getRelativePath()); document = "path/file.apt"; - docRenderingContext = new DocumentRenderingContext(baseDir, "test", document, "", "apt", false); + docRenderingContext = new DocumentRenderingContext(baseDir, baseDirRelativePath, document, "", "apt", false); assertEquals("path/file.html", docRenderingContext.getOutputPath()); assertEquals("..", docRenderingContext.getRelativePath());
