From e2291c39dcf8992a133c86fad8fd78d34a61d1bb Mon Sep 17 00:00:00 2001 From: Henry Lee Date: Thu, 30 Nov 2023 20:01:16 +0900 Subject: [PATCH] refactor(java): maven-doxia-sitetools --- Java-base/maven-doxia-sitetools/Dockerfile | 28 + .../AbstractDocumentRenderer_532/buggy.java | 713 ++++ .../AbstractDocumentRenderer_532/npe.json | 7 + .../bugs/DecorationUtils_113/buggy.java | 127 + .../bugs/DecorationUtils_113/npe.json | 7 + .../bugs/DecorationUtils_78/buggy.java | 122 + .../bugs/DecorationUtils_78/npe.json | 7 + .../bugs/DecorationUtils_98/buggy.java | 125 + .../bugs/DecorationUtils_98/npe.json | 7 + .../buggy.java | 458 +++ .../npe.json | 7 + .../buggy.java | 458 +++ .../npe.json | 7 + .../buggy.java | 458 +++ .../npe.json | 7 + .../bugs/DefaultSiteRenderer_190/buggy.java | 1178 +++++++ .../bugs/DefaultSiteRenderer_190/npe.json | 7 + .../bugs/DefaultSiteRenderer_525/buggy.java | 1177 +++++++ .../bugs/DefaultSiteRenderer_525/npe.json | 7 + .../bugs/DefaultSiteRenderer_647/buggy.java | 1180 +++++++ .../bugs/DefaultSiteRenderer_647/npe.json | 7 + .../bugs/DefaultSiteRenderer_736/buggy.java | 1161 +++++++ .../bugs/DefaultSiteRenderer_736/npe.json | 7 + .../bugs/DefaultSiteRenderer_738/buggy.java | 1161 +++++++ .../bugs/DefaultSiteRenderer_738/npe.json | 7 + .../bugs/DefaultSiteRenderer_773/buggy.java | 1161 +++++++ .../bugs/DefaultSiteRenderer_773/npe.json | 7 + .../bugs/DefaultSiteRenderer_821/buggy.java | 1169 +++++++ .../bugs/DefaultSiteRenderer_821/npe.json | 7 + .../bugs/DefaultSiteRenderer_828/buggy.java | 1169 +++++++ .../bugs/DefaultSiteRenderer_828/npe.json | 7 + .../bugs/DefaultSiteRenderer_832/buggy.java | 1169 +++++++ .../bugs/DefaultSiteRenderer_832/npe.json | 7 + .../bugs/DefaultSiteTool_1036/buggy.java | 1539 +++++++++ .../bugs/DefaultSiteTool_1036/npe.json | 7 + .../bugs/DefaultSiteTool_349/buggy.java | 1529 +++++++++ .../bugs/DefaultSiteTool_349/npe.json | 7 + .../bugs/DefaultSiteTool_492/buggy.java | 1503 +++++++++ .../bugs/DefaultSiteTool_492/npe.json | 7 + .../bugs/DefaultSiteTool_587/buggy.java | 1491 +++++++++ .../bugs/DefaultSiteTool_587/npe.json | 7 + .../bugs/DefaultSiteTool_686/buggy.java | 1520 +++++++++ .../bugs/DefaultSiteTool_686/npe.json | 7 + .../bugs/DefaultSiteTool_771/buggy.java | 1519 +++++++++ .../bugs/DefaultSiteTool_771/npe.json | 7 + .../bugs/FoPdfRenderer_90/buggy.java | 325 ++ .../bugs/FoPdfRenderer_90/npe.json | 7 + .../bugs/ITextPdfRenderer_152/buggy.java | 669 ++++ .../bugs/ITextPdfRenderer_152/npe.json | 7 + .../bugs/PathDescriptor_118/buggy.java | 248 ++ .../bugs/PathDescriptor_118/npe.json | 7 + .../bugs/PathDescriptor_136/buggy.java | 245 ++ .../bugs/PathDescriptor_136/npe.json | 7 + .../bugs/PathDescriptor_141/buggy.java | 245 ++ .../bugs/PathDescriptor_141/npe.json | 7 + .../bugs/PathDescriptor_206/buggy.java | 252 ++ .../bugs/PathDescriptor_206/npe.json | 7 + .../bugs/PathDescriptor_232/buggy.java | 252 ++ .../bugs/PathDescriptor_232/npe.json | 7 + .../bugs/PathUtils_57/buggy.java | 151 + .../bugs/PathUtils_57/npe.json | 7 + .../bugs/SiteRendererSink_269/buggy.java | 354 ++ .../bugs/SiteRendererSink_269/npe.json | 7 + .../bugs/SiteRendererSink_305/buggy.java | 341 ++ .../bugs/SiteRendererSink_305/npe.json | 7 + .../bugs/URIPathDescriptor_166/buggy.java | 267 ++ .../bugs/URIPathDescriptor_166/npe.json | 7 + .../bugs/URIPathDescriptor_222/buggy.java | 261 ++ .../bugs/URIPathDescriptor_222/npe.json | 7 + .../maven-doxia-sitetools/src/Jenkinsfile | 20 + Java-base/maven-doxia-sitetools/src/README.md | 99 + .../src/doxia-decoration-model/pom.xml | 98 + .../site/decoration/DecorationUtils.java | 115 + .../DecorationModelInheritanceAssembler.java | 72 + ...ltDecorationModelInheritanceAssembler.java | 469 +++ .../inheritance/PathDescriptor.java | 255 ++ .../decoration/inheritance/PathUtils.java | 145 + .../inheritance/URIPathDescriptor.java | 265 ++ .../src/main/mdo/decoration.mdo | 784 +++++ .../src/site/apt/index.apt | 48 + .../doxia-decoration-model/src/site/site.xml | 44 + .../site/decoration/DecorationUtilsTest.java | 67 + ...corationModelInheritanceAssemblerTest.java | 957 ++++++ .../decoration/inheritance/Doxia91Test.java | 72 + .../inheritance/PathDescriptorTest.java | 655 ++++ .../decoration/inheritance/PathUtilsTest.java | 80 + .../inheritance/URIPathDescriptorTest.java | 294 ++ .../src/test/resources/empty.xml | 26 + .../src/test/resources/external-urls.xml | 54 + .../test/resources/fully-populated-child.xml | 69 + .../test/resources/fully-populated-merged.xml | 64 + .../resources/fully-populated-unresolved.xml | 64 + .../inheritance-child-no-inheritance.xml | 38 + .../src/test/resources/inheritance-child.xml | 37 + .../test/resources/inheritance-expected.xml | 69 + .../src/test/resources/inheritance-parent.xml | 64 + .../test/resources/relative-urls-resolved.xml | 53 + .../src/test/resources/relative-urls.xml | 53 + ...ubsite-relative-urls-multiple-resolved.xml | 53 + .../subsite-relative-urls-resolved.xml | 53 + .../src/test/resources/subsite-urls-file.xml | 53 + .../src/test/resources/subsite-urls.xml | 53 + .../src/doxia-doc-renderer/pom.xml | 169 + .../docrenderer/AbstractDocumentRenderer.java | 727 ++++ .../maven/doxia/docrenderer/DocRenderer.java | 65 + .../doxia/docrenderer/DocumentRenderer.java | 109 + .../docrenderer/DocumentRendererContext.java | 140 + .../DocumentRendererException.java | 54 + .../itext/AbstractITextRender.java | 519 +++ .../docrenderer/itext/DefaultPdfRenderer.java | 65 + .../docrenderer/itext/DefaultRtfRenderer.java | 65 + .../doxia/docrenderer/itext/PdfRenderer.java | 35 + .../doxia/docrenderer/itext/RtfRenderer.java | 35 + .../docrenderer/pdf/AbstractPdfRenderer.java | 53 + .../doxia/docrenderer/pdf/PdfRenderer.java | 48 + .../docrenderer/pdf/fo/FoPdfRenderer.java | 366 ++ .../pdf/itext/ITextPdfRenderer.java | 689 ++++ .../doxia/docrenderer/itext/package.html | 27 + .../doxia/docrenderer/pdf/itext/TOC.xslt | 243 ++ .../src/doxia-doc-renderer/src/site/site.xml | 41 + .../docrenderer/DocumentRendererTest.java | 145 + .../src/test/resources/site/apt/index.apt | 66 + .../src/test/resources/site/apt/overview.apt | 104 + .../src/test/resources/site/apt/resources.apt | 49 + .../src/test/resources/site/fml/faq.fml | 67 + .../src/test/resources/site/pdf.xml | 57 + .../resources/site/resources/css/site.css | 57 + .../site/resources/images/architecture.odg | Bin 0 -> 12767 bytes .../site/resources/images/architecture.png | Bin 0 -> 19581 bytes .../site/resources/images/asf_logo_wide.png | Bin 0 -> 7090 bytes .../site/resources/images/doxia-logo.png | Bin 0 -> 16375 bytes .../src/test/resources/site/site.xml | 84 + .../site/xdoc/references/fml-format.xml | 101 + .../site/xdoc/references/xdoc-format.xml | 279 ++ .../src/doxia-integration-tools/pom.xml | 191 ++ .../maven/doxia/tools/DefaultSiteTool.java | 1529 +++++++++ .../maven/doxia/tools/MojoLogWrapper.java | 159 + .../maven/doxia/tools/ReportComparator.java | 61 + .../apache/maven/doxia/tools/SiteTool.java | 206 ++ .../maven/doxia/tools/SiteToolException.java | 66 + .../src/main/resources/default-site.xml | 33 + .../src/main/resources/site-tool.properties | 22 + .../main/resources/site-tool_ca.properties | 22 + .../main/resources/site-tool_cs.properties | 22 + .../main/resources/site-tool_da.properties | 22 + .../main/resources/site-tool_de.properties | 22 + .../main/resources/site-tool_en.properties | 23 + .../main/resources/site-tool_es.properties | 22 + .../main/resources/site-tool_fr.properties | 22 + .../main/resources/site-tool_gl.properties | 22 + .../main/resources/site-tool_hu.properties | 22 + .../main/resources/site-tool_it.properties | 22 + .../main/resources/site-tool_ja.properties | 22 + .../main/resources/site-tool_ko.properties | 22 + .../main/resources/site-tool_lt.properties | 22 + .../main/resources/site-tool_nl.properties | 22 + .../main/resources/site-tool_no.properties | 22 + .../main/resources/site-tool_pl.properties | 22 + .../main/resources/site-tool_pt.properties | 22 + .../main/resources/site-tool_pt_BR.properties | 22 + .../main/resources/site-tool_ru.properties | 22 + .../main/resources/site-tool_sk.properties | 22 + .../main/resources/site-tool_sv.properties | 22 + .../main/resources/site-tool_tr.properties | 22 + .../main/resources/site-tool_zh_CN.properties | 22 + .../main/resources/site-tool_zh_TW.properties | 22 + .../src/site/apt/index.apt | 73 + .../src/site/apt/usage.apt | 71 + .../src/site/resources/download.cgi | 22 + .../doxia-integration-tools/src/site/site.xml | 41 + .../src/site/xdoc/download.xml.vm | 126 + .../doxia/tools/DefaultSiteToolTest.java | 116 + .../maven/doxia/tools/SiteToolTest.java | 409 +++ .../tools/stubs/SiteToolMavenProjectStub.java | 152 + .../resources/unit/interpolated-site/pom.xml | 80 + .../interpolated-site/src/site/apt/test.apt | 29 + .../unit/interpolated-site/src/site/site.xml | 43 + .../unit/interpolation-child-test/pom.xml | 37 + .../src/site/site.xml | 24 + .../unit/interpolation-parent-test/pom.xml | 36 + .../src/site/site.xml | 52 + .../test/resources/unit/no-site-test/pom.xml | 40 + .../resources/unit/site-tool-test/pom.xml | 40 + .../unit/site-tool-test/src/site/site.xml | 45 + .../src/doxia-site-renderer/pom.xml | 233 ++ .../siterenderer/DefaultSiteRenderer.java | 1192 +++++++ .../doxia/siterenderer/DocumentContent.java | 66 + .../doxia/siterenderer/DocumentRenderer.java | 75 + .../siterenderer/DoxiaDocumentRenderer.java | 78 + .../ExtraDoxiaModuleReference.java | 60 + .../maven/doxia/siterenderer/Renderer.java | 185 + .../doxia/siterenderer/RendererException.java | 53 + .../doxia/siterenderer/RenderingContext.java | 295 ++ .../siterenderer/SiteRenderingContext.java | 473 +++ .../siterenderer/SkinResourceLoader.java | 125 + .../siterenderer/sink/SiteRendererSink.java | 353 ++ .../siterenderer/resources/css/maven-base.css | 168 + .../siterenderer/resources/css/print.css | 26 + .../resources/default-site-macros.vm | 494 +++ .../siterenderer/resources/default-site.vm | 100 + .../resources/images/collapsed.gif | Bin 0 -> 53 bytes .../resources/images/expanded.gif | Bin 0 -> 52 bytes .../images/logos/build-by-maven-black.png | Bin 0 -> 2294 bytes .../images/logos/build-by-maven-white.png | Bin 0 -> 2260 bytes .../resources/images/logos/maven-feather.png | Bin 0 -> 3330 bytes .../siterenderer/resources/resources.txt | 24 + .../main/resources/site-renderer.properties | 20 + .../resources/site-renderer_de.properties | 19 + .../resources/site-renderer_en.properties | 23 + .../resources/site-renderer_es.properties | 20 + .../resources/site-renderer_fr.properties | 19 + .../resources/site-renderer_it.properties | 20 + .../resources/site-renderer_ja.properties | 20 + .../resources/site-renderer_nl.properties | 20 + .../resources/site-renderer_pl.properties | 19 + .../resources/site-renderer_pt_BR.properties | 20 + .../resources/site-renderer_sv.properties | 20 + .../resources/site-renderer_zh_CN.properties | 20 + .../src/site/apt/doxia-site-renderer.odg | Bin 0 -> 13307 bytes .../src/site/apt/index.apt.vm | 193 ++ .../site/resources/doxia-site-renderer.png | Bin 0 -> 40267 bytes .../src/doxia-site-renderer/src/site/site.xml | 46 + .../doxia/siterenderer/AbstractVerifier.java | 70 + .../maven/doxia/siterenderer/AptVerifier.java | 180 + .../siterenderer/AttributesVerifier.java | 246 ++ .../doxia/siterenderer/CommentsVerifier.java | 41 + .../siterenderer/DefaultSiteRendererTest.java | 671 ++++ .../doxia/siterenderer/EntitiesVerifier.java | 189 ++ .../maven/doxia/siterenderer/FaqVerifier.java | 198 ++ .../doxia/siterenderer/HeadVerifier.java | 102 + .../siterenderer/JavascriptVerifier.java | 106 + .../doxia/siterenderer/MiscVerifier.java | 64 + .../siterenderer/MultipleBlockVerifier.java | 132 + .../siterenderer/NestedItemsVerifier.java | 320 ++ .../siterenderer/RenderingContextTest.java | 76 + .../siterenderer/SkinResourceLoaderTest.java | 63 + .../src/test/resources/dtd/xhtml-lat1.ent | 196 ++ .../src/test/resources/dtd/xhtml-special.ent | 80 + .../src/test/resources/dtd/xhtml-symbol.ent | 237 ++ .../resources/dtd/xhtml1-transitional.dtd | 1201 +++++++ .../src/test/resources/log4j.properties | 24 + .../velocity-toolmanager.expected.txt | 42 + .../siterenderer/velocity-toolmanager.vm | 7 + .../src/test/resources/site-plugin.properties | 19 + .../site-validate/xdoc/entityTest.xml | 71 + .../resources/site-validate/xdoc/xdoc-2.0.xsd | 2963 +++++++++++++++++ .../src/test/resources/site/apt/apt.apt | 61 + .../src/test/resources/site/apt/cdc.apt | 107 + .../site/apt/extension.apt.not.at.end.apt | 32 + .../test/resources/site/apt/interpolation.apt | 33 + .../confluence/confluence/anchor.confluence | 7 + .../confluence/confluence/code.confluence | 21 + .../confluence/confluence/escapes.confluence | 6 + .../confluence/confluence/figure.confluence | 22 + .../confluence/linebreak.confluence | 5 + .../confluence/confluence/link.confluence | 5 + .../confluence/nested-format.confluence | 6 + .../nested-list-heterogenous.confluence | 4 + .../confluence/nested-list.confluence | 8 + .../confluence/note-tip-info.confluence | 27 + .../confluence/paragraph-figure.confluence | 6 + .../confluence/paragraph-header.confluence | 6 + .../confluence/paragraph-list.confluence | 8 + .../confluence/confluence/section.confluence | 9 + .../confluence/simple-list.confluence | 14 + .../confluence/simple-paragraph.confluence | 8 + .../confluence/table-link.confluence | 4 + .../confluence/confluence/table.confluence | 13 + .../confluence/confluence/test.confluence | 98 + .../confluence/unknown-macro.confluence | 5 + .../test/resources/site/docbook/docbook.xml | 31 + .../resources/site/docbook/sdocbook_full.xml | 546 +++ .../src/test/resources/site/fml/faq.fml | 60 + .../images/build-by-maven-white.png | Bin 0 -> 2260 bytes .../src/test/resources/site/site.xml | 57 + .../test/resources/site/xdoc/attributes.xml | 93 + .../src/test/resources/site/xdoc/head.xml | 39 + .../test/resources/site/xdoc/javascript.xml | 45 + .../src/test/resources/site/xdoc/macro.xml | 75 + .../src/test/resources/site/xdoc/misc.xml | 33 + .../resources/site/xdoc/multipleblock.xml | 44 + .../test/resources/site/xdoc/nestedItems.xml | 195 ++ .../src/test/resources/xhtml-lat1.ent | 196 ++ .../src/doxia-skin-model/pom.xml | 88 + .../doxia-skin-model/src/main/mdo/skin.mdo | 101 + .../doxia-skin-model/src/site/apt/index.apt | 54 + .../src/doxia-skin-model/src/site/site.xml | 44 + Java-base/maven-doxia-sitetools/src/pom.xml | 411 +++ .../src/src/site/resources/download.cgi | 22 + .../resources/images/doxia-sitetools-deps.png | Bin 0 -> 55078 bytes .../src/src/site/site.xml | 53 + .../src/src/site/xdoc/download.xml.vm | 126 + .../src/site/xdoc/doxia-sitetools-deps.odg | Bin 0 -> 14097 bytes .../src/src/site/xdoc/index.xml | 64 + .../Dockerfile | 18 + .../buggy.java | 713 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 127 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 122 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 125 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 458 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 458 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 458 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1178 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1177 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1180 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1161 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1161 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1161 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1169 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1169 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1169 +++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1539 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1529 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1503 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1491 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1520 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 1519 +++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 325 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 669 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 248 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 245 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 245 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 252 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 252 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 151 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 354 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 341 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 267 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 261 ++ .../metadata.json | 21 + .../npe.json | 7 + 430 files changed, 81926 insertions(+) create mode 100644 Java-base/maven-doxia-sitetools/Dockerfile create mode 100644 Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathUtils_57/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/PathUtils_57/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/npe.json create mode 100644 Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/buggy.java create mode 100644 Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/npe.json create mode 100644 Java-base/maven-doxia-sitetools/src/Jenkinsfile create mode 100644 Java-base/maven-doxia-sitetools/src/README.md create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssembler.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/mdo/decoration.mdo create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/apt/index.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/DecorationUtilsTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssemblerTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/Doxia91Test.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptorTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtilsTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptorTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/empty.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/external-urls.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-child.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-merged.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-unresolved.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child-no-inheritance.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-expected.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-parent.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls-resolved.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-multiple-resolved.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-resolved.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls-file.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererContext.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererException.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/AbstractITextRender.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultPdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultRtfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/PdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/RtfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/AbstractPdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/PdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/javadoc/org/apache/maven/doxia/docrenderer/itext/package.html create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/resources/org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/java/org/apache/maven/doxia/docrenderer/DocumentRendererTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/index.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/overview.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/resources.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/fml/faq.fml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/pdf.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/css/site.css create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.odg create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/asf_logo_wide.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/doxia-logo.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/fml-format.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/xdoc-format.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/MojoLogWrapper.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/ReportComparator.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteTool.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteToolException.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/default-site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ca.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_cs.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_da.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_de.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_en.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_es.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_fr.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_gl.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_hu.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_it.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ja.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ko.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_lt.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_nl.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_no.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pl.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt_BR.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ru.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sk.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sv.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_tr.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_CN.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_TW.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/index.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/usage.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/resources/download.cgi create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/xdoc/download.xml.vm create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/DefaultSiteToolTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/SiteToolTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/stubs/SiteToolMavenProjectStub.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/apt/test.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/no-site-test/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentContent.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DoxiaDocumentRenderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/ExtraDoxiaModuleReference.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/Renderer.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RendererException.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RenderingContext.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SkinResourceLoader.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/maven-base.css create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/print.css create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site-macros.vm create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site.vm create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/collapsed.gif create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/expanded.gif create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-black.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-white.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/maven-feather.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/resources.txt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_de.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_en.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_es.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_fr.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_it.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_ja.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_nl.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pl.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pt_BR.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_sv.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_zh_CN.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/doxia-site-renderer.odg create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/index.apt.vm create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/resources/doxia-site-renderer.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AbstractVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AptVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AttributesVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/CommentsVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/EntitiesVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/FaqVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/HeadVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/JavascriptVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MiscVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MultipleBlockVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/NestedItemsVerifier.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/SkinResourceLoaderTest.java create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-lat1.ent create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-special.ent create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-symbol.ent create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml1-transitional.dtd create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/log4j.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.expected.txt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-plugin.properties create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/entityTest.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/xdoc-2.0.xsd create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/apt.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/cdc.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/extension.apt.not.at.end.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/interpolation.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/anchor.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/code.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/escapes.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/figure.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/linebreak.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/link.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-format.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list-heterogenous.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/note-tip-info.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-figure.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-header.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-list.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/section.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-list.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-paragraph.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table-link.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/test.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/unknown-macro.confluence create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/docbook.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/sdocbook_full.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/fml/faq.fml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/resources/confluence/images/build-by-maven-white.png create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/attributes.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/head.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/javascript.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/macro.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/misc.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/multipleblock.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/nestedItems.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/xhtml-lat1.ent create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-skin-model/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/main/mdo/skin.mdo create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/apt/index.apt create mode 100644 Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/pom.xml create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/resources/download.cgi create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/resources/images/doxia-sitetools-deps.png create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/site.xml create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/xdoc/download.xml.vm create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/xdoc/doxia-sitetools-deps.odg create mode 100644 Java-base/maven-doxia-sitetools/src/src/site/xdoc/index.xml create mode 100644 Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/Dockerfile create mode 100644 Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/buggy.java create mode 100644 Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/metadata.json create mode 100644 Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/npe.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_113/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_113/buggy.java create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_113/metadata.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_113/npe.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_78/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_78/buggy.java create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_78/metadata.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_78/npe.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_98/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_98/buggy.java create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_98/metadata.json create mode 100644 Java/maven-doxia-sitetools-DecorationUtils_98/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_190/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_190/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_190/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_190/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_525/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_525/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_525/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_525/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_647/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_647/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_647/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_647/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_736/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_736/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_736/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_736/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_738/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_738/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_738/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_738/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_773/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_773/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_773/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_773/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_821/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_821/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_821/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_821/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_828/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_828/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_828/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_828/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_832/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_832/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_832/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteRenderer_832/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_1036/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_1036/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_1036/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_1036/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_349/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_349/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_349/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_349/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_492/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_492/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_492/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_492/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_587/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_587/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_587/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_587/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_686/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_686/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_686/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_686/npe.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_771/Dockerfile create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_771/buggy.java create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_771/metadata.json create mode 100644 Java/maven-doxia-sitetools-DefaultSiteTool_771/npe.json create mode 100644 Java/maven-doxia-sitetools-FoPdfRenderer_90/Dockerfile create mode 100644 Java/maven-doxia-sitetools-FoPdfRenderer_90/buggy.java create mode 100644 Java/maven-doxia-sitetools-FoPdfRenderer_90/metadata.json create mode 100644 Java/maven-doxia-sitetools-FoPdfRenderer_90/npe.json create mode 100644 Java/maven-doxia-sitetools-ITextPdfRenderer_152/Dockerfile create mode 100644 Java/maven-doxia-sitetools-ITextPdfRenderer_152/buggy.java create mode 100644 Java/maven-doxia-sitetools-ITextPdfRenderer_152/metadata.json create mode 100644 Java/maven-doxia-sitetools-ITextPdfRenderer_152/npe.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_118/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_118/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_118/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_118/npe.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_136/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_136/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_136/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_136/npe.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_141/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_141/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_141/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_141/npe.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_206/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_206/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_206/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_206/npe.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_232/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_232/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_232/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathDescriptor_232/npe.json create mode 100644 Java/maven-doxia-sitetools-PathUtils_57/Dockerfile create mode 100644 Java/maven-doxia-sitetools-PathUtils_57/buggy.java create mode 100644 Java/maven-doxia-sitetools-PathUtils_57/metadata.json create mode 100644 Java/maven-doxia-sitetools-PathUtils_57/npe.json create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_269/Dockerfile create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_269/buggy.java create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_269/metadata.json create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_269/npe.json create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_305/Dockerfile create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_305/buggy.java create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_305/metadata.json create mode 100644 Java/maven-doxia-sitetools-SiteRendererSink_305/npe.json create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_166/Dockerfile create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_166/buggy.java create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_166/metadata.json create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_166/npe.json create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_222/Dockerfile create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_222/buggy.java create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_222/metadata.json create mode 100644 Java/maven-doxia-sitetools-URIPathDescriptor_222/npe.json diff --git a/Java-base/maven-doxia-sitetools/Dockerfile b/Java-base/maven-doxia-sitetools/Dockerfile new file mode 100644 index 000000000..e208c4890 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update \ + && apt-get install -y \ + build-essential \ + git \ + vim \ + jq \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/list/* + +RUN apt-get -y install sudo \ + openjdk-8-jdk \ + maven + +RUN bash -c "echo 2 | update-alternatives --config java" + +COPY src /workspace +WORKDIR /workspace + +RUN mvn install -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false + +RUN mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 + +ENV TZ=Asia/Seoul diff --git a/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/buggy.java b/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/buggy.java new file mode 100644 index 000000000..707694137 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/buggy.java @@ -0,0 +1,713 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.util.XmlValidator; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.context.Context; + +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.SiteResourceLoader; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + * Abstract document renderer. + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +public abstract class AbstractDocumentRenderer + extends AbstractLogEnabled + implements DocumentRenderer +{ + @Requirement + protected ParserModuleManager parserModuleManager; + + @Requirement + protected Doxia doxia; + + @Requirement + private VelocityComponent velocity; + + /** + * The common base directory of source files. + */ + private String baseDir; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @deprecated since 1.1.2, use {@link #render(Map, File, DocumentModel, DocumentRendererContext)} + */ + public abstract void render( Map filesToProcess, File outputDirectory, + DocumentModel documentModel ) + throws DocumentRendererException, IOException; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** {@inheritDoc} */ + public void render( Collection files, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( files ), outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, documentModel, null ); + } + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render a document from the files found in a source directory, depending on a rendering context. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * If the model contains a TOC, only the files found in this TOC are rendered, + * otherwise all files found under baseDirectory will be processed. + * If the model is null, render all files from baseDirectory individually. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( baseDirectory ), outputDirectory, documentModel, context ); + } + + /** + * Render a document from the files found in baseDirectory. This just forwards to + * {@link #render(File,File,DocumentModel)} with a new DocumentModel. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File, DocumentModel) + */ + public void render( File baseDirectory, File outputDirectory ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, (DocumentModel) null ); + } + + /** + * Render a document from the files found in baseDirectory. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentDescriptor a file containing the document model. + * If this file does not exist or is null, some default settings will be used. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File) if documentDescriptor does not exist or is null + * @see #render(Map, File, DocumentModel) otherwise + */ + public void render( File baseDirectory, File outputDirectory, File documentDescriptor ) + throws DocumentRendererException, IOException + { + if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) ) + { + getLogger().warn( "No documentDescriptor found: using default settings!" ); + + render( baseDirectory, outputDirectory ); + } + else + { + render( getFilesToProcess( baseDirectory ), outputDirectory, readDocumentModel( documentDescriptor ), + null ); + } + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.1 + * @deprecated since 1.1.2, use {@link #renderIndividual(Map, File, DocumentRendererContext)} + */ + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @return a Map of files to process. + * @throws java.io.IOException in case of a problem reading the files under baseDirectory. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + */ + public Map getFilesToProcess( File baseDirectory ) + throws IOException, DocumentRendererException + { + if ( !baseDirectory.isDirectory() ) + { + getLogger().warn( "No files found to process!" ); + + return new HashMap(); + } + + setBaseDir( baseDirectory.getAbsolutePath() ); + + Map filesToProcess = new LinkedHashMap(); + Map duplicatesFiles = new LinkedHashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( baseDirectory, module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + // TODO: handle in/excludes + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", null, false ); + + String[] extensions = getExtensions( module ); + List docs = new LinkedList( allFiles ); + // Take care of extension case + for ( Iterator it = docs.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, extensions ) ) + { + it.remove(); + } + } + + String[] vmExtensions = new String[extensions.length]; + for ( int i = 0; i < extensions.length; i++ ) + { + vmExtensions[i] = extensions[i] + ".vm"; + } + List velocityFiles = new LinkedList( allFiles ); + // *.xml.vm + for ( Iterator it = velocityFiles.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, vmExtensions ) ) + { + it.remove(); + } + } + docs.addAll( velocityFiles ); + + for ( String filePath : docs ) + { + filePath = filePath.trim(); + + if ( filePath.lastIndexOf( '.' ) > 0 ) + { + String key = filePath.substring( 0, filePath.lastIndexOf( '.' ) ); + + if ( duplicatesFiles.containsKey( key ) ) + { + throw new DocumentRendererException( "Files '" + module.getSourceDirectory() + + File.separator + filePath + "' clashes with existing '" + + duplicatesFiles.get( key ) + "'." ); + } + + duplicatesFiles.put( key, module.getSourceDirectory() + File.separator + filePath ); + } + + filesToProcess.put( filePath, module ); + } + } + } + + return filesToProcess; + } + + protected static String[] getExtensions( ParserModule module ) + { + String[] extensions = new String[module.getExtensions().length]; + for ( int i = module.getExtensions().length - 1; i >= 0; i-- ) + { + extensions[i] = '.' + module.getExtensions()[i]; + } + return extensions; + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + protected static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + protected static boolean endsWithIgnoreCase( String str, String[] searchStrs ) + { + for ( String searchStr : searchStrs ) + { + if ( endsWithIgnoreCase( str, searchStr ) ) + { + return true; + } + } + return false; + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param files The Collection of source files. + * @return a Map of files to process. + */ + public Map getFilesToProcess( Collection files ) + { + // ---------------------------------------------------------------------- + // Map all the file names to parser ids + // ---------------------------------------------------------------------- + + Map filesToProcess = new HashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + String[] extensions = getExtensions( module ); + + String sourceDirectory = File.separator + module.getSourceDirectory() + File.separator; + + for ( String file : files ) + { + // first check if the file path contains one of the recognized source dir identifiers + // (there's trouble if a pathname contains 2 identifiers), then match file extensions (not unique). + + if ( file.indexOf( sourceDirectory ) != -1 ) + { + filesToProcess.put( file, module ); + } + else + { + // don't overwrite if it's there already + if ( endsWithIgnoreCase( file, extensions ) && !filesToProcess.containsKey( file ) ) + { + filesToProcess.put( file, module ); + } + } + } + } + + return filesToProcess; + } + + /** {@inheritDoc} */ + public DocumentModel readDocumentModel( File documentDescriptor ) + throws DocumentRendererException, IOException + { + DocumentModel documentModel; + + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( documentDescriptor ); + documentModel = new DocumentXpp3Reader().read( reader ); + } + catch ( XmlPullParserException e ) + { + throw new DocumentRendererException( "Error parsing document descriptor", e ); + } + finally + { + IOUtil.close( reader ); + } + + return documentModel; + } + + /** + * Sets the current base directory. + * + * @param newDir the absolute path to the base directory to set. + */ + public void setBaseDir( String newDir ) + { + this.baseDir = newDir; + } + + /** + * Return the current base directory. + * + * @return the current base directory. + */ + public String getBaseDir() + { + return this.baseDir; + } + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + * @deprecated since 1.1.2, use {@link #parse(String, String, Sink, DocumentRendererContext)} + */ + protected void parse( String fullDocPath, String parserId, Sink sink ) + throws DocumentRendererException, IOException + { + parse( fullDocPath, parserId, sink, null ); + } + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + */ +/** + * Parse a source document into a sink. + * + * @param fullDocPath + * absolute path to the source document. + * @param parserId + * determines the parser to use. + * @param sink + * the sink to receive the events. + * @param context + * the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException + * in case of a parsing error. + * @throws java.io.IOException + * if the source document cannot be opened. + */ +protected void parse(java.lang.String fullDocPath, java.lang.String parserId, org.apache.maven.doxia.sink.Sink sink, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + if (getLogger().isDebugEnabled()) { + getLogger().debug("Parsing file " + fullDocPath); + } + java.io.Reader reader = null; + try { + java.io.File f = new java.io.File(fullDocPath); + org.apache.maven.doxia.parser.Parser parser = doxia.getParser(parserId); + switch (parser.getType()) { + case org.apache.maven.doxia.parser.Parser.XML_TYPE : + reader = org.codehaus.plexus.util.ReaderFactory.newXmlReader(f); + if (org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer.isVelocityFile(f)) { + reader = getVelocityReader(f, ((org.codehaus.plexus.util.xml.XmlStreamReader) (reader)).getEncoding(), context); + } + if ((context != null) && java.lang.Boolean.TRUE.equals(((java.lang.Boolean) (context.get("validate"))))) { + reader = validate(reader, fullDocPath); + } + break; + case org.apache.maven.doxia.parser.Parser.TXT_TYPE : + case org.apache.maven.doxia.parser.Parser.UNKNOWN_TYPE : + default : + if (org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer.isVelocityFile(f)) { + reader = getVelocityReader(f, context == null ? org.codehaus.plexus.util.ReaderFactory.FILE_ENCODING : context.getInputEncoding(), context); + } else { + { + reader = org.codehaus.plexus.util.ReaderFactory.newReader(f, /* NPEX_NULL_EXP */ + context.getInputEncoding()); + } + } + } + sink.enableLogging(new org.apache.maven.doxia.logging.PlexusLoggerWrapper(getLogger())); + doxia.parse(reader, parserId, sink); + } catch (org.apache.maven.doxia.parser.manager.ParserNotFoundException e) { + throw new org.apache.maven.doxia.docrenderer.DocumentRendererException((((("No parser '" + parserId) + "' found for ") + fullDocPath) + ": ") + e.getMessage(), e); + } catch (org.apache.maven.doxia.parser.ParseException e) { + throw new org.apache.maven.doxia.docrenderer.DocumentRendererException((("Error parsing " + fullDocPath) + ": ") + e.getMessage(), e); + } finally { + org.codehaus.plexus.util.IOUtil.close(reader); + sink.flush(); + } +} + + /** + * Copies the contents of the resource directory to an output folder. + * + * @param outputDirectory the destination folder. + * @throws java.io.IOException if any. + */ + protected void copyResources( File outputDirectory ) + throws IOException + { + File resourcesDirectory = new File( getBaseDir(), "resources" ); + + if ( !resourcesDirectory.isDirectory() ) + { + return; + } + + if ( !outputDirectory.exists() ) + { + outputDirectory.mkdirs(); + } + + copyDirectory( resourcesDirectory, outputDirectory ); + } + + /** + * Copy content of a directory, excluding scm-specific files. + * + * @param source directory that contains the files and sub-directories to be copied. + * @param destination destination folder. + * @throws java.io.IOException if any. + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.isDirectory() && destination.isDirectory() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + /** + * @param documentModel not null + * @return the output name defined in the documentModel without the output extension. If the output name is not + * defined, return target by default. + * @since 1.1.1 + * @see org.apache.maven.doxia.document.DocumentModel#getOutputName() + * @see #getOutputExtension() + */ + protected String getOutputName( DocumentModel documentModel ) + { + String outputName = documentModel.getOutputName(); + if ( outputName == null ) + { + getLogger().info( "No outputName is defined in the document descriptor. Using 'target'" ); + + documentModel.setOutputName( "target" ); + } + + outputName = outputName.trim(); + if ( outputName.toLowerCase( Locale.ENGLISH ).endsWith( "." + getOutputExtension() ) ) + { + outputName = + outputName.substring( 0, outputName.toLowerCase( Locale.ENGLISH ) + .lastIndexOf( "." + getOutputExtension() ) ); + } + documentModel.setOutputName( outputName ); + + return documentModel.getOutputName(); + } + + /** + * TODO: DOXIA-111: we need a general filter here that knows how to alter the context + * + * @param f the file to process, not null + * @param encoding the wanted encoding, not null + * @param context the current render document context not null + * @return a reader with + * @throws DocumentRendererException + */ + private Reader getVelocityReader( File f, String encoding, DocumentRendererContext context ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Velocity render for " + f.getAbsolutePath() ); + } + + SiteResourceLoader.setResource( f.getAbsolutePath() ); + + Context velocityContext = new VelocityContext(); + + if ( context.getKeys() != null ) + { + for ( int i = 0; i < context.getKeys().length; i++ ) + { + String key = (String) context.getKeys()[i]; + + velocityContext.put( key, context.get( key ) ); + } + } + + StringWriter sw = new StringWriter(); + try + { + velocity.getEngine().mergeTemplate( f.getAbsolutePath(), encoding, velocityContext, sw ); + } + catch ( Exception e ) + { + throw new DocumentRendererException( "Error whenn parsing Velocity file " + f.getAbsolutePath() + ": " + + e.getMessage(), e ); + } + + return new StringReader( sw.toString() ); + } + + /** + * @param f not null + * @return true if file has a vm extension, false otherwise. + */ + private static boolean isVelocityFile( File f ) + { + return FileUtils.getExtension( f.getAbsolutePath() ).toLowerCase( Locale.ENGLISH ).endsWith( "vm" ); + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/npe.json b/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/npe.json new file mode 100644 index 000000000..e5d1ce951 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/AbstractDocumentRenderer_532/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java", + "line": 534, + "npe_method": "parse", + "deref_field": "context", + "npe_class": "AbstractDocumentRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/buggy.java new file mode 100644 index 000000000..3aa767149 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/buggy.java @@ -0,0 +1,127 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ + public static Xpp3Dom getCustomChild( Xpp3Dom custom, String path ) + { + String[] elements = path.split( "\\." ); + for ( String element : elements ) + { + if ( custom == null ) + { + return null; + } + custom = custom.getChild( element ); + } + return custom; + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? null : custom.getValue(); + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @param defaultValue + * default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ +public static java.lang.String getCustomValue(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path, java.lang.String defaultValue) { + custom = org.apache.maven.doxia.site.decoration.DecorationUtils.getCustomChild(/* NPEX_NULL_EXP */ + custom, path); + return custom.getValue(); +} +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/npe.json b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/npe.json new file mode 100644 index 000000000..1bc58a283 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_113/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 124, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/buggy.java new file mode 100644 index 000000000..75dd00cef --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/buggy.java @@ -0,0 +1,122 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ +public static org.codehaus.plexus.util.xml.Xpp3Dom getCustomChild(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path) { + java.lang.String[] elements = path.split("\\."); + for (java.lang.String element : elements) { + { + custom = /* NPEX_NULL_EXP */ + custom.getChild(element); + } + } + return custom; +} + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? null : custom.getValue(); + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path, String defaultValue ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? defaultValue : custom.getValue(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/npe.json b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/npe.json new file mode 100644 index 000000000..94ad4391a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_78/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 88, + "npe_method": "getCustomChild", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/buggy.java new file mode 100644 index 000000000..b829f8b02 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/buggy.java @@ -0,0 +1,125 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ + public static Xpp3Dom getCustomChild( Xpp3Dom custom, String path ) + { + String[] elements = path.split( "\\." ); + for ( String element : elements ) + { + if ( custom == null ) + { + return null; + } + custom = custom.getChild( element ); + } + return custom; + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ +public static java.lang.String getCustomValue(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path) { + custom = org.apache.maven.doxia.site.decoration.DecorationUtils.getCustomChild(/* NPEX_NULL_EXP */ + custom, path); + return custom.getValue(); +} + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path, String defaultValue ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? defaultValue : custom.getValue(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/npe.json b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/npe.json new file mode 100644 index 000000000..43859cec3 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DecorationUtils_98/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 107, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/buggy.java new file mode 100644 index 000000000..6f5f43f73 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + { + relativizeBannerPaths(/* NPEX_NULL_EXP */ + decoration.getBannerLeft(), baseUrl); + } + if (decoration.getBannerRight() != null) { + relativizeBannerPaths(decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + if (decoration.getBody() != null) { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/npe.json new file mode 100644 index 000000000..9b36f9053 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_126/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 128, + "npe_method": "resolvePaths", + "deref_field": "getBannerLeft", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/buggy.java new file mode 100644 index 000000000..31a154fe2 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + if (decoration.getBannerLeft() != null) { + relativizeBannerPaths(decoration.getBannerLeft(), baseUrl); + } + { + relativizeBannerPaths(/* NPEX_NULL_EXP */ + decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + if (decoration.getBody() != null) { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/npe.json new file mode 100644 index 000000000..a41cc7848 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_131/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 131, + "npe_method": "resolvePaths", + "deref_field": "getBannerRight", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/buggy.java new file mode 100644 index 000000000..e02a26aff --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + if (decoration.getBannerLeft() != null) { + relativizeBannerPaths(decoration.getBannerLeft(), baseUrl); + } + if (decoration.getBannerRight() != null) { + relativizeBannerPaths(decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : /* NPEX_NULL_EXP */ + decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/npe.json new file mode 100644 index 000000000..19914fc51 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultDecorationModelInheritanceAssembler_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 137, + "npe_method": "resolvePaths", + "deref_field": "getBody", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/buggy.java new file mode 100644 index 000000000..2a14c2ead --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/buggy.java @@ -0,0 +1,1178 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public java.util.Map locateDocumentFiles(org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext, boolean editable) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + java.util.Map files = new java.util.LinkedHashMap(); + java.util.Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + // look in every site directory (in general src/site or target/generated-site) + for (java.io.File siteDirectory : siteRenderingContext.getSiteDirectories()) { + if (siteDirectory.exists()) { + java.util.Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for (org.apache.maven.doxia.parser.module.ParserModule module : modules) { + java.io.File moduleBasedir = new java.io.File(siteDirectory, module.getSourceDirectory()); + java.lang.String excludes = /* NPEX_NULL_EXP */ + moduleExcludes.get(module.getParserId()); + addModuleFiles(siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, editable); + } + } + } + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for (org.apache.maven.doxia.siterenderer.ExtraDoxiaModuleReference module : siteRenderingContext.getModules()) { + try { + org.apache.maven.doxia.parser.module.ParserModule parserModule = parserModuleManager.getParserModule(module.getParserId()); + java.lang.String excludes = (moduleExcludes == null) ? null : moduleExcludes.get(module.getParserId()); + addModuleFiles(siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, files, editable); + } catch (org.apache.maven.doxia.parser.module.ParserModuleNotFoundException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Unable to find module: " + e.getMessage(), e); + } + } + return files; +} + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/npe.json new file mode 100644 index 000000000..d7a4ae3bd --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_190/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 186, + "npe_method": "locateDocumentFiles", + "deref_field": "moduleExcludes", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/buggy.java new file mode 100644 index 000000000..acfd6afc5 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/buggy.java @@ -0,0 +1,1177 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ +/** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext + * the site rendering context + * @return a Velocity tools managed context + */ +protected org.apache.velocity.context.Context createToolManagedVelocityContext(org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) { + java.util.Locale locale = siteRenderingContext.getLocale(); + java.lang.String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + org.apache.velocity.tools.config.EasyFactoryConfiguration config = new org.apache.velocity.tools.config.EasyFactoryConfiguration(false); + config.property("safeMode", java.lang.Boolean.FALSE); + config.toolbox(org.apache.velocity.tools.Scope.REQUEST).tool(org.apache.velocity.tools.generic.ContextTool.class).tool(org.apache.velocity.tools.generic.LinkTool.class).tool(org.apache.velocity.tools.generic.LoopTool.class).tool(org.apache.velocity.tools.generic.RenderTool.class); + config.toolbox(org.apache.velocity.tools.Scope.APPLICATION).property("locale", locale).tool(org.apache.velocity.tools.generic.AlternatorTool.class).tool(org.apache.velocity.tools.generic.ClassTool.class).tool(org.apache.velocity.tools.generic.ComparisonDateTool.class).property("format", dateFormat).tool(org.apache.velocity.tools.generic.ConversionTool.class).property("dateFormat", dateFormat).tool(org.apache.velocity.tools.generic.DisplayTool.class).tool(org.apache.velocity.tools.generic.EscapeTool.class).tool(org.apache.velocity.tools.generic.FieldTool.class).tool(org.apache.velocity.tools.generic.MathTool.class).tool(org.apache.velocity.tools.generic.NumberTool.class).tool(org.apache.velocity.tools.generic.ResourceTool.class).property("bundles", new java.lang.String[]{ "site-renderer" }).tool(org.apache.velocity.tools.generic.SortTool.class).tool(org.apache.velocity.tools.generic.XmlTool.class); + org.apache.velocity.tools.config.FactoryConfiguration customConfig = org.apache.velocity.tools.config.ConfigurationUtils.findInClasspath(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.TOOLS_LOCATION); + { + config.addConfiguration(/* NPEX_NULL_EXP */ + customConfig); + } + org.apache.velocity.tools.ToolManager manager = new org.apache.velocity.tools.ToolManager(false, false); + manager.configure(config); + return manager.createContext(); +} + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/npe.json new file mode 100644 index 000000000..58e29ad93 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_525/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 514, + "npe_method": "createToolManagedVelocityContext", + "deref_field": "customConfig", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/buggy.java new file mode 100644 index 000000000..5f9eeeebf --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/buggy.java @@ -0,0 +1,1180 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ +/** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content + * the document content to be merged into the template + * @param siteRenderingContext + * the site rendering context + * @return + */ +protected org.apache.velocity.context.Context createSiteTemplateVelocityContext(org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) { + // first get the context from document + org.apache.velocity.context.Context context = createDocumentVelocityContext(content.getRenderingContext(), siteRenderingContext); + // then add data objects from rendered document + // Add infos from document + context.put("authors", content.getAuthors()); + context.put("shortTitle", content.getTitle()); + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + java.lang.String title = ""; + if ((siteRenderingContext.getDecoration() != null) && (siteRenderingContext.getDecoration().getName() != null)) { + title = siteRenderingContext.getDecoration().getName(); + } else { + { + title = /* NPEX_NULL_EXP */ + siteRenderingContext.getDefaultWindowTitle(); + } + } + if (title.length() > 0) { + title += " – ";// Symbol Name: En Dash, Html Entity: – + + } + title += content.getTitle(); + context.put("title", title); + context.put("headContent", content.getHead()); + context.put("bodyContent", content.getBody()); + // document date (got from Doxia Sink date() API) + java.lang.String documentDate = content.getDate(); + if (org.codehaus.plexus.util.StringUtils.isNotEmpty(documentDate)) { + context.put("documentDate", documentDate); + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try { + // we support only ISO 8601 date + java.util.Date creationDate = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(documentDate); + context.put("creationDate", creationDate); + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMdd"); + context.put("dateCreation", sdf.format(creationDate)); + } catch (java.text.ParseException e) { + getLogger().warn(((("Could not parse date '" + documentDate) + "' from ") + content.getRenderingContext().getInputName()) + " (expected yyyy-MM-dd format), ignoring!"); + } + } + // document rendering context, to get eventual inputName + context.put("docRenderingContext", content.getRenderingContext()); + return context; +} + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/npe.json new file mode 100644 index 000000000..34b3d9d45 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_647/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 651, + "npe_method": "createSiteTemplateVelocityContext", + "deref_field": "getDefaultWindowTitle", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/buggy.java new file mode 100644 index 000000000..53c640474 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = /* NPEX_NULL_EXP */ + skinModel.getEncoding(); + template = (encoding == null) ? velocity.getEngine().getTemplate(templateName) : velocity.getEngine().getTemplate(templateName, encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + if (old != null) { + java.lang.Thread.currentThread().setContextClassLoader(old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/npe.json new file mode 100644 index 000000000..02e49157f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_736/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 725, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "skinModel", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/buggy.java new file mode 100644 index 000000000..fdaf4cd5f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = (skinModel == null) ? null : skinModel.getEncoding(); + template = velocity.getEngine().getTemplate(templateName, /* NPEX_NULL_EXP */ + encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + if (old != null) { + java.lang.Thread.currentThread().setContextClassLoader(old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/npe.json new file mode 100644 index 000000000..c87c1ad1a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_738/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 726, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "encoding", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/buggy.java new file mode 100644 index 000000000..0515ad48f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = (skinModel == null) ? null : skinModel.getEncoding(); + template = (encoding == null) ? velocity.getEngine().getTemplate(templateName) : velocity.getEngine().getTemplate(templateName, encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + { + java.lang.Thread.currentThread().setContextClassLoader(/* NPEX_NULL_EXP */ + old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/npe.json new file mode 100644 index 000000000..55be19965 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_773/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 744, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "old", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/buggy.java new file mode 100644 index 000000000..c771b6653 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(/* NPEX_NULL_EXP */ + skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = (skinModel.getPrerequisites() == null) ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = (p == null) ? null : p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/npe.json new file mode 100644 index 000000000..76c2d1eb4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_821/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 814, + "npe_method": "createContextForSkin", + "deref_field": "skinDescriptorEntry", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/buggy.java new file mode 100644 index 000000000..1add25b38 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = /* NPEX_NULL_EXP */ + skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = (p == null) ? null : p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/npe.json new file mode 100644 index 000000000..0a6d2c958 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_828/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 817, + "npe_method": "createContextForSkin", + "deref_field": "getPrerequisites", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/buggy.java new file mode 100644 index 000000000..e680c8ab9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = (skinModel.getPrerequisites() == null) ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = /* NPEX_NULL_EXP */ + p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/npe.json new file mode 100644 index 000000000..3fb36cc22 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteRenderer_832/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 819, + "npe_method": "createContextForSkin", + "deref_field": "p", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/buggy.java new file mode 100644 index 000000000..2f4af0b05 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/buggy.java @@ -0,0 +1,1539 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ +// ---------------------------------------------------------------------- +// Protected methods +// ---------------------------------------------------------------------- +/** + * + * + * @param path + * could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ +protected static java.lang.String getNormalizedPath(java.lang.String path) { + java.lang.String normalized = org.apache.commons.io.FilenameUtils.normalize(path); + if (/* NPEX_NULL_EXP */ + normalized == null) { + normalized = path; + } + return normalized.replace('\\', '/'); +} + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/npe.json new file mode 100644 index 000000000..6ccc98492 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_1036/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 1043, + "npe_method": "getNormalizedPath", + "deref_field": "normalized", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/buggy.java new file mode 100644 index 000000000..0088db730 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/buggy.java @@ -0,0 +1,1529 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public java.io.File getSiteDescriptor(java.io.File siteDirectory, java.util.Locale locale) { + checkNotNull("siteDirectory", siteDirectory); + final java.util.Locale llocale = /* NPEX_NULL_EXP */ + locale; + java.io.File siteDescriptor = new java.io.File(siteDirectory, ("site_" + llocale.getLanguage()) + ".xml"); + if (!siteDescriptor.isFile()) { + siteDescriptor = new java.io.File(siteDirectory, "site.xml"); + } + return siteDescriptor; +} + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/npe.json new file mode 100644 index 000000000..a61ad53a0 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_349/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 352, + "npe_method": "getSiteDescriptor", + "deref_field": "locale", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/buggy.java new file mode 100644 index 000000000..26e43d495 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/buggy.java @@ -0,0 +1,1503 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.site.decoration.DecorationModel getDecorationModel(java.io.File siteDirectory, java.util.Locale locale, org.apache.maven.project.MavenProject project, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository, java.util.List repositories) throws org.apache.maven.doxia.tools.SiteToolException { + checkNotNull("project", project); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + checkNotNull("repositories", repositories); + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + getLogger().debug((("Computing decoration model of " + project.getId()) + " for locale ") + llocale); + java.util.Map.Entry result = getDecorationModel(0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories); + org.apache.maven.doxia.site.decoration.DecorationModel decorationModel = result.getKey(); + org.apache.maven.project.MavenProject parentProject = result.getValue(); + if (decorationModel == null) { + getLogger().debug("Using default site descriptor"); + java.lang.String siteDescriptorContent; + java.io.Reader reader = null; + try { + // Note the default is not a super class - it is used when nothing else is found + reader = org.codehaus.plexus.util.ReaderFactory.newXmlReader(getClass().getResourceAsStream("/default-site.xml")); + siteDescriptorContent = readSiteDescriptor(reader, "default-site.xml"); + } catch (java.io.IOException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Error reading default site descriptor: " + e.getMessage(), e); + } finally { + org.codehaus.plexus.util.IOUtil.close(reader); + } + decorationModel = readDecorationModel(siteDescriptorContent); + } + // DecorationModel back to String to interpolate, then go back to DecorationModel + java.lang.String siteDescriptorContent = decorationModelToString(decorationModel); + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent(project, siteDescriptorContent, false); + decorationModel = readDecorationModel(siteDescriptorContent); + { + populateParentMenu(decorationModel, llocale, project, /* NPEX_NULL_EXP */ + parentProject, true); + } + try { + populateModulesMenu(decorationModel, llocale, project, reactorProjects, localRepository, true); + } catch (java.io.IOException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Error while populating modules menu: " + e.getMessage(), e); + } + if (decorationModel.getBannerLeft() == null) { + // extra default to set + org.apache.maven.doxia.site.decoration.Banner banner = new org.apache.maven.doxia.site.decoration.Banner(); + banner.setName(project.getName()); + decorationModel.setBannerLeft(banner); + } + return decorationModel; +} + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/npe.json new file mode 100644 index 000000000..119293c86 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_492/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 475, + "npe_method": "getDecorationModel", + "deref_field": "parentProject", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/buggy.java new file mode 100644 index 000000000..d79a3fb55 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/buggy.java @@ -0,0 +1,1491 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.project.MavenProject getParentProject(org.apache.maven.project.MavenProject aProject, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository) { + checkNotNull("aProject", aProject); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + if (org.apache.maven.doxia.tools.DefaultSiteTool.isMaven3OrMore()) { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + org.apache.maven.project.MavenProject parentProject = null; + org.apache.maven.project.MavenProject origParent = aProject.getParent(); + { + for (org.apache.maven.project.MavenProject reactorProject : reactorProjects) { + if ((reactorProject.getGroupId().equals(/* NPEX_NULL_EXP */ + origParent.getGroupId()) && reactorProject.getArtifactId().equals(origParent.getArtifactId())) && reactorProject.getVersion().equals(origParent.getVersion())) { + parentProject = reactorProject; + getLogger().debug(("Parent project " + origParent.getId()) + " picked from reactor"); + break; + } + } + if (((parentProject == null) && (aProject.getBasedir() != null)) && org.codehaus.plexus.util.StringUtils.isNotEmpty(aProject.getModel().getParent().getRelativePath())) { + try { + java.lang.String relativePath = aProject.getModel().getParent().getRelativePath(); + java.io.File pomFile = new java.io.File(aProject.getBasedir(), relativePath); + if (pomFile.isDirectory()) { + pomFile = new java.io.File(pomFile, "pom.xml"); + } + pomFile = new java.io.File(org.apache.maven.doxia.tools.DefaultSiteTool.getNormalizedPath(pomFile.getPath())); + if (pomFile.isFile()) { + org.apache.maven.project.MavenProject mavenProject = mavenProjectBuilder.build(pomFile, localRepository, null); + if ((mavenProject.getGroupId().equals(origParent.getGroupId()) && mavenProject.getArtifactId().equals(origParent.getArtifactId())) && mavenProject.getVersion().equals(origParent.getVersion())) { + parentProject = mavenProject; + getLogger().debug((("Parent project " + origParent.getId()) + " loaded from a relative path: ") + relativePath); + } + } + } catch (org.apache.maven.project.ProjectBuildingException e) { + getLogger().info((("Unable to load parent project " + origParent.getId()) + " from a relative path: ") + e.getMessage()); + } + } + if (parentProject == null) { + try { + parentProject = mavenProjectBuilder.buildFromRepository(aProject.getParentArtifact(), aProject.getRemoteArtifactRepositories(), localRepository); + getLogger().debug(("Parent project " + origParent.getId()) + " loaded from repository"); + } catch (org.apache.maven.project.ProjectBuildingException e) { + getLogger().warn((("Unable to load parent project " + origParent.getId()) + " from repository: ") + e.getMessage()); + } + } + if (parentProject == null) { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + parentProject = origParent; + getLogger().debug(("Parent project " + origParent.getId()) + " picked from original value"); + } + } + return parentProject; +} + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/npe.json new file mode 100644 index 000000000..07e55beb0 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_587/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 587, + "npe_method": "getParentProject", + "deref_field": "origParent", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/buggy.java new file mode 100644 index 000000000..fbcecbbd7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/buggy.java @@ -0,0 +1,1520 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ +/** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel + * the Doxia Sitetools DecorationModel, not null. + * @param locale + * the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project + * a Maven project, not null. + * @param parentProject + * a Maven parent project, not null. + * @param keepInheritedRefs + * used for inherited references. + */ +private void populateParentMenu(org.apache.maven.doxia.site.decoration.DecorationModel decorationModel, java.util.Locale locale, org.apache.maven.project.MavenProject project, org.apache.maven.project.MavenProject parentProject, boolean keepInheritedRefs) { + checkNotNull("decorationModel", decorationModel); + checkNotNull("project", project); + checkNotNull("parentProject", parentProject); + org.apache.maven.doxia.site.decoration.Menu menu = decorationModel.getMenuRef("parent"); + { + if (keepInheritedRefs && /* NPEX_NULL_EXP */ + menu.isInheritAsRef()) { + return; + } + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + java.lang.String parentUrl = org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(parentProject); + if (parentUrl != null) { + if (parentUrl.endsWith("/")) { + parentUrl += "index.html"; + } else { + parentUrl += "/index.html"; + } + parentUrl = getRelativePath(parentUrl, org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(project)); + } else { + // parent has no url, assume relative path is given by site structure + java.io.File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if (parentBasedir != null) { + // Try to find the relative path to the parent via the file system + java.lang.String parentPath = parentBasedir.getAbsolutePath(); + java.lang.String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath(parentPath, projectPath) + "/index.html"; + } + } + // Only add the parent menu if we were able to find a URL for it + if (parentUrl == null) { + getLogger().warn("Unable to find a URL to the parent project. The parent menu will NOT be added."); + } else { + if (menu.getName() == null) { + menu.setName(i18n.getString("site-tool", llocale, "decorationModel.menu.parentproject")); + } + org.apache.maven.doxia.site.decoration.MenuItem item = new org.apache.maven.doxia.site.decoration.MenuItem(); + item.setName(parentProject.getName()); + item.setHref(parentUrl); + menu.addItem(item); + } + } +} + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/npe.json new file mode 100644 index 000000000..62b60039b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_686/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 699, + "npe_method": "populateParentMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/buggy.java b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/buggy.java new file mode 100644 index 000000000..f98f1f211 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/buggy.java @@ -0,0 +1,1519 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ +/** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel + * the Doxia Sitetools DecorationModel, not null. + * @param locale + * the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project + * a Maven project, not null. + * @param reactorProjects + * the Maven reactor projects, not null. + * @param localRepository + * the Maven local repository, not null. + * @param keepInheritedRefs + * used for inherited references. + * @throws SiteToolException + * if any + * @throws IOException + * + */ +private void populateModulesMenu(org.apache.maven.doxia.site.decoration.DecorationModel decorationModel, java.util.Locale locale, org.apache.maven.project.MavenProject project, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository, boolean keepInheritedRefs) throws org.apache.maven.doxia.tools.SiteToolException, java.io.IOException { + checkNotNull("project", project); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + checkNotNull("decorationModel", decorationModel); + org.apache.maven.doxia.site.decoration.Menu menu = decorationModel.getMenuRef("modules"); + { + if (keepInheritedRefs && /* NPEX_NULL_EXP */ + menu.isInheritAsRef()) { + return; + } + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + // we require child modules and reactors to process module menu + if (project.getModules().size() > 0) { + if (menu.getName() == null) { + menu.setName(i18n.getString("site-tool", llocale, "decorationModel.menu.projectmodules")); + } + for (java.lang.String module : ((java.util.List) (project.getModules()))) { + org.apache.maven.project.MavenProject moduleProject = org.apache.maven.doxia.tools.DefaultSiteTool.getModuleFromReactor(project, reactorProjects, module); + if (moduleProject == null) { + getLogger().warn(("Module " + module) + " not found in reactor: loading locally"); + java.io.File f = new java.io.File(project.getBasedir(), module + "/pom.xml"); + if (f.exists()) { + try { + moduleProject = mavenProjectBuilder.build(f, localRepository, null); + } catch (org.apache.maven.project.ProjectBuildingException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Unable to read local module-POM", e); + } + } else { + getLogger().warn("No filesystem module-POM available"); + moduleProject = new org.apache.maven.project.MavenProject(); + moduleProject.setName(module); + moduleProject.setDistributionManagement(new org.apache.maven.model.DistributionManagement()); + moduleProject.getDistributionManagement().setSite(new org.apache.maven.model.Site()); + moduleProject.getDistributionManagement().getSite().setUrl(module); + } + } + java.lang.String siteUrl = org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(moduleProject); + java.lang.String itemName = (moduleProject.getName() == null) ? moduleProject.getArtifactId() : moduleProject.getName(); + appendMenuItem(project, menu, itemName, siteUrl, moduleProject.getArtifactId()); + } + } else if (decorationModel.getMenuRef("modules").getInherit() == null) { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef("modules"); + } + } +} + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/npe.json b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/npe.json new file mode 100644 index 000000000..4a81867d6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/DefaultSiteTool_771/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 788, + "npe_method": "populateModulesMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/buggy.java b/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/buggy.java new file mode 100644 index 000000000..3b4d030c8 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/buggy.java @@ -0,0 +1,325 @@ +package org.apache.maven.doxia.docrenderer.pdf.fo; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.xml.transform.TransformerException; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOC; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.fo.FoAggregateSink; +import org.apache.maven.doxia.module.fo.FoSink; +import org.apache.maven.doxia.module.fo.FoSinkFactory; +import org.apache.maven.doxia.module.fo.FoUtils; +import org.apache.maven.doxia.parser.module.ParserModule; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; + +import org.xml.sax.SAXParseException; + +/** + * PDF renderer that uses Doxia's FO module. + * + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "fo" ) +public class FoPdfRenderer + extends AbstractPdfRenderer +{ + /** + * {@inheritDoc} + * @see org.apache.maven.doxia.module.fo.FoUtils#convertFO2PDF(File, File, String) + */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + // Should take care of the document model for the metadata... + generatePdf( inputFile, pdfFile, null ); + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void render(java.util.Map filesToProcess, java.io.File outputDirectory, org.apache.maven.doxia.document.DocumentModel documentModel, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + // copy resources, images, etc. + copyResources(outputDirectory); + { + java.lang.String outputName = getOutputName(/* NPEX_NULL_EXP */ + documentModel); + java.io.File outputFOFile = new java.io.File(outputDirectory, outputName + ".fo"); + if (!outputFOFile.getParentFile().exists()) { + outputFOFile.getParentFile().mkdirs(); + } + java.io.File pdfOutputFile = new java.io.File(outputDirectory, outputName + ".pdf"); + if (!pdfOutputFile.getParentFile().exists()) { + pdfOutputFile.getParentFile().mkdirs(); + } + java.io.Writer writer = null; + try { + writer = org.codehaus.plexus.util.WriterFactory.newXmlWriter(outputFOFile); + org.apache.maven.doxia.module.fo.FoAggregateSink sink = new org.apache.maven.doxia.module.fo.FoAggregateSink(writer); + java.io.File fOConfigFile = new java.io.File(outputDirectory, "pdf-config.xml"); + if (fOConfigFile.exists()) { + sink.load(fOConfigFile); + getLogger().debug("Loaded pdf config file: " + fOConfigFile.getAbsolutePath()); + } + java.lang.String generateTOC = ((context != null) && (context.get("generateTOC") != null)) ? context.get("generateTOC").toString().trim() : "start"; + int tocPosition = 0; + if ("start".equalsIgnoreCase(generateTOC)) { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_START; + } else if ("end".equalsIgnoreCase(generateTOC)) { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_END; + } else { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_NONE; + } + sink.setDocumentModel(documentModel, tocPosition); + sink.beginDocument(); + sink.coverPage(); + if (tocPosition == org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_START) { + sink.toc(); + } + if ((documentModel.getToc() == null) || (documentModel.getToc().getItems() == null)) { + getLogger().info("No TOC is defined in the document descriptor. Merging all documents."); + mergeAllSources(filesToProcess, sink, context); + } else { + getLogger().debug("Using TOC defined in the document descriptor."); + mergeSourcesFromTOC(documentModel.getToc(), sink, context); + } + if (tocPosition == org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_END) { + sink.toc(); + } + sink.endDocument(); + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + } + generatePdf(outputFOFile, pdfOutputFile, documentModel); + } +} + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputFOFile = new File( outputDirectory, output + ".fo" ); + if ( !outputFOFile.getParentFile().exists() ) + { + outputFOFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + FoSink sink = + (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() ); + sink.beginDocument(); + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + sink.endDocument(); + + generatePdf( outputFOFile, pdfOutputFile, null ); + } + } + + private void mergeAllSources( Map filesToProcess, FoAggregateSink sink, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + sink.setDocumentName( key ); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + } + + private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + parseTocItems( toc.getItems(), sink, context ); + } + + private void parseTocItems( List items, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + for ( DocumentTOCItem tocItem : items ) + { + if ( tocItem.getRef() == null ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No ref defined for tocItem " + tocItem.getName() ); + } + + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + renderModules( href, sink, tocItem, context ); + + if ( tocItem.getItems() != null ) + { + parseTocItems( tocItem.getItems(), sink, context ); + } + } + } + + private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + sink.setDocumentName( doc ); + sink.setDocumentTitle( tocItem.getName() ); + + parse( source.getPath(), module.getParserId(), sink, context ); + } + } + } + } + } + + /** + * @param inputFile + * @param pdfFile + * @param documentModel could be null + * @throws DocumentRendererException if any + * @since 1.1.1 + */ + private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating: " + pdfFile ); + } + + try + { + FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel ); + } + catch ( TransformerException e ) + { + if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) ) + { + SAXParseException sax = (SAXParseException) e.getCause(); + + StringBuilder sb = new StringBuilder(); + sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" ) + .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" ); + sb.append( e.getMessage() ); + + throw new DocumentRendererException( sb.toString() ); + } + + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/npe.json b/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/npe.json new file mode 100644 index 000000000..45abac0f4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/FoPdfRenderer_90/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java", + "line": 91, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "FoPdfRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/buggy.java b/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/buggy.java new file mode 100644 index 000000000..af95bb63a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/buggy.java @@ -0,0 +1,669 @@ +package org.apache.maven.doxia.docrenderer.pdf.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +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.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentCover; +import org.apache.maven.doxia.document.DocumentMeta; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.itext.ITextSink; +import org.apache.maven.doxia.module.itext.ITextSinkFactory; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.xml.utils.DefaultErrorHandler; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.lowagie.text.ElementTags; + +/** + * Abstract document render with the iText framework + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "itext" ) +public class ITextPdfRenderer + extends AbstractPdfRenderer +{ + /** The xslt style sheet used to transform a Document to an iText file. */ + private static final String XSLT_RESOURCE = "TOC.xslt"; + + /** The TransformerFactory. */ + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + /** The DocumentBuilderFactory. */ + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + + /** The DocumentBuilder. */ + private static final DocumentBuilder DOCUMENT_BUILDER; + + static + { + TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() ); + + try + { + DOCUMENT_BUILDER = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + } + catch ( ParserConfigurationException e ) + { + throw new RuntimeException( "Error building document :" + e.getMessage() ); + } + } + + /** {@inheritDoc} */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating : " + pdfFile ); + } + + try + { + ITextUtil.writePdf( new FileInputStream( inputFile ), new FileOutputStream( pdfFile ) ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( "Cannot create PDF from " + inputFile + ": " + e.getMessage(), e ); + } + catch ( RuntimeException e ) + { + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void render(java.util.Map filesToProcess, java.io.File outputDirectory, org.apache.maven.doxia.document.DocumentModel documentModel, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + // copy resources, images, etc. + copyResources(outputDirectory); + { + java.lang.String outputName = getOutputName(/* NPEX_NULL_EXP */ + documentModel); + java.io.File outputITextFile = new java.io.File(outputDirectory, outputName + ".xml"); + if (!outputITextFile.getParentFile().exists()) { + outputITextFile.getParentFile().mkdirs(); + } + java.io.File pdfOutputFile = new java.io.File(outputDirectory, outputName + ".pdf"); + if (!pdfOutputFile.getParentFile().exists()) { + pdfOutputFile.getParentFile().mkdirs(); + } + java.util.List iTextFiles; + if ((documentModel.getToc() == null) || (documentModel.getToc().getItems() == null)) { + getLogger().info("No TOC is defined in the document descriptor. Merging all documents."); + iTextFiles = parseAllFiles(filesToProcess, outputDirectory, context); + } else { + getLogger().debug("Using TOC defined in the document descriptor."); + iTextFiles = parseTOCFiles(outputDirectory, documentModel, context); + } + java.lang.String generateTOC = ((context != null) && (context.get("generateTOC") != null)) ? context.get("generateTOC").toString() : "start"; + java.io.File iTextFile = new java.io.File(outputDirectory, outputName + ".xml"); + java.io.File iTextOutput = new java.io.File(outputDirectory, (outputName + ".") + getOutputExtension()); + org.w3c.dom.Document document = generateDocument(iTextFiles); + transform(documentModel, document, iTextFile, generateTOC); + generatePdf(iTextFile, iTextOutput); + } +} + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputITextFile = new File( outputDirectory, output + ".xml" ); + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + parse( fullDoc, module, outputITextFile, context ); + + generatePdf( outputITextFile, pdfOutputFile ); + } + } + + //-------------------------------------------- + // + //-------------------------------------------- + + + /** + * Parse a source document and emit results into a sink. + * + * @param fullDocPath file to the source document. + * @param module the site module associated with the source document (determines the parser to use). + * @param iTextFile the resulting iText xml file. + * @throws DocumentRendererException in case of a parsing problem. + * @throws IOException if the source and/or target document cannot be opened. + */ + private void parse( File fullDoc, ParserModule module, File iTextFile, DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Parsing file " + fullDoc.getAbsolutePath() ); + } + + System.setProperty( "itext.basedir", iTextFile.getParentFile().getAbsolutePath() ); + + Writer writer = null; + ITextSink sink = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + sink = (ITextSink) new ITextSinkFactory().createSink( writer ); + + sink.setClassLoader( new URLClassLoader( new URL[] { iTextFile.getParentFile().toURI().toURL() } ) ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + finally + { + if ( sink != null ) + { + sink.flush(); + sink.close(); + } + IOUtil.close( writer ); + System.getProperties().remove( "itext.basedir" ); + } + } + + /** + * Merge all iTextFiles to a single one. + * + * @param iTextFiles list of iText xml files. + * @return Document. + * @throws DocumentRendererException if any. + * @throws IOException if any. + */ + private Document generateDocument( List iTextFiles ) + throws DocumentRendererException, IOException + { + Document document = DOCUMENT_BUILDER.newDocument(); + document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root + + for ( File iTextFile : iTextFiles ) + { + Document iTextDocument; + + try + { + iTextDocument = DOCUMENT_BUILDER.parse( iTextFile ); + } + catch ( SAXException e ) + { + throw new DocumentRendererException( "SAX Error : " + e.getMessage() ); + } + + // Only one chapter per doc + Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 ); + + try + { + document.getDocumentElement().appendChild( document.importNode( chapter, true ) ); + } + catch ( DOMException e ) + { + throw new DocumentRendererException( "Error appending chapter for " + + iTextFile + " : " + e.getMessage() ); + } + } + + return document; + } + + /** + * Initialize the transformer object. + * + * @return an instance of a transformer object. + * @throws DocumentRendererException if any. + */ + private Transformer initTransformer() + throws DocumentRendererException + { + try + { + Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( ITextPdfRenderer.class + .getResourceAsStream( XSLT_RESOURCE ) ) ); + + transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() ); + + transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" ); + + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + + // No doctype since itext doctype is not up to date! + + return transformer; + } + catch ( TransformerConfigurationException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + catch ( IllegalArgumentException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + } + + /** + * Add transformer parameters from a DocumentModel. + * + * @param transformer the Transformer to set the parameters. + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param iTextFile the iTextFile not null for the relative paths. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + */ + private void addTransformerParameters( Transformer transformer, DocumentModel documentModel, File iTextFile, + String generateTOC ) + { + if ( documentModel == null ) + { + return; + } + + // TOC + addTransformerParameter( transformer, "toc.position", generateTOC ); + + // Meta parameters + boolean hasNullMeta = false; + if ( documentModel.getMeta() == null ) + { + hasNullMeta = true; + documentModel.setMeta( new DocumentMeta() ); + } + addTransformerParameter( transformer, "meta.author", documentModel.getMeta().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + addTransformerParameter( transformer, "meta.creator", documentModel.getMeta().getCreator(), + System.getProperty( "user.name", "null" ) ); + // see com.lowagie.text.Document#addCreationDate() + SimpleDateFormat sdf = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy" ); + addTransformerParameter( transformer, "meta.creationdate", documentModel.getMeta().getCreationdate(), + sdf.format( new Date() ) ); + addTransformerParameter( transformer, "meta.keywords", documentModel.getMeta().getAllKeyWords() ); + addTransformerParameter( transformer, "meta.pagesize", documentModel.getMeta().getPageSize(), + ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) ); + addTransformerParameter( transformer, "meta.producer", documentModel.getMeta().getGenerator(), + "Apache Doxia iText" ); + addTransformerParameter( transformer, "meta.subject", documentModel.getMeta().getSubject(), + ( documentModel.getMeta().getTitle() != null ? documentModel.getMeta().getTitle() + : "" ) ); + addTransformerParameter( transformer, "meta.title", documentModel.getMeta().getTitle() ); + if ( hasNullMeta ) + { + documentModel.setMeta( null ); + } + + // cover parameter + boolean hasNullCover = false; + if ( documentModel.getCover() == null ) + { + hasNullCover = true; + documentModel.setCover( new DocumentCover() ); + } + addTransformerParameter( transformer, "cover.author", documentModel.getCover().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + String companyLogo = getLogoURL( documentModel.getCover().getCompanyLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.companyLogo", companyLogo ); + addTransformerParameter( transformer, "cover.companyName", documentModel.getCover().getCompanyName() ); + if ( documentModel.getCover().getCoverdate() == null ) + { + documentModel.getCover().setCoverDate( new Date() ); + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + documentModel.getCover().setCoverDate( null ); + } + else + { + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + } + addTransformerParameter( transformer, "cover.subtitle", documentModel.getCover().getCoverSubTitle() ); + addTransformerParameter( transformer, "cover.title", documentModel.getCover().getCoverTitle() ); + addTransformerParameter( transformer, "cover.type", documentModel.getCover().getCoverType() ); + addTransformerParameter( transformer, "cover.version", documentModel.getCover().getCoverVersion() ); + String projectLogo = getLogoURL( documentModel.getCover().getProjectLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.projectLogo", projectLogo ); + addTransformerParameter( transformer, "cover.projectName", documentModel.getCover().getProjectName() ); + if ( hasNullCover ) + { + documentModel.setCover( null ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @param defaultValue could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value, String defaultValue ) + { + if ( StringUtils.isEmpty( value ) ) + { + addTransformerParameter( transformer, name, defaultValue ); + } + else + { + addTransformerParameter( transformer, name, value ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value ) + { + if ( StringUtils.isEmpty( value ) ) + { + return; + } + + transformer.setParameter( name, value ); + } + + /** + * Transform a document to an iTextFile. + * + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param document the Document to transform. + * @param iTextFile the resulting iText xml file. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + * @throws DocumentRendererException in case of a transformation error. + */ + private void transform( DocumentModel documentModel, Document document, File iTextFile, String generateTOC ) + throws DocumentRendererException + { + Transformer transformer = initTransformer(); + + addTransformerParameters( transformer, documentModel, iTextFile, generateTOC ); + + // need a writer for StreamResult to prevent FileNotFoundException when iTextFile contains spaces + Writer writer = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + transformer.transform( new DOMSource( document ), new StreamResult( writer ) ); + } + catch ( TransformerException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + finally + { + IOUtil.close( writer ); + } + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseAllFiles( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String outputITextName = key.substring( 0, key.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( fullDoc, module, outputITextFileTmp, context ); + } + + return iTextFiles; + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseTOCFiles( File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Iterator it = documentModel.getToc().getItems().iterator(); it.hasNext(); ) + { + DocumentTOCItem tocItem = it.next(); + + if ( tocItem.getRef() == null ) + { + getLogger().debug( + "No ref defined for the tocItem '" + tocItem.getName() + + "' in the document descriptor. IGNORING" ); + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( source, module, outputITextFileTmp, context ); + } + } + } + } + } + + return iTextFiles; + } + + /** + * @param logo + * @param parentFile + * @return the logo url or null if unable to create it. + * @since 1.1.1 + */ + private String getLogoURL( String logo, File parentFile ) + { + if ( logo == null ) + { + return null; + } + + try + { + return new URL( logo ).toString(); + } + catch ( MalformedURLException e ) + { + try + { + File f = new File( parentFile, logo ); + if ( !f.exists() ) + { + getLogger().warn( "The logo " + f.getAbsolutePath() + " doesnt exist. IGNORING" ); + } + else + { + return f.toURI().toURL().toString(); + } + } + catch ( MalformedURLException e1 ) + { + getLogger().debug( "Failed to convert to URL: " + logo, e1 ); + } + } + + return null; + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/npe.json b/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/npe.json new file mode 100644 index 000000000..438f06b81 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/ITextPdfRenderer_152/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java", + "line": 153, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "ITextPdfRenderer" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/buggy.java new file mode 100644 index 000000000..81e127a37 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/buggy.java @@ -0,0 +1,248 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + +private static java.net.URL buildBaseUrl(final java.lang.String base) throws java.net.MalformedURLException { + { + try { + return new java.net.URL(/* NPEX_NULL_EXP */ + base); + } catch (java.net.MalformedURLException e) { + return new java.io.File(base).toURI().toURL(); + } + } +} + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/npe.json new file mode 100644 index 000000000..949de8328 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_118/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 119, + "npe_method": "buildBaseUrl", + "deref_field": "base", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/buggy.java new file mode 100644 index 000000000..6b42bf732 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/buggy.java @@ -0,0 +1,245 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + +private static java.net.URL buildUrl(final java.net.URL baseUrl, final java.lang.String path) throws java.net.MalformedURLException { + { + if (path == null) { + return /* NPEX_NULL_EXP */ + baseUrl; + } + if (baseUrl.getProtocol().equals("file")) { + return new java.io.File(baseUrl.getFile(), path).toURI().toURL(); + } + if (path.startsWith("/") && baseUrl.getPath().endsWith("/")) { + return new java.net.URL(baseUrl, path.substring(1)); + } + return new java.net.URL(baseUrl, path); + } +} + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/npe.json new file mode 100644 index 000000000..03eb52e74 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_136/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 137, + "npe_method": "buildUrl", + "deref_field": "baseUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/buggy.java new file mode 100644 index 000000000..0d15abb59 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/buggy.java @@ -0,0 +1,245 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + +private static java.net.URL buildUrl(final java.net.URL baseUrl, final java.lang.String path) throws java.net.MalformedURLException { + if (baseUrl == null) { + throw new java.net.MalformedURLException("Base is null!"); + } + { + if (baseUrl.getProtocol().equals("file")) { + return new java.io.File(baseUrl.getFile(), /* NPEX_NULL_EXP */ + path).toURI().toURL(); + } + if (path.startsWith("/") && baseUrl.getPath().endsWith("/")) { + return new java.net.URL(baseUrl, path.substring(1)); + } + return new java.net.URL(baseUrl, path); + } +} + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/npe.json new file mode 100644 index 000000000..5026743d7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 140, + "npe_method": "buildUrl", + "deref_field": "path", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/buggy.java new file mode 100644 index 000000000..dc33280df --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/buggy.java @@ -0,0 +1,252 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ +/** + * Get the path. + * + * @return the path. + */ +public java.lang.String getPath() { + { + if (isFile()) { + return org.codehaus.plexus.util.StringUtils.stripEnd(/* NPEX_NULL_EXP */ + getPathUrl().getPath(), "/"); + } else { + return getPathUrl().getPath(); + } + } +} + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/npe.json new file mode 100644 index 000000000..44d7ca90e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_206/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 213, + "npe_method": "getPath", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/buggy.java new file mode 100644 index 000000000..b941fdd6d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/buggy.java @@ -0,0 +1,252 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ +/** + * Get the location for files. + * + * @return the location. + */ +public java.lang.String getLocation() { + if (isFile()) { + { + return org.codehaus.plexus.util.StringUtils.stripEnd(/* NPEX_NULL_EXP */ + getPathUrl().getFile(), "/"); + } + } else { + return getPathUrl().toExternalForm(); + } +} + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/npe.json new file mode 100644 index 000000000..79a9f5cc1 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathDescriptor_232/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 237, + "npe_method": "getLocation", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/buggy.java b/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/buggy.java new file mode 100644 index 000000000..718484ff7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/buggy.java @@ -0,0 +1,151 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.PathTool; + +/** + * Utilities that allow conversion of old and new pathes and URLs relative to each other. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + * @deprecated this only operates on deprecated classes, it is not used anymore. + */ +public abstract class PathUtils +{ + /** + * Private constructor. + */ + private PathUtils() + { + // do not instantiate + } + + /** + *

convertPath.

+ * + * @param oldPath not null + * @param newPath not null + * @return a PathDescriptor converted by the new path + * @throws java.net.MalformedURLException if any + */ +/** + *

convertPath.

+ * + * @param oldPath + * not null + * @param newPath + * not null + * @return a PathDescriptor converted by the new path + * @throws java.net.MalformedURLException + * if any + */ +public static final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor convertPath(final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor oldPath, final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor newPath) throws java.net.MalformedURLException { + java.lang.String relative = org.apache.maven.doxia.site.decoration.inheritance.PathUtils.getRelativePath(oldPath, newPath); + { + return new org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor(/* NPEX_NULL_EXP */ + relative); + } +} + + /** + *

getRelativePath.

+ * + * @param oldPathDescriptor not null + * @param newPathDescriptor not null + * @return a relative path depending if PathDescriptor is a file or a web url. + * @see PathTool#getRelativeFilePath(String, String) + * @see PathTool#getRelativeWebPath(String, String) + */ + public static final String getRelativePath( final PathDescriptor oldPathDescriptor, + final PathDescriptor newPathDescriptor ) + { + // Cannot convert from URL to file. + if ( oldPathDescriptor.isFile() ) + { + if ( !newPathDescriptor.isFile() ) + { + // We want to convert from a file to an URL. This is normally not possible... + if ( oldPathDescriptor.isRelative() ) + { + // unless the old path is a relative path. Then we might convert an existing + // site into a new URL using resolvePaths()... + return oldPathDescriptor.getPath(); + } + + // The old path is not relative. Bail out. + return null; + } + else + { + // both are files, if either of them is relative, bail out + // see DOXIASITETOOLS-29, MSITE-404, PLXUTILS-116 + if ( oldPathDescriptor.isRelative() || newPathDescriptor.isRelative() ) + { + return null; + } + } + } + + // Don't optimize to else. This might also be old.isFile && new.isFile ... + if ( !oldPathDescriptor.isFile() ) + { + // URLs, determine if they share protocol and domain info + URL oldUrl = oldPathDescriptor.getPathUrl(); + URL newUrl = newPathDescriptor.getPathUrl(); + + if ( oldUrl == null || newUrl == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + if ( ( newUrl.getProtocol().equalsIgnoreCase( oldUrl.getProtocol() ) ) + && ( newUrl.getHost().equalsIgnoreCase( oldUrl.getHost() ) ) + && ( newUrl.getPort() == oldUrl.getPort() ) ) + { + // Both paths point to the same site. So we can use relative paths. + + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + return PathTool.getRelativeWebPath( newPath, oldPath ); + } + + // Different sites. No relative Path possible. + return null; + } + + // Both Descriptors point to an absolute path. We can build a relative path. + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + if ( oldPath == null || newPath == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + return PathTool.getRelativeFilePath( oldPath, newPath ); + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/npe.json b/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/npe.json new file mode 100644 index 000000000..20dc3fb9b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/PathUtils_57/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java", + "line": 67, + "npe_method": "convertPath", + "deref_field": "relative", + "npe_class": "PathUtils" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/buggy.java b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/buggy.java new file mode 100644 index 000000000..e3fb4826b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/buggy.java @@ -0,0 +1,354 @@ +package org.apache.maven.doxia.siterenderer.sink; + +/* + * 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. + */ + +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.text.html.HTML.Attribute; + +import org.apache.maven.doxia.markup.HtmlMarkup; +import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.siterenderer.DocumentContent; +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.apache.maven.doxia.util.HtmlTools; +import org.codehaus.plexus.util.StringUtils; + +/** + * Sink for site rendering of a document, to allow later merge document's output with a template. + * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating + * into skin or template: title, date, authors, head, body + * + * @author Emmanuel Venisse + */ +@SuppressWarnings( "checkstyle:methodname" ) +public class SiteRendererSink + extends Xhtml5Sink + implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent +{ + private String date = ""; + + private String title = ""; + + private List authors = new ArrayList(); + + private final StringWriter headWriter; + + private StringBuilder sectionTitleBuffer; + + private StringBuilder sectionTitleWriteBuffer; + + private boolean sectionHasID; + + private boolean isSectionTitle; + + private Set anchorsInSectionTitle; + + private final Writer writer; + + private RenderingContext renderingContext; + + /** + * Construct a new SiteRendererSink for a document. + * + * @param renderingContext the document's RenderingContext. + */ + public SiteRendererSink( RenderingContext renderingContext ) + { + this( new StringWriter(), renderingContext ); + } + + /** + * Construct a new SiteRendererSink for a document. + * + * @param writer the writer for the sink. + * @param renderingContext the document's RenderingContext. + */ + private SiteRendererSink( StringWriter writer, RenderingContext renderingContext ) + { + super( writer ); + + this.writer = writer; + this.headWriter = new StringWriter(); + this.renderingContext = renderingContext; + + /* the template is expected to have used the main tag, which can be used only once */ + super.contentStack.push( HtmlMarkup.MAIN ); + } + + /** {@inheritDoc} */ + @Override + public void title_() + { + if ( getTextBuffer().length() > 0 ) + { + title = getTextBuffer().toString(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Reset text buffer, since text content before title mustn't be in title. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title() + */ + @Override + public void title() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author_() + { + if ( getTextBuffer().length() > 0 ) + { + String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); + text = StringUtils.replace( text, "&#", "&#" ); + authors.add( text.trim() ); + } + + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date_() + { + if ( getTextBuffer().length() > 0 ) + { + date = getTextBuffer().toString().trim(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_() + */ + @Override + public void body_() + { + // nop + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body() + */ + @Override + public void body() + { + // nop + } + + /** {@inheritDoc} */ + @Override + public void head_() + { + setHeadFlag( false ); + } + + /** {@inheritDoc} */ + @Override + public void head() + { + setHeadFlag( true ); + } + + /** {@inheritDoc} */ + @Override + public void anchor( String name, SinkEventAttributes attributes ) + { + super.anchor( name, attributes ); + if ( isSectionTitle ) + { + if ( anchorsInSectionTitle == null ) + { + anchorsInSectionTitle = new HashSet(); + } + anchorsInSectionTitle.add( name ); + } + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle( int depth, SinkEventAttributes attributes ) + { + sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) ); + isSectionTitle = true; + + super.onSectionTitle( depth, attributes ); + + this.sectionTitleBuffer = new StringBuilder(); + this.sectionTitleWriteBuffer = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle_( int depth ) + { + String sectionTitle = sectionTitleBuffer.toString(); + this.sectionTitleBuffer = null; + String sectionWriteTitle = sectionTitleWriteBuffer.toString(); + this.sectionTitleWriteBuffer = null; + + if ( !StringUtils.isEmpty( sectionTitle ) ) + { + if ( sectionHasID ) + { + sectionHasID = false; + } + else + { + String id = HtmlTools.encodeId( sectionTitle ); + if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) ) + { + anchor( id ); + anchor_(); + } + } + } + + super.write( sectionWriteTitle ); + + this.isSectionTitle = false; + anchorsInSectionTitle = null; + super.onSectionTitle_( depth ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void text(java.lang.String text) { + { + // this implies we're inside a section title, collect text events for anchor generation + /* NPEX_NULL_EXP */ + sectionTitleBuffer.append(text); + } + super.text(text); +} + + /** {@inheritDoc} */ + @Override + protected void write( String text ) + { + String txt = text; + + if ( isHeadFlag() ) + { + headWriter.write( unifyEOLs( txt ) ); + + return; + } + + if ( renderingContext != null ) + { + String relativePathToBasedir = renderingContext.getRelativePath(); + + if ( relativePathToBasedir == null ) + { + txt = StringUtils.replace( txt, "$relativePath", "." ); + } + else + { + txt = StringUtils.replace( txt, "$relativePath", relativePathToBasedir ); + } + } + + if ( sectionTitleWriteBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleWriteBuffer.append( txt ); + } + else + { + super.write( txt ); + } + } + + // DocumentContent interface + + /** {@inheritDoc} */ + public String getTitle() + { + return title; + } + + /** {@inheritDoc} */ + public List getAuthors() + { + return authors; + } + + /** {@inheritDoc} */ + public String getDate() + { + return date; + } + + /** {@inheritDoc} */ + public String getBody() + { + return writer.toString(); + } + + /** {@inheritDoc} */ + public String getHead() + { + return headWriter.toString(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/npe.json b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/npe.json new file mode 100644 index 000000000..a97d8b44b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_269/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 274, + "npe_method": "text", + "deref_field": "sectionTitleBuffer", + "npe_class": "SiteRendererSink" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/buggy.java b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/buggy.java new file mode 100644 index 000000000..0cdc44736 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/buggy.java @@ -0,0 +1,341 @@ +package org.apache.maven.doxia.siterenderer.sink; + +/* + * 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. + */ + +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.text.html.HTML.Attribute; + +import org.apache.maven.doxia.markup.HtmlMarkup; +import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.siterenderer.DocumentContent; +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.apache.maven.doxia.util.HtmlTools; +import org.codehaus.plexus.util.StringUtils; + +/** + * Sink for site rendering of a document, to allow later merge document's output with a template. + * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating + * into skin or template: title, date, authors, head, body + * + * @author Emmanuel Venisse + */ +@SuppressWarnings( "checkstyle:methodname" ) +public class SiteRendererSink + extends Xhtml5Sink + implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent +{ + private String date = ""; + + private String title = ""; + + private List authors = new ArrayList(); + + private final StringWriter headWriter; + + private StringBuilder sectionTitleBuffer; + + private StringBuilder sectionTitleWriteBuffer; + + private boolean sectionHasID; + + private boolean isSectionTitle; + + private Set anchorsInSectionTitle; + + private final Writer writer; + + private RenderingContext renderingContext; + + /** + * Construct a new SiteRendererSink for a document. + * + * @param renderingContext the document's RenderingContext. + */ + public SiteRendererSink( RenderingContext renderingContext ) + { + this( new StringWriter(), renderingContext ); + } + + /** + * Construct a new SiteRendererSink for a document. + * + * @param writer the writer for the sink. + * @param renderingContext the document's RenderingContext. + */ + private SiteRendererSink( StringWriter writer, RenderingContext renderingContext ) + { + super( writer ); + + this.writer = writer; + this.headWriter = new StringWriter(); + this.renderingContext = renderingContext; + + /* the template is expected to have used the main tag, which can be used only once */ + super.contentStack.push( HtmlMarkup.MAIN ); + } + + /** {@inheritDoc} */ + @Override + public void title_() + { + if ( getTextBuffer().length() > 0 ) + { + title = getTextBuffer().toString(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Reset text buffer, since text content before title mustn't be in title. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title() + */ + @Override + public void title() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author_() + { + if ( getTextBuffer().length() > 0 ) + { + String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); + text = StringUtils.replace( text, "&#", "&#" ); + authors.add( text.trim() ); + } + + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date_() + { + if ( getTextBuffer().length() > 0 ) + { + date = getTextBuffer().toString().trim(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_() + */ + @Override + public void body_() + { + // nop + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body() + */ + @Override + public void body() + { + // nop + } + + /** {@inheritDoc} */ + @Override + public void head_() + { + setHeadFlag( false ); + } + + /** {@inheritDoc} */ + @Override + public void head() + { + setHeadFlag( true ); + } + + /** {@inheritDoc} */ + @Override + public void anchor( String name, SinkEventAttributes attributes ) + { + super.anchor( name, attributes ); + if ( isSectionTitle ) + { + if ( anchorsInSectionTitle == null ) + { + anchorsInSectionTitle = new HashSet(); + } + anchorsInSectionTitle.add( name ); + } + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle( int depth, SinkEventAttributes attributes ) + { + sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) ); + isSectionTitle = true; + + super.onSectionTitle( depth, attributes ); + + this.sectionTitleBuffer = new StringBuilder(); + this.sectionTitleWriteBuffer = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle_( int depth ) + { + String sectionTitle = sectionTitleBuffer.toString(); + this.sectionTitleBuffer = null; + String sectionWriteTitle = sectionTitleWriteBuffer.toString(); + this.sectionTitleWriteBuffer = null; + + if ( !StringUtils.isEmpty( sectionTitle ) ) + { + if ( sectionHasID ) + { + sectionHasID = false; + } + else + { + String id = HtmlTools.encodeId( sectionTitle ); + if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) ) + { + anchor( id ); + anchor_(); + } + } + } + + super.write( sectionWriteTitle ); + + this.isSectionTitle = false; + anchorsInSectionTitle = null; + super.onSectionTitle_( depth ); + } + + /** {@inheritDoc} */ + @Override + public void text( String text ) + { + if ( sectionTitleBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleBuffer.append( text ); + } + + super.text( text ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +protected void write(java.lang.String text) { + java.lang.String txt = text; + if (isHeadFlag()) { + headWriter.write(org.apache.maven.doxia.sink.impl.AbstractSink.unifyEOLs(txt)); + return; + } + if (renderingContext != null) { + java.lang.String relativePathToBasedir = renderingContext.getRelativePath(); + if (relativePathToBasedir == null) { + txt = org.codehaus.plexus.util.StringUtils.replace(txt, "$relativePath", "."); + } else { + txt = org.codehaus.plexus.util.StringUtils.replace(txt, "$relativePath", relativePathToBasedir); + } + } + { + // this implies we're inside a section title, collect text events for anchor generation + /* NPEX_NULL_EXP */ + sectionTitleWriteBuffer.append(txt); + } +} + + // DocumentContent interface + + /** {@inheritDoc} */ + public String getTitle() + { + return title; + } + + /** {@inheritDoc} */ + public List getAuthors() + { + return authors; + } + + /** {@inheritDoc} */ + public String getDate() + { + return date; + } + + /** {@inheritDoc} */ + public String getBody() + { + return writer.toString(); + } + + /** {@inheritDoc} */ + public String getHead() + { + return headWriter.toString(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/npe.json b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/npe.json new file mode 100644 index 000000000..67b09b048 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/SiteRendererSink_305/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 300, + "npe_method": "write", + "deref_field": "sectionTitleWriteBuffer", + "npe_class": "SiteRendererSink" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/buggy.java b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/buggy.java new file mode 100644 index 000000000..bfbad3cf4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/buggy.java @@ -0,0 +1,267 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; +import java.net.URISyntaxException; + +import org.codehaus.plexus.util.PathTool; + +/** + * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptor +{ + private final URI baseURI; + private final URI link; + + /** + * A URIPathDescriptor consists of a base URI and a link. + * Both arguments to this constructor have to be parsable to URIs. + * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. + * + * Before being parsed to {@link URI}s, the arguments are modified to catch + * some common bad practices: first all Windows-style backslashes '\' are replaced by + * forward slashes '/'. + * If the baseURI does not end with '/', a slash is appended. + * If the link starts with a '/', the first character is stripped. + * + * @param baseURI The base URI. Has to be a valid absolute URI. + * In addition, the path of the URI should not have any file part, + * ie http://maven.apache.org/ is valid, + * http://maven.apache.org/index.html is not. + * Even though the latter form is accepted without warning, + * the methods in this class will not return what is probably expected, + * because a slash is appended during construction, as noted above. + * @param link the link. This may be a relative link or an absolute link. + * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. + * + * @throws IllegalArgumentException if either argument is not parsable as a URI, + * or if baseURI is not absolute. + */ + public URIPathDescriptor( final String baseURI, final String link ) + { + final String llink = sanitizeLink( link ); + final String bbase = sanitizeBase( baseURI ); + + this.baseURI = URI.create( bbase ).normalize(); + this.link = URI.create( llink ).normalize(); + + if ( !this.baseURI.isAbsolute() ) + { + throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); + } + } + + /** + * Return the base of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized base URI. + */ + public URI getBaseURI() + { + return baseURI; + } + + /** + * Return the link of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized link URI. + */ + public URI getLink() + { + return link; + } + + /** + * Resolve the link to the base. + * This always returns an absolute URI. If link is absolute, link is returned. + * + * @return the resolved link. This is equivalent to calling + * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). + */ + public URI resolveLink() + { + return baseURI.resolve( link ); + } + + /** + * Calculate the relative link with respect to the base. + * The original link is returned if either + * link is relative; + * or link and base do not share the {@link #sameSite(java.net.URI) same site}. + * + * @return the link as a relative URI. + */ + public URI relativizeLink() + { + return relativizeLink( baseURI.toString(), link ); + } + + // NOTE: URI.relativize does not work as expected, see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 + private static URI relativizeLink( final String base, final URI link ) + { + if ( !link.isAbsolute() ) + { + return link; + } + + final URI newBaseURI = URI.create( base ); + + if ( !sameSite( link, newBaseURI ) ) + { + return link; + } + + final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); + + return URI.create( correctRelativePath( relativePath ) ); + } + + /** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase the new base URI. Has to be parsable as a URI. + *. + * @return a new relative link or the original link {@link #resolveLink() resolved}, + * i.e. as an absolute link, if the link cannot be re-based. + */ +/** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase + * the new base URI. Has to be parsable as a URI. + * . + * @return a new relative link or the original link {@link #resolveLink() resolved}, +i.e. as an absolute link, if the link cannot be re-based. + */ +public java.net.URI rebaseLink(final java.lang.String newBase) { + if (link.isAbsolute()) { + return link; + } + { + final java.net.URI newBaseURI; + try { + newBaseURI = new java.net.URI(/* NPEX_NULL_EXP */ + newBase); + } catch (java.net.URISyntaxException ex) { + return resolveLink(); + } + if (!sameSite(newBaseURI)) { + return resolveLink(); + } + final java.lang.String relativeBasePath = org.codehaus.plexus.util.PathTool.getRelativeWebPath(newBaseURI.getPath(), baseURI.getPath()); + return java.net.URI.create(org.apache.maven.doxia.site.decoration.inheritance.URIPathDescriptor.correctRelativePath(relativeBasePath)).resolve(link); + } +} + + private static String correctRelativePath( final String relativePath ) + { + if ( "".equals( relativePath ) || "/".equals( relativePath ) ) + { + return "./"; + } + else + { + return relativePath; + } + } + + /** + * Check if this URIPathDescriptor lives on the same site as the given URI. + * + * @param uri a URI to compare with. + * May be null, in which case false is returned. + * + * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI + * where null values are allowed. + */ + public boolean sameSite( final URI uri ) + { + return ( uri != null ) && sameSite( this.baseURI, uri ); + } + + private static boolean sameSite( final URI baseURI, final URI newBaseURI ) + { + final boolean sameScheme = + ( newBaseURI.getScheme() == null ? false : baseURI.getScheme().equalsIgnoreCase( newBaseURI.getScheme() ) ); + final boolean sameHost = + ( baseURI.getHost() == null ? newBaseURI.getHost() == null + : baseURI.getHost().equalsIgnoreCase( newBaseURI.getHost() ) ); + final boolean samePort = ( baseURI.getPort() == newBaseURI.getPort() ); + + return ( sameScheme && samePort && sameHost ); + } + + /** + * Construct a string representation of this URIPathDescriptor. + * This is equivalent to calling {@link #resolveLink()}.toString(). + * + * @return this URIPathDescriptor as a String. + */ + @Override + public String toString() + { + return resolveLink().toString(); + } + + private static String sanitizeBase( final String base ) + { + String sane = base.replace( '\\', '/' ); + + if ( !sane.endsWith( "/" ) ) + { + sane += "/"; + } + + return sane; + } + + private static String sanitizeLink( final String link ) + { + String sane = link.replace( '\\', '/' ); + + if ( sane.startsWith( "/" ) ) + { + sane = sane.substring( 1 ); + } + + return sane; + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/npe.json b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/npe.json new file mode 100644 index 000000000..ef09d5e34 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_166/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 182, + "npe_method": "rebaseLink", + "deref_field": "newBase", + "npe_class": "URIPathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/buggy.java b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/buggy.java new file mode 100644 index 000000000..63e69fb2c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/buggy.java @@ -0,0 +1,261 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; +import java.net.URISyntaxException; + +import org.codehaus.plexus.util.PathTool; + +/** + * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptor +{ + private final URI baseURI; + private final URI link; + + /** + * A URIPathDescriptor consists of a base URI and a link. + * Both arguments to this constructor have to be parsable to URIs. + * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. + * + * Before being parsed to {@link URI}s, the arguments are modified to catch + * some common bad practices: first all Windows-style backslashes '\' are replaced by + * forward slashes '/'. + * If the baseURI does not end with '/', a slash is appended. + * If the link starts with a '/', the first character is stripped. + * + * @param baseURI The base URI. Has to be a valid absolute URI. + * In addition, the path of the URI should not have any file part, + * ie http://maven.apache.org/ is valid, + * http://maven.apache.org/index.html is not. + * Even though the latter form is accepted without warning, + * the methods in this class will not return what is probably expected, + * because a slash is appended during construction, as noted above. + * @param link the link. This may be a relative link or an absolute link. + * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. + * + * @throws IllegalArgumentException if either argument is not parsable as a URI, + * or if baseURI is not absolute. + */ + public URIPathDescriptor( final String baseURI, final String link ) + { + final String llink = sanitizeLink( link ); + final String bbase = sanitizeBase( baseURI ); + + this.baseURI = URI.create( bbase ).normalize(); + this.link = URI.create( llink ).normalize(); + + if ( !this.baseURI.isAbsolute() ) + { + throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); + } + } + + /** + * Return the base of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized base URI. + */ + public URI getBaseURI() + { + return baseURI; + } + + /** + * Return the link of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized link URI. + */ + public URI getLink() + { + return link; + } + + /** + * Resolve the link to the base. + * This always returns an absolute URI. If link is absolute, link is returned. + * + * @return the resolved link. This is equivalent to calling + * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). + */ + public URI resolveLink() + { + return baseURI.resolve( link ); + } + + /** + * Calculate the relative link with respect to the base. + * The original link is returned if either + * link is relative; + * or link and base do not share the {@link #sameSite(java.net.URI) same site}. + * + * @return the link as a relative URI. + */ + public URI relativizeLink() + { + return relativizeLink( baseURI.toString(), link ); + } + + // NOTE: URI.relativize does not work as expected, see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 + private static URI relativizeLink( final String base, final URI link ) + { + if ( !link.isAbsolute() ) + { + return link; + } + + final URI newBaseURI = URI.create( base ); + + if ( !sameSite( link, newBaseURI ) ) + { + return link; + } + + final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); + + return URI.create( correctRelativePath( relativePath ) ); + } + + /** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase the new base URI. Has to be parsable as a URI. + *. + * @return a new relative link or the original link {@link #resolveLink() resolved}, + * i.e. as an absolute link, if the link cannot be re-based. + */ + public URI rebaseLink( final String newBase ) + { + if ( link.isAbsolute() ) + { + return link; + } + + if ( newBase == null ) + { + return resolveLink(); + } + + final URI newBaseURI; + + try + { + newBaseURI = new URI( newBase ); + } + catch ( URISyntaxException ex ) + { + return resolveLink(); + } + + if ( !sameSite( newBaseURI ) ) + { + return resolveLink(); + } + + final String relativeBasePath = PathTool.getRelativeWebPath( newBaseURI.getPath(), baseURI.getPath() ); + + return URI.create( correctRelativePath( relativeBasePath ) ).resolve( link ); + } + + private static String correctRelativePath( final String relativePath ) + { + if ( "".equals( relativePath ) || "/".equals( relativePath ) ) + { + return "./"; + } + else + { + return relativePath; + } + } + + /** + * Check if this URIPathDescriptor lives on the same site as the given URI. + * + * @param uri a URI to compare with. + * May be null, in which case false is returned. + * + * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI + * where null values are allowed. + */ + public boolean sameSite( final URI uri ) + { + return ( uri != null ) && sameSite( this.baseURI, uri ); + } + +private static boolean sameSite(final java.net.URI baseURI, final java.net.URI newBaseURI) { + final boolean sameScheme = (newBaseURI.getScheme() == null) ? false : baseURI.getScheme().equalsIgnoreCase(newBaseURI.getScheme()); + final boolean sameHost = /* NPEX_NULL_EXP */ + baseURI.getHost().equalsIgnoreCase(newBaseURI.getHost()); + final boolean samePort = baseURI.getPort() == newBaseURI.getPort(); + return (sameScheme && samePort) && sameHost; +} + + /** + * Construct a string representation of this URIPathDescriptor. + * This is equivalent to calling {@link #resolveLink()}.toString(). + * + * @return this URIPathDescriptor as a String. + */ + @Override + public String toString() + { + return resolveLink().toString(); + } + + private static String sanitizeBase( final String base ) + { + String sane = base.replace( '\\', '/' ); + + if ( !sane.endsWith( "/" ) ) + { + sane += "/"; + } + + return sane; + } + + private static String sanitizeLink( final String link ) + { + String sane = link.replace( '\\', '/' ); + + if ( sane.startsWith( "/" ) ) + { + sane = sane.substring( 1 ); + } + + return sane; + } +} diff --git a/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/npe.json b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/npe.json new file mode 100644 index 000000000..8db35eb63 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/bugs/URIPathDescriptor_222/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 221, + "npe_method": "sameSite", + "deref_field": "getHost", + "npe_class": "URIPathDescriptor" +} \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/Jenkinsfile b/Java-base/maven-doxia-sitetools/src/Jenkinsfile new file mode 100644 index 000000000..09ac70f12 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/Jenkinsfile @@ -0,0 +1,20 @@ +/** + * 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. + */ + +asfMavenTlpStdBuild() diff --git a/Java-base/maven-doxia-sitetools/src/README.md b/Java-base/maven-doxia-sitetools/src/README.md new file mode 100644 index 000000000..bc0034dca --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/README.md @@ -0,0 +1,99 @@ + +Contributing to [Apache Maven Doxia Sitetools](https://maven.apache.org/doxia/doxia-sitetools/) +====================== + +[![ASF Jira](https://img.shields.io/endpoint?url=https%3A%2F%2Fmaven.apache.org%2Fbadges%2Fasf_jira-DOXIASITETOOLS.json)][jira] +[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven.doxia/doxia-sitetools.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.apache.maven.doxia/doxia-sitetools) +[![Jenkins Status](https://img.shields.io/jenkins/s/https/builds.apache.org/job/maven-box/job/maven-doxia-sitetools/job/master.svg)][build] +[![Jenkins tests](https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven-doxia-sitetools/job/master.svg)][test-results] + + +You have found a bug or you have an idea for a cool new feature? Contributing +code is a great way to give something back to the open source community. Before +you dig right into the code, there are a few guidelines that we need +contributors to follow so that we can have a chance of keeping on top of +things. + +Getting Started +--------------- + ++ Make sure you have a [JIRA account](https://issues.apache.org/jira/). ++ Make sure you have a [GitHub account](https://github.com/signup/free). ++ If you're planning to implement a new feature, it makes sense to discuss your changes + on the [dev list][ml-list] first. + This way you can make sure you're not wasting your time on something that isn't + considered to be in Apache Maven's scope. ++ Submit a ticket for your issue, assuming one does not already exist. + + Clearly describe the issue, including steps to reproduce when it is a bug. + + Make sure you fill in the earliest version that you know has the issue. ++ Fork the repository on GitHub. + +Making and Submitting Changes +-------------- + +We accept Pull Requests via GitHub. The [developer mailing list][ml-list] is the +main channel of communication for contributors. +There are some guidelines which will make applying PRs easier for us: ++ Create a topic branch from where you want to base your work (this is usually the master branch). + Push your changes to a topic branch in your fork of the repository. ++ Make commits of logical units. ++ Respect the original code style: by using the same [codestyle][code-style], + patches should only highlight the actual difference, not being disturbed by any formatting issues: + + Only use spaces for indentation. + + Create minimal diffs - disable on save actions like reformat source code or organize imports. + If you feel the source code should be reformatted, create a separate PR for this change. + + Check for unnecessary whitespace with `git diff --check` before committing. ++ Make sure your commit messages are in the proper format. Your commit message should contain the key of the JIRA issue. +``` +[DOXIASITETOOLS-XXX] - Subject of the JIRA Ticket + Optional supplemental description. +``` ++ Make sure you have added the necessary tests (JUnit/IT) for your changes. ++ Run all the tests with `mvn -Prun-its verify` to assure nothing else was accidentally broken. ++ Submit a pull request to the repository in the Apache organization. ++ Update your JIRA ticket and include a link to the pull request in the ticket. + +If you plan to contribute on a regular basis, please consider filing a [contributor license agreement][cla]. + +Making Trivial Changes +---------------------- + +For changes of a trivial nature to comments and documentation, it is not always +necessary to create a new ticket in JIRA. In this case, it is appropriate to +start the first line of a commit with '(doc)' instead of a ticket number. + +Additional Resources +-------------------- + ++ [Contributing patches](https://maven.apache.org/guides/development/guide-maven-development.html#Creating_and_submitting_a_patch) ++ [Apache Maven Doxia Sitetools JIRA project page][jira] ++ [Contributor License Agreement][cla] ++ [General GitHub documentation](https://help.github.com/) ++ [GitHub pull request documentation](https://help.github.com/send-pull-requests/) ++ [Apache Maven Twitter Account](https://twitter.com/ASFMavenProject) ++ #Maven IRC channel on freenode.org + +[jira]: https://issues.apache.org/jira/projects/DOXIASITETOOLS/ +[license]: https://www.apache.org/licenses/LICENSE-2.0 +[ml-list]: https://maven.apache.org/mailing-lists.html +[code-style]: https://maven.apache.org/developers/conventions/code.html +[cla]: https://www.apache.org/licenses/#clas +[maven-wiki]: https://cwiki.apache.org/confluence/display/MAVEN/Index +[test-results]: https://builds.apache.org/job/maven-box/job/maven-doxia-sitetools/job/master/lastCompletedBuild/testReport/ +[build]: https://builds.apache.org/job/maven-box/job/maven-doxia-sitetools/job/master/ diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/pom.xml new file mode 100644 index 000000000..600b1a700 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/pom.xml @@ -0,0 +1,98 @@ + + + + + + 4.0.0 + + + org.apache.maven.doxia + doxia-sitetools + 1.9.3-SNAPSHOT + ../pom.xml + + + doxia-decoration-model + + Doxia Sitetools :: Decoration Model + The Decoration Model handles the decoration descriptor for sites, also known as site.xml. + + + + org.codehaus.plexus + plexus-component-annotations + + + org.codehaus.plexus + plexus-utils + + + junit + junit + test + + + + + + + org.codehaus.modello + modello-maven-plugin + + + src/main/mdo/decoration.mdo + + + 1.8.0 + 1.0.0 + + + + descriptor + generate-sources + + xpp3-writer + java + xpp3-reader + xsd + + + + descriptor-xdoc + pre-site + + xdoc + + + + descriptor-xsd + pre-site + + xsd + + + ${project.build.directory}/generated-site/resources/xsd + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java new file mode 100644 index 000000000..20dd732e7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java @@ -0,0 +1,115 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ + public static Xpp3Dom getCustomChild( Xpp3Dom custom, String path ) + { + String[] elements = path.split( "\\." ); + for ( String element : elements ) + { + if ( custom == null ) + { + return null; + } + custom = custom.getChild( element ); + } + return custom; + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? null : custom.getValue(); + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path, String defaultValue ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? defaultValue : custom.getValue(); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssembler.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssembler.java new file mode 100644 index 000000000..52ebdce0c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssembler.java @@ -0,0 +1,72 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import org.apache.maven.doxia.site.decoration.DecorationModel; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + */ +public interface DecorationModelInheritanceAssembler +{ + /** Plexus lookup role. */ + String ROLE = DecorationModelInheritanceAssembler.class.getName(); + + /** + * Manage inheritance of the decoration model between a parent and child. + * + * Any relative links in the parent model will be re-based to work from the merged child + * model, otherwise no content from either the parent or child model should be modified. + * + * @param name a name, used for breadcrumb. + * If the parent model contains breadcrumbs and the child doesn't, + * a child breadcrumb will be added to the merged model with this name. Not null. + * @param child the child DecorationModel to be merged with parent. + * Not null. If parent == null, the child is unchanged, otherwise + * child will contain the merged model upon exit. + * @param parent the parent DecorationModel. Unchanged upon exit. + * May be null in which case the child is not changed. + * @param childBaseUrl the child base URL. + * May be null, in which case relative links inherited from the parent + * will not be resolved in the merged child. + * @param parentBaseUrl the parent base URL. + * May be null, in which case relative links inherited from the parent + * will not be resolved in the merged child. + */ + void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ); + + /** + * Resolve relative paths for a DecorationModel given a base URL. + * + * Note that 'resolve' here means 'relativize' in the sense of + * {@link java.net.URI#relativize(java.net.URI)}, ie if any link in the decoration model + * has a base URL that is equal to the given baseUrl, it is replaced by a relative link + * with respect to that base. + * + * @param decoration the DecorationModel. + * Not null. + * @param baseUrl the base URL. + * May be null in which case the decoration model is unchanged. + */ + void resolvePaths( DecorationModel decoration, String baseUrl ); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java new file mode 100644 index 000000000..fb4195b83 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java @@ -0,0 +1,469 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ + public void resolvePaths( final DecorationModel decoration, final String baseUrl ) + { + if ( baseUrl == null ) + { + return; + } + + if ( decoration.getBannerLeft() != null ) + { + relativizeBannerPaths( decoration.getBannerLeft(), baseUrl ); + } + + if ( decoration.getBannerRight() != null ) + { + relativizeBannerPaths( decoration.getBannerRight(), baseUrl ); + } + + for ( Logo logo : decoration.getPoweredBy() ) + { + relativizeLogoPaths( logo, baseUrl ); + } + + if ( decoration.getBody() != null ) + { + for ( LinkItem linkItem : decoration.getBody().getLinks() ) + { + relativizeLinkItemPaths( linkItem, baseUrl ); + } + + for ( LinkItem linkItem : decoration.getBody().getBreadcrumbs() ) + { + relativizeLinkItemPaths( linkItem, baseUrl ); + } + + for ( Menu menu : decoration.getBody().getMenus() ) + { + relativizeMenuPaths( menu.getItems(), baseUrl ); + } + } + } + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List
mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java new file mode 100644 index 000000000..6c5f55076 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java @@ -0,0 +1,255 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java new file mode 100644 index 000000000..fad4afbab --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java @@ -0,0 +1,145 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.PathTool; + +/** + * Utilities that allow conversion of old and new pathes and URLs relative to each other. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + * @deprecated this only operates on deprecated classes, it is not used anymore. + */ +public abstract class PathUtils +{ + /** + * Private constructor. + */ + private PathUtils() + { + // do not instantiate + } + + /** + *

convertPath.

+ * + * @param oldPath not null + * @param newPath not null + * @return a PathDescriptor converted by the new path + * @throws java.net.MalformedURLException if any + */ + public static final PathDescriptor convertPath( final PathDescriptor oldPath, final PathDescriptor newPath ) + throws MalformedURLException + { + String relative = getRelativePath( oldPath, newPath ); + + if ( relative == null ) + { + return oldPath; + } + + return new PathDescriptor( relative ); + } + + /** + *

getRelativePath.

+ * + * @param oldPathDescriptor not null + * @param newPathDescriptor not null + * @return a relative path depending if PathDescriptor is a file or a web url. + * @see PathTool#getRelativeFilePath(String, String) + * @see PathTool#getRelativeWebPath(String, String) + */ + public static final String getRelativePath( final PathDescriptor oldPathDescriptor, + final PathDescriptor newPathDescriptor ) + { + // Cannot convert from URL to file. + if ( oldPathDescriptor.isFile() ) + { + if ( !newPathDescriptor.isFile() ) + { + // We want to convert from a file to an URL. This is normally not possible... + if ( oldPathDescriptor.isRelative() ) + { + // unless the old path is a relative path. Then we might convert an existing + // site into a new URL using resolvePaths()... + return oldPathDescriptor.getPath(); + } + + // The old path is not relative. Bail out. + return null; + } + else + { + // both are files, if either of them is relative, bail out + // see DOXIASITETOOLS-29, MSITE-404, PLXUTILS-116 + if ( oldPathDescriptor.isRelative() || newPathDescriptor.isRelative() ) + { + return null; + } + } + } + + // Don't optimize to else. This might also be old.isFile && new.isFile ... + if ( !oldPathDescriptor.isFile() ) + { + // URLs, determine if they share protocol and domain info + URL oldUrl = oldPathDescriptor.getPathUrl(); + URL newUrl = newPathDescriptor.getPathUrl(); + + if ( oldUrl == null || newUrl == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + if ( ( newUrl.getProtocol().equalsIgnoreCase( oldUrl.getProtocol() ) ) + && ( newUrl.getHost().equalsIgnoreCase( oldUrl.getHost() ) ) + && ( newUrl.getPort() == oldUrl.getPort() ) ) + { + // Both paths point to the same site. So we can use relative paths. + + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + return PathTool.getRelativeWebPath( newPath, oldPath ); + } + + // Different sites. No relative Path possible. + return null; + } + + // Both Descriptors point to an absolute path. We can build a relative path. + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + if ( oldPath == null || newPath == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + return PathTool.getRelativeFilePath( oldPath, newPath ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java new file mode 100644 index 000000000..1bf75a52c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java @@ -0,0 +1,265 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; +import java.net.URISyntaxException; + +import org.codehaus.plexus.util.PathTool; + +/** + * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptor +{ + private final URI baseURI; + private final URI link; + + /** + * A URIPathDescriptor consists of a base URI and a link. + * Both arguments to this constructor have to be parsable to URIs. + * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. + * + * Before being parsed to {@link URI}s, the arguments are modified to catch + * some common bad practices: first all Windows-style backslashes '\' are replaced by + * forward slashes '/'. + * If the baseURI does not end with '/', a slash is appended. + * If the link starts with a '/', the first character is stripped. + * + * @param baseURI The base URI. Has to be a valid absolute URI. + * In addition, the path of the URI should not have any file part, + * ie http://maven.apache.org/ is valid, + * http://maven.apache.org/index.html is not. + * Even though the latter form is accepted without warning, + * the methods in this class will not return what is probably expected, + * because a slash is appended during construction, as noted above. + * @param link the link. This may be a relative link or an absolute link. + * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. + * + * @throws IllegalArgumentException if either argument is not parsable as a URI, + * or if baseURI is not absolute. + */ + public URIPathDescriptor( final String baseURI, final String link ) + { + final String llink = sanitizeLink( link ); + final String bbase = sanitizeBase( baseURI ); + + this.baseURI = URI.create( bbase ).normalize(); + this.link = URI.create( llink ).normalize(); + + if ( !this.baseURI.isAbsolute() ) + { + throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); + } + } + + /** + * Return the base of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized base URI. + */ + public URI getBaseURI() + { + return baseURI; + } + + /** + * Return the link of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized link URI. + */ + public URI getLink() + { + return link; + } + + /** + * Resolve the link to the base. + * This always returns an absolute URI. If link is absolute, link is returned. + * + * @return the resolved link. This is equivalent to calling + * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). + */ + public URI resolveLink() + { + return baseURI.resolve( link ); + } + + /** + * Calculate the relative link with respect to the base. + * The original link is returned if either + * link is relative; + * or link and base do not share the {@link #sameSite(java.net.URI) same site}. + * + * @return the link as a relative URI. + */ + public URI relativizeLink() + { + return relativizeLink( baseURI.toString(), link ); + } + + // NOTE: URI.relativize does not work as expected, see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 + private static URI relativizeLink( final String base, final URI link ) + { + if ( !link.isAbsolute() ) + { + return link; + } + + final URI newBaseURI = URI.create( base ); + + if ( !sameSite( link, newBaseURI ) ) + { + return link; + } + + final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); + + return URI.create( correctRelativePath( relativePath ) ); + } + + /** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase the new base URI. Has to be parsable as a URI. + *. + * @return a new relative link or the original link {@link #resolveLink() resolved}, + * i.e. as an absolute link, if the link cannot be re-based. + */ + public URI rebaseLink( final String newBase ) + { + if ( link.isAbsolute() ) + { + return link; + } + + if ( newBase == null ) + { + return resolveLink(); + } + + final URI newBaseURI; + + try + { + newBaseURI = new URI( newBase ); + } + catch ( URISyntaxException ex ) + { + return resolveLink(); + } + + if ( !sameSite( newBaseURI ) ) + { + return resolveLink(); + } + + final String relativeBasePath = PathTool.getRelativeWebPath( newBaseURI.getPath(), baseURI.getPath() ); + + return URI.create( correctRelativePath( relativeBasePath ) ).resolve( link ); + } + + private static String correctRelativePath( final String relativePath ) + { + if ( "".equals( relativePath ) || "/".equals( relativePath ) ) + { + return "./"; + } + else + { + return relativePath; + } + } + + /** + * Check if this URIPathDescriptor lives on the same site as the given URI. + * + * @param uri a URI to compare with. + * May be null, in which case false is returned. + * + * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI + * where null values are allowed. + */ + public boolean sameSite( final URI uri ) + { + return ( uri != null ) && sameSite( this.baseURI, uri ); + } + + private static boolean sameSite( final URI baseURI, final URI newBaseURI ) + { + final boolean sameScheme = + ( newBaseURI.getScheme() == null ? false : baseURI.getScheme().equalsIgnoreCase( newBaseURI.getScheme() ) ); + final boolean sameHost = + ( baseURI.getHost() == null ? newBaseURI.getHost() == null + : baseURI.getHost().equalsIgnoreCase( newBaseURI.getHost() ) ); + final boolean samePort = ( baseURI.getPort() == newBaseURI.getPort() ); + + return ( sameScheme && samePort && sameHost ); + } + + /** + * Construct a string representation of this URIPathDescriptor. + * This is equivalent to calling {@link #resolveLink()}.toString(). + * + * @return this URIPathDescriptor as a String. + */ + @Override + public String toString() + { + return resolveLink().toString(); + } + + private static String sanitizeBase( final String base ) + { + String sane = base.replace( '\\', '/' ); + + if ( !sane.endsWith( "/" ) ) + { + sane += "/"; + } + + return sane; + } + + private static String sanitizeLink( final String link ) + { + String sane = link.replace( '\\', '/' ); + + if ( sane.startsWith( "/" ) ) + { + sane = sane.substring( 1 ); + } + + return sane; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/mdo/decoration.mdo b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/mdo/decoration.mdo new file mode 100644 index 000000000..50539334f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/main/mdo/decoration.mdo @@ -0,0 +1,784 @@ + + + + + + decoration + Decoration + This is a reference for the site decoration descriptor used in Doxia Sitetools, also known as site.xml: + it is used to configure a site template (aka skin).

+

An XSD is available at:

+ + ]]>
+ + + + package + org.apache.maven.doxia.site.decoration + + + + + + DecorationModel + <project> element is the root of the site decoration descriptor. + ]]> + 1.0.0+ + + + name + The full name of the project. + 1.0.0+ + String + true + + + merge) + or not (override). + ]]> + combineSelf + 1.6.0+ + String + merge + + + bannerLeft + Banner logo on the masthead of the site to the left. + 1.0.0+ + + Banner + + true + + + bannerRight + Banner logo on the masthead of the site to the right. + 1.0.0+ + + Banner + + true + + + googleAdSenseClient + Your Google AdSense client id. + 1.3.0+ + String + true + + + googleAdSenseSlot + Your Google AdSense slot id. + 1.3.0+ + String + true + + + googleAnalyticsAccountId + The id for your Google Analytics account. + 1.1.0+ + String + true + + + publishDate + Modify the date published display properties. + 1.0.0+ + + PublishDate + + true + + + version + Modify the version published display properties. + 1.0.0+ + + Version + + true + + + edit + ${project.scm.url} value should do the job. + ]]> + 1.8.0+ + String + true + + + poweredBy + Powered by logos list. + 1.0.0+ + + Logo + * + + true + + + skin + The artifact containing the skin for the site. + 1.0.0+ + + Skin + + true + + + body + The main site content decoration. + 1.0.0+ + + Body + + true + + + custom + $decoration.custom variable as DOM content. + Example: $decoration.custom.getChild( 'customElement' ).getValue() + ]]> + 1.0.0+ + DOM + true + + + lastModified + Timestamp of the last modification of this decoration model. + 1.0.1+ + long + + + + + 1.0.0+ + + menusByRef; + + /** + * @param key not null + * @return the menu ref defined by the given key. + */ + public Menu getMenuRef( String key ) + { + if ( menusByRef == null ) + { + menusByRef = new java.util.HashMap(); + + if ( body != null ) + { + for ( Menu menu : body.getMenus() ) + { + if ( menu.getRef() != null ) + { + menusByRef.put( menu.getRef(), menu ); + } + } + } + } + return menusByRef.get( key ); + } + + /** + * @param key not null + */ + public void removeMenuRef( String key ) + { + if ( body != null ) + { + for ( java.util.Iterator
i = body.getMenus().iterator(); i.hasNext(); ) + { + Menu menu = i.next(); + if ( key.equals( menu.getRef() ) ) + { + i.remove(); + } + } + } + } + + /** + * @return the menus list or EMPTY_LIST. + */ + public java.util.List getMenus() + { + java.util.List menus; + if ( body != null && body.getMenus() != null ) + { + menus = body.getMenus(); + } + else + { + menus = java.util.Collections.emptyList(); + } + return menus; + } + ]]> + + + + 1.7.0+ + + /** + * @since 1.7 + * @see DecorationUtils#isLink + */ + public boolean isLink( String href ) + { + return DecorationUtils.isLink( href ); + } + + + + 1.8.0+ + + /** + * @since 1.8 + * @see DecorationUtils#getCustomChild + */ + public Object getCustomChild( String path ) + { + return DecorationUtils.getCustomChild( (org.codehaus.plexus.util.xml.Xpp3Dom) custom, path ); + } + + /** + * @since 1.8 + * @see DecorationUtils#getCustomValue + */ + public String getCustomValue( String path ) + { + return DecorationUtils.getCustomValue( (org.codehaus.plexus.util.xml.Xpp3Dom) custom, path ); + } + + /** + * @since 1.8 + * @see DecorationUtils#getCustomValue + */ + public String getCustomValue( String path, String defaultValue ) + { + return DecorationUtils.getCustomValue( (org.codehaus.plexus.util.xml.Xpp3Dom) custom, path, defaultValue ); + } + + + + + + + Banner + Banner logo on the masthead of the site. + 1.0.0+ + + + + name + 1.0.0+ + The name of the banner. + String + true + + + src + 1.0.0+ + The source location of an image for the banner. + String + true + + + alt + 1.0.0+ + The alt description for the banner image. + String + true + + + href + 1.0.0+ + The href of a link to be used for the banner image. + String + true + + + border + The border to use for the banner image. + 1.0.1+ + String + true + + + width + The width to use for the banner image. + 1.0.1+ + String + true + + + height + The height to use for the banner image. + 1.0.1+ + String + true + + + title + 1.3.0+ + The title for the banner image. + String + true + + + + + + PublishDate + Modify display properties for date published. + 1.0.0+ + + + position + Where to place the date published (left, right, navigation-top, navigation-bottom, bottom). + 1.0.0+ + String + true + left + + + format + Date format to use. + 1.0.0+ + String + true + yyyy-MM-dd + + + + + + Version + Modify display properties for version published. + 1.0.0+ + + + position + Where to place the version published (left, right, navigation-top, navigation-bottom, bottom). + 1.0.0+ + String + true + left + + + + + + Logo + Power by logo on the navigation. + 1.0.0+ + LinkItem + + + + Body + The main content decoration. + 1.0.0+ + + + head + Additional content (like JavaScript) to include in the HEAD block of the generated pages. + 1.0.0/1.6.0 + DOM + true + + + head + Additional content (like JavaScript) to include in the HEAD block of the generated pages. + 1.7.0+ + String + true + + + links + A list of links to display in the navigation. + 1.0.0+ + + LinkItem + * + + true + + + breadcrumbs + A list of breadcrumbs to display in the navigation. + 1.0.0+ + + LinkItem + * + + true + + + menus + A list of menus to include in the navigation. + 1.0.0+ + + Menu + * + + true + + + footer + If present, the contained text will be used instead of the generated copyright text. + 1.7.0+ + String + true + + + footer + If present, the contained text will be used instead of the generated copyright text. + 1.1.0/1.6.0 + DOM + true + + + + + + LinkItem + A link in the navigation. + 1.0.0+ + + + name + The name to display for the link. + 1.0.0+ + String + true + + + href + The href to use for the link. + 1.0.0+ + String + true + + + img + The source location of an image. + 1.0.0+ + String + true + + + position + Where to place the image regarding the displayed name (left or right). + 1.0.1+ + String + true + left + + + alt + The alt to use for the image. + 1.0.1+ + String + true + + + border + The border to use for the image. + 1.0.1+ + String + true + + + width + The width to use for the image. + 1.0.1+ + String + true + + + height + The height to use for the image. + 1.0.1+ + String + true + + + target + Where the new document will be displayed when the user follows a link, i.e. _blank opens the new document in a new window. + 1.0.1+ + String + true + + + title + The title to use for the image. + 1.3.0+ + String + true + + + + + + Menu + A menu in the navigation. + 1.0.0+ + + + name + The name to display for the menu. + 1.0.0+ + String + true + + + inherit + top, bottom. + ]]> + 1.0.0+ + String + true + + + inheritAsRef + true means that it will be populated + in the project, whereas if it is false, it is populated in the parent and then inherited. + ]]> + 1.0.0+ + boolean + + + ref + reports, modules + or parent. It will be populated at runtime with corresponding pre-defined content. + ]]> + 1.0.0+ + String + true + + + img + The source location of an menu image. + 1.0.0+ + String + true + + + alt + 1.0.1+ + The alt description for the image. + String + true + + + position + Where to place the image regarding the displayed name (left or right). + 1.0.1+ + String + true + left + + + border + The border to use for the menu image. + 1.0.1+ + String + true + + + width + The width to use for the menu image. + 1.0.1+ + String + true + + + height + The height to use for the menu image. + 1.0.1+ + String + true + + + title + 1.3.0+ + The title for the image. + String + true + + + items + A list of menu item. + 1.0.0+ + + MenuItem + * + + true + + + + + + MenuItem + A menu item. + 1.0.0+ + LinkItem + + + description + A description of the menu item. This is used on any summary pages for a menu. + 1.0.0+ + String + true + + + collapse + Whether to collapse children elements of an item menu (by default). + 1.0.0+ + boolean + true + + + ref + A reference to a pre-defined menu item, such as a report (specified by the report goal + name). Any elements explicitly given override those from the pre-defined reference. + 1.0.0+ + String + true + + + items + A list of menu item. + 1.0.0+ + + MenuItem + * + + true + + + + + + Skin + An skin artifact declaration. + 1.0.0+ + + + groupId + The skin group ID. + 1.0.0+ + String + true + true + + + artifactId + The skin artifact ID. + 1.0.0+ + String + true + true + + + version + The skin version. + 1.0.0+ + String + true + + + + + 1.0.0+ + + org.apache.maven.skins:maven-default-skin:1.3. + */ + public static Skin getDefaultSkin() + { + Skin skin = new Skin(); + skin.setGroupId( "org.apache.maven.skins" ); + skin.setArtifactId( "maven-default-skin" ); + skin.setVersion( "1.3" ); + return skin; + } + ]]> + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/apt/index.apt b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/apt/index.apt new file mode 100644 index 000000000..9cec49b65 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/apt/index.apt @@ -0,0 +1,48 @@ + ----- + Doxia Sitetool Decoration Model + ----- + Hervé Boutemy + ----- + 2011-08-18 + ----- + + ~~ 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. + + ~~ NOTE: For help with the syntax of this file, see: + ~~ http://maven.apache.org/doxia/references/apt-format.html + +Doxia Sitetool Decoration Model + + This is strictly the model for Doxia Sitetool Decoration Model, used in maven-site-plugin as <<>> to inject + parameters into decoration template: see {{{../doxia-site-renderer/}Doxia Site Renderer}} for more details on site rendering. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with Reader and Writers for the Xpp3 XML parser + + * A {{{./decoration.html}Descriptor Reference}} + + * An XSD referenced in the {{{./decoration.html}Descriptor Reference}}. + +* Inheritance + + Decoration model can be merged from a parent <<>> into a child <<>> using + <<>> ({{{./apidocs/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssembler.html}javadoc}}) + with its <<>> implementation + ({{{./xref/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.html}source}}). + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/site.xml new file mode 100644 index 000000000..83e81a94e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/site/site.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/DecorationUtilsTest.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/DecorationUtilsTest.java new file mode 100644 index 000000000..4d1e1c76d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/DecorationUtilsTest.java @@ -0,0 +1,67 @@ +package org.apache.maven.doxia.site.decoration; + +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class DecorationUtilsTest +{ + @Test + public void testIsLink() + { + assertFalse( DecorationUtils.isLink( null ) ); + assertFalse( DecorationUtils.isLink( "" ) ); + assertFalse( DecorationUtils.isLink( " " ) ); + assertTrue( DecorationUtils.isLink( "http://maven.apache.org/" ) ); + assertTrue( DecorationUtils.isLink( "https://maven.apache.org/" ) ); + assertTrue( DecorationUtils.isLink( "ftp://maven.apache.org/pub/" ) ); + assertTrue( DecorationUtils.isLink( "file:///home" ) ); + assertTrue( DecorationUtils.isLink( "mailto:toto@maven.org" ) ); + assertTrue( DecorationUtils.isLink( "any-protocol://" ) ); + } + + @Test + public void testGetCustomChild() + { + Xpp3Dom dom = new Xpp3Dom( "root" ); + Xpp3Dom level1 = new Xpp3Dom( "level1" ); + dom.addChild( level1 ); + Xpp3Dom level2 = new Xpp3Dom( "level2" ); + level2.setValue( "value" ); + level1.addChild( level2 ); + + assertEquals( level1, DecorationUtils.getCustomChild( dom, "level1" ) ); + assertEquals( level2, DecorationUtils.getCustomChild( dom, "level1.level2" ) ); + assertNull( DecorationUtils.getCustomChild( dom, "no.level2" ) ); + assertNull( DecorationUtils.getCustomChild( dom, "level1.no" ) ); + + assertEquals( "value", DecorationUtils.getCustomValue( dom, "level1.level2" ) ); + assertNull( DecorationUtils.getCustomValue( dom, "no.level2" ) ); + assertNull( DecorationUtils.getCustomValue( dom, "level1.no" ) ); + + assertEquals( "value", DecorationUtils.getCustomValue( dom, "level1.level2", "default" ) ); + assertEquals( "default", DecorationUtils.getCustomValue( dom, "no.level2", "default" ) ); + assertEquals( "default", DecorationUtils.getCustomValue( dom, "level1.no", "default" ) ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssemblerTest.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssemblerTest.java new file mode 100644 index 000000000..38699224c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/DecorationModelInheritanceAssemblerTest.java @@ -0,0 +1,957 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.IOException; +import java.io.Reader; + +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Test the inheritance assembler. + * + * @author Brett Porter + */ +public class DecorationModelInheritanceAssemblerTest +{ + private DecorationModelInheritanceAssembler assembler = new DefaultDecorationModelInheritanceAssembler(); + + private static final String NAME = "Name"; + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testInheritance() + throws IOException, XmlPullParserException + { + DecorationModel childModel = readModel( "inheritance-child.xml" ); + DecorationModel parentModel = readModel( "inheritance-parent.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + DecorationModel expectedModel = readModel( "inheritance-expected.xml" ); + + assertEquals( "Check result", expectedModel, childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "inheritance-child.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assertEquals( "Check scp result", expectedModel, childModel ); + + assertEquals( "Modified parent!", readModel( "inheritance-parent.xml" ), parentModel ); + + // late inheritance in links can't be rebased: check friendly message + parentModel.getBannerLeft().setHref( "${project.url}" ); + childModel = readModel( "inheritance-child.xml" ); + try + { + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + fail( "late interpolation in link should cause IllegalArgumentException" ); + } + catch ( IllegalArgumentException iae ) + { + assertTrue( iae.getMessage().startsWith( "site.xml late interpolation" ) ); + } + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testSuppressedInheritance() + throws IOException, XmlPullParserException + { + DecorationModel unassembledChildModel = readModel( "inheritance-child-no-inheritance.xml" ); + DecorationModel childModel = readModel( "inheritance-child-no-inheritance.xml" ); + DecorationModel parentModel = readModel( "inheritance-parent.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + assertEquals( "Check result", unassembledChildModel, childModel ); + + // 2 levels of inheritance + DecorationModel childOfchildModel = new DecorationModel(); + assembler.assembleModelInheritance( "Child of Child", childOfchildModel, childModel, + "http://maven.apache.org/doxia/child", "http://maven.apache.org/doxia" ); + assembler.assembleModelInheritance( NAME, childOfchildModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + // check that the 3 breadcrumb items from parent.xml are not inherited + assertEquals( "child of child no inheritance: breadcrumbs count", 0, + childOfchildModel.getBody().getBreadcrumbs().size() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedWhenEmpty() + throws IOException, XmlPullParserException + { + // Test an empty model avoids NPEs + DecorationModel childModel = readModel( "empty.xml" ); + DecorationModel parentModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + DecorationModel mergedModel = readModel( "empty.xml" ); + + assertEquals( "Check result", mergedModel, childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assertEquals( "Check scp result", mergedModel, childModel ); + + assertEquals( "Modified parent!", readModel( "empty.xml" ), parentModel ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsNotResolvedForExternalUrls() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "external-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + assertPathsNotResolvedForExternalUrls( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assertPathsNotResolvedForExternalUrls( childModel ); + + assertEquals( "Modified parent!", readModel( "external-urls.xml" ), parentModel ); + } + + private static void assertPathsNotResolvedForExternalUrls( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "http://jakarta.apache.org/", + childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "http://jakarta.apache.org/images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "http://jakarta.apache.org/commons/sandbox", + childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "http://jakarta.apache.org/commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "http://tomcat.apache.org/", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "http://tomcat.apache.org/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "http://www.apache.org/", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "http://www.bouncycastle.org", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "http://www.apache.org/special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForRelativeUrls() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "relative-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia/", + "http://maven.apache.org" ); + assertPathsResolvedForRelativeUrls( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assertPathsResolvedForRelativeUrls( childModel ); + + assertEquals( "Modified parent!", readModel( "relative-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForRelativeUrls( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "../banner/left", childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "../images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "../banner/right/", childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "../commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "../tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "../tomcat/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "../apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "../bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "../special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForSubsiteUrls() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "subsite-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia/", + "http://maven.apache.org" ); + assembler.resolvePaths( childModel, "http://maven.apache.org/doxia" ); + + assertPathsResolvedForSubsiteUrls( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assembler.resolvePaths( childModel, "http://maven.apache.org/doxia" ); + assertPathsResolvedForSubsiteUrls( childModel ); + + assertEquals( "Modified parent!", readModel( "subsite-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForSubsiteUrls( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "../banner/left", childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "../images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "../banner/right/", childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "../commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "../tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "../tomcat/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "../apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "../bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "../special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForRelativeUrlsDepthOfTwo() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "relative-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia/core", + "http://maven.apache.org" ); + assertPathsResolvedForRelativeUrlsDepthOfTwo( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/doxia/core", + "scp://people.apache.org" ); + assertPathsResolvedForRelativeUrlsDepthOfTwo( childModel ); + + assertEquals( "Modified parent!", readModel( "relative-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForRelativeUrlsDepthOfTwo( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "../../banner/left", childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "../../images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "../../banner/right/", childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "../../commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "../../tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "../../tomcat/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "../../apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "../../bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "../../special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForReverseRelativeUrls() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "relative-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/", + "http://maven.apache.org/doxia/" ); + assertPathsResolvedForReverseRelativeUrls( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/", + "scp://people.apache.org/doxia/" ); + assertPathsResolvedForReverseRelativeUrls( childModel ); + + assertEquals( "Modified parent!", readModel( "relative-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForReverseRelativeUrls( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "doxia/banner/left", childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "doxia/images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "doxia/banner/right/", childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "doxia/commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "doxia/tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "doxia/tomcat/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "doxia/apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "doxia/bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "doxia/special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForReverseRelativeUrlsDepthOfTwo() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "relative-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/", + "http://maven.apache.org/doxia/core/" ); + assertPathsResolvedForReverseRelativeUrlsDepthOfTwo( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/", + "scp://people.apache.org/doxia/core/" ); + assertPathsResolvedForReverseRelativeUrlsDepthOfTwo( childModel ); + + assertEquals( "Modified parent!", readModel( "relative-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForReverseRelativeUrlsDepthOfTwo( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "doxia/core/banner/left", childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "doxia/core/images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "doxia/core/banner/right/", + childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "doxia/core/commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "doxia/core/tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "doxia/core/tomcat/logo.gif", poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "doxia/core/apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "doxia/core/bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "doxia/core/special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testPathsResolvedForUnrelatedRelativeUrls() + throws IOException, XmlPullParserException + { + DecorationModel parentModel = readModel( "relative-urls.xml" ); + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org", + "http://jakarta.apache.org" ); + assertPathsResolvedForUnrelatedRelativeUrls( childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://people.apache.org/", + "http://jakarta.apache.org" ); + assertPathsResolvedForUnrelatedRelativeUrls( childModel ); + + assertEquals( "Modified parent!", readModel( "relative-urls.xml" ), parentModel ); + } + + private static void assertPathsResolvedForUnrelatedRelativeUrls( final DecorationModel childModel ) + { + assertEquals( "check left banner href", "http://jakarta.apache.org/banner/left", + childModel.getBannerLeft().getHref() ); + assertEquals( "check left banner image", "http://jakarta.apache.org/images/jakarta-logo.gif", + childModel.getBannerLeft().getSrc() ); + + assertEquals( "check right banner href", "http://jakarta.apache.org/banner/right/", + childModel.getBannerRight().getHref() ); + assertEquals( "check right banner image", "http://jakarta.apache.org/commons/images/logo.png", + childModel.getBannerRight().getSrc() ); + + Logo poweredBy = childModel.getPoweredBy().get( 0 ); + assertEquals( "check powered by logo href", "http://jakarta.apache.org/tomcat", poweredBy.getHref() ); + assertEquals( "check powered by logo image", "http://jakarta.apache.org/tomcat/logo.gif", + poweredBy.getImg() ); + + LinkItem breadcrumb = childModel.getBody().getBreadcrumbs().get( 0 ); + assertEquals( "check breadcrumb href", "http://jakarta.apache.org/apache", breadcrumb.getHref() ); + + LinkItem link = childModel.getBody().getLinks().get( 0 ); + assertEquals( "check link href", "http://jakarta.apache.org/bouncycastle/", link.getHref() ); + + Menu menu = childModel.getBody().getMenus().get( 0 ); + LinkItem menuItem = menu.getItems().get( 0 ); + assertEquals( "check menu item href", "http://jakarta.apache.org/special/", menuItem.getHref() ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testNullParent() + throws IOException, XmlPullParserException + { + DecorationModel childModel = readModel( "empty.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, null, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + DecorationModel mergedModel = readModel( "empty.xml" ); + + assertEquals( "Check result", mergedModel, childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, null, "scp://people.apache.org/doxia", + "scp://people.apache.org" ); + assertEquals( "Check scp result", mergedModel, childModel ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testFullyPopulatedChild() + throws IOException, XmlPullParserException + { + DecorationModel childModel = readModel( "fully-populated-child.xml" ); + DecorationModel parentModel = readModel( "fully-populated-child.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://foo.apache.org/doxia", + "http://foo.apache.org" ); + DecorationModel mergedModel = readModel( "fully-populated-child.xml" ); + + assertEquals( "Check result", mergedModel, childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "fully-populated-child.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://foo.apache.org/doxia", + "scp://foo.apache.org" ); + assertEquals( "Check scp result", mergedModel, childModel ); + + assertEquals( "Modified parent!", readModel( "fully-populated-child.xml" ), parentModel ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testFullyPopulatedParentAndEmptyChild() + throws IOException, XmlPullParserException + { + DecorationModel childModel = readModel( "empty.xml" ); + DecorationModel parentModel = readModel( "fully-populated-child.xml" ); + + assembler.assembleModelInheritance( NAME, childModel, parentModel, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + + DecorationModel unresolvedModel = readModel( "fully-populated-unresolved.xml" ); + assertEquals( "Check result", unresolvedModel, childModel ); + + assembler.resolvePaths( childModel, "http://maven.apache.org/doxia" ); + DecorationModel mergedModel = readModel( "fully-populated-merged.xml" ); + + assertEquals( "Check result", mergedModel, childModel ); + + // same with scp url, DOXIASITETOOLS-47 + childModel = readModel( "empty.xml" ); + assembler.assembleModelInheritance( NAME, childModel, parentModel, "scp://maven.apache.org/doxia", + "scp://maven.apache.org" ); + assembler.resolvePaths( childModel, "http://maven.apache.org/doxia" ); + assertEquals( "Check scp result", mergedModel, childModel ); + + assertEquals( "Modified parent!", readModel( "fully-populated-child.xml" ), parentModel ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testResolvingAllExternalUrls() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "external-urls.xml" ); + + assembler.resolvePaths( model, "http://foo.com/" ); + DecorationModel mergedModel = readModel( "external-urls.xml" ); + + assertEquals( "Check result", mergedModel, model ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testResolvingAllRelativeUrls() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "relative-urls.xml" ); + + assembler.resolvePaths( model, "http://foo.com/" ); + + DecorationModel resolvedModel = readModel( "relative-urls-resolved.xml" ); + + assertEquals( "Check result", resolvedModel, model ); + } + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testResolvingAllSiteUrls() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "subsite-urls.xml" ); + + assembler.resolvePaths( model, "http://maven.apache.org/" ); + + DecorationModel resolvedModel = readModel( "relative-urls-resolved.xml" ); + assertEquals( "Check result", resolvedModel, model ); + } + +/* [MSITE-62] This is to test the ../ relative paths, which I am inclined not to use + public void testResolvingAllSiteChildUrls() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "subsite-urls.xml" ); + + assembler.resolvePaths( model, "http://maven.apache.org/foo" ); + + DecorationModel resolvedModel = readModel( "subsite-relative-urls-resolved.xml" ); + assertEquals( "Check result", resolvedModel, model ); + } + + public void testResolvingAllSiteChildUrlsMultipleLevels() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "subsite-urls.xml" ); + + assembler.resolvePaths( model, "http://maven.apache.org/banner/right" ); + + DecorationModel resolvedModel = readModel( "subsite-relative-urls-multiple-resolved.xml" ); + assertEquals( "Check result", resolvedModel, model ); + } + + public void testResolvingAllSiteChildFilesystemUrls() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "subsite-urls-file.xml" ); + + assembler.resolvePaths( model, "file://localhost/www/maven.apache.org/foo" ); + + DecorationModel resolvedModel = readModel( "subsite-relative-urls-resolved.xml" ); + assertEquals( "Check result", resolvedModel, model ); + } + +*/ + + /** + * + * @throws IOException + * @throws XmlPullParserException + */ + @Test + public void testResolvingEmptyDescriptor() + throws IOException, XmlPullParserException + { + DecorationModel model = readModel( "empty.xml" ); + assembler.resolvePaths( model, "http://maven.apache.org" ); + DecorationModel mergedModel = readModel( "empty.xml" ); + + assertEquals( "Check result", mergedModel, model ); + } + + /** + * + */ + @Test + public void testDuplicateParentElements() + { + DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + model.getBody().addLink( createLinkItem( "Foo", "http://foo.apache.org" ) ); + model.getBody().addLink( createLinkItem( "Foo", "http://foo.apache.org" ) ); + + model.addPoweredBy( createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ) ); + model.addPoweredBy( createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ) ); + + DecorationModel child = new DecorationModel(); + assembler.assembleModelInheritance( NAME, child, model, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + + assertEquals( "Check size", 1, child.getBody().getLinks().size() ); + assertEquals( "Check item", createLinkItem( "Foo", "http://foo.apache.org" ), + child.getBody().getLinks().get( 0 ) ); + + assertEquals( "Check size", 1, child.getPoweredBy().size() ); + assertEquals( "Check item", + createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ), + child.getPoweredBy().get( 0 ) ); + } + + /** + * + */ + @Test + public void testDuplicateChildElements() + { + DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + model.getBody().addLink( createLinkItem( "Foo", "http://foo.apache.org" ) ); + model.getBody().addLink( createLinkItem( "Foo", "http://foo.apache.org" ) ); + + model.addPoweredBy( createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ) ); + model.addPoweredBy( createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ) ); + + DecorationModel parent = new DecorationModel(); + assembler.assembleModelInheritance( NAME, model, parent, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + + assertEquals( "Check size", 1, model.getBody().getLinks().size() ); + assertEquals( "Check item", createLinkItem( "Foo", "http://foo.apache.org" ), + model.getBody().getLinks().get( 0 ) ); + + assertEquals( "Check size", 1, model.getPoweredBy().size() ); + assertEquals( "Check item", + createLogo( "Foo", "http://foo.apache.org", "http://foo.apache.org/foo.jpg" ), + model.getPoweredBy().get( 0 ) ); + + assertEquals( "Modified parent!", new DecorationModel(), parent ); + } + + /** + * + */ + @Test + public void testBadHref() + { + final DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + model.getBody().addBreadcrumb( createLinkItem( "Foo", "http://foo.apache.org/${property}" ) ); + assembler.resolvePaths( model, "http://foo.apache.org" ); + assertEquals( "Check size", 1, model.getBody().getBreadcrumbs().size() ); + assertEquals( "Check item", createLinkItem( "Foo", "http://foo.apache.org/${property}" ), + model.getBody().getBreadcrumbs().get( 0 ) ); + } + + /** + * + */ + @Test + public void testBreadcrumbWithoutHref() + { + DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + model.getBody().addBreadcrumb( createLinkItem( "Foo", null ) ); + assembler.resolvePaths( model, "http://foo.apache.org" ); + assertEquals( "Check size", 1, model.getBody().getBreadcrumbs().size() ); + assertEquals( "Check item", createLinkItem( "Foo", null ), model.getBody().getBreadcrumbs().get( 0 ) ); + } + + /** + * + */ + @Test + public void testBreadcrumbs() + { + String parentHref = "http://parent.com/index.html"; + + final DecorationModel parent = new DecorationModel(); + parent.setBody( new Body() ); + parent.getBody().addBreadcrumb( createLinkItem( "Parent", parentHref ) ); + + DecorationModel child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "http://parent.com/child", "http://parent.com" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + + + // same with trailing slash + child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "http://parent.com/child/", "http://parent.com/" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + + // now mixed + child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "http://parent.com/child/", "http://parent.com" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + + // and other way round + child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "http://parent.com/child", "http://parent.com/" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + + + // now with child breadcrumb + child = new DecorationModel(); + child.setBody( new Body() ); + child.getBody().addBreadcrumb( createLinkItem( "Child", "index.html" ) ); + assembler.assembleModelInheritance( "childName", child, parent, + "http://parent.com/child/", "http://parent.com/" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "Child", parentHref ); + + + // now with file url + parentHref = "file://parent.com/index.html"; + ( parent.getBody().getBreadcrumbs().get( 0 ) ).setHref( parentHref ); + child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "file://parent.com/child/", "file://parent.com/" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + + + // now with scp url + parentHref = "scp://parent.com/index.html"; + ( parent.getBody().getBreadcrumbs().get( 0 ) ).setHref( parentHref ); + child = new DecorationModel(); + assembler.assembleModelInheritance( "childName", child, parent, + "scp://parent.com/child/", "scp://parent.com/" ); + assertBreadcrumbsCorrect( child.getBody().getBreadcrumbs(), "childName", parentHref ); + } + + private static void assertBreadcrumbsCorrect( final List breadcrumbs, final String childName, + final String parentHref ) + { + assertEquals( "Check size", 2, breadcrumbs.size() ); + assertEquals( "Check parent item", createLinkItem( "Parent", parentHref ), breadcrumbs.get( 0 ) ); + assertEquals( "Check child item", createLinkItem( childName, "index.html" ), breadcrumbs.get( 1 ) ); + } + + /** + * https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + */ + @Test + public void testBreadcrumbCutParentAfterDuplicate() + { + DecorationModel child = new DecorationModel(); // B > E + child.setBody( new Body() ); + child.getBody().addBreadcrumb( createLinkItem( "B", null ) ); + child.getBody().addBreadcrumb( createLinkItem( "E", null ) ); + + DecorationModel parent = new DecorationModel(); // A > B > C > D + parent.setBody( new Body() ); + parent.getBody().addBreadcrumb( createLinkItem( "A", null ) ); + parent.getBody().addBreadcrumb( createLinkItem( "B", null ) ); + parent.getBody().addBreadcrumb( createLinkItem( "C", null ) ); + parent.getBody().addBreadcrumb( createLinkItem( "D", null ) ); + + assembler.assembleModelInheritance( NAME, child, parent, "http://maven.apache.org/doxia", + "http://maven.apache.org" ); + + final List breadcrumbs = child.getBody().getBreadcrumbs(); // expected: A > B > E + assertEquals( "Check size", 3, breadcrumbs.size() ); + assertEquals( "Check item", createLinkItem( "A", null ), breadcrumbs.get( 0 ) ); + assertEquals( "Check item", createLinkItem( "B", null ), breadcrumbs.get( 1 ) ); + assertEquals( "Check item", createLinkItem( "E", null ), breadcrumbs.get( 2 ) ); + } + + /** + * + */ + @Test + public void testBannerWithoutHref() + { + DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + + Banner banner = createBanner( "Left", null, "/images/src.gif", "alt" ); + + model.setBannerLeft( banner ); + + assembler.resolvePaths( model, "http://foo.apache.org" ); + + assertEquals( "Check banner", createBanner( "Left", null, "images/src.gif", "alt" ), + model.getBannerLeft() ); + } + + /** + * + */ + @Test + public void testLogoWithoutImage() + { + // This should actually be validated in the model, it doesn't really make sense + DecorationModel model = new DecorationModel(); + model.setBody( new Body() ); + model.addPoweredBy( createLogo( "Foo", "http://foo.apache.org", null ) ); + assembler.resolvePaths( model, "http://foo.apache.org" ); + assertEquals( "Check size", 1, model.getPoweredBy().size() ); + assertEquals( "Check item", createLogo( "Foo", "./", null ), model.getPoweredBy().get( 0 ) ); + } + + private static Banner createBanner( String name, String href, String src, String alt ) + { + Banner banner = new Banner(); + banner.setName( name ); + banner.setHref( href ); + banner.setSrc( src ); + banner.setAlt( alt ); + return banner; + } + + private Logo createLogo( String name, String href, String img ) + { + Logo logo = new Logo(); + logo.setHref( href ); + logo.setImg( img ); + logo.setName( name ); + return logo; + } + + private static LinkItem createLinkItem( String name, String href ) + { + LinkItem item = new LinkItem(); + item.setName( name ); + item.setHref( href ); + return item; + } + + private DecorationModel readModel( String name ) + throws IOException, XmlPullParserException + { + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/" + name ) ); + return new DecorationXpp3Reader().read( reader ); + } + finally + { + IOUtil.close( reader ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/Doxia91Test.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/Doxia91Test.java new file mode 100644 index 000000000..bcb8585f6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/Doxia91Test.java @@ -0,0 +1,72 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for DOXIA-91 problems. All tests make sure that a passed in null will not generate any path conversion but + * just returns the old path. + * + * @author Henning P. Schmiedehausen + */ +public class Doxia91Test +{ + /** @throws Exception */ + @Test + public void testOldPathNull() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( null ); + PathDescriptor newPath = new PathDescriptor( "http://www.apache.org/" ); + + PathDescriptor diff = PathUtils.convertPath( oldPath, newPath ); + + assertEquals( diff, oldPath ); + } + + /** @throws Exception */ + @Test + public void testNewPathNull() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( "http://www.apache.org/", "file:///home/henning/foo" ); + PathDescriptor newPath = new PathDescriptor( null ); + + PathDescriptor diff = PathUtils.convertPath( oldPath, newPath ); + + assertEquals( diff, oldPath ); + } + + /** @throws Exception */ + @Test + public void testBothPathNull() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( null ); + PathDescriptor newPath = new PathDescriptor( null ); + + PathDescriptor diff = PathUtils.convertPath( oldPath, newPath ); + + assertEquals( diff, oldPath ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptorTest.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptorTest.java new file mode 100644 index 000000000..3662b726f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptorTest.java @@ -0,0 +1,655 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; + +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.StringUtils; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Test the PathDescriptor creation under various circumstances. + * + * @author Henning P. Schmiedehausen + */ +public class PathDescriptorTest +{ + /** @throws Exception */ + @Test + public void testAbsPath() + throws Exception + { + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( "/" + path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testRelPath() + throws Exception + { + String path = "relativePath"; + + PathDescriptor desc = new PathDescriptor( path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testEmptyAbsPath() + throws Exception + { + String path = ""; + + PathDescriptor desc = new PathDescriptor( "/" + path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testEmptyRelPath() + throws Exception + { + String path = ""; + + PathDescriptor desc = new PathDescriptor( path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullPath() + throws Exception + { + String path = null; + + PathDescriptor desc = new PathDescriptor( path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNull( desc.getPath() ); + assertNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullBaseAbsPath() + throws Exception + { + String base = null; + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullBaseRelPath() + throws Exception + { + String base = null; + String path = "relativePath"; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullBaseEmptyAbsPath() + throws Exception + { + String base = null; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullBaseEmptyRelPath() + throws Exception + { + String base = null; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testNullBaseNullPath() + throws Exception + { + String base = null; + String path = null; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertTrue( desc.isRelative() ); + assertNull( desc.getBaseUrl() ); + assertNull( desc.getPathUrl() ); + assertNull( desc.getPath() ); + assertNull( desc.getLocation() ); + assertEquals( "wrong path", path, desc.getPath() ); + assertEquals( "wrong location", path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testUrlBaseAbsPath() + throws Exception + { + String base = "http://maven.apache.org/"; + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testUrlBaseRelPath() + throws Exception + { + String base = "http://maven.apache.org/"; + String path = "relativePath"; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testUrlBaseEmptyAbsPath() + throws Exception + { + String base = "http://maven.apache.org/"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testUrlBaseEmptyRelPath() + throws Exception + { + String base = "http://maven.apache.org/"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + path, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testUrlBaseNullPath() + throws Exception + { + String base = "http://maven.apache.org/"; + String path = null; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/", desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + + /** @throws Exception */ + @Test + public void testFileBaseAbsPath() + throws Exception + { + String base = "/tmp/foo"; + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( "file://" + base, "/" + path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base + "/" + path ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base + "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + "/" + path, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testFileBaseRelPath() + throws Exception + { + String base = "/tmp/foo"; + String path = "relativePath"; + + PathDescriptor desc = new PathDescriptor( "file://" + base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base + "/" + path ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base + "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + "/" + path, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testFileBaseEmptyAbsPath() + throws Exception + { + String base = "/tmp/foo"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( "file://" + base, "/" + path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testFileBaseEmptyRelPath() + throws Exception + { + String base = "/tmp/foo"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( "file://" + base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testFileBaseNullPath() + throws Exception + { + String base = "/tmp/foo"; + String path = null; + + PathDescriptor desc = new PathDescriptor( "file://" + base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + +/* + // same as testUrlBaseAbsPath with scp, this fails!? DOXIASITETOOLS-47 + public void testUriBaseAbsPath() + throws Exception + { + String base = "scp://people.apache.org/"; + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertFalse( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( "wrong path", "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + path, desc.getLocation() ); + } +*/ + + /** @throws Exception */ + @Test + public void testPathBaseAbsPath() + throws Exception + { + String base = "/tmp/foo"; + String path = "absolutePath"; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base + "/" + path ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base + "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + "/" + path, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testPathBaseRelPath() + throws Exception + { + String base = "/tmp/foo"; + String path = "relativePath"; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base + "/" + path ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base + "/" + path, desc.getPath() ); + assertEquals( "wrong location", base + "/" + path, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testPathBaseEmptyAbsPath() + throws Exception + { + String base = "/tmp/foo"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, "/" + path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testPathBaseEmptyRelPath() + throws Exception + { + String base = "/tmp/foo"; + String path = ""; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testPathBaseNullPath() + throws Exception + { + String base = "/tmp/foo"; + String path = null; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + String s = StringUtils.replace( new File( base ).toURI().toURL().toString(), "file:", "" ); + assertEquals( "wrong path", s, desc.getPath() ); + assertEquals( "wrong location", s, desc.getLocation() ); + } + else + { + assertEquals( "wrong path", base, desc.getPath() ); + assertEquals( "wrong location", base, desc.getLocation() ); + } + } + + /** @throws Exception */ + @Test + public void testPathRelBase() + throws Exception + { + String base = "../msite-404"; + String path = "index.html"; + + PathDescriptor desc = new PathDescriptor( base, path ); + + assertTrue( desc.isFile() ); + assertFalse( desc.isRelative() ); + assertNotNull( desc.getBaseUrl() ); + assertNotNull( desc.getPathUrl() ); + assertNotNull( desc.getPath() ); + assertNotNull( desc.getLocation() ); + assertEquals( desc.getPath(), desc.getLocation() ); + // Hudson doesn't like this? + //assertEquals( desc.getPathUrl().toString(), desc.getBaseUrl().toString() + "/" + path ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtilsTest.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtilsTest.java new file mode 100644 index 000000000..06130c539 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtilsTest.java @@ -0,0 +1,80 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * + * @author ltheussl + */ +public class PathUtilsTest +{ + private static final String SLASH = File.separator; + + /** @throws Exception */ + @Test + public void testConvertPath() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( (String) null, "base" ); + PathDescriptor newPath = new PathDescriptor( "/tmp", "target" ); + assertEquals( oldPath, PathUtils.convertPath( oldPath, newPath ) ); + assertEquals( newPath, PathUtils.convertPath( newPath, oldPath ) ); + } + + /** @throws Exception */ + @Test + public void testGetRelativePath() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( "/tmp/foo", "base" ); + PathDescriptor newPath = new PathDescriptor( "/tmp", "target" ); + assertEquals( ".." + SLASH + ".." + SLASH + "target", PathUtils.getRelativePath( oldPath, newPath ) ); + + oldPath = new PathDescriptor( (String) null, "base" ); + assertNull( PathUtils.getRelativePath( oldPath, newPath ) ); + assertNull( PathUtils.getRelativePath( newPath, oldPath ) ); + + oldPath = new PathDescriptor( "/tmp/foo", null ); + assertEquals( ".." + SLASH + "target", PathUtils.getRelativePath( oldPath, newPath ) ); + assertEquals( ".." + SLASH + "foo", PathUtils.getRelativePath( newPath, oldPath ) ); + } + + /** @throws Exception */ + @Test + public void testRelativePathScpBase() + throws Exception + { + PathDescriptor oldPath = new PathDescriptor( "http://maven.apache.org/", "source" ); + PathDescriptor newPath = new PathDescriptor( "http://maven.apache.org/", "target" ); + assertEquals( "../source", PathUtils.getRelativePath( oldPath, newPath ) ); + + oldPath = new PathDescriptor( "scp://people.apache.org/", "source" ); + newPath = new PathDescriptor( "scp://people.apache.org/", "target" ); + // same with scp URLs fails?! DOXIASITETOOLS-47 + //assertEquals( "../source", PathUtils.getRelativePath( oldPath, newPath ) ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptorTest.java b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptorTest.java new file mode 100644 index 000000000..d76789244 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptorTest.java @@ -0,0 +1,294 @@ + +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptorTest +{ + private static final String BASE_URL = "http://maven.apache.org/"; + + /** + * Test of constructor, of class URIPathDescriptor. + * + * @throws Exception + */ + @Test + public void testConstructor() + throws Exception + { + final String expected = BASE_URL + "doxia"; + + final URIPathDescriptor path = new URIPathDescriptor( BASE_URL, "doxia" ); + assertEquals( expected, path.toString() ); + assertEquals( BASE_URL, path.getBaseURI().toString() ); + assertEquals( "doxia", path.getLink().toString() ); + + URIPathDescriptor compare = new URIPathDescriptor( "http://maven.apache.org", "/doxia" ); + assertEquals( expected, compare.toString() ); + + compare = new URIPathDescriptor( "http://maven.apache.org/./doxia/../", "/sub/./sub/../../doxia" ); + assertEquals( expected, compare.toString() ); + + compare = new URIPathDescriptor( "http://maven.apache.org/doxia", "" ); + assertEquals( expected + "/", compare.toString() ); + + compare = new URIPathDescriptor( "file:///C:\\Foo\\bar1", "" ); + assertEquals( "file:///C:/Foo/bar1/", compare.getBaseURI().toString() ); + // toString() calls resolve() which removes two slashes because authority is empty + assertEquals( "file:/C:/Foo/bar1/", compare.toString() ); + + compare = new URIPathDescriptor( "file:///C:/Documents%20and%20Settings/foo/", "bar" ); + assertEquals( "file:/C:/Documents%20and%20Settings/foo/bar", compare.toString() ); + + compare = new URIPathDescriptor( "file:////Users/", "user" ); + assertEquals( "file:/Users/user", compare.toString() ); + + compare = new URIPathDescriptor( "file:/C:/Documents%20and%20Settings/foo/", "bar" ); + assertEquals( "file:/C:/Documents%20and%20Settings/foo/bar", compare.toString() ); + + compare = new URIPathDescriptor( "file://C:/Documents%20and%20Settings/foo/", "bar" ); + // toString() calls resolve() which removes the colon if port is empty, C is the host here! + assertEquals( "file://C/Documents%20and%20Settings/foo/bar", compare.toString() ); + + compare = new URIPathDescriptor( "file://C:8080/Documents%20and%20Settings/foo/", "bar" ); + assertEquals( "file://C:8080/Documents%20and%20Settings/foo/bar", compare.toString() ); + + compare = new URIPathDescriptor( "C:\\Foo\\bar", "bar" ); + assertEquals( "C:/Foo/bar/bar", compare.toString() ); // NOTE: C: is the scheme here! + + assertFailure( "/doxia", BASE_URL ); + assertFailure( "file:///C:/Documents and Settings/foo/", "bar" ); + } + + /** + * Test of resolveLink method, of class URIPathDescriptor. + * + * @throws Exception + */ + @Test + public void testResolveLink() + throws Exception + { + final String expected = BASE_URL + "source"; + + URIPathDescriptor oldPath = new URIPathDescriptor( BASE_URL, "source" ); + assertEquals( expected, oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "source/" ); + assertEquals( expected + "/", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "/source" ); + assertEquals( expected, oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( "http://maven.apache.org", "source" ); + assertEquals( expected, oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "source/index.html" ); + assertEquals( expected + "/index.html", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "source/index.html?var=foo&var2=bar" ); + assertEquals( expected + "/index.html?var=foo&var2=bar", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( "file:////Users/", "user" ); + assertEquals( "file:/Users/user", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( "file:///C:/Documents%20and%20Settings/", "source" ); + // resolve() removes two slashes because authority is empty + assertEquals( "file:/C:/Documents%20and%20Settings/source", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( "file://C:/Documents%20and%20Settings/", "source" ); + // resolve() removes the colon if port is empty + assertEquals( "file://C/Documents%20and%20Settings/source", oldPath.resolveLink().toString() ); + + oldPath = new URIPathDescriptor( "file:/C:/Documents%20and%20Settings/", "source" ); + assertEquals( "file:/C:/Documents%20and%20Settings/source", oldPath.resolveLink().toString() ); + } + + /** + * Test of rebaseLink method, of class URIPathDescriptor. + * + * @throws Exception + */ + @Test + public void testRebaseLink() + throws Exception + { + URIPathDescriptor oldPath = new URIPathDescriptor( BASE_URL, "source" ); + assertEquals( "../source", oldPath.rebaseLink( "http://maven.apache.org/doxia/" ).toString() ); + assertEquals( "http://maven.apache.org/source", oldPath.rebaseLink( null ).toString() ); + assertEquals( "http://maven.apache.org/source", + oldPath.rebaseLink( "C:/Documents and Settings/" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "./" ); + assertEquals( "", oldPath.rebaseLink( "http://maven.apache.org/" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "" ); + assertEquals( "", oldPath.rebaseLink( "http://maven.apache.org/" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "source/index.html" ); + assertEquals( "../source/index.html", + oldPath.rebaseLink( "http://maven.apache.org/doxia/" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "source/index.html?var=foo&var2=bar" ); + assertEquals( "../source/index.html?var=foo&var2=bar", + oldPath.rebaseLink( "http://maven.apache.org/doxia/" ).toString() ); + + oldPath = new URIPathDescriptor( "scp://people.apache.org/", "source" ); + assertEquals( "../source", oldPath.rebaseLink( "scp://people.apache.org/doxia" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "banner/left" ); + assertEquals( "../banner/left", oldPath.rebaseLink( "http://maven.apache.org/doxia/" ).toString() ); + + oldPath = new URIPathDescriptor( BASE_URL, "index.html?var=foo&var2=bar" ); + assertEquals( "../index.html?var=foo&var2=bar", + oldPath.rebaseLink( "http://maven.apache.org/doxia/" ).toString() ); + + oldPath = new URIPathDescriptor( "http://jakarta.apache.org/", "banner/left" ); + assertEquals( "http://jakarta.apache.org/banner/left", oldPath.rebaseLink( BASE_URL ).toString() ); + + oldPath = new URIPathDescriptor( "file:////Users/", "user" ); + assertEquals( "../user", oldPath.rebaseLink( "file:////Users/target" ).toString() ); + assertEquals( "../user", oldPath.rebaseLink( "file:/Users/target" ).toString() ); + + oldPath = new URIPathDescriptor( "file:///C:/Documents%20and%20Settings/", "source" ); + assertEquals( "../source", + oldPath.rebaseLink( "file:///C:/Documents%20and%20Settings/target" ).toString() ); + + oldPath = new URIPathDescriptor( "file://C:/Documents%20and%20Settings/", "source" ); + assertEquals( "../source", + oldPath.rebaseLink( "file://C:/Documents%20and%20Settings/target" ).toString() ); + + oldPath = new URIPathDescriptor( "file:/C:/Documents%20and%20Settings/", "source" ); + assertEquals( "../source", + oldPath.rebaseLink( "file:/C:/Documents%20and%20Settings/target" ).toString() ); + } + + /** + * Test of relativizeLink method, of class URIPathDescriptor. + * + * @throws Exception + */ + @Test + public void testRelativizeLink() + throws Exception + { + URIPathDescriptor path = new URIPathDescriptor( BASE_URL, "source" ); + assertEquals( "source", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( BASE_URL, "http://maven.apache.org/source" ); + assertEquals( "source", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( BASE_URL, "http://maven.apache.org/" ); + assertEquals( "./", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( BASE_URL, "http://maven.apache.org" ); + assertEquals( "./", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "http://maven.apache.org", BASE_URL ); + assertEquals( "./", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "http://maven.apache.org", "http://maven.apache.org" ); + assertEquals( "./", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "http://maven.apache.org/doxia/", "http://maven.apache.org/source/" ); + assertEquals( "../source/", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "http://maven.apache.org/doxia", "http://maven.apache.org/source" ); + assertEquals( "../source", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( BASE_URL, "http://maven.apache.org/index.html" ); + assertEquals( "index.html", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( BASE_URL, "http://maven.apache.org/index.html?var=foo&var2=bar" ); + assertEquals( "index.html?var=foo&var2=bar", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "file:////Users/", "index.html" ); + assertEquals( "index.html", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "file:///C:/Documents%20and%20Settings/", "index.html" ); + assertEquals( "index.html", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "file://C:/Documents%20and%20Settings/", "index.html" ); + assertEquals( "index.html", path.relativizeLink().toString() ); + + path = new URIPathDescriptor( "file:/C:/Documents%20and%20Settings/", "index.html" ); + assertEquals( "index.html", path.relativizeLink().toString() ); + } + + /** + * Test of sameSite method, of class URIPathDescriptor. + * + * @throws Exception + */ + @Test + public void testSameSite() + throws Exception + { + final URIPathDescriptor path = new URIPathDescriptor( BASE_URL, "doxia" ); + + assertTrue( path.sameSite( new URI( "http://maven.apache.org/" ) ) ); + assertTrue( path.sameSite( new URI( "http://maven.apache.org" ) ) ); + assertTrue( path.sameSite( new URI( "HTTP://maven.apache.org/" ) ) ); + assertTrue( path.sameSite( new URI( "http://MAVEN.apache.org/" ) ) ); + assertTrue( path.sameSite( new URI( "http://maven.apache.org/wagon/index.html" ) ) ); + + assertFalse( path.sameSite( null ) ); + assertFalse( path.sameSite( new URI( "https://maven.apache.org/" ) ) ); + assertFalse( path.sameSite( new URI( "http://ant.apache.org/" ) ) ); + assertFalse( path.sameSite( new URI( "http://maven.apache.org:80" ) ) ); + assertFalse( path.sameSite( new URI( "/usr/share/bin/" ) ) ); + assertFalse( path.sameSite( new URI( "http:///maven.apache.org/" ) ) ); + + final URIPathDescriptor nullHost = new URIPathDescriptor( "http:///maven.apache.org/", "doxia" ); + assertTrue( nullHost.sameSite( new URI( "http:///maven.apache.org/" ) ) ); + assertFalse( nullHost.sameSite( new URI( "http://maven.apache.org/" ) ) ); + + URIPathDescriptor newPath = new URIPathDescriptor( "file:///C:/Documents%20and%20Settings/", "source" ); + assertTrue( newPath.sameSite( new URI( "file:///C:/Documents%20and%20Settings/" ) ) ); + assertFalse( newPath.sameSite( new URI( "file://C:/Documents%20and%20Settings/" ) ) ); + // authority is empty + assertTrue( newPath.sameSite( new URI( "file:/C:/Documents%20and%20Settings/" ) ) ); + } + + private static void assertFailure( final String base, final String link ) + { + try + { + final URIPathDescriptor test = new URIPathDescriptor( base, link ); + fail( "Should fail: " + test.toString() ); + } + catch ( IllegalArgumentException ex ) + { + assertNotNull( ex ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/empty.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/empty.xml new file mode 100644 index 000000000..182b9dd2e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/empty.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/external-urls.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/external-urls.xml new file mode 100644 index 000000000..0791a9672 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/external-urls.xml @@ -0,0 +1,54 @@ + + + + + + + The Jakarta Project + http://jakarta.apache.org/images/jakarta-logo.gif + http://jakarta.apache.org/ + + + Jakarta Commons Sandbox + http://jakarta.apache.org/commons/images/logo.png + http://jakarta.apache.org/commons/sandbox + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-child.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-child.xml new file mode 100644 index 000000000..09208b71c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-child.xml @@ -0,0 +1,69 @@ + + + + + + + name + src + href + + + name + src + href + + + + + + + + + + + org.apache.maven.skins + maven-default-skin + + + + + + ]]> + + + + + + + + + + + + + + junk + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-merged.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-merged.xml new file mode 100644 index 000000000..47e68581b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-merged.xml @@ -0,0 +1,64 @@ + + + + + + + name + ../src + ../href + + + name + ../src + ../href + + + + + + + + + + + org.apache.maven.skins + maven-default-skin + + + + + + ]]> + + + + + + + + + junk + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-unresolved.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-unresolved.xml new file mode 100644 index 000000000..0f9c66daa --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/fully-populated-unresolved.xml @@ -0,0 +1,64 @@ + + + + + + + name + ../src + ../href + + + name + ../src + ../href + + + + + + + + + + + org.apache.maven.skins + maven-default-skin + + + + + + ]]> + + + + + + + + + junk + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child-no-inheritance.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child-no-inheritance.xml new file mode 100644 index 000000000..64c545c29 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child-no-inheritance.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child.xml new file mode 100644 index 000000000..30482a8ca --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-child.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-expected.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-expected.xml new file mode 100644 index 000000000..355e775bb --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-expected.xml @@ -0,0 +1,69 @@ + + + + + + + The Jakarta Project + http://jakarta.apache.org/ + + + Jakarta Commons Sandbox + http://jakarta.apache.org/commons/images/logo.png + http://jakarta.apache.org/commons/sandbox + + + + + + org.apache.maven.skins + maven-default-skin + + ${project.scm.url} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-parent.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-parent.xml new file mode 100644 index 000000000..8965e2b59 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/inheritance-parent.xml @@ -0,0 +1,64 @@ + + + + + + + The Jakarta Project + http://jakarta.apache.org/ + + + Jakarta Commons Sandbox + http://jakarta.apache.org/commons/images/logo.png + http://jakarta.apache.org/commons/sandbox + + + + + + org.apache.maven.skins + maven-default-skin + + ${project.scm.url} + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls-resolved.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls-resolved.xml new file mode 100644 index 000000000..5493373a3 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls-resolved.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + images/jakarta-logo.gif + banner/left + + + Jakarta Commons Sandbox + commons/images/logo.png + banner/right/ + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls.xml new file mode 100644 index 000000000..8353486a7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/relative-urls.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + /images/jakarta-logo.gif + banner/left + + + Jakarta Commons Sandbox + commons/images/logo.png + /banner/right/ + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-multiple-resolved.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-multiple-resolved.xml new file mode 100644 index 000000000..71b50e408 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-multiple-resolved.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + ../../images/jakarta-logo.gif + ../left + + + Jakarta Commons Sandbox + ../../commons/images/logo.png + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-resolved.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-resolved.xml new file mode 100644 index 000000000..68d894d07 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-relative-urls-resolved.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + ../images/jakarta-logo.gif + ../banner/left + + + Jakarta Commons Sandbox + ../commons/images/logo.png + ../banner/right/ + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls-file.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls-file.xml new file mode 100644 index 000000000..de3c78a7a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls-file.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + file:///www/maven.apache.org/images/jakarta-logo.gif + file:///www/maven.apache.org/banner/left + + + Jakarta Commons Sandbox + file:///www/maven.apache.org/commons/images/logo.png + file:///www/maven.apache.org/banner/right/ + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls.xml b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls.xml new file mode 100644 index 000000000..5452755ce --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-decoration-model/src/test/resources/subsite-urls.xml @@ -0,0 +1,53 @@ + + + + + + + The Jakarta Project + http://maven.apache.org/images/jakarta-logo.gif + http://maven.apache.org/banner/left + + + Jakarta Commons Sandbox + http://maven.apache.org/commons/images/logo.png + http://maven.apache.org/banner/right/ + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/pom.xml new file mode 100644 index 000000000..ae553874b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/pom.xml @@ -0,0 +1,169 @@ + + + + + + 4.0.0 + + + doxia-sitetools + org.apache.maven.doxia + 1.9.3-SNAPSHOT + ../pom.xml + + + doxia-doc-renderer + + Doxia Sitetools :: Document Renderer + The Document Renderer handles the rendering of documents, in formats like PDF and RTF. + + + + + org.apache.maven.doxia + doxia-core + + + org.apache.maven.doxia + doxia-logging-api + + + org.apache.maven.doxia + doxia-sink-api + + + org.apache.maven.doxia + doxia-module-itext + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-fo + ${doxiaVersion} + + + + org.apache.maven.doxia + doxia-module-apt + runtime + + + org.apache.maven.doxia + doxia-module-fml + runtime + + + org.apache.maven.doxia + doxia-module-xdoc + runtime + + + org.apache.maven.doxia + doxia-module-xhtml + runtime + + + org.apache.maven.doxia + doxia-module-markdown + runtime + + + + + org.codehaus.plexus + plexus-component-annotations + + + org.codehaus.plexus + plexus-container-default + + + org.codehaus.plexus + plexus-utils + + + org.codehaus.plexus + plexus-velocity + 1.2 + + + org.codehaus.plexus + plexus-component-api + + + + + + + org.apache.velocity + velocity + + + + + xalan + xalan + 2.7.2 + + + xml-apis + xml-apis + 2.0.2 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + java.awt.headless + true + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-bytecode-version + + + + + org.apache.maven.doxia:doxia-module-markdown + org.nibor.autolink:autolink + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java new file mode 100644 index 000000000..bc9f7e549 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java @@ -0,0 +1,727 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.util.XmlValidator; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.context.Context; + +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.SiteResourceLoader; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + * Abstract document renderer. + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +public abstract class AbstractDocumentRenderer + extends AbstractLogEnabled + implements DocumentRenderer +{ + @Requirement + protected ParserModuleManager parserModuleManager; + + @Requirement + protected Doxia doxia; + + @Requirement + private VelocityComponent velocity; + + /** + * The common base directory of source files. + */ + private String baseDir; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @deprecated since 1.1.2, use {@link #render(Map, File, DocumentModel, DocumentRendererContext)} + */ + public abstract void render( Map filesToProcess, File outputDirectory, + DocumentModel documentModel ) + throws DocumentRendererException, IOException; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** {@inheritDoc} */ + public void render( Collection files, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( files ), outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, documentModel, null ); + } + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render a document from the files found in a source directory, depending on a rendering context. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * If the model contains a TOC, only the files found in this TOC are rendered, + * otherwise all files found under baseDirectory will be processed. + * If the model is null, render all files from baseDirectory individually. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( baseDirectory ), outputDirectory, documentModel, context ); + } + + /** + * Render a document from the files found in baseDirectory. This just forwards to + * {@link #render(File,File,DocumentModel)} with a new DocumentModel. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File, DocumentModel) + */ + public void render( File baseDirectory, File outputDirectory ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, (DocumentModel) null ); + } + + /** + * Render a document from the files found in baseDirectory. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentDescriptor a file containing the document model. + * If this file does not exist or is null, some default settings will be used. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File) if documentDescriptor does not exist or is null + * @see #render(Map, File, DocumentModel) otherwise + */ + public void render( File baseDirectory, File outputDirectory, File documentDescriptor ) + throws DocumentRendererException, IOException + { + if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) ) + { + getLogger().warn( "No documentDescriptor found: using default settings!" ); + + render( baseDirectory, outputDirectory ); + } + else + { + render( getFilesToProcess( baseDirectory ), outputDirectory, readDocumentModel( documentDescriptor ), + null ); + } + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.1 + * @deprecated since 1.1.2, use {@link #renderIndividual(Map, File, DocumentRendererContext)} + */ + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @return a Map of files to process. + * @throws java.io.IOException in case of a problem reading the files under baseDirectory. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + */ + public Map getFilesToProcess( File baseDirectory ) + throws IOException, DocumentRendererException + { + if ( !baseDirectory.isDirectory() ) + { + getLogger().warn( "No files found to process!" ); + + return new HashMap(); + } + + setBaseDir( baseDirectory.getAbsolutePath() ); + + Map filesToProcess = new LinkedHashMap(); + Map duplicatesFiles = new LinkedHashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( baseDirectory, module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + // TODO: handle in/excludes + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", null, false ); + + String[] extensions = getExtensions( module ); + List docs = new LinkedList( allFiles ); + // Take care of extension case + for ( Iterator it = docs.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, extensions ) ) + { + it.remove(); + } + } + + String[] vmExtensions = new String[extensions.length]; + for ( int i = 0; i < extensions.length; i++ ) + { + vmExtensions[i] = extensions[i] + ".vm"; + } + List velocityFiles = new LinkedList( allFiles ); + // *.xml.vm + for ( Iterator it = velocityFiles.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, vmExtensions ) ) + { + it.remove(); + } + } + docs.addAll( velocityFiles ); + + for ( String filePath : docs ) + { + filePath = filePath.trim(); + + if ( filePath.lastIndexOf( '.' ) > 0 ) + { + String key = filePath.substring( 0, filePath.lastIndexOf( '.' ) ); + + if ( duplicatesFiles.containsKey( key ) ) + { + throw new DocumentRendererException( "Files '" + module.getSourceDirectory() + + File.separator + filePath + "' clashes with existing '" + + duplicatesFiles.get( key ) + "'." ); + } + + duplicatesFiles.put( key, module.getSourceDirectory() + File.separator + filePath ); + } + + filesToProcess.put( filePath, module ); + } + } + } + + return filesToProcess; + } + + protected static String[] getExtensions( ParserModule module ) + { + String[] extensions = new String[module.getExtensions().length]; + for ( int i = module.getExtensions().length - 1; i >= 0; i-- ) + { + extensions[i] = '.' + module.getExtensions()[i]; + } + return extensions; + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + protected static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + protected static boolean endsWithIgnoreCase( String str, String[] searchStrs ) + { + for ( String searchStr : searchStrs ) + { + if ( endsWithIgnoreCase( str, searchStr ) ) + { + return true; + } + } + return false; + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param files The Collection of source files. + * @return a Map of files to process. + */ + public Map getFilesToProcess( Collection files ) + { + // ---------------------------------------------------------------------- + // Map all the file names to parser ids + // ---------------------------------------------------------------------- + + Map filesToProcess = new HashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + String[] extensions = getExtensions( module ); + + String sourceDirectory = File.separator + module.getSourceDirectory() + File.separator; + + for ( String file : files ) + { + // first check if the file path contains one of the recognized source dir identifiers + // (there's trouble if a pathname contains 2 identifiers), then match file extensions (not unique). + + if ( file.indexOf( sourceDirectory ) != -1 ) + { + filesToProcess.put( file, module ); + } + else + { + // don't overwrite if it's there already + if ( endsWithIgnoreCase( file, extensions ) && !filesToProcess.containsKey( file ) ) + { + filesToProcess.put( file, module ); + } + } + } + } + + return filesToProcess; + } + + /** {@inheritDoc} */ + public DocumentModel readDocumentModel( File documentDescriptor ) + throws DocumentRendererException, IOException + { + DocumentModel documentModel; + + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( documentDescriptor ); + documentModel = new DocumentXpp3Reader().read( reader ); + } + catch ( XmlPullParserException e ) + { + throw new DocumentRendererException( "Error parsing document descriptor", e ); + } + finally + { + IOUtil.close( reader ); + } + + return documentModel; + } + + /** + * Sets the current base directory. + * + * @param newDir the absolute path to the base directory to set. + */ + public void setBaseDir( String newDir ) + { + this.baseDir = newDir; + } + + /** + * Return the current base directory. + * + * @return the current base directory. + */ + public String getBaseDir() + { + return this.baseDir; + } + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + * @deprecated since 1.1.2, use {@link #parse(String, String, Sink, DocumentRendererContext)} + */ + protected void parse( String fullDocPath, String parserId, Sink sink ) + throws DocumentRendererException, IOException + { + parse( fullDocPath, parserId, sink, null ); + } + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + */ + protected void parse( String fullDocPath, String parserId, Sink sink, DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Parsing file " + fullDocPath ); + } + + Reader reader = null; + try + { + File f = new File( fullDocPath ); + + Parser parser = doxia.getParser( parserId ); + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( f ); + + if ( isVelocityFile( f ) ) + { + reader = getVelocityReader( f, ( (XmlStreamReader) reader ).getEncoding(), context ); + } + if ( context != null && Boolean.TRUE.equals( (Boolean) context.get( "validate" ) ) ) + { + reader = validate( reader, fullDocPath ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + if ( isVelocityFile( f ) ) + { + reader = + getVelocityReader( f, ( context == null ? ReaderFactory.FILE_ENCODING + : context.getInputEncoding() ), context ); + } + else + { + if ( context == null ) + { + reader = ReaderFactory.newPlatformReader( f ); + } + else + { + reader = ReaderFactory.newReader( f, context.getInputEncoding() ); + } + } + } + + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, parserId, sink ); + } + catch ( ParserNotFoundException e ) + { + throw new DocumentRendererException( "No parser '" + parserId + + "' found for " + fullDocPath + ": " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + throw new DocumentRendererException( "Error parsing " + fullDocPath + ": " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + + sink.flush(); + } + } + + /** + * Copies the contents of the resource directory to an output folder. + * + * @param outputDirectory the destination folder. + * @throws java.io.IOException if any. + */ + protected void copyResources( File outputDirectory ) + throws IOException + { + File resourcesDirectory = new File( getBaseDir(), "resources" ); + + if ( !resourcesDirectory.isDirectory() ) + { + return; + } + + if ( !outputDirectory.exists() ) + { + outputDirectory.mkdirs(); + } + + copyDirectory( resourcesDirectory, outputDirectory ); + } + + /** + * Copy content of a directory, excluding scm-specific files. + * + * @param source directory that contains the files and sub-directories to be copied. + * @param destination destination folder. + * @throws java.io.IOException if any. + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.isDirectory() && destination.isDirectory() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + /** + * @param documentModel not null + * @return the output name defined in the documentModel without the output extension. If the output name is not + * defined, return target by default. + * @since 1.1.1 + * @see org.apache.maven.doxia.document.DocumentModel#getOutputName() + * @see #getOutputExtension() + */ + protected String getOutputName( DocumentModel documentModel ) + { + String outputName = documentModel.getOutputName(); + if ( outputName == null ) + { + getLogger().info( "No outputName is defined in the document descriptor. Using 'target'" ); + + documentModel.setOutputName( "target" ); + } + + outputName = outputName.trim(); + if ( outputName.toLowerCase( Locale.ENGLISH ).endsWith( "." + getOutputExtension() ) ) + { + outputName = + outputName.substring( 0, outputName.toLowerCase( Locale.ENGLISH ) + .lastIndexOf( "." + getOutputExtension() ) ); + } + documentModel.setOutputName( outputName ); + + return documentModel.getOutputName(); + } + + /** + * TODO: DOXIA-111: we need a general filter here that knows how to alter the context + * + * @param f the file to process, not null + * @param encoding the wanted encoding, not null + * @param context the current render document context not null + * @return a reader with + * @throws DocumentRendererException + */ + private Reader getVelocityReader( File f, String encoding, DocumentRendererContext context ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Velocity render for " + f.getAbsolutePath() ); + } + + SiteResourceLoader.setResource( f.getAbsolutePath() ); + + Context velocityContext = new VelocityContext(); + + if ( context.getKeys() != null ) + { + for ( int i = 0; i < context.getKeys().length; i++ ) + { + String key = (String) context.getKeys()[i]; + + velocityContext.put( key, context.get( key ) ); + } + } + + StringWriter sw = new StringWriter(); + try + { + velocity.getEngine().mergeTemplate( f.getAbsolutePath(), encoding, velocityContext, sw ); + } + catch ( Exception e ) + { + throw new DocumentRendererException( "Error whenn parsing Velocity file " + f.getAbsolutePath() + ": " + + e.getMessage(), e ); + } + + return new StringReader( sw.toString() ); + } + + /** + * @param f not null + * @return true if file has a vm extension, false otherwise. + */ + private static boolean isVelocityFile( File f ) + { + return FileUtils.getExtension( f.getAbsolutePath() ).toLowerCase( Locale.ENGLISH ).endsWith( "vm" ); + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocRenderer.java new file mode 100644 index 000000000..6bc737a85 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocRenderer.java @@ -0,0 +1,65 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; + +/** + * Base renderer interface for the document + * + * @author Vincent Siveton + * @deprecated Since 1.1, use {@link DocumentRenderer} instead. + */ +public interface DocRenderer +{ + /** Plexus lookup. */ + String ROLE = DocRenderer.class.getName(); + + /** + * Render all files from a site directory to an output directory + * + * @param siteDirectory the input directory contains files to be generated + * @param outputDirectory the output directory where files are generated + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + void render( File siteDirectory, File outputDirectory ) + throws DocumentRendererException, IOException; + + /** + * Render a document depending a context and a document descriptor + * + * @param siteDirectory the input directory contains files to be generated + * @param outputDirectory the output directory where file are generated + * @param documentDescriptor the document descriptor + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + void render( File siteDirectory, File outputDirectory, File documentDescriptor ) + throws DocumentRendererException, IOException; + + /** + * Get the output extension supported + * + * @return the ouput extension supported + */ + String getOutputExtension(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRenderer.java new file mode 100644 index 000000000..6f151c52f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRenderer.java @@ -0,0 +1,109 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Collection; + +import org.apache.maven.doxia.document.DocumentModel; + +/** + * Base interface for rendering documents from a set of input files. + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +public interface DocumentRenderer +{ + /** Plexus lookup role. */ + String ROLE = DocumentRenderer.class.getName(); + + /** + * Render a document from a set of files, depending on a rendering context. + * + * @param files the path name Strings (relative to a common base directory) + * of files to include in the document generation. + * @param outputDirectory the output directory where the document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * If the model contains a TOC, only the files found in this TOC are rendered, + * otherwise all files from the Collection of files will be processed. + * If the model is null, render all files individually. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any. + * @throws java.io.IOException if any. + */ + void render( Collection files, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException; + + /** + * Render a document from the files found in a source directory, depending on a rendering context. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * If the model contains a TOC, only the files found in this TOC are rendered, + * otherwise all files found under baseDirectory will be processed. + * If the model is null, render all files from baseDirectory individually. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any +// * @deprecated since 1.1.2, use {@link #render(File, File, DocumentModel, DocumentRendererContext)} + */ + void render( File baseDirectory, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException; + +// /** +// * Render a document from the files found in a source directory, depending on a rendering context. +// * +// * @param baseDirectory the directory containing the source files. +// * This should follow the standard Maven convention, ie containing all the site modules. +// * @param outputDirectory the output directory where the document should be generated. +// * @param documentModel the document model, containing all the metadata, etc. +// * If the model contains a TOC, only the files found in this TOC are rendered, +// * otherwise all files found under baseDirectory will be processed. +// * If the model is null, render all files from baseDirectory individually. +// * @param context the rendering context when processing files. +// * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any +// * @throws java.io.IOException if any +// * @since 1.1.2 +// */ +// void render( File baseDirectory, File outputDirectory, DocumentModel documentModel, +// DocumentRendererContext context ) +// throws DocumentRendererException, IOException; + + /** + * Read a document model from a file. + * + * @param documentDescriptor a document descriptor file that contains the document model. + * @return the document model, containing all the metadata, etc. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + DocumentModel readDocumentModel( File documentDescriptor ) + throws DocumentRendererException, IOException; + + /** + * Get the output extension associated with this DocumentRenderer. + * + * @return the ouput extension. + */ + String getOutputExtension(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererContext.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererContext.java new file mode 100644 index 000000000..4610637c8 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererContext.java @@ -0,0 +1,140 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.plexus.util.ReaderFactory; + +/** + * Context when processing Velocity files using a {@link java.util.HashMap} for data storage. + * + * @author Vincent Siveton + * @since 1.1.2 + */ +public class DocumentRendererContext +{ + private String inputEncoding = ReaderFactory.UTF_8; + + /** + * Storage for key/value pairs. + */ + private final Map context; + + /** + * Default constructor. + */ + public DocumentRendererContext() + { + context = new HashMap(); + } + + /** + * @return The input encoding when processing files. + */ + public String getInputEncoding() + { + return inputEncoding; + } + + /** + * @param inputEncoding new input encoding value when processing files. + */ + public void setInputEncoding( String inputEncoding ) + { + this.inputEncoding = inputEncoding; + } + + /** + * Adds a name/value pair to the context. + * + * @param key The name to key the provided value with. + * @param value The corresponding value. + * @return Object that was replaced in the the Context if applicable or null if not. + */ + public Object put( String key, Object value ) + { + if ( key == null ) + { + return null; + } + + return context.put( key, value ); + } + + /** + * Gets the value corresponding to the provided key from the context. + * + * @param key The name of the desired value. + * @return The value corresponding to the provided key or null if the key param is null. + */ + public Object get( String key ) + { + if ( key == null ) + { + return null; + } + + return context.get( key ); + } + + /** + * Indicates whether the specified key is in the context. + * + * @param key The key to look for. + * @return true if the key is in the context, false if not. + */ + public boolean containsKey( Object key ) + { + if ( !( key instanceof String ) ) // this includes null check + { + return false; + } + + return context.containsKey( key.toString() ); + } + + /** + * Get all the keys for the values in the context + * + * @return Object[] of keys in the Context. + */ + public Object[] getKeys() + { + return context.keySet().toArray(); + } + + /** + * Removes the value associated with the specified key from the context. + * + * @param key The name of the value to remove. + * @return The value that the key was mapped to, or null if unmapped. + */ + public Object remove( Object key ) + { + if ( !( key instanceof String ) ) // this includes null check + { + return null; + } + + return context.remove( key.toString() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererException.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererException.java new file mode 100644 index 000000000..b4a91607e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/DocumentRendererException.java @@ -0,0 +1,54 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +/** + * A document renderer exception + * + * @author Vincent Siveton + * @since 1.1 + */ +public class DocumentRendererException + extends Exception +{ + /** serialVersionUID */ + static final long serialVersionUID = 295967936746221567L; + + /** + * Default constructor. + * + * @param message An error message. + */ + public DocumentRendererException( String message ) + { + super( message ); + } + + /** + * Other constructor. + * + * @param message An error message. + * @param t The cause. + */ + public DocumentRendererException( String message, Throwable t ) + { + super( message, t ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/AbstractITextRender.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/AbstractITextRender.java new file mode 100644 index 000000000..e672b80bb --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/AbstractITextRender.java @@ -0,0 +1,519 @@ +package org.apache.maven.doxia.docrenderer.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.docrenderer.DocRenderer; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader; +import org.apache.maven.doxia.module.itext.ITextSink; +import org.apache.maven.doxia.module.itext.ITextSinkFactory; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.xml.utils.DefaultErrorHandler; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.XmlUtil; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.lowagie.text.ElementTags; + +/** + * Abstract document render with the iText framework + * + * @author Vincent Siveton + * @deprecated since 1.1, use an implementation of {@link org.apache.maven.doxia.docrenderer.DocumentRenderer}. + */ +public abstract class AbstractITextRender + extends AbstractLogEnabled + implements DocRenderer +{ + private static final String XSLT_RESOURCE = "org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt"; + + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + + /** + * @plexus.requirement + */ + protected ParserModuleManager parserModuleManager; + + /** + * @plexus.requirement + */ + protected Doxia doxia; + + static + { + TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() ); + } + + private List getModuleFileNames( ParserModule module, File moduleBasedir ) + throws IOException + { + StringBuilder includes = new StringBuilder(); + + for ( String extension: module.getExtensions() ) + { + if ( includes.length() > 0 ) + { + includes.append( ',' ); + } + includes.append( "**/*." ); + includes.append( extension ); + } + + return FileUtils.getFileNames( moduleBasedir, includes.toString(), null, false ); + } + + /** {@inheritDoc} */ + public void render( File siteDirectory, File outputDirectory ) + throws DocumentRendererException, IOException + { + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + List docs = getModuleFileNames( module, moduleBasedir ); + + for ( String doc : docs ) + { + String fullPathDoc = new File( moduleBasedir, doc ).getPath(); + + String outputITextName = doc.substring( 0, doc.indexOf( '.' ) + 1 ) + "xml"; + File outputITextFile = new File( outputDirectory, outputITextName ); + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + String iTextOutputName = doc.substring( 0, doc.indexOf( '.' ) + 1 ) + getOutputExtension(); + File iTextOutputFile = new File( outputDirectory, iTextOutputName ); + if ( !iTextOutputFile.getParentFile().exists() ) + { + iTextOutputFile.getParentFile().mkdirs(); + } + + parse( fullPathDoc, module, outputITextFile ); + + generateOutput( outputITextFile, iTextOutputFile ); + } + } + } + } + + /** {@inheritDoc} */ + public void render( File siteDirectory, File outputDirectory, File documentDescriptor ) + throws DocumentRendererException, IOException + { + if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No documentDescriptor is found. Generate all documents." ); + } + render( siteDirectory, outputDirectory ); + return; + } + + DocumentModel documentModel; + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( documentDescriptor ); + documentModel = new DocumentXpp3Reader().read( reader ); + } + catch ( XmlPullParserException e ) + { + throw new DocumentRendererException( "Error parsing document descriptor", e ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( "Error reading document descriptor", e ); + } + finally + { + IOUtil.close( reader ); + } + + if ( documentModel.getOutputName() == null ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No outputName is defined in the document descriptor. Using 'generated_itext'" ); + } + documentModel.setOutputName( "generated_itext" ); + } + + if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." ); + } + } + + List iTextFiles = new LinkedList(); + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + @SuppressWarnings ( "unchecked" ) + List docs = getModuleFileNames( module, moduleBasedir ); + + for ( String doc : docs ) + { + String fullPathDoc = new File( moduleBasedir, doc ).getPath(); + + String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFile = new File( outputDirectory, outputITextName ); + + if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) ) + { + iTextFiles.add( outputITextFile ); + + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + parse( fullPathDoc, module, outputITextFile ); + } + else + { + for ( Iterator k = documentModel.getToc().getItems().iterator(); k.hasNext(); ) + { + DocumentTOCItem tocItem = k.next(); + + if ( tocItem.getRef() == null ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No ref defined for an tocItem in the document descriptor." ); + } + continue; + } + + String outTmp = StringUtils.replace( outputITextFile.getAbsolutePath(), "\\", "/" ); + outTmp = outTmp.substring( 0, outTmp.lastIndexOf( '.' ) ); + + String outRef = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( outRef.lastIndexOf( '.' ) != -1 ) + { + outRef = outRef.substring( 0, outRef.lastIndexOf( '.' ) ); + } + else + { + outRef = outRef.substring( 0, outRef.length() ); + } + + if ( outTmp.indexOf( outRef ) != -1 ) + { + iTextFiles.add( outputITextFile ); + + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + parse( fullPathDoc, module, outputITextFile ); + } + } + } + } + } + } + + File iTextFile = new File( outputDirectory, documentModel.getOutputName() + ".xml" ); + File iTextOutput = new File( outputDirectory, documentModel.getOutputName() + "." + getOutputExtension() ); + Document document = generateDocument( iTextFiles ); + transform( documentModel, document, iTextFile ); + generateOutput( iTextFile, iTextOutput ); + } + + /** + * Generate an ouput file with the iText framework + * + * @param iTextFile + * @param iTextOutput + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + public abstract void generateOutput( File iTextFile, File iTextOutput ) + throws DocumentRendererException, IOException; + + /** + * Parse a sink + * + * @param fullPathDoc + * @param module + * @param outputITextFile + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException + * @throws java.io.IOException + */ + private void parse( String fullPathDoc, ParserModule module, File outputITextFile ) + throws DocumentRendererException, IOException + { + Writer writer = WriterFactory.newXmlWriter( outputITextFile ); + ITextSink sink = (ITextSink) new ITextSinkFactory().createSink( writer ); + + sink.setClassLoader( new URLClassLoader( new URL[] { outputITextFile.getParentFile().toURI().toURL() } ) ); + + Reader reader = null; + try + { + File f = new File( fullPathDoc ); + if ( XmlUtil.isXml( f ) ) + { + reader = ReaderFactory.newXmlReader( f ); + } + else + { + // TODO Platform dependent? + reader = ReaderFactory.newPlatformReader( f ); + } + + System.setProperty( "itext.basedir", outputITextFile.getParentFile().getAbsolutePath() ); + + doxia.parse( reader, module.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new DocumentRendererException( "Error getting a parser for '" + + fullPathDoc + "': " + e.getMessage() ); + } + catch ( ParseException e ) + { + throw new DocumentRendererException( "Error parsing '" + + fullPathDoc + "': line [" + e.getLineNumber() + "] " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + + sink.flush(); + + sink.close(); + + IOUtil.close( writer ); + + System.getProperties().remove( "itext.basedir" ); + } + } + + /** + * Merge all iTextFiles to a single one + * + * @param iTextFiles + * @return a document + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + private Document generateDocument( List iTextFiles ) + throws DocumentRendererException, IOException + { + Document document; + try + { + document = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().newDocument(); + } + catch ( ParserConfigurationException e ) + { + throw new DocumentRendererException( "Error building document :" + e.getMessage() ); + } + document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root + + for ( File iTextFile : iTextFiles ) + { + Document iTextDocument; + try + { + iTextDocument = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse( iTextFile ); + } + catch ( SAXException e ) + { + throw new DocumentRendererException( "SAX Error : " + e.getMessage() ); + } + catch ( ParserConfigurationException e ) + { + throw new DocumentRendererException( "Error parsing configuration : " + e.getMessage() ); + } + + // Only one chapter per doc + Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 ); + try + { + document.getDocumentElement().appendChild( document.importNode( chapter, true ) ); + } + catch ( DOMException e ) + { + throw new DocumentRendererException( "Error appending chapter for " + + iTextFile + " : " + e.getMessage() ); + } + } + + return document; + } + + /** + * Init the transformer object + * + * @return an instanced transformer object + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + */ + private Transformer initTransformer() + throws DocumentRendererException + { + try + { + Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( DefaultPdfRenderer.class + .getResourceAsStream( "/" + XSLT_RESOURCE ) ) ); + transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() ); + + transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" ); + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + + return transformer; + } + catch ( TransformerConfigurationException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + catch ( IllegalArgumentException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + } + + /** + * Add transformer parameters + * + * @param transformer + * @param documentModel + */ + private void addTransformerParameters( Transformer transformer, DocumentModel documentModel ) + { + if ( documentModel.getMeta().getTitle() != null ) + { + transformer.setParameter( "title", documentModel.getMeta().getTitle() ); + } + if ( documentModel.getMeta().getAuthor() != null ) + { + transformer.setParameter( "author", documentModel.getMeta().getAuthor() ); + } + transformer.setParameter( "creationdate", new Date().toString() ); + if ( documentModel.getMeta().getSubject() != null ) + { + transformer.setParameter( "subject", documentModel.getMeta().getSubject() ); + } + if ( documentModel.getMeta().getKeywords() != null ) + { + transformer.setParameter( "keywords", documentModel.getMeta().getKeywords() ); + } + transformer.setParameter( "producer", "Generated with Doxia by " + System.getProperty( "user.name" ) ); + if ( ITextUtil.isPageSizeSupported( documentModel.getMeta().getTitle() ) ) + { + transformer.setParameter( "pagesize", documentModel.getMeta().getPageSize() ); + } + else + { + transformer.setParameter( "pagesize", "A4" ); + } + + transformer.setParameter( "frontPageHeader", "" ); + if ( documentModel.getMeta().getTitle() != null ) + { + transformer.setParameter( "frontPageTitle", documentModel.getMeta().getTitle() ); + } + transformer.setParameter( "frontPageFooter", "Generated date " + new Date().toString() ); + } + + /** + * Transform a document to an iTextFile + * + * @param documentModel + * @param document + * @param iTextFile + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any. + */ + private void transform( DocumentModel documentModel, Document document, File iTextFile ) + throws DocumentRendererException + { + Transformer transformer = initTransformer(); + + addTransformerParameters( transformer, documentModel ); + + try + { + transformer.transform( new DOMSource( document ), new StreamResult( iTextFile ) ); + } + catch ( TransformerException e ) + { + throw new DocumentRendererException( "Error transformer Document from " + + document + ": " + e.getMessage() ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultPdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultPdfRenderer.java new file mode 100644 index 000000000..35a1efb43 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultPdfRenderer.java @@ -0,0 +1,65 @@ +package org.apache.maven.doxia.docrenderer.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.codehaus.plexus.component.annotations.Component; + +/** + * PDF render with the iText framework + * + * @author Vincent Siveton + * @deprecated since 1.1, use {@link org.apache.maven.doxia.docrenderer.pdf.itext.ITextPdfRenderer}. + */ +@Component( role = PdfRenderer.class, hint = "itext.pdf" ) +public class DefaultPdfRenderer + extends AbstractITextRender + implements PdfRenderer +{ + /** {@inheritDoc} */ + public String getOutputExtension() + { + return "pdf"; + } + + /** {@inheritDoc} */ + public void generateOutput( File iTextFile, File iTextOutput ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Writing : " + iTextOutput ); + } + try + { + ITextUtil.writePdf( new FileInputStream( iTextFile ), new FileOutputStream( iTextOutput ) ); + } + catch ( RuntimeException e ) + { + throw new DocumentRendererException( "Error writing PDF from " + iTextOutput + ": " + e.getMessage() ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultRtfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultRtfRenderer.java new file mode 100644 index 000000000..5f788b21f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/DefaultRtfRenderer.java @@ -0,0 +1,65 @@ +package org.apache.maven.doxia.docrenderer.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.codehaus.plexus.component.annotations.Component; + +/** + * RTF render with the iText framework + * + * @author Vincent Siveton + * @deprecated since 1.1, use {@link org.apache.maven.doxia.docrenderer.pdf.itext.ITextPdfRenderer}. + */ +@Component( role = RtfRenderer.class, hint = "itext.rtf" ) +public class DefaultRtfRenderer + extends AbstractITextRender + implements RtfRenderer +{ + /** {@inheritDoc} */ + public String getOutputExtension() + { + return "rtf"; + } + + /** {@inheritDoc} */ + public void generateOutput( File iTextFile, File iTextOutput ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Writing : " + iTextOutput ); + } + try + { + ITextUtil.writeRtf( new FileInputStream( iTextFile ), new FileOutputStream( iTextOutput ) ); + } + catch ( RuntimeException e ) + { + throw new DocumentRendererException( "Error writing RTF from " + iTextOutput + ": " + e.getMessage() ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/PdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/PdfRenderer.java new file mode 100644 index 000000000..e3773eeca --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/PdfRenderer.java @@ -0,0 +1,35 @@ +package org.apache.maven.doxia.docrenderer.itext; + +/* + * 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. + */ + +import org.apache.maven.doxia.docrenderer.DocRenderer; + +/** + * PDF renderer interface for the iText framework + * + * @author Vincent Siveton + * @deprecated since 1.1, use an implementation of {@link org.apache.maven.doxia.docrenderer.DocumentRenderer}. + */ +@SuppressWarnings( "checkstyle:interfaceistype" ) +public interface PdfRenderer + extends DocRenderer +{ + String ROLE = PdfRenderer.class.getName(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/RtfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/RtfRenderer.java new file mode 100644 index 000000000..2a28a8fd2 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/itext/RtfRenderer.java @@ -0,0 +1,35 @@ +package org.apache.maven.doxia.docrenderer.itext; + +/* + * 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. + */ + +import org.apache.maven.doxia.docrenderer.DocRenderer; + +/** + * RTF renderer interface for the iText framework + * + * @author Vincent Siveton + * @deprecated since 1.1, use an implementation of {@link org.apache.maven.doxia.docrenderer.DocumentRenderer}. + */ +@SuppressWarnings( "checkstyle:interfaceistype" ) +public interface RtfRenderer + extends DocRenderer +{ + String ROLE = RtfRenderer.class.getName(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/AbstractPdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/AbstractPdfRenderer.java new file mode 100644 index 000000000..ade91b06b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/AbstractPdfRenderer.java @@ -0,0 +1,53 @@ +package org.apache.maven.doxia.docrenderer.pdf; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.parser.module.ParserModule; + +/** + * Abstract pdf renderer, this doesn't depend on the framework. + * + * @author ltheussl + * @since 1.1 + */ +public abstract class AbstractPdfRenderer + extends AbstractDocumentRenderer + implements PdfRenderer +{ + /** {@inheritDoc} */ + public String getOutputExtension() + { + return "pdf"; + } + + /** {@inheritDoc} */ + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/PdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/PdfRenderer.java new file mode 100644 index 000000000..6e961e2b9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/PdfRenderer.java @@ -0,0 +1,48 @@ +package org.apache.maven.doxia.docrenderer.pdf; + +/* + * 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. + */ + +import java.io.File; + +import org.apache.maven.doxia.docrenderer.DocumentRenderer; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; + +/** + * PDF renderer interface. + * + * @author ltheussl + * @since 1.1 + */ +public interface PdfRenderer + extends DocumentRenderer +{ + /** Plexus lookup role. */ + String ROLE = PdfRenderer.class.getName(); + + /** + * Generate a final pdf ouput file from an intermediate format file. + * + * @param inputFile eg a fo or an itext file. + * @param pdfFile the pdf file to generate. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any. + */ + void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException; +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java new file mode 100644 index 000000000..e264f8375 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java @@ -0,0 +1,366 @@ +package org.apache.maven.doxia.docrenderer.pdf.fo; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.xml.transform.TransformerException; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOC; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.fo.FoAggregateSink; +import org.apache.maven.doxia.module.fo.FoSink; +import org.apache.maven.doxia.module.fo.FoSinkFactory; +import org.apache.maven.doxia.module.fo.FoUtils; +import org.apache.maven.doxia.parser.module.ParserModule; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; + +import org.xml.sax.SAXParseException; + +/** + * PDF renderer that uses Doxia's FO module. + * + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "fo" ) +public class FoPdfRenderer + extends AbstractPdfRenderer +{ + /** + * {@inheritDoc} + * @see org.apache.maven.doxia.module.fo.FoUtils#convertFO2PDF(File, File, String) + */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + // Should take care of the document model for the metadata... + generatePdf( inputFile, pdfFile, null ); + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // copy resources, images, etc. + copyResources( outputDirectory ); + + if ( documentModel == null ) + { + getLogger().debug( "No document model, generating all documents individually." ); + + renderIndividual( filesToProcess, outputDirectory, context ); + return; + } + + String outputName = getOutputName( documentModel ); + + File outputFOFile = new File( outputDirectory, outputName + ".fo" ); + if ( !outputFOFile.getParentFile().exists() ) + { + outputFOFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, outputName + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + Writer writer = null; + try + { + writer = WriterFactory.newXmlWriter( outputFOFile ); + + FoAggregateSink sink = new FoAggregateSink( writer ); + + File fOConfigFile = new File( outputDirectory, "pdf-config.xml" ); + + if ( fOConfigFile.exists() ) + { + sink.load( fOConfigFile ); + getLogger().debug( "Loaded pdf config file: " + fOConfigFile.getAbsolutePath() ); + } + + String generateTOC = + ( context != null && context.get( "generateTOC" ) != null ) + ? context.get( "generateTOC" ).toString().trim() + : "start"; + int tocPosition = 0; + if ( "start".equalsIgnoreCase( generateTOC ) ) + { + tocPosition = FoAggregateSink.TOC_START; + } + else if ( "end".equalsIgnoreCase( generateTOC ) ) + { + tocPosition = FoAggregateSink.TOC_END; + } + else + { + tocPosition = FoAggregateSink.TOC_NONE; + } + sink.setDocumentModel( documentModel, tocPosition ); + + sink.beginDocument(); + + sink.coverPage(); + + if ( tocPosition == FoAggregateSink.TOC_START ) + { + sink.toc(); + } + + if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) ) + { + getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." ); + + mergeAllSources( filesToProcess, sink, context ); + } + else + { + getLogger().debug( "Using TOC defined in the document descriptor." ); + + mergeSourcesFromTOC( documentModel.getToc(), sink, context ); + } + + if ( tocPosition == FoAggregateSink.TOC_END ) + { + sink.toc(); + } + + sink.endDocument(); + } + finally + { + IOUtil.close( writer ); + } + + generatePdf( outputFOFile, pdfOutputFile, documentModel ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputFOFile = new File( outputDirectory, output + ".fo" ); + if ( !outputFOFile.getParentFile().exists() ) + { + outputFOFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + FoSink sink = + (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() ); + sink.beginDocument(); + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + sink.endDocument(); + + generatePdf( outputFOFile, pdfOutputFile, null ); + } + } + + private void mergeAllSources( Map filesToProcess, FoAggregateSink sink, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + sink.setDocumentName( key ); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + } + + private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + parseTocItems( toc.getItems(), sink, context ); + } + + private void parseTocItems( List items, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + for ( DocumentTOCItem tocItem : items ) + { + if ( tocItem.getRef() == null ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No ref defined for tocItem " + tocItem.getName() ); + } + + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + renderModules( href, sink, tocItem, context ); + + if ( tocItem.getItems() != null ) + { + parseTocItems( tocItem.getItems(), sink, context ); + } + } + } + + private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + sink.setDocumentName( doc ); + sink.setDocumentTitle( tocItem.getName() ); + + parse( source.getPath(), module.getParserId(), sink, context ); + } + } + } + } + } + + /** + * @param inputFile + * @param pdfFile + * @param documentModel could be null + * @throws DocumentRendererException if any + * @since 1.1.1 + */ + private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating: " + pdfFile ); + } + + try + { + FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel ); + } + catch ( TransformerException e ) + { + if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) ) + { + SAXParseException sax = (SAXParseException) e.getCause(); + + StringBuilder sb = new StringBuilder(); + sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" ) + .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" ); + sb.append( e.getMessage() ); + + throw new DocumentRendererException( sb.toString() ); + } + + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java new file mode 100644 index 000000000..dd477430d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java @@ -0,0 +1,689 @@ +package org.apache.maven.doxia.docrenderer.pdf.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +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.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentCover; +import org.apache.maven.doxia.document.DocumentMeta; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.itext.ITextSink; +import org.apache.maven.doxia.module.itext.ITextSinkFactory; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.xml.utils.DefaultErrorHandler; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.lowagie.text.ElementTags; + +/** + * Abstract document render with the iText framework + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "itext" ) +public class ITextPdfRenderer + extends AbstractPdfRenderer +{ + /** The xslt style sheet used to transform a Document to an iText file. */ + private static final String XSLT_RESOURCE = "TOC.xslt"; + + /** The TransformerFactory. */ + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + /** The DocumentBuilderFactory. */ + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + + /** The DocumentBuilder. */ + private static final DocumentBuilder DOCUMENT_BUILDER; + + static + { + TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() ); + + try + { + DOCUMENT_BUILDER = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + } + catch ( ParserConfigurationException e ) + { + throw new RuntimeException( "Error building document :" + e.getMessage() ); + } + } + + /** {@inheritDoc} */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating : " + pdfFile ); + } + + try + { + ITextUtil.writePdf( new FileInputStream( inputFile ), new FileOutputStream( pdfFile ) ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( "Cannot create PDF from " + inputFile + ": " + e.getMessage(), e ); + } + catch ( RuntimeException e ) + { + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // copy resources, images, etc. + copyResources( outputDirectory ); + + if ( documentModel == null ) + { + getLogger().debug( "No document model, generating all documents individually." ); + + renderIndividual( filesToProcess, outputDirectory, context ); + return; + } + + String outputName = getOutputName( documentModel ); + + File outputITextFile = new File( outputDirectory, outputName + ".xml" ); + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, outputName + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + List iTextFiles; + if ( ( documentModel.getToc() == null ) || ( documentModel.getToc().getItems() == null ) ) + { + getLogger().info( "No TOC is defined in the document descriptor. Merging all documents." ); + + iTextFiles = parseAllFiles( filesToProcess, outputDirectory, context ); + } + else + { + getLogger().debug( "Using TOC defined in the document descriptor." ); + + iTextFiles = parseTOCFiles( outputDirectory, documentModel, context ); + } + + String generateTOC = + ( context != null && context.get( "generateTOC" ) != null ? context.get( "generateTOC" ).toString() + : "start" ); + + File iTextFile = new File( outputDirectory, outputName + ".xml" ); + File iTextOutput = new File( outputDirectory, outputName + "." + getOutputExtension() ); + Document document = generateDocument( iTextFiles ); + transform( documentModel, document, iTextFile, generateTOC ); + generatePdf( iTextFile, iTextOutput ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputITextFile = new File( outputDirectory, output + ".xml" ); + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + parse( fullDoc, module, outputITextFile, context ); + + generatePdf( outputITextFile, pdfOutputFile ); + } + } + + //-------------------------------------------- + // + //-------------------------------------------- + + + /** + * Parse a source document and emit results into a sink. + * + * @param fullDocPath file to the source document. + * @param module the site module associated with the source document (determines the parser to use). + * @param iTextFile the resulting iText xml file. + * @throws DocumentRendererException in case of a parsing problem. + * @throws IOException if the source and/or target document cannot be opened. + */ + private void parse( File fullDoc, ParserModule module, File iTextFile, DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Parsing file " + fullDoc.getAbsolutePath() ); + } + + System.setProperty( "itext.basedir", iTextFile.getParentFile().getAbsolutePath() ); + + Writer writer = null; + ITextSink sink = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + sink = (ITextSink) new ITextSinkFactory().createSink( writer ); + + sink.setClassLoader( new URLClassLoader( new URL[] { iTextFile.getParentFile().toURI().toURL() } ) ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + finally + { + if ( sink != null ) + { + sink.flush(); + sink.close(); + } + IOUtil.close( writer ); + System.getProperties().remove( "itext.basedir" ); + } + } + + /** + * Merge all iTextFiles to a single one. + * + * @param iTextFiles list of iText xml files. + * @return Document. + * @throws DocumentRendererException if any. + * @throws IOException if any. + */ + private Document generateDocument( List iTextFiles ) + throws DocumentRendererException, IOException + { + Document document = DOCUMENT_BUILDER.newDocument(); + document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root + + for ( File iTextFile : iTextFiles ) + { + Document iTextDocument; + + try + { + iTextDocument = DOCUMENT_BUILDER.parse( iTextFile ); + } + catch ( SAXException e ) + { + throw new DocumentRendererException( "SAX Error : " + e.getMessage() ); + } + + // Only one chapter per doc + Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 ); + + try + { + document.getDocumentElement().appendChild( document.importNode( chapter, true ) ); + } + catch ( DOMException e ) + { + throw new DocumentRendererException( "Error appending chapter for " + + iTextFile + " : " + e.getMessage() ); + } + } + + return document; + } + + /** + * Initialize the transformer object. + * + * @return an instance of a transformer object. + * @throws DocumentRendererException if any. + */ + private Transformer initTransformer() + throws DocumentRendererException + { + try + { + Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( ITextPdfRenderer.class + .getResourceAsStream( XSLT_RESOURCE ) ) ); + + transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() ); + + transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" ); + + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + + // No doctype since itext doctype is not up to date! + + return transformer; + } + catch ( TransformerConfigurationException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + catch ( IllegalArgumentException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + } + + /** + * Add transformer parameters from a DocumentModel. + * + * @param transformer the Transformer to set the parameters. + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param iTextFile the iTextFile not null for the relative paths. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + */ + private void addTransformerParameters( Transformer transformer, DocumentModel documentModel, File iTextFile, + String generateTOC ) + { + if ( documentModel == null ) + { + return; + } + + // TOC + addTransformerParameter( transformer, "toc.position", generateTOC ); + + // Meta parameters + boolean hasNullMeta = false; + if ( documentModel.getMeta() == null ) + { + hasNullMeta = true; + documentModel.setMeta( new DocumentMeta() ); + } + addTransformerParameter( transformer, "meta.author", documentModel.getMeta().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + addTransformerParameter( transformer, "meta.creator", documentModel.getMeta().getCreator(), + System.getProperty( "user.name", "null" ) ); + // see com.lowagie.text.Document#addCreationDate() + SimpleDateFormat sdf = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy" ); + addTransformerParameter( transformer, "meta.creationdate", documentModel.getMeta().getCreationdate(), + sdf.format( new Date() ) ); + addTransformerParameter( transformer, "meta.keywords", documentModel.getMeta().getAllKeyWords() ); + addTransformerParameter( transformer, "meta.pagesize", documentModel.getMeta().getPageSize(), + ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) ); + addTransformerParameter( transformer, "meta.producer", documentModel.getMeta().getGenerator(), + "Apache Doxia iText" ); + addTransformerParameter( transformer, "meta.subject", documentModel.getMeta().getSubject(), + ( documentModel.getMeta().getTitle() != null ? documentModel.getMeta().getTitle() + : "" ) ); + addTransformerParameter( transformer, "meta.title", documentModel.getMeta().getTitle() ); + if ( hasNullMeta ) + { + documentModel.setMeta( null ); + } + + // cover parameter + boolean hasNullCover = false; + if ( documentModel.getCover() == null ) + { + hasNullCover = true; + documentModel.setCover( new DocumentCover() ); + } + addTransformerParameter( transformer, "cover.author", documentModel.getCover().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + String companyLogo = getLogoURL( documentModel.getCover().getCompanyLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.companyLogo", companyLogo ); + addTransformerParameter( transformer, "cover.companyName", documentModel.getCover().getCompanyName() ); + if ( documentModel.getCover().getCoverdate() == null ) + { + documentModel.getCover().setCoverDate( new Date() ); + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + documentModel.getCover().setCoverDate( null ); + } + else + { + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + } + addTransformerParameter( transformer, "cover.subtitle", documentModel.getCover().getCoverSubTitle() ); + addTransformerParameter( transformer, "cover.title", documentModel.getCover().getCoverTitle() ); + addTransformerParameter( transformer, "cover.type", documentModel.getCover().getCoverType() ); + addTransformerParameter( transformer, "cover.version", documentModel.getCover().getCoverVersion() ); + String projectLogo = getLogoURL( documentModel.getCover().getProjectLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.projectLogo", projectLogo ); + addTransformerParameter( transformer, "cover.projectName", documentModel.getCover().getProjectName() ); + if ( hasNullCover ) + { + documentModel.setCover( null ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @param defaultValue could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value, String defaultValue ) + { + if ( StringUtils.isEmpty( value ) ) + { + addTransformerParameter( transformer, name, defaultValue ); + } + else + { + addTransformerParameter( transformer, name, value ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value ) + { + if ( StringUtils.isEmpty( value ) ) + { + return; + } + + transformer.setParameter( name, value ); + } + + /** + * Transform a document to an iTextFile. + * + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param document the Document to transform. + * @param iTextFile the resulting iText xml file. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + * @throws DocumentRendererException in case of a transformation error. + */ + private void transform( DocumentModel documentModel, Document document, File iTextFile, String generateTOC ) + throws DocumentRendererException + { + Transformer transformer = initTransformer(); + + addTransformerParameters( transformer, documentModel, iTextFile, generateTOC ); + + // need a writer for StreamResult to prevent FileNotFoundException when iTextFile contains spaces + Writer writer = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + transformer.transform( new DOMSource( document ), new StreamResult( writer ) ); + } + catch ( TransformerException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + finally + { + IOUtil.close( writer ); + } + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseAllFiles( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String outputITextName = key.substring( 0, key.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( fullDoc, module, outputITextFileTmp, context ); + } + + return iTextFiles; + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseTOCFiles( File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Iterator it = documentModel.getToc().getItems().iterator(); it.hasNext(); ) + { + DocumentTOCItem tocItem = it.next(); + + if ( tocItem.getRef() == null ) + { + getLogger().debug( + "No ref defined for the tocItem '" + tocItem.getName() + + "' in the document descriptor. IGNORING" ); + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( source, module, outputITextFileTmp, context ); + } + } + } + } + } + + return iTextFiles; + } + + /** + * @param logo + * @param parentFile + * @return the logo url or null if unable to create it. + * @since 1.1.1 + */ + private String getLogoURL( String logo, File parentFile ) + { + if ( logo == null ) + { + return null; + } + + try + { + return new URL( logo ).toString(); + } + catch ( MalformedURLException e ) + { + try + { + File f = new File( parentFile, logo ); + if ( !f.exists() ) + { + getLogger().warn( "The logo " + f.getAbsolutePath() + " doesnt exist. IGNORING" ); + } + else + { + return f.toURI().toURL().toString(); + } + } + catch ( MalformedURLException e1 ) + { + getLogger().debug( "Failed to convert to URL: " + logo, e1 ); + } + } + + return null; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/javadoc/org/apache/maven/doxia/docrenderer/itext/package.html b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/javadoc/org/apache/maven/doxia/docrenderer/itext/package.html new file mode 100644 index 000000000..7dc901f8f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/javadoc/org/apache/maven/doxia/docrenderer/itext/package.html @@ -0,0 +1,27 @@ + + + + + +

DEPRECATED.

+

Since 1.1, the org.apache.maven.doxia.docrenderer.itext package is all deprecated and is unmaintained. + It will be removed in the future. You could use an implementation of + DocumentRenderer interface.

+ diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/resources/org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/resources/org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt new file mode 100644 index 000000000..85753f764 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/main/resources/org/apache/maven/doxia/docrenderer/pdf/itext/TOC.xslt @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + Table Of Contents + + + + + + + + + + + + + + + + + + + + + Table Of Contents + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + + + + + + + + + + + + + + + + +
diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/site/site.xml new file mode 100644 index 000000000..9ea2c6e04 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/site/site.xml @@ -0,0 +1,41 @@ + + + + + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/java/org/apache/maven/doxia/docrenderer/DocumentRendererTest.java b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/java/org/apache/maven/doxia/docrenderer/DocumentRendererTest.java new file mode 100644 index 000000000..dcb1a5bcc --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/java/org/apache/maven/doxia/docrenderer/DocumentRendererTest.java @@ -0,0 +1,145 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.File; +import java.util.List; + +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentModel; +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; + +/** + * @author Vincent Siveton + * @since 1.1.1 + */ +public class DocumentRendererTest + extends PlexusTestCase +{ + private PdfRenderer docRenderer; + + private File siteDirectoryFile; + + /** @throws java.lang.Exception */ + @Override + protected void setUp() + throws Exception + { + super.setUp(); + + siteDirectoryFile = getTestFile( "src/test/resources/site" ); + } + + /** @throws java.lang.Exception */ + @Override + protected void tearDown() + throws Exception + { + release( docRenderer ); + super.tearDown(); + } + + /** @throws java.lang.Exception */ + public void testFo() + throws Exception + { + renderImpl( "fo" ); + } + + /** @throws java.lang.Exception */ + public void testFoAggregate() + throws Exception + { + renderAggregatedImpl( "fo" ); + } + + /** @throws java.lang.Exception */ + public void testIText() + throws Exception + { + renderImpl( "itext" ); + } + + /** @throws java.lang.Exception */ + public void testITextAggregate() + throws Exception + { + renderAggregatedImpl( "itext" ); + } + + private void renderImpl( String implementation ) + throws Exception + { + File outputDirectory = getTestFile( "target/output/" + implementation ); + if ( outputDirectory.exists() ) + { + FileUtils.deleteDirectory( outputDirectory ); + } + outputDirectory.mkdirs(); + + docRenderer = (PdfRenderer) lookup( PdfRenderer.ROLE, implementation ); + assertNotNull( docRenderer ); + + docRenderer.render( siteDirectoryFile, outputDirectory, null ); + + List files = + FileUtils.getFileNames( new File( siteDirectoryFile, "apt" ), "**/*.apt", + FileUtils.getDefaultExcludesAsString(), false ); + files.addAll( FileUtils.getFileNames( new File( siteDirectoryFile, "fml" ), "**/*.fml", + FileUtils.getDefaultExcludesAsString(), false ) ); + files.addAll( FileUtils.getFileNames( new File( siteDirectoryFile, "xdoc" ), "**/*.xml", + FileUtils.getDefaultExcludesAsString(), false ) ); + + for ( String relativeFile : files ) + { + String relativePdf = StringUtils.replace( relativeFile, FileUtils.getExtension( relativeFile ), "pdf" ); + File pdf = new File( outputDirectory, relativePdf ); + + assertTrue( pdf.exists() ); + assertTrue( pdf.length() > 0 ); + } + } + + private void renderAggregatedImpl( String implementation ) + throws Exception + { + File outputDirectory = getTestFile( "target/output/" + implementation + "-aggregated" ); + if ( outputDirectory.exists() ) + { + FileUtils.deleteDirectory( outputDirectory ); + } + outputDirectory.mkdirs(); + + docRenderer = (PdfRenderer) lookup( PdfRenderer.ROLE, implementation ); + assertNotNull( docRenderer ); + + DocumentModel descriptor = docRenderer.readDocumentModel( new File( siteDirectoryFile, "pdf.xml" ) ); + assertNotNull( descriptor ); + + docRenderer.render( siteDirectoryFile, outputDirectory, descriptor ); + + File pdf = new File( outputDirectory, descriptor.getOutputName() + ".pdf" ); + + assertTrue( pdf.exists() ); + assertTrue( pdf.length() > 0 ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/index.apt b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/index.apt new file mode 100644 index 000000000..5a9b4616e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/index.apt @@ -0,0 +1,66 @@ + ----- + Doxia + ----- + Jason van Zyl + Vincent Siveton + ------ + July 2007 + ------ + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Maven Doxia + + Doxia is a content generation framework which aims to provide its users with powerful + techniques for generating static and dynamic content: Doxia can be used in web-based + publishing context to generate static sites, in addition to being incorporated into + dynamic content generation systems like blogs, wikis and content management systems. + + Doxia supports markup languages with simple syntaxes. Lightweight markup languages + are used by people who might be expected to read the document source as well as the rendered output. + + Doxia is used extensively by Maven and it powers the entire documentation system of Maven. + It gives Maven the ability to take any document that Doxia supports and output it any format. + +* Brief History + + Based on the {{{http://www.xmlmind.com/aptconvert.html}Aptconvert}} project developed by + {{{http://www.xmlmind.com/}Xmlmind}} company, Doxia was initially hosted by Codehaus, to become + a sub-project of Maven early in 2006. + +* Main Features + + * Developed in Java + + * Support of several markup formats: APT (Almost Plain Text), Confluence, DocBook, + FML (FAQ Markup Language), LaTeX, RTF, TWiki, XDoc (popular in Apache land), XHTML + +~~ iText should be replaced by FOP + + * Easy to learn the syntax of the supported markup formats + + * Macro support + + * No need to have a corporate infrastructure (like wiki) to host your documentation + + * Extensible framework + + [] diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/overview.apt b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/overview.apt new file mode 100644 index 000000000..5360991a4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/overview.apt @@ -0,0 +1,104 @@ + ----- + Overview Of The Doxia Framework + ----- + Vincent Siveton + ------ + July 2007 + ------ + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Overview Of The Doxia Framework + + The following figure represents the main components of the Doxia Framework. + +[images/architecture.png] Doxia Framework + + <>: Just like Maven, Doxia uses {{{http://plexus.codehaus.org/}Plexus}} extensively. + +*Sink API + + The interface is a generic markup language interface. It contains several methods that + encapsulate common text syntax. A start tag is denoted by method + and a end of tag by method. + + For instance, you could do things like: + +----- + sink.paragraph(); + sink.text( "my text" ); + sink.paragraph_(); +----- + + similar to this HTML markup: + +----- +

my text

+----- + + To find out more about the Sink API, you could read the Javadoc + {{{http://maven.apache.org/doxia/doxia-sink-api/apidocs/org/apache/maven/doxia/sink/Sink.html}here}}. + +*Doxia Core + + The is the API to parse a source and populate it in a object. The interface + contains only one method: + +----- +void parse( Reader source, Sink sink ) + throws ParseException; +----- + + The class has the responsibility to catch all parsing exceptions. It provides an + helper method, , which helps to find where an error occurred. + + The class is an abstract implementation of the . It provides a macro mechanism + to give dynamic functionalities for the parsing. For more information on macros, read the + {{{./macros/index.html}Doxia Macro Guide}}. + + Finally, the interface is the last part of the puzzle. It provides main definitions of a + given Doxia module and it is used by the site tools. + +*Doxia Modules + + A Doxia module is an implementation of a given markup language like APT or Xdoc. Each module should + implement these interfaces: + + * interface, more specifically the class + + * interface + + [] + + Several modules provide also a implementation to handle a specific markup language. + + For more information on modules, read the {{{./modules/index.html}Doxia Module Guide}}. + +*Doxia Sitetools + + The are a collection of tools to renderer an output. The main tool used by Maven, + specifically the {{{http://maven.apache.org/plugins/maven-site-plugin/}Maven Site Plugin}}, is the + which renders in HTML any documents wrote with supported markup syntax. It used + {{{http://velocity.apache.org/}Velocity templates}} to customize the renderer and the + tool to decorate the renderer. This component describes the layout of the site + defined in the file. + + The tool is used to renderer any document in another document. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/resources.apt b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/resources.apt new file mode 100644 index 000000000..9c606ec4c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/apt/resources.apt @@ -0,0 +1,49 @@ + ----- + External Resources + ----- + Vincent Siveton + ------ + July 2007 + ------ + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +External Resources + +*Articles + +*-----------+--------------+--------------+ +|| Title || Publisher || Author +*-----------+--------------+--------------+ +| {{{http://software.newsforge.com/article.pl?sid=04/04/16/1428219}Quick and dirty typesetting with APT}} | newsforge.com | Scott Nesbitt +*-----------+--------------+--------------+ +| {{{http://en.wikipedia.org/wiki/Lightweight_markup_language}Lightweight markup language}} | wikipedia.org | ? +*-----------+--------------+--------------+ +| {{{http://project.knowledgeforge.net/kforge/trac/wiki/TextProcessing}Simple (Ascii-Based) Text Formats}} | project.knowledgeforge.net | ? +*-----------+--------------+--------------+ + +*Tools + +*-----------+--------------+ +|| Name || Author +*-----------+--------------+ +| {{{http://apteditor.sourceforge.net/}APT Editor (Eclipse plugin)}} | Mathieu Avoine +*-----------+--------------+ diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/fml/faq.fml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/fml/faq.fml new file mode 100644 index 000000000..94ec21d99 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/fml/faq.fml @@ -0,0 +1,67 @@ + + + + + + + + How to handle style in the APT markup language? + +

+ APT doesn't currently support style. It is in the roadmap. +

+
+
+ + How to export in PDF? + +

+ An iText module exists using the iText XML document. + Unfortunately, the iText team discontinued the XML to PDF functionalities. +

+

+ A FOP module is currently in development in the + Doxia sandbox. You can get the source + here. +

+
+
+ + Is it possible to create a book? + +

+ Doxia also has a fairly simple tool for writing books. It comes complete with a Maven plugin + to produce PDFs, LaTeX documents and Xdoc for direct integration in your Maven site. +

+

+ The Doxia Book code is still in the + Doxia sandbox, + but it is fully functional allthough limited. +

+

+ See Writing Books in Doxia for more information. +

+
+
+
+
\ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/pdf.xml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/pdf.xml new file mode 100644 index 000000000..4b9180e9f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/pdf.xml @@ -0,0 +1,57 @@ + + + + + + + Apache Maven Doxia + The Apache Maven Project + + Content generation framework + Lightweight markup language + APT + FML + XDOC + + + + + + + + + + + + + + Apache Maven Doxia Test + v. 1.1.1-SNAPSHOT + User Guide + The Apache Software Foundation + ./images/asf_logo_wide.png + ./images/doxia-logo.png + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/css/site.css b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/css/site.css new file mode 100644 index 000000000..0310f3011 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/css/site.css @@ -0,0 +1,57 @@ +/* + * 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. + */ + +a.externalLink, a.externalLink:link, a.externalLink:visited, a.externalLink:active, a.externalLink:hover { + background: none; + padding-right: 0; +} + +body ul { + list-style-type: square; +} + +#downloadbox { + float: right; + margin-left: 2em; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 1em; + border: 1px solid #999; + background-color: #eee; + width: 17.5em; +} + +#downloadbox h5 { + color: #000; + margin: 0; + border-bottom: 1px solid #aaaaaa; + font-size: smaller; + padding: 0; + margin-top: 1em; +} + +#downloadbox p { + margin-top: 1em; + margin-bottom: 0; +} + +#downloadbox li { + text-indent: inherit; +} + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.odg b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.odg new file mode 100644 index 0000000000000000000000000000000000000000..6a8ae412737ad307f9011fe520b3ae9fc7359913 GIT binary patch literal 12767 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 He}DRaJ(2W3 literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.png b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..f37791d96e6d6e7aee291fb4af851148c5fc7e59 GIT binary patch literal 19581 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 literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/asf_logo_wide.png b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/asf_logo_wide.png new file mode 100644 index 0000000000000000000000000000000000000000..c584ebad9b419ddfa86bff74701dbd5ae8f27fd8 GIT binary patch literal 7090 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 Xztelm;N7P5Zw}z;>g#f450m?U5x2|H literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/doxia-logo.png b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/resources/images/doxia-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c156558a020b25d36bba8a84ea6688c886fa713 GIT binary patch literal 16375 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 + + + + + + Doxia + images/apache-maven-project-2.png + + + images/maven-logo-2.gif + + + + org.apache.maven.skins + maven-stylus-skin + 1.0.1 + + + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/fml-format.xml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/fml-format.xml new file mode 100644 index 000000000..f08979468 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/fml-format.xml @@ -0,0 +1,101 @@ + + + + + The FML (FAQ Markup Language) format + Lukas Theussl + + + + +
+ + + +

+ An 'fml' is an XML document conforming to a small and simple set of tags. + The format was first used in the Maven 1, + version of the FAQ plugin. +

+ +
+ + + +

+ The following is a sample FML document: +

+ + + + + + General + + + + What is Foo? + + +

some markup goes here

+ + some source code + +

some markup goes here

+
+
+ + + + What is Bar? + + +

some markup goes here

+
+
+
+ + + + Installation + + + + How do I install Foo? + + +

some markup goes here

+
+
+ +
+ +
+]]> + +
+ +
+ + + +
diff --git a/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/xdoc-format.xml b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/xdoc-format.xml new file mode 100644 index 000000000..ff935e607 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-doc-renderer/src/test/resources/site/xdoc/references/xdoc-format.xml @@ -0,0 +1,279 @@ + + + + + The Xdoc format + Lukas Theussl + + + + +
+ + + +

+ An 'xdoc' is an XML document conforming to a small and simple set of tags. + Xdoc was the primary documentation format in Maven 1, + Maven 2 largely replaced this by Apt, but xdoc is still supported. +

+ +

+ Historically, the xdoc format can be traced back to the + Anakia format, as once used by the + Apache Jakarta project. +

+ +

+ The Maven 1 Xdoc plugin introduced a few additions to the Anakia format, they are highlighted in the + plugin documentation. +

+ +
+ + + +

+ The following is a sample XDoc document: +

+ + + + Page Title + John Doe + + + + + + + + + + +
+ + +

Hi!

+ + + +

Subsection!

+
+ +
+ +
+ + + +code line 1 +code line 2 + + +
+ + + +]]> + +
+ + + +

+ Doxia will produce <h2> and + <h3> headings for <section> + and <subsection> elements, respectively. + It is therefore perfectly valid to put some sub-headings + (<h4>, <h5>, + <h6>) inside a subsection. For instance, +

+ + A subsubsection]]> + +

+ will produce: +

+ +
+ + + +

+ The core doxia modules do not construct anchors from + section/subsection names. If you want to reference a section, + you have to provide an explicit anchor: +

+ + +
+ + + + + +
]]> + +

+ Note that this differs from previous behavior, where anchors + were constructed from section/subsection names, replacing special + characters by underscores. This behavior presents two shortcomings: +

+ +
    + +
  • + If two sections or subsections have identical names + (within one source document), you will get an ambiguity when + referencing them. Also the resulting html document will not be + valid XHTML. For other output formats (eg pdf), it might even be impossible + to generate the target document. +
  • + +
  • + For long section titles, this leads to rather cumbersome anchor names. +
  • + +
+ +

+ If automatic anchor generation is desired for a particular output format, + it should be implemented / overridden by the corresponding low-level Sink. +

+ +
+ +
+ +
+ +

+ Doxia is currently not able to validate your xdoc files as no schema or DTD + exists yet (however this is planned before the 1.0 release). + It is therefore necessary to check manually whether your source files are valid xdocs, + this should ensure that the generated html files are valid + XHTML1-transitional. +

+ +

+ Here is a list of common mistakes to be aware of: +

+ + + +

Wrong:

+ + + Here's a list: +
    +
  • item 1
  • +
  • item 2
  • +
+ of things to do. +

]]> + +

Correct:

+ + + Here's a list: +

+
    +
  • item 1
  • +
  • item 2
  • +
+

+ of things to do. +

]]> + +

+ Typical block level elements are list elements, + <table>, <source>, + <div>, <p> and + <pre>. +

+ +
+ + + +

Wrong:

+ + + Downloads +
]]> + +

Correct:

+ + +

+ Downloads +

+]]> + +

+ Typical inline elements are + <a>, <strong>, + <code>, <font>, + <br> and <img>. +

+ + + + + +

+ The <title> element has to come before + <author>. +

+ +
+ + + +

Wrong:

+ + + The following command executes the program: + java -jar CoolApp.jar +

]]> + +

Correct:

+ + + The following command executes the program: +

+java -jar CoolApp.jar]]> + +

+ However, you may put <source> elements inside + list items or table rows. +

+ +
+ + + + + +
diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/pom.xml new file mode 100644 index 000000000..dabf9b3c6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/pom.xml @@ -0,0 +1,191 @@ + + + + + + 4.0.0 + + + org.apache.maven.doxia + doxia-sitetools + 1.9.3-SNAPSHOT + ../pom.xml + + + doxia-integration-tools + + Doxia Sitetools :: Integration Tools + A collection of tools to help the integration of Doxia Sitetools in Maven plugins. + + + 2.2.1 + + + + + org.apache.maven.reporting + maven-reporting-api + 3.0 + + + + commons-io + commons-io + + + + + org.apache.maven + maven-artifact + ${mavenVersion} + + + org.apache.maven + maven-artifact-manager + ${mavenVersion} + provided + + + org.apache.maven + maven-model + ${mavenVersion} + + + org.apache.maven + maven-project + ${mavenVersion} + provided + + + org.apache.maven + maven-plugin-api + ${mavenVersion} + + + + + org.apache.maven.doxia + doxia-logging-api + + + + + org.apache.maven.doxia + doxia-decoration-model + + + + + org.codehaus.plexus + plexus-container-default + 1.0-alpha-9 + + + org.codehaus.plexus + plexus-i18n + + + org.codehaus.plexus + plexus-component-api + + + + + org.codehaus.plexus + plexus-utils + + + org.codehaus.plexus + plexus-component-annotations + + + org.codehaus.plexus + plexus-interpolation + 1.25 + + + + + junit + junit + test + + + org.apache.maven.shared + maven-plugin-testing-harness + 1.1 + test + + + + + + + org.codehaus.plexus + plexus-component-metadata + + + create-component-descriptor + + generate-metadata + + + + + + + + + + + org.codehaus.mojo + l10n-maven-plugin + 1.0-alpha-2 + + + ca + cs + da + de + es + fr + gl + hu + it + ja + ko + lt + nl + no + pl + pt + pt_BR + ru + sk + sv + tr + zh_CN + zh_TW + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java new file mode 100644 index 000000000..f7cea889d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java @@ -0,0 +1,1529 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "
" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

+ * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/MojoLogWrapper.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/MojoLogWrapper.java new file mode 100644 index 000000000..ad8b28997 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/MojoLogWrapper.java @@ -0,0 +1,159 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import org.apache.maven.doxia.logging.Log; + +/** + * Wrap a Mojo logger into a Doxia logger. + * + * @author Vincent Siveton + * @since 1.1 + * @see org.apache.maven.plugin.logging.Log + */ +public class MojoLogWrapper + implements Log +{ + private final org.apache.maven.plugin.logging.Log mojoLog; + + /** + * @param log a Mojo log + */ + public MojoLogWrapper( org.apache.maven.plugin.logging.Log log ) + { + this.mojoLog = log; + } + + /** {@inheritDoc} */ + public void setLogLevel( int level ) + { + // nop + } + + /** {@inheritDoc} */ + public void debug( CharSequence content ) + { + mojoLog.debug( toString( content ) ); + } + + /** {@inheritDoc} */ + public void debug( CharSequence content, Throwable error ) + { + mojoLog.debug( toString( content ), error ); + } + + /** {@inheritDoc} */ + public void debug( Throwable error ) + { + mojoLog.debug( "", error ); + } + + /** {@inheritDoc} */ + public void info( CharSequence content ) + { + mojoLog.info( toString( content ) ); + } + + /** {@inheritDoc} */ + public void info( CharSequence content, Throwable error ) + { + mojoLog.info( toString( content ), error ); + } + + /** {@inheritDoc} */ + public void info( Throwable error ) + { + mojoLog.info( "", error ); + } + + /** {@inheritDoc} */ + public void warn( CharSequence content ) + { + mojoLog.warn( toString( content ) ); + } + + /** {@inheritDoc} */ + public void warn( CharSequence content, Throwable error ) + { + mojoLog.warn( toString( content ), error ); + } + + /** {@inheritDoc} */ + public void warn( Throwable error ) + { + mojoLog.warn( "", error ); + } + + /** {@inheritDoc} */ + public void error( CharSequence content ) + { + mojoLog.error( toString( content ) ); + } + + /** {@inheritDoc} */ + public void error( CharSequence content, Throwable error ) + { + mojoLog.error( toString( content ), error ); + } + + /** {@inheritDoc} */ + public void error( Throwable error ) + { + mojoLog.error( "", error ); + } + + /** {@inheritDoc} */ + public boolean isDebugEnabled() + { + return mojoLog.isDebugEnabled(); + } + + /** {@inheritDoc} */ + public boolean isInfoEnabled() + { + return mojoLog.isInfoEnabled(); + } + + /** {@inheritDoc} */ + public boolean isWarnEnabled() + { + return mojoLog.isWarnEnabled(); + } + + /** {@inheritDoc} */ + public boolean isErrorEnabled() + { + return mojoLog.isErrorEnabled(); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + private String toString( CharSequence content ) + { + if ( content == null ) + { + return ""; + } + + return content.toString(); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/ReportComparator.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/ReportComparator.java new file mode 100644 index 000000000..2a3b245f3 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/ReportComparator.java @@ -0,0 +1,61 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.text.Collator; +import java.util.Comparator; +import java.util.Locale; + +import org.apache.maven.reporting.MavenReport; + +/** + * Sorts reports. + * + * @author Brett Porter + * @todo move to reporting API? + * @todo allow reports to define their order in some other way? + */ +public class ReportComparator + implements Comparator +{ + /** the local */ + private final Locale locale; + + /** + * Default constructor. + * + * @param locale not null + */ + public ReportComparator( Locale locale ) + { + if ( locale == null ) + { + throw new IllegalArgumentException( "locale should be defined" ); + } + this.locale = locale; + } + + /** {@inheritDoc} */ + public int compare( MavenReport r1, MavenReport r2 ) + { + Collator collator = Collator.getInstance( locale ); + return collator.compare( r1.getName( locale ), r2.getName( locale ) ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteTool.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteTool.java new file mode 100644 index 000000000..e7ead6525 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteTool.java @@ -0,0 +1,206 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.MavenReport; + +/** + * Tool to play with Doxia objects + * like DecorationModel. + * + * @author Vincent Siveton + */ +public interface SiteTool +{ + /** Plexus Role */ + String ROLE = SiteTool.class.getName(); + + /** + * The locale by default for a Maven Site + * @see Locale#ENGLISH + */ + Locale DEFAULT_LOCALE = Locale.ENGLISH; + + /** + * Get a skin artifact from one of the repositories. + * + * @param localRepository the Maven local repository, not null. + * @param remoteArtifactRepositories the Maven remote repositories, not null. + * @param decoration the Doxia site descriptor model, not null. + * @return the Skin artifact defined in a DecorationModel from a given project and a + * local repository + * @throws SiteToolException if any + */ + Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException; + + /** + * Get the default skin artifact for a project from one of the repositories. + * + * @param localRepository the Maven local repository, not null. + * @param remoteArtifactRepositories the Maven remote repositories, not null. + * @return the default Skin artifact from a given project and a local repository + * @throws SiteToolException if any + * @see org.apache.maven.doxia.site.decoration.Skin#getDefaultSkin() + * @see #getSkinArtifactFromRepository(ArtifactRepository, List, DecorationModel) + */ + Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException; + + /** + * Get a site descriptor from the project's site directory. + * + * @param siteDirectory the site directory, not null + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor file + */ // used by maven-pdf-plugin (should not?) + File getSiteDescriptor( File siteDirectory, Locale locale ); + + /** + * Interpolating several expressions in the site descriptor content. Actually, the expressions can be in + * the project, the environment variables and the specific properties like encoding. + *

+ * For instance: + *

+ *
${project.name}
+ *
The value from the POM of: + *

+ * <project>
+ *   <name>myProjectName</name>
+ * </project> + *

+ *
${my.value}
+ *
The value from the POM of: + *

+ * <properties>
+ *   <my.value>hello</my.value>
+ * </properties> + *

+ *
${JAVA_HOME}
+ *
The value of JAVA_HOME in the environment variables
+ *
+ * + * @param props a map used for interpolation, not null. + * @param aProject a Maven project, not null. + * @param siteDescriptorContent the site descriptor file, not null. + * @return the interpolated site descriptor content. + * @throws SiteToolException if errors happened during the interpolation. + */ // used by maven-pdf-plugin (should not?) + String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException; + + /** + * Get a decoration model for a project. + * + * @param siteDirectory the site directory, may be null if project from repository + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project the Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @return the DecorationModel object corresponding to the site.xml file with some + * interpolations. + * @throws SiteToolException if any + * @since 1.7, was previously with other parameter types and order + */ + DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException; + + /** + * Populate the pre-defined reports menu of the decoration model, + * if used through <menu ref="reports"/>. Notice this menu reference is translated into + * 2 separate menus: "Project Information" and "Project Reports". + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param reportsPerCategory reports per category to put in "Reports" or "Information" menus, not null. + * @see MavenReport#CATEGORY_PROJECT_INFORMATION + * @see MavenReport#CATEGORY_PROJECT_REPORTS + */ + void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> reportsPerCategory ); + + /** + * Extracts from a comma-separated list the locales that are available in site-tool + * resource bundle. Notice that default value will be changed to the default locale of + * the JVM. + * + * @param locales A comma separated list of locales + * @return a list of Locale, which at least contains the Maven default locale which is english + * @since 1.7, was previously getAvailableLocales(String) + */ + List getSiteLocales( String locales ); + + /** + * Calculate the relative path between two URLs or between two files. + * + * For example: + *
+ *
to = "http://maven.apache.org" and from = "http://maven.apache.org"
+ *
return ""
+ *
to = "http://maven.apache.org" and from = "http://maven.apache.org/plugins/maven-site-plugin/"
+ *
return "../.."
+ *
to = "http://maven.apache.org/plugins/maven-site-plugin/" and from = "http://maven.apache.org"
+ *
return "plugins/maven-site-plugin"
+ *
to = "/myproject/myproject-module1" and from = "/myproject/myproject"
+ *
return "../myproject-module1"
+ *
+ * Note: The file separator depends on the system. + * Maven-specific urls are supported, like dav:https://dav.codehaus.org/ or + * scm:svn:https://svn.apache.org/repos/asf. + * + * @param to the to url of file as string + * @param from the from url of file as string + * @return a relative path from from to to. + */ + String getRelativePath( String to, String from ); + + /** + * Returns the parent POM with interpolated URLs. + * If called from Maven 3, just returns project.getParent(), which is already + * interpolated. But when called from Maven 2, attempts to source this value from the + * reactorProjects parameters if available (reactor env model attributes + * are interpolated), or if the reactor is unavailable (-N) resorts to the + * project.getParent().getUrl() value which will NOT have been interpolated. + * + * @param aProject a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @return the parent project with interpolated URLs. + */ + MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteToolException.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteToolException.java new file mode 100644 index 000000000..1b2fd106d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/SiteToolException.java @@ -0,0 +1,66 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +/** + * An exception occurring during the execution of this tool. + * + * @author Vincent Siveton + */ +public class SiteToolException + extends Exception +{ + /** serialVersionUID */ + static final long serialVersionUID = 2331441332996055959L; + + /** + * Construct a new SiteToolException exception wrapping an underlying Exception + * and providing a message. + * + * @param message could be null + * @param cause could be null + */ + public SiteToolException( String message, Exception cause ) + { + super( message, cause ); + } + + /** + * Construct a new SiteToolException exception wrapping an underlying Throwable + * and providing a message. + * + * @param message could be null + * @param cause could be null + */ + public SiteToolException( String message, Throwable cause ) + { + super( message, cause ); + } + + /** + * Construct a new SiteToolException exception providing a message. + * + * @param message could be null + */ + public SiteToolException( String message ) + { + super( message ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/default-site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/default-site.xml new file mode 100644 index 000000000..5ba93bf99 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/default-site.xml @@ -0,0 +1,33 @@ + + + + + + ${project.name} + + + + + +
+ + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool.properties new file mode 100644 index 000000000..a3062d882 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Parent Project +decorationModel.menu.projectdocumentation = Project Documentation +decorationModel.menu.projectinformation = Project Information +decorationModel.menu.projectmodules = Modules +decorationModel.menu.projectreports = Project Reports \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ca.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ca.properties new file mode 100644 index 000000000..8e516e2a9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ca.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Projecte pare +decorationModel.menu.projectdocumentation = Documentaci\u00f3 del projecte +decorationModel.menu.projectinformation = Informaci\u00f3 del projecte +decorationModel.menu.projectmodules = M\u00f2duls +decorationModel.menu.projectreports = Informes del projecte diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_cs.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_cs.properties new file mode 100644 index 000000000..70e234109 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_cs.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Nad\u0159azen\u00fd projekt +decorationModel.menu.projectdocumentation = Dokumentace projektu +decorationModel.menu.projectinformation = Informace o projektu +decorationModel.menu.projectmodules = Moduly +decorationModel.menu.projectreports = Souhrny projektu diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_da.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_da.properties new file mode 100644 index 000000000..0f8d339b4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_da.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Overordnet Projekt +decorationModel.menu.projectdocumentation = Projekt Dokumentation +decorationModel.menu.projectinformation = Projekt Information +decorationModel.menu.projectmodules = Moduler +decorationModel.menu.projectreports = Projekt Rapporter \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_de.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_de.properties new file mode 100644 index 000000000..930249877 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_de.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \u00fcbergeordnetes Projekt +decorationModel.menu.projectdocumentation = Projektdokumentation +decorationModel.menu.projectinformation = Projektinformationen +decorationModel.menu.projectmodules = Module +decorationModel.menu.projectreports = Projektberichte diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_en.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_en.properties new file mode 100644 index 000000000..0c479bc64 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_en.properties @@ -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. + +# NOTE: +# This bundle is intentionally empty because English strings are provided by the base bundle via the parent chain. It +# must be provided nevertheless such that a request for locale "en" will not errorneously pick up the bundle for the +# JVM's default locale (which need not be "en"). See the method javadoc about +# ResourceBundle.getBundle(String, Locale, ClassLoader) +# for a full description of the lookup strategy. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_es.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_es.properties new file mode 100644 index 000000000..563f6ae29 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_es.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Proyecto padre +decorationModel.menu.projectdocumentation = Documentaci\u00f3n del proyecto +decorationModel.menu.projectinformation = Informaci\u00f3n del proyecto +decorationModel.menu.projectmodules = M\u00f3dulos +decorationModel.menu.projectreports = Informes del proyecto diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_fr.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_fr.properties new file mode 100644 index 000000000..86c374fee --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_fr.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Projet parent +decorationModel.menu.projectdocumentation = Documentation sur le projet +decorationModel.menu.projectinformation = Info Projet +decorationModel.menu.projectmodules = Modules +decorationModel.menu.projectreports = Rapports Projet diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_gl.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_gl.properties new file mode 100644 index 000000000..7e40badb1 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_gl.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Proxecto pai +decorationModel.menu.projectdocumentation = Documentaci\u00f3n do proxecto +decorationModel.menu.projectinformation = Informaci\u00f3n do proxecto +decorationModel.menu.projectmodules = M\u00f3dulos +decorationModel.menu.projectreports = Informes do proxecto diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_hu.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_hu.properties new file mode 100644 index 000000000..732017552 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_hu.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Sz\u00fcl\u0151 projekt +decorationModel.menu.projectdocumentation = Projekt dokument\u00e1ci\u00f3 +decorationModel.menu.projectinformation = Projekt inform\u00e1ci\u00f3 +decorationModel.menu.projectmodules = Modulok +decorationModel.menu.projectreports = Projekt riportok diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_it.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_it.properties new file mode 100644 index 000000000..23e45346e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_it.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Progetto Padre +decorationModel.menu.projectdocumentation = Documentazione del Progetto +decorationModel.menu.projectinformation = Informazioni sul Progetto +decorationModel.menu.projectmodules = Moduli +decorationModel.menu.projectreports = Rapporti del Progetto diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ja.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ja.properties new file mode 100644 index 000000000..89607d098 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ja.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \u89aa\u30d7\u30ed\u30b8\u30a7\u30af\u30c8 +decorationModel.menu.projectdocumentation = \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u6587\u66f8 +decorationModel.menu.projectinformation = \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u60c5\u5831 +decorationModel.menu.projectmodules = \u30e2\u30b8\u30e5\u30fc\u30eb +decorationModel.menu.projectreports = \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30ec\u30dd\u30fc\u30c8 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ko.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ko.properties new file mode 100644 index 000000000..472686a69 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ko.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \ubd80\ubaa8 \ud504\ub85c\uc81d\ud2b8 +decorationModel.menu.projectdocumentation = \ud504\ub85c\uc81d\ud2b8 \ubb38\uc11c\ud654 +decorationModel.menu.projectinformation = \ud504\ub85c\uc81d\ud2b8 \uc815\ubcf4 +decorationModel.menu.projectmodules = \ubaa8\ub4c8 +decorationModel.menu.projectreports = \ud504\ub85c\uc81d\ud2b8 \ubcf4\uace0\uc11c diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_lt.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_lt.properties new file mode 100644 index 000000000..9402a3763 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_lt.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = T\u0117vinis projektas +decorationModel.menu.projectdocumentation = Projekt\u0173 dokumentacija +decorationModel.menu.projectinformation = Projekto informacija +decorationModel.menu.projectmodules = Moduliai +decorationModel.menu.projectreports = Projekto ataskaitos diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_nl.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_nl.properties new file mode 100644 index 000000000..503545a1a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_nl.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Bevattend project +decorationModel.menu.projectdocumentation = Project documentatie +decorationModel.menu.projectinformation = Project informatie +decorationModel.menu.projectmodules = Modulen +decorationModel.menu.projectreports = Project rapporten diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_no.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_no.properties new file mode 100644 index 000000000..7504f3fde --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_no.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Hovedprosjekt +decorationModel.menu.projectdocumentation = Prosjekt dokumentasjon +decorationModel.menu.projectinformation = Prosjekt informasjon +decorationModel.menu.projectmodules = Moduler +decorationModel.menu.projectreports = Prosjekt rapporter diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pl.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pl.properties new file mode 100644 index 000000000..1d0c490cd --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pl.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Projekt nadrz\u0119dny +decorationModel.menu.projectdocumentation = Dokumentacja projektu +decorationModel.menu.projectinformation = Informacja o projekcie +decorationModel.menu.projectmodules = Modu\u0142y +decorationModel.menu.projectreports = Raporty projektu diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt.properties new file mode 100644 index 000000000..29b2dea82 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Projecto Pai +decorationModel.menu.projectdocumentation = Documenta\u00e7\u00e3o do Projeto +decorationModel.menu.projectinformation = Informa\u00e7\u00e3o do Projeto +decorationModel.menu.projectmodules = M\u00f3dulos +decorationModel.menu.projectreports = Relat\u00f3rios do Projeto diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt_BR.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt_BR.properties new file mode 100644 index 000000000..74785c6f8 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_pt_BR.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Projeto Pai +decorationModel.menu.projectdocumentation = Documenta\u00e7\u00e3o do Projeto +decorationModel.menu.projectinformation = Informa\u00e7\u00f5es sobre o Projeto +decorationModel.menu.projectmodules = M\u00f3dulo +decorationModel.menu.projectreports = Relat\u00f3rios do Projeto diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ru.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ru.properties new file mode 100644 index 000000000..60506ebc6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_ru.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u041f\u0440\u043e\u0435\u043a\u0442 +decorationModel.menu.projectdocumentation = \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u041f\u0440\u043e\u0435\u043a\u0442\u0430 +decorationModel.menu.projectinformation = \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u041f\u0440\u043e\u0435\u043a\u0442\u0435 +decorationModel.menu.projectmodules = \u041c\u043e\u0434\u0443\u043b\u0438 +decorationModel.menu.projectreports = \u041e\u0442\u0447\u0435\u0442\u044b \u041f\u0440\u043e\u0435\u043a\u0442\u0430 \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sk.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sk.properties new file mode 100644 index 000000000..e21927d50 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sk.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Nadraden\u00fd projekt +decorationModel.menu.projectdocumentation = Projektov\u00e1 dokument\u00e1cia +decorationModel.menu.projectinformation = Inform\u00e1cie o projekte +decorationModel.menu.projectmodules = Moduly +decorationModel.menu.projectreports = Projektov\u00e9 zostavy diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sv.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sv.properties new file mode 100644 index 000000000..4647faa5c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_sv.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = F\u00f6r\u00e4ldraprojekt +decorationModel.menu.projectdocumentation = Projektdokumentation +decorationModel.menu.projectinformation = Projektinformation +decorationModel.menu.projectmodules = Moduler +decorationModel.menu.projectreports = Projektrapporter \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_tr.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_tr.properties new file mode 100644 index 000000000..2b5a3f2d4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_tr.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = Ana Proje +decorationModel.menu.projectdocumentation = Proje Dok\u00fcmantasyonu +decorationModel.menu.projectinformation = Proje Bilgileri +decorationModel.menu.projectmodules = Mod\u00fcller +decorationModel.menu.projectreports = Proje Raporlar\u00fd diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_CN.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_CN.properties new file mode 100644 index 000000000..42794f85c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_CN.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \u7236\u9879\u76ee +decorationModel.menu.projectdocumentation = \u9879\u76ee\u6587\u6863 +decorationModel.menu.projectinformation = \u9879\u76ee\u4fe1\u606f +decorationModel.menu.projectmodules = \u6a21\u5757 +decorationModel.menu.projectreports = \u9879\u76ee\u62a5\u8868 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_TW.properties b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_TW.properties new file mode 100644 index 000000000..f6ebd1860 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/main/resources/site-tool_zh_TW.properties @@ -0,0 +1,22 @@ +# 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. + +decorationModel.menu.parentproject = \u7236\u5c08\u6848 (Parent Project) +decorationModel.menu.projectdocumentation = \u5c08\u6848\u6587\u4ef6 (Project Documentation) +decorationModel.menu.projectinformation = \u5c08\u6848\u8cc7\u8a0a (Project Information) +decorationModel.menu.projectmodules = \u6a21\u7d44 (Modules) +decorationModel.menu.projectreports = \u5c08\u6848\u5831\u8868 (Project Reports) diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/index.apt b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/index.apt new file mode 100644 index 000000000..ff336c254 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/index.apt @@ -0,0 +1,73 @@ + ------ + Introduction + ------ + Dennis Lundberg + Hervé Boutemy + ------ + 2016-03-27 + ------ + + ~~ 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. + + ~~ NOTE: For help with the syntax of this file, see: + ~~ http://maven.apache.org/doxia/references/apt-format.html + + +Maven Doxia Integration Tools + + This shared component has some utilities that are useful when integrating Doxia in Maven, + mainly for site generation and report creation. + + The main entry point is the + {{{./apidocs/org/apache/maven/doxia/tools/SiteTool.html}SiteTool}} Plexus component. + +* Usage + + Instructions on how to use the integration of Doxia in Maven can be found {{{./usage.html}here}}. + +* <<>> Decoration Model Interpolation + + Interpolation of {{{../doxia-decoration-model/decoration.html}<<>> decoration model}} injects + Maven project's information, replacing <<<$\{...\}>>> with calculated values + like it happens in {{{/ref/current/maven-model-builder/#Model_Interpolation}Maven model interpolation}}. + + Interpolation can be <> or <>: + + * with <> interpolation, replacement happens <> inheritance. This is the classical behaviour in Maven pom, + + * with <> interpolation, replacement happens <> inheritance: this was the default behaviour for <<>> + values until Doxia Sitetools 1.7, when these early and late interpolation definitions didn't exist. + Since Doxia Sitetools 1.7.1, early interpolation happens for <<>> values. + + [] + + Values are evaluated in sequence from different syntaxes: + +*-------------+--------------+--------------------+------------------+ +|| late value || early value || evaluation result || common examples || +*-------------+--------------+--------------------+------------------+ +| <<>>\ +<<<*>>> () | <<>> | POM content (see {{{/ref/current/maven-model/maven.html}POM reference}}) | <<<$\{project.version\}>>>\ + | | | <<<$\{this.url\}>>> +*-------------+--------------+--------------------+------------------+ +| <<<*>>> | <<>> | model properties, such as project properties set in the pom | <<<$\{any.key\}>>>\ + | | | <<<$\{this.any.key\}>>> +*-------------+--------------+--------------------+------------------+ +| <<>>\ +<<<*>>> | | environment variables | <<<$\{env.PATH\}>>>\ +*-------------+--------------+--------------------+------------------+ diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/usage.apt b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/usage.apt new file mode 100644 index 000000000..748b3a472 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/apt/usage.apt @@ -0,0 +1,71 @@ + ------ + Usage + ------ + Dennis Lundberg + Vincent Siveton + ------ + 2008-04-27 + ------ + + ~~ 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. + + ~~ NOTE: For help with the syntax of this file, see: + ~~ http://maven.apache.org/doxia/references/apt-format.html + +Using the SiteTool in a Mojo + ++----- +... +import org.apache.maven.doxia.tools.SiteTool; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +... + +/** + * Your own mojo. + */ +@Mojo( name = "your-own" ) +public class YourOwnMojo extends AbstractMojo +{ + ... + + /** + * SiteTool. + */ + @Component + protected SiteTool siteTool; + + ... + + public someMethod() + { + List localesList = siteTool.getSiteLocales( locales ); + String relativePath = siteTool.getRelativePath( "C:/foo/child", + "C:/foo/master" ); + ... + } + + ... +} ++----- + +* References + + [[1]] {{{./apidocs/org/apache/maven/doxia/tools/SiteTool.html}SiteTool API}} + + [] \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/resources/download.cgi b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/resources/download.cgi new file mode 100644 index 000000000..1b178d2e6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/resources/download.cgi @@ -0,0 +1,22 @@ +#!/bin/sh +# +# 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. +# +# Just call the standard mirrors.cgi script. It will use download.html +# as the input template. +exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $* \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/site.xml new file mode 100644 index 000000000..e2e57b3bb --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/site.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/xdoc/download.xml.vm b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/xdoc/download.xml.vm new file mode 100644 index 000000000..252fdae39 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/site/xdoc/download.xml.vm @@ -0,0 +1,126 @@ + + + + + + + Download ${project.name} Source + + +
+ +

${project.name} ${project.version} is distributed in source format. Use a source archive if you intend to build + ${project.name} yourself. Otherwise, simply use the ready-made binary artifacts from central repository.

+ +

You will be prompted for a mirror - if the file is not found on yours, please be patient, as it may take 24 + hours to reach all mirrors.

+ +

In order to guard against corrupted downloads/installations, it is highly recommended to + verify the signature + of the release bundles against the public KEYS used by the Apache Maven + developers.

+ +

${project.name} is distributed under the Apache License, version 2.0.

+ +

We strongly encourage our users to configure a Maven repository mirror closer to their location, please read How to Use Mirrors for Repositories.

+ + + + +

+ [if-any logo] + + logo + + [end] + The currently selected mirror is + [preferred]. + If you encounter a problem with this mirror, + please select another mirror. + If all mirrors are failing, there are + backup + mirrors + (at the end of the mirrors list) that should be available. +

+ +
+ Other mirrors: + + +
+ +

+ You may also consult the + complete list of + mirrors. +

+ + + + + +

This is the current stable version of ${project.name}.

+ + + + + + + + + + + + + + + + + + +
LinkChecksumSignature
${project.name} ${project.version} (Source zip)maven/doxia/${project.artifactId}-${project.version}-source-release.zipmaven/doxia/${project.artifactId}-${project.version}-source-release.zip.sha512maven/doxia/${project.artifactId}-${project.version}-source-release.zip.asc
+
+ + + +

Older non-recommended releases can be found on our archive site.

+ +
+
+ +
+ diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/DefaultSiteToolTest.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/DefaultSiteToolTest.java new file mode 100644 index 000000000..1698e2d45 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/DefaultSiteToolTest.java @@ -0,0 +1,116 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.junit.Before; + +/** + * @author Vincent Siveton + */ +public class DefaultSiteToolTest +{ + + private DefaultSiteTool tool = new DefaultSiteTool(); + + @Before + public void setUp() { + Logger logger = new ConsoleLogger(Logger.LEVEL_WARN, "tool"); + tool.enableLogging(logger); + } + + /** + * test getNormalizedPath(). + */ + @Test + public void testGetNormalizedPath() + { + assertNull( DefaultSiteTool.getNormalizedPath( null ) ); + assertEquals( "", DefaultSiteTool.getNormalizedPath( "" ) ); + assertEquals( "", DefaultSiteTool.getNormalizedPath( "." ) ); + assertEquals( "", DefaultSiteTool.getNormalizedPath( "./" ) ); + assertEquals( "foo", DefaultSiteTool.getNormalizedPath( "foo" ) ); + assertEquals( "foo/bar", DefaultSiteTool.getNormalizedPath( "foo/bar" ) ); + assertEquals( "foo/bar", DefaultSiteTool.getNormalizedPath( "foo\\bar" ) ); + assertEquals( "foo/bar", DefaultSiteTool.getNormalizedPath( "foo/./bar" ) ); + assertEquals( "foo/bar", DefaultSiteTool.getNormalizedPath( "foo//bar" ) ); + assertEquals( "", DefaultSiteTool.getNormalizedPath( "foo/../" ) ); + assertEquals( "", DefaultSiteTool.getNormalizedPath( "foo/.." ) ); + assertEquals( "bar", DefaultSiteTool.getNormalizedPath( "foo/../bar" ) ); + assertEquals( "foo", DefaultSiteTool.getNormalizedPath( "./foo" ) ); + assertEquals( "../foo", DefaultSiteTool.getNormalizedPath( "../foo" ) ); + assertEquals( "../../foo", DefaultSiteTool.getNormalizedPath( "../../foo" ) ); + assertEquals( "index.html", DefaultSiteTool.getNormalizedPath( "./foo/../index.html" ) ); + + // note: space is preserved and double slash is removed! + assertEquals( "file:/Documents and Settings/", + DefaultSiteTool.getNormalizedPath( "file://Documents and Settings/" ) ); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetRelativePath() + { + assertEquals( + ".." + File.separator + "bar.html", + tool.getRelativePath("http://example.com/foo/bar.html", "http://example.com/foo/baz.html")); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetRelativePath_same() + { + assertTrue( + tool.getRelativePath( "http://example.com/foo/bar.html", "http://example.com/foo/bar.html" ).isEmpty() ); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetRelativePath_differentSchemes() { + assertEquals( + "scp://example.com/foo/bar.html", + tool.getRelativePath( "scp://example.com/foo/bar.html", "http://example.com/foo/bar.html" ) ); + assertEquals( + "file:///tmp/bloop", + tool.getRelativePath( "file:///tmp/bloop", "scp://localhost:/tmp/blop" ) ); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetRelativePath_differentDomains() { + assertEquals( + "https://example.org/bar.html", + tool.getRelativePath( "https://example.org/bar.html", "https://example.com/bar.html" ) ); + assertEquals( + "dav:https://nexus2.mysite.net:123/nexus/content/sites/site/mysite-child/2.0.0/", + tool.getRelativePath( + "dav:https://nexus2.mysite.net:123/nexus/content/sites/site/mysite-child/2.0.0/", + "dav:https://nexus1.mysite.net:123/nexus/content/sites/site/mysite-parent/1.0.0/" )); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/SiteToolTest.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/SiteToolTest.java new file mode 100644 index 000000000..6e12e4c35 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/SiteToolTest.java @@ -0,0 +1,409 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; +import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; +import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.doxia.tools.stubs.SiteToolMavenProjectStub; +import org.apache.maven.project.MavenProject; + +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.WriterFactory; + +/** + * @author Vincent Siveton + */ +public class SiteToolTest + extends PlexusTestCase +{ + /** + * @return the repo. + * + * @throws Exception + */ + protected ArtifactRepository getLocalRepo() + throws Exception + { + String updatePolicyFlag = ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS; + String checksumPolicyFlag = ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN; + ArtifactRepositoryPolicy snapshotsPolicy = new ArtifactRepositoryPolicy( true, updatePolicyFlag, + checksumPolicyFlag ); + ArtifactRepositoryPolicy releasesPolicy = new ArtifactRepositoryPolicy( true, updatePolicyFlag, + checksumPolicyFlag ); + ArtifactRepositoryFactory artifactRepositoryFactory = (ArtifactRepositoryFactory) lookup( ArtifactRepositoryFactory.ROLE ); + ArtifactRepositoryLayout defaultArtifactRepositoryLayout = (ArtifactRepositoryLayout) lookup( + ArtifactRepositoryLayout.ROLE, + "default" ); + return artifactRepositoryFactory.createArtifactRepository( "local", getTestFile( "target/local-repo" ).toURI().toURL() + .toString(), defaultArtifactRepositoryLayout, snapshotsPolicy, releasesPolicy ); + } + + /** + * @return the local repo directory. + * + * @throws Exception + */ + protected File getLocalRepoDir() + throws Exception + { + return new File( getLocalRepo().getBasedir() ); + } + + /** + * @throws Exception + */ + public void testGetDefaultSkinArtifact() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "site-tool-test" ); + assertNotNull( tool.getDefaultSkinArtifact( getLocalRepo(), project.getRemoteArtifactRepositories() ) ); + } + + /** + * @throws Exception + */ + public void testGetSkinArtifactFromRepository() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "site-tool-test" ); + DecorationModel decorationModel = new DecorationModel(); + Skin skin = new Skin(); + skin.setGroupId( "org.apache.maven.skins" ); + skin.setArtifactId( "maven-stylus-skin" ); + decorationModel.setSkin( skin ); + assertNotNull( tool.getSkinArtifactFromRepository( getLocalRepo(), project.getRemoteArtifactRepositories(), + decorationModel ) ); + } + + private void checkGetRelativePathDirectory( SiteTool tool, String relative, String to, String from ) + { + assertEquals( relative, tool.getRelativePath( to, from ) ); + assertEquals( relative, tool.getRelativePath( to + '/', from ) ); + assertEquals( relative, tool.getRelativePath( to, from + '/' ) ); + assertEquals( relative, tool.getRelativePath( to + '/', from + '/' ) ); + } + + /** + * @throws Exception + */ + public void testGetRelativePath() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + checkGetRelativePathDirectory( tool, "", "http://maven.apache.org", "http://maven.apache.org" ); + + checkGetRelativePathDirectory( tool, ".." + File.separator + "..", "http://maven.apache.org", + "http://maven.apache.org/plugins/maven-site-plugin" ); + + checkGetRelativePathDirectory( tool, "plugins" + File.separator + "maven-site-plugin", + "http://maven.apache.org/plugins/maven-site-plugin", "http://maven.apache.org" ); + + checkGetRelativePathDirectory( tool, "", "dav:https://maven.apache.org", "dav:https://maven.apache.org" ); + + checkGetRelativePathDirectory( tool, "plugins" + File.separator + "maven-site-plugin", + "dav:http://maven.apache.org/plugins/maven-site-plugin", + "dav:http://maven.apache.org" ); + + checkGetRelativePathDirectory( tool, "", "scm:svn:https://maven.apache.org", "scm:svn:https://maven.apache.org" ); + + checkGetRelativePathDirectory( tool, "plugins" + File.separator + "maven-site-plugin", + "scm:svn:https://maven.apache.org/plugins/maven-site-plugin", + "scm:svn:https://maven.apache.org" ); + + String to = "http://maven.apache.org/downloads.html"; + String from = "http://maven.apache.org/index.html"; + + // MSITE-600, MSHARED-203 + to = "file:///tmp/bloop"; + from = "scp://localhost:/tmp/blop"; + assertEquals( tool.getRelativePath( to, from ), to ); + + // note: 'tmp' is the host here which is probably not the intention, but at least the result is correct + to = "file://tmp/bloop"; + from = "scp://localhost:/tmp/blop"; + assertEquals( to, tool.getRelativePath( to, from ) ); + + // Tests between files as described in MIDEA-102 + to = "C:/dev/voca/gateway/parser/gateway-parser.iml"; + from = "C:/dev/voca/gateway/"; + assertEquals( "Child file using Windows drive letter", + "parser" + File.separator + "gateway-parser.iml", tool.getRelativePath( to, from ) ); + to = "C:/foo/child"; + from = "C:/foo/master"; + assertEquals( "Sibling directory using Windows drive letter", + ".." + File.separator + "child", tool.getRelativePath( to, from ) ); + to = "/myproject/myproject-module1"; + from = "/myproject/myproject"; + assertEquals( "Sibling directory with similar name", + ".." + File.separator + "myproject-module1", tool.getRelativePath( to, from ) ); + + // Normalized paths as described in MSITE-284 + assertEquals( ".." + File.separator + "project-module-1" + File.separator + "src" + File.separator + "site", + tool.getRelativePath( "Z:\\dir\\project\\project-module-1\\src\\site", + "Z:\\dir\\project\\project-module-1\\..\\project-parent" ) ); + assertEquals( ".." + File.separator + ".." + File.separator + ".." + File.separator + "project-parent", + tool.getRelativePath( "Z:\\dir\\project\\project-module-1\\..\\project-parent", + "Z:\\dir\\project\\project-module-1\\src\\site" ) ); + + assertEquals( ".." + File.separator + "foo", tool.getRelativePath( "../../foo/foo", "../../foo/bar" ) ); + } + + /** + * @throws Exception + */ + public void testGetSiteDescriptorFromBasedir() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "site-tool-test" ); + assertEquals( tool.getSiteDescriptor( new File( project.getBasedir(), "src/site" ), null ).toString(), + project.getBasedir() + File.separator + "src" + File.separator + "site" + File.separator + "site.xml" ); + assertEquals( tool.getSiteDescriptor( new File( project.getBasedir(), "src/site" ), Locale.ENGLISH ).toString(), + project.getBasedir() + File.separator + "src" + File.separator + "site" + File.separator + "site.xml" ); + String siteDir = "src/blabla"; + assertEquals( tool.getSiteDescriptor( new File( project.getBasedir(), siteDir ), null ).toString(), + project.getBasedir() + File.separator + "src" + File.separator + "blabla" + File.separator + "site.xml" ); + } + + /** + * @throws Exception + */ + public void testGetSiteDescriptorFromRepository() + throws Exception + { + DefaultSiteTool tool = (DefaultSiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "site-tool-test" ); + project.setGroupId( "org.apache.maven" ); + project.setArtifactId( "maven-site" ); + project.setVersion( "1.0" ); + String result = getLocalRepoDir() + File.separator + "org" + File.separator + "apache" + File.separator + + "maven" + File.separator + "maven-site" + File.separator + "1.0" + File.separator + + "maven-site-1.0-site.xml"; + + assertEquals( tool.getSiteDescriptorFromRepository( project, getLocalRepo(), + project.getRemoteArtifactRepositories(), Locale.ENGLISH ) + .toString(), result ); + } + + /** + * @throws Exception + */ + public void testGetDecorationModel() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "site-tool-test" ); + List reactorProjects = new ArrayList(); + + // model from current local build + DecorationModel model = + tool.getDecorationModel( new File( project.getBasedir(), "src/site" ), Locale.getDefault(), project, + reactorProjects, getLocalRepo(), project.getRemoteArtifactRepositories() ); + assertNotNull( model ); + assertNotNull( model.getBannerLeft() ); + assertEquals( "Maven Site", model.getBannerLeft().getName() ); + assertEquals( "http://maven.apache.org/images/apache-maven-project.png", model.getBannerLeft().getSrc() ); + assertEquals( "http://maven.apache.org/", model.getBannerLeft().getHref() ); + assertNotNull( model.getBannerRight() ); + assertNull( model.getBannerRight().getName() ); + assertEquals( "http://maven.apache.org/images/maven-small.gif", model.getBannerRight().getSrc() ); + assertNull( model.getBannerRight().getHref() ); + + // model from repo: https://repo1.maven.org/maven2/org/apache/maven/maven-site/1.0/maven-site-1.0-site.xml + // TODO Enable this test as soon as we haven a site.xml with head content as string + /*project.setBasedir( null ); + project.setGroupId( "org.apache.maven" ); + project.setArtifactId( "maven-site" ); + project.setVersion( "1.0" ); + DecorationModel modelFromRepo = + tool.getDecorationModel( null, Locale.getDefault(), project, reactorProjects, getLocalRepo(), + project.getRemoteArtifactRepositories() ); + assertNotNull( modelFromRepo ); + assertNotNull( modelFromRepo.getBannerLeft() ); + assertEquals( "Maven", modelFromRepo.getBannerLeft().getName() ); + assertEquals( "images/apache-maven-project-2.png", modelFromRepo.getBannerLeft().getSrc() ); + assertEquals( "http://maven.apache.org/", modelFromRepo.getBannerLeft().getHref() ); + assertNotNull( modelFromRepo.getBannerRight() ); + assertNull( modelFromRepo.getBannerRight().getName() ); + assertEquals( "images/maven-logo-2.gif", modelFromRepo.getBannerRight().getSrc() ); + assertNull( modelFromRepo.getBannerRight().getHref() );*/ + } + + /** + * @throws Exception + */ + public void testGetDefaultDecorationModel() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "no-site-test" ); + String siteDirectory = "src/site"; + List reactorProjects = new ArrayList(); + + DecorationModel model = + tool.getDecorationModel( new File( project.getBasedir(), siteDirectory ), Locale.getDefault(), project, + reactorProjects, getLocalRepo(), project.getRemoteArtifactRepositories() ); + assertNotNull( model ); + } + + public void testGetAvailableLocales() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + + assertEquals( Arrays.asList( new Locale[] { SiteTool.DEFAULT_LOCALE } ), tool.getSiteLocales( "en" ) ); + + assertEquals( Arrays.asList( new Locale[] { SiteTool.DEFAULT_LOCALE, Locale.FRENCH, Locale.ITALIAN } ), + tool.getSiteLocales( "en,fr,it" ) ); + + // by default, only DEFAULT_LOCALE + assertEquals( Arrays.asList( new Locale[] { SiteTool.DEFAULT_LOCALE } ), tool.getSiteLocales( "" ) ); + } + + public void testGetInterpolatedSiteDescriptorContent() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + File pomXmlFile = getTestFile( "src/test/resources/unit/interpolated-site/pom.xml" ); + assertNotNull( pomXmlFile ); + assertTrue( pomXmlFile.exists() ); + + File descriptorFile = getTestFile( "src/test/resources/unit/interpolated-site/src/site/site.xml" ); + assertNotNull( descriptorFile ); + assertTrue( descriptorFile.exists() ); + + String siteDescriptorContent = FileUtils.fileRead( descriptorFile ); + assertNotNull( siteDescriptorContent ); + assertTrue( siteDescriptorContent.contains( "${project.name}" ) ); + assertFalse( siteDescriptorContent.contains( "Interpolatesite" ) ); + + SiteToolMavenProjectStub project = new SiteToolMavenProjectStub( "interpolated-site" ); + + SiteTool siteTool = (SiteTool) lookup( SiteTool.ROLE ); + siteDescriptorContent = + siteTool.getInterpolatedSiteDescriptorContent( new HashMap(), project, + siteDescriptorContent ); + assertNotNull( siteDescriptorContent ); + assertFalse( siteDescriptorContent.contains( "${project.name}" ) ); + assertTrue( siteDescriptorContent.contains( "Interpolatesite" ) ); + } + + // MSHARED-217 -> DOXIATOOLS-34 -> DOXIASITETOOLS-118 + public void testDecorationModelInheritanceAndInterpolation() + throws Exception + { + SiteTool tool = (SiteTool) lookup( SiteTool.ROLE ); + assertNotNull( tool ); + + SiteToolMavenProjectStub parentProject = new SiteToolMavenProjectStub( "interpolation-parent-test" ); + parentProject.setDistgributionManagementSiteUrl( "dav:https://davs.codehaus.org/site" ); + + SiteToolMavenProjectStub childProject = new SiteToolMavenProjectStub( "interpolation-child-test" ); + childProject.setParent( parentProject ); + childProject.setDistgributionManagementSiteUrl( "dav:https://davs.codehaus.org/site/child" ); + + List reactorProjects = Collections.singletonList( parentProject ); + + DecorationModel model = tool.getDecorationModel( new File( childProject.getBasedir(), "src/site" ), + Locale.getDefault(), childProject, reactorProjects, + getLocalRepo(), childProject.getRemoteArtifactRepositories() ); + assertNotNull( model ); + + writeModel( model, "unit/interpolation-child-test/effective-site.xml" ); + + assertEquals( "MSHARED-217 Child", model.getName() ); + // late (classical) interpolation + assertEquals( "project.artifactId = mshared-217-child", model.getBannerLeft().getName() ); + // early interpolation: DOXIASITETOOLS-158 + assertEquals( "this.artifactId = mshared-217-parent", model.getBannerRight().getName() ); + // href rebase + assertEquals( "../../index.html", model.getBody().getBreadcrumbs().iterator().next().getHref() ); + Iterator links = model.getBody().getLinks().iterator(); + // late interpolation of pom content (which happens first: properties can't override) + assertEquals( "project.name = MSHARED-217 Child", links.next().getName() ); + assertEquals( "name = MSHARED-217 Child", links.next().getName() ); + // early interpolation: DOXIASITETOOLS-158 + assertEquals( "this.name = MSHARED-217 Parent", links.next().getName() ); + + // late interpolation of project properties + assertEquals( "my_property = from child pom.xml", links.next().getName() ); + // early interpolation of project properties: DOXIASITETOOLS-158 + assertEquals( "this.my_property = from parent pom.xml", links.next().getName() ); + + // Env Var interpolation + String envPath = links.next().getName(); + assertTrue( envPath.startsWith( "env.PATH = " ) ); + assertFalse( envPath.contains( "${" ) ); + assertNotSame( "env.PATH = PATH property from pom", envPath ); + + // property overrides env + assertEquals( "PATH = PATH property from pom", links.next().getName() ); + } + + private void writeModel( DecorationModel model, String to ) + throws Exception + { + Writer writer = WriterFactory.newXmlWriter( getTestFile( "target/test-classes/" + to ) ); + try + { + new DecorationXpp3Writer().write( writer, model ); + } + finally + { + IOUtil.close( writer ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/stubs/SiteToolMavenProjectStub.java b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/stubs/SiteToolMavenProjectStub.java new file mode 100644 index 000000000..4a30d7d6e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/java/org/apache/maven/doxia/tools/stubs/SiteToolMavenProjectStub.java @@ -0,0 +1,152 @@ +package org.apache.maven.doxia.tools.stubs; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.repository.DefaultArtifactRepository; +import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.model.Build; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.Site; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; + +/** + * @author Vincent Siveton + */ +public class SiteToolMavenProjectStub + extends MavenProjectStub +{ + private Build build; + + private File basedir; + + private DistributionManagement distributionManagement; + + private Properties properties; + + public SiteToolMavenProjectStub( String projectName ) + { + basedir = new File( super.getBasedir() + "/src/test/resources/unit/" + projectName ); + + Model model = null; + + try + { + model = new MavenXpp3Reader().read( new FileReader( new File( getBasedir(), "pom.xml" ) ) ); + setModel( model ); + } + catch ( Exception e ) + { + throw new RuntimeException( e ); + } + + setGroupId( model.getGroupId() ); + setArtifactId( model.getArtifactId() ); + setVersion( model.getVersion() ); + setName( model.getName() ); + setUrl( model.getUrl() ); + setPackaging( model.getPackaging() ); + setProperties( model.getProperties() ); + + build = new Build(); + build.setFinalName( model.getArtifactId() ); + build.setDirectory( super.getBasedir() + "/target/test/unit/" + projectName + "/target" ); + build.setSourceDirectory( getBasedir() + "/src/main/java" ); + build.setOutputDirectory( build.getDirectory() + "/classes" ); + build.setTestSourceDirectory( getBasedir() + "/src/test/java" ); + build.setTestOutputDirectory( build.getDirectory() + "/test-classes" ); + + List compileSourceRoots = new ArrayList(); + compileSourceRoots.add( getBasedir() + "/src/main/java" ); + setCompileSourceRoots( compileSourceRoots ); + + List testCompileSourceRoots = new ArrayList(); + testCompileSourceRoots.add( getBasedir() + "/src/test/java" ); + setTestCompileSourceRoots( testCompileSourceRoots ); + } + + /** {@inheritDoc} */ + public Build getBuild() + { + return build; + } + + /** {@inheritDoc} */ + public void setBuild( Build build ) + { + this.build = build; + } + + /** {@inheritDoc} */ + public File getBasedir() + { + return basedir; + } + + /** {@inheritDoc} */ + public void setBasedir( File basedir ) + { + this.basedir = basedir; + } + + /** {@inheritDoc} */ + public List getRemoteArtifactRepositories() + { + ArtifactRepository repository = new DefaultArtifactRepository( "central", "https://repo1.maven.org/maven2", + new DefaultRepositoryLayout() ); + + return Collections.singletonList( repository ); + } + + /** {@inheritDoc} */ + public Properties getProperties() + { + return properties; + } + + /** {@inheritDoc} */ + public void setProperties( Properties properties ) + { + this.properties = properties; + } + + public void setDistgributionManagementSiteUrl( String url ) + { + Site site = new Site(); + site.setUrl( url ); + distributionManagement = new DistributionManagement(); + distributionManagement.setSite( site ); + } + + /** {@inheritDoc} */ + public DistributionManagement getDistributionManagement() + { + return distributionManagement; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/pom.xml new file mode 100644 index 000000000..a135e210c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/pom.xml @@ -0,0 +1,80 @@ + + + + + + 4.0.0 + org.apache.maven.plugin.site.unit + interpolatesite + jar + 1.0-SNAPSHOT + 2006 + Interpolatesite + Test interpolation for site.xml. + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + + + vsiveton + Vincent Siveton + vsiveton@apache.org + Apache Software Foundation + + Java Developer + + -5 + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + org.apache.maven.plugins + maven-site-plugin + + + ISO-8859-1 + ISO-8859-1 + + + + + + + foo + bar + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/apt/test.apt b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/apt/test.apt new file mode 100644 index 000000000..90671376d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/apt/test.apt @@ -0,0 +1,29 @@ + ------ + Configuring Site Plugin + ------ + Vincent Siveton + ------ + November 2006 + ------ + + ~~ 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. + + +Site Plugin Test + + Test diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/site.xml new file mode 100644 index 000000000..58a27760a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolated-site/src/site/site.xml @@ -0,0 +1,43 @@ + + + + + + + Maven Site + http://maven.apache.org/images/apache-maven-project.png + http://maven.apache.org/ + + + http://maven.apache.org/images/maven-small.gif + + + + + + + + + + + + ${reports} + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/pom.xml new file mode 100644 index 000000000..b27a81443 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/pom.xml @@ -0,0 +1,37 @@ + + + + + 4.0.0 + + org.apache.maven.shared.its + mshared-217-child + 1.0-SNAPSHOT + + MSHARED-217 Child + http://maven.apache.org/mshared-217/child + + + from child pom.xml + PATH property from pom + name property + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/src/site/site.xml new file mode 100644 index 000000000..091ed5199 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-child-test/src/site/site.xml @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/pom.xml new file mode 100644 index 000000000..28545f85d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/pom.xml @@ -0,0 +1,36 @@ + + + + + 4.0.0 + + org.apache.maven.shared.its + mshared-217-parent + 1.0-SNAPSHOT + + MSHARED-217 Parent + http://maven.apache.org/mshared-217 + + + from parent pom.xml + name property + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/src/site/site.xml new file mode 100644 index 000000000..27a291361 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/interpolation-parent-test/src/site/site.xml @@ -0,0 +1,52 @@ + + + + + + + project.artifactId = ${project.artifactId} + + + this.artifactId = ${this.artifactId} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/no-site-test/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/no-site-test/pom.xml new file mode 100644 index 000000000..6d048ee0a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/no-site-test/pom.xml @@ -0,0 +1,40 @@ + + + + + 4.0.0 + + org.apache.maven.shared + no-site-test + 1.0-SNAPSHOT + jar + + dummy + + + + junit + junit + 3.8.2 + test + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/pom.xml new file mode 100644 index 000000000..fb40c17fe --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/pom.xml @@ -0,0 +1,40 @@ + + + + + 4.0.0 + + org.apache.maven.shared + site-tool-test + 1.0-SNAPSHOT + jar + + dummy + + + + junit + junit + 3.8.1 + test + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/src/site/site.xml new file mode 100644 index 000000000..41db45ef7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-integration-tools/src/test/resources/unit/site-tool-test/src/site/site.xml @@ -0,0 +1,45 @@ + + + + + + + Maven Site + http://maven.apache.org/images/apache-maven-project.png + http://maven.apache.org/ + + + http://maven.apache.org/images/maven-small.gif + + + org.apache.maven.skins + maven-stylus-skin + + + + + + + + + + ${reports} + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/pom.xml new file mode 100644 index 000000000..ba4c0f6a4 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/pom.xml @@ -0,0 +1,233 @@ + + + + + + 4.0.0 + + + org.apache.maven.doxia + doxia-sitetools + 1.9.3-SNAPSHOT + ../pom.xml + + + doxia-site-renderer + + Doxia Sitetools :: Site Renderer + The Site Renderer handles the rendering of sites, merging site decoration with document content. + + + + + org.apache.maven + maven-artifact + 3.0 + + + + + org.apache.maven.doxia + doxia-core + + + org.apache.maven.doxia + doxia-logging-api + + + org.apache.maven.doxia + doxia-sink-api + + + org.apache.maven.doxia + doxia-decoration-model + + + org.apache.maven.doxia + doxia-skin-model + + + + org.apache.maven.doxia + doxia-module-apt + test + + + org.apache.maven.doxia + doxia-module-confluence + test + + + org.apache.maven.doxia + doxia-module-docbook-simple + test + + + org.apache.maven.doxia + doxia-module-xdoc + test + + + org.apache.maven.doxia + doxia-module-xhtml + + + org.apache.maven.doxia + doxia-module-xhtml5 + + + org.apache.maven.doxia + doxia-module-fml + test + + + + + org.codehaus.plexus + plexus-component-annotations + + + org.codehaus.plexus + plexus-i18n + + + org.codehaus.plexus + plexus-container-default + + + org.codehaus.plexus + plexus-velocity + + + org.codehaus.plexus + plexus-utils + + + + + org.apache.velocity + velocity + + + org.apache.velocity + velocity-tools + 2.0 + + + + javax.servlet + servlet-api + + + org.apache.struts + struts-core + + + org.apache.struts + struts-taglib + + + org.apache.struts + struts-tiles + + + sslext + sslext + + + commons-validator + commons-validator + + + + + commons-collections + commons-collections + 3.2.2 + + + org.apache.commons + commons-lang3 + 3.8.1 + + + + + junit + junit + test + + + commons-io + commons-io + test + + + org.apache.maven.doxia + doxia-core + ${doxiaVersion} + test-jar + test + + + net.sourceforge.htmlunit + htmlunit + 2.24 + test + + + org.mockito + mockito-core + 2.28.2 + test + + + + + + reporting + + + + org.codehaus.mojo + l10n-maven-plugin + 1.0-alpha-2 + + + de + en + es + fr + it + ja + nl + pl + pt_BR + sv + zh_CN + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java new file mode 100644 index 000000000..6950b60f3 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java @@ -0,0 +1,1192 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

DefaultSiteRenderer class.

+ * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentContent.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentContent.java new file mode 100644 index 000000000..4bcbb9edd --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentContent.java @@ -0,0 +1,66 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.util.List; + +/** + * Document content, that will be merged into a site template. + * + * @since 1.8 + */ +public interface DocumentContent +{ + /** + * Get the title of the document. + * @return the document title + */ + String getTitle(); + + /** + * Get the date of the document. + * @return the document date + */ + String getDate(); + + /** + * Get the authors of the document. + * @return the document authors + */ + List getAuthors(); + + /** + * Get the html head of the document. + * @return the document html head + */ + String getHead(); + + /** + * Get the html body of the document. + * @return the document body head + */ + String getBody(); + + /** + * Get the document rendering context. + * @return the document rendering context + */ + RenderingContext getRenderingContext(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderer.java new file mode 100644 index 000000000..74485a65d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DocumentRenderer.java @@ -0,0 +1,75 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +/** + * Renders a page in a site, whatever the source is: a Doxia source file, a report or anything else. + * + * @author Brett Porter + * @see RenderingContext document rendering context + */ +public interface DocumentRenderer +{ + /** + * Render a document in a site. + * + * @param writer the Writer for the document output. + * @param siteRenderer the site renderer to merge document content to. + * @param siteRenderingContext the site rendering context. + * @throws org.apache.maven.doxia.siterenderer.RendererException if it bombs. + * @throws java.io.FileNotFoundException if it bombs. + * @throws java.io.UnsupportedEncodingException if it bombs. + */ + void renderDocument( Writer writer, Renderer siteRenderer, SiteRenderingContext siteRenderingContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException; + + /** + * The name of the output document. + * + * @return the name of the output document. + */ + String getOutputName(); + + /** + * Return the RenderingContext of the document. + * + * @return RenderingContext. + */ + RenderingContext getRenderingContext(); + + /** + * Whether to always overwrite the document, or only do so when it is changed. + * + * @return whether to overwrite + */ + boolean isOverwrite(); + + /** + * Whether this document is an external report, independent from the site templating. + * + * @return {@code true} if report is external, otherwise {@code false} + * @since 1.7 + */ + boolean isExternalReport(); +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DoxiaDocumentRenderer.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DoxiaDocumentRenderer.java new file mode 100644 index 000000000..30ffda062 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DoxiaDocumentRenderer.java @@ -0,0 +1,78 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +/** + * Renderer for a document that has a source file to be parsed by Doxia. + * Details about the source file are in {@link RenderingContext}, which is expected to have + * a non-null parserId and extension. + * + * @author Brett Porter + */ +public class DoxiaDocumentRenderer + implements DocumentRenderer +{ + private RenderingContext renderingContext; + + /** + * Constructor. + * + * @param renderingContext the document's RenderingContext to use. + */ + public DoxiaDocumentRenderer( RenderingContext renderingContext ) + { + this.renderingContext = renderingContext; + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, Renderer siteRenderer, SiteRenderingContext siteRenderingContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + siteRenderer.renderDocument( writer, renderingContext, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public String getOutputName() + { + return renderingContext.getOutputName(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } + + /** {@inheritDoc} */ + public boolean isOverwrite() + { + return false; + } + + public boolean isExternalReport() + { + return false; + } + +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/ExtraDoxiaModuleReference.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/ExtraDoxiaModuleReference.java new file mode 100644 index 000000000..42cab469e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/ExtraDoxiaModuleReference.java @@ -0,0 +1,60 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; + +/** + * Holds an extra Doxia source module reference in the list of added modules to the site rendering context. + * + * @author Brett Porter + */ +class ExtraDoxiaModuleReference +{ + private final String parserId; + + private final File basedir; + + ExtraDoxiaModuleReference( String parserId, File basedir ) + { + this.parserId = parserId; + this.basedir = basedir; + } + + /** + *

Getter for the field parserId.

+ * + * @return Doxia parser id associated to this source module. + */ + String getParserId() + { + return parserId; + } + + /** + *

Getter for the field basedir.

+ * + * @return The base directory for module's source files. + */ + File getBasedir() + { + return basedir; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/Renderer.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/Renderer.java new file mode 100644 index 000000000..148cef953 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/Renderer.java @@ -0,0 +1,185 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; + +/** + *

Site Renderer interface: render a collection of documents into a site, ie decored with a site template + * (eventually packaged as skin).

+ * + * @author Emmanuel Venisse + */ +public interface Renderer // TODO rename to SiteRenderer +{ + /** + * Plexus lookup role. + */ + String ROLE = Renderer.class.getName(); + + /** + * Render a collection of documents into a site. + * + * @param documents the documents to render. + * @param siteRenderingContext the SiteRenderingContext to use. + * @param outputDirectory the output directory to write results. + * @throws RendererException if it bombs. + * @throws IOException if it bombs. + */ + void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException; + + /** + * Generate a document output from a Doxia SiteRenderer Sink, i.e. merge the document content into + * the site template. + * + * @param writer the Writer to use. + * @param sink the Site Renderer Sink that received the Doxia events during document content rendering. + * @param siteRenderingContext the SiteRenderingContext to use. + * @throws RendererException if it bombs. + * @deprecated since 1.8, use mergeDocumentIntoSite + */ + void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException; + + /** + * Generate a document output integrated in a site from a document content, + * i.e. merge the document content into the site template. + * + * @param writer the Writer to use. + * @param content the document content to be merged + * @param siteRenderingContext the SiteRenderingContext to use. + * @throws RendererException if it bombs. + * @since 1.8 + */ + void mergeDocumentIntoSite( Writer writer, DocumentContent content, SiteRenderingContext siteRenderingContext ) + throws RendererException; + + /** + * Create a Site Rendering Context for a site using a skin. + * + * @param skin + * @param attributes + * @param decoration + * @param defaultWindowTitle + * @param locale + * @return a SiteRenderingContext. + * @throws java.io.IOException if it bombs. + * @since 1.7.3 was previously with skin as File instead of Artifact + */ + SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + throws RendererException, IOException; + + /** + * Create a Site Rendering Context for a site using a local template. + * + * @param templateFile + * @param attributes + * @param decoration + * @param defaultWindowTitle + * @param locale + * @return a SiteRenderingContext. + * @throws MalformedURLException if it bombs. + * @since 1.7, had an additional skinFile parameter before + * @deprecated Deprecated without replacement, use skins only. + * @see #createContextForSkin(File, Map, DecorationModel, String, Locale) + */ + @Deprecated + SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException; + + /** + * Copy resource files. + * + * @param siteRenderingContext + * @param resourcesDirectory + * @param outputDirectory + * @throws IOException if it bombs. + * @deprecated since 1.7, use copyResources without resourcesDirectory parameter + */ + void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, File outputDirectory ) + throws IOException; + + /** + * Copy resource files from skin, template, and site resources. + * + * @param siteRenderingContext + * @param outputDirectory + * @throws IOException if it bombs. + * @since 1.7 + */ + void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException; + + /** + * Locate Doxia document source files in the site source context. + * + * @param siteRenderingContext + * @return the Doxia document renderers in a Map keyed by output file name. + * @throws IOException if it bombs. + * @throws RendererException if it bombs. + * @deprecated since 1.8, use locateDocumentFiles with editable parameter + */ + Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException; + + /** + * Locate Doxia document source files in the site source context. + * + * @param siteRenderingContext + * @param mark Doxia document renderer as editable? (should not mark editable if generated Doxia source) + * @return the Doxia document renderers in a Map keyed by output file name. + * @throws IOException if it bombs. + * @throws RendererException if it bombs. + * @since 1.8 + */ + Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, boolean editable ) + throws IOException, RendererException; + + /** + * Render a document written in a Doxia markup language. This method is an internal method, used by + * {@link DoxiaDocumentRenderer}. + * + * @param writer the writer to render the document to. + * @param docRenderingContext the document's rendering context, which is expected to have a non-null parser id. + * @param siteContext the site's rendering context + * @throws RendererException if it bombs. + * @throws FileNotFoundException if it bombs. + * @throws UnsupportedEncodingException if it bombs. + */ + void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException; +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RendererException.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RendererException.java new file mode 100644 index 000000000..b664eaa2c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RendererException.java @@ -0,0 +1,53 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +/** + *

RendererException class.

+ * + * @author Emmanuel Venisse + */ +public class RendererException + extends Exception +{ + + private static final long serialVersionUID = 3141592653589793238L; + + /** + * Construct a RendererException with a message. + * + * @param message a custom message. + */ + public RendererException( String message ) + { + super( message ); + } + + /** + * Construct a RendererException with a message and a cause. + * + * @param message a custom message. + * @param t the cause. + */ + public RendererException( String message, Throwable t ) + { + super( message, t ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RenderingContext.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RenderingContext.java new file mode 100644 index 000000000..239e6ec56 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/RenderingContext.java @@ -0,0 +1,295 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.StringUtils; + +/** + * The rendering context of a document. + * If not rendered from a Doxia markup source, parserId and extension will be null. + * + * @author Jason van Zyl + * @since 1.5 (was since 1.1 in o.a.m.d.sink.render) + */ +public class RenderingContext // TODO rename to DocumentRenderingContext +{ + private final File basedir; + + private final String basedirRelativePath; + + private final String inputName; + + private final String outputName; + + private final String parserId; + + private final String relativePath; + + private final String extension; + + private Map attributes; + + private final boolean editable; + + private final String generator; + + @Deprecated + public RenderingContext( File basedir, String document ) + { + this( basedir, null, document, null, null, false, null ); + } + + /** + *

+ * Constructor for RenderingContext when document is not rendered from a Doxia markup source. + *

+ * + * @param basedir the pseudo-source base directory. + * @param document the pseudo-source document name: will be used to compute output name (same name with extension + * replaced with .html). + * @param generator the generator (in general a reporting goal: groupId:artifactId:version:goal) + * @since 1.8 + */ + public RenderingContext( File basedir, String document, String generator ) + { + this( basedir, null, document, null, null, false, generator ); + } + + @Deprecated + public RenderingContext( File basedir, String document, String parserId, String extension ) + { + this( basedir, null, document, parserId, extension, false, null ); + } + + public RenderingContext( File basedir, String basedirRelativePath, String document, String parserId, + String extension, boolean editable ) + { + this( basedir, basedirRelativePath, document, parserId, extension, editable, null ); + } + + /** + *

+ * Constructor for document RenderingContext. + *

+ * + * @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 name. + * @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 editable is the document editable as source, i.e. not generated? + * @param generator the generator (in general a reporting goal: groupId:artifactId:version:goal) + * @since 1.8 + */ + public RenderingContext( File basedir, String basedirRelativePath, String document, String parserId, + String extension, boolean editable, String generator ) + { + this.basedir = basedir; + this.basedirRelativePath = basedirRelativePath; + this.inputName = document; + this.parserId = parserId; + this.extension = extension; + this.generator = generator; + this.attributes = new HashMap(); + + if ( StringUtils.isNotEmpty( extension ) ) + { + // document comes from a Doxia source: see DoxiaDocumentRenderer + this.editable = editable; + + // here we know the parserId and extension, we can play with this to get output name from document: + // - index.xml -> index.html + // - index.xml.vm -> index.html + // - download.apt.vm --> download.html + if ( DefaultSiteRenderer.endsWithIgnoreCase( document, ".vm" ) ) + { + document = document.substring( 0, document.length() - 3 ); + } + String fileNameWithoutExt = document.substring( 0, document.length() - extension.length() - 1 ); + this.outputName = fileNameWithoutExt + ".html"; + } + else + { + // document does not come from a Doxia source but direct Sink API + this.editable = false; + // make sure output name ends in .html + this.outputName = document.substring( 0, document.lastIndexOf( '.' ) ).replace( '\\', '/' ) + ".html"; + } + + this.relativePath = PathTool.getRelativePath( basedir.getPath(), new File( basedir, inputName ).getPath() ); + } + + /** + *

Getter for the field basedir.

+ * + * @return a {@link java.io.File} object. + */ + public File getBasedir() + { + return basedir; + } + + /** + *

Getter for the field inputName.

+ * + * @return a {@link java.lang.String} object. + */ + public String getInputName() + { + return inputName; + } + + /** + * Get html output name, relative to site root. + * + * @return html output name + * @see PathTool#getRelativePath(String) + */ + public String getOutputName() + { + return outputName; + } + + /** + * Get the parserId when document comes from a Doxia source. + * + * @return parser id, or null if not froma DOxia source. + */ + public String getParserId() + { + return parserId; + } + + /** + * Get the relative path to site root. + * + * @return the relative path to site root + */ + public String getRelativePath() + { + return relativePath; + } + + /** + *

setAttribute.

+ * + * @param key a {@link java.lang.String} object. + * @param value a {@link java.lang.String} object. + */ + public void setAttribute( String key, String value ) + { + attributes.put( key, value ); + } + + /** + *

getAttribute.

+ * + * @param key a {@link java.lang.String} object. + * @return a {@link java.lang.String} object. + */ + public String getAttribute( String key ) + { + return attributes.get( key ); + } + + /** + * Get the source document filename extension (when a Doxia source) + * + * @return the source document filename extension when a Doxia source, or null if not a Doxia source + */ + public String getExtension() + { + return extension; + } + + /** + * Is the source document editable? + * + * @return true if comes from an editable Doxia source (not generated one). + * @since 1.8 + */ + public boolean isEditable() + { + return editable; + } + + /** + * Is the document rendered from a Doxia source? + * + * @return true if comes from a Doxia source. + * @since 1.8 + */ + public boolean isDoxiaSource() + { + return StringUtils.isNotEmpty( extension ); + } + + /** + * What is the generator (if any)? + * + * @return null if no known generator + * @since 1.8 + */ + public String getGenerator() + { + return generator; + } + + /** + * Get the relative path of basedir (when a Doxia source) + * + * @return the relative path of basedir when a Doxia source, or null if not a Doxia source + * @since 1.8 + */ + public String getBasedirRelativePath() + { + return basedirRelativePath; + } + + /** + * Get the relative path to Doxia source from build root. + * + * @return the relative path to Doxia source from build root, or null if not a Doxia source + * @since 1.8 + */ + public String getDoxiaSourcePath() + { + return isDoxiaSource() ? ( basedirRelativePath + '/' + inputName ) : null; + } + + /** + * Get url of the Doxia source calculate from given base url. + * + * @param base the base url to use + * @return the resulting url + * @since 1.8 + */ + public String getDoxiaSourcePath( String base ) + { + return PathTool.calculateLink( getDoxiaSourcePath(), base ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java new file mode 100644 index 000000000..994e2a1e2 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SiteRenderingContext.java @@ -0,0 +1,473 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.WriterFactory; + +/** + * Context for a site rendering. + * + * @author Brett Porter + */ +public class SiteRenderingContext +{ + private String inputEncoding = ReaderFactory.FILE_ENCODING; + + private String outputEncoding = WriterFactory.UTF_8; + + private String templateName; + + private ClassLoader templateClassLoader; + + private Map templateProperties; + + private Locale locale = Locale.getDefault(); + + private List siteLocales = new ArrayList(); + + private DecorationModel decoration; + + private String defaultWindowTitle; + + private Artifact skin; + + private SkinModel skinModel; + + private boolean usingDefaultTemplate; + + private File rootDirectory; + + private List siteDirectories = new ArrayList(); + + private Map moduleExcludes; + + private List modules = new ArrayList(); + + private boolean validate; + + private Date publishDate; + + private File processedContentOutput; + + /** + * If input documents should be validated before parsing. + * By default no validation is performed. + * + * @return true if validation is switched on. + * @since 1.1.3 + */ + public boolean isValidate() + { + return validate; + } + + /** + * Switch on/off validation. + * + * @param validate true to switch on validation. + * @since 1.1.3 + */ + public void setValidate( boolean validate ) + { + this.validate = validate; + } + + /** + *

Getter for the field templateName.

+ * + * @return a {@link java.lang.String} object. + */ + public String getTemplateName() + { + return templateName; + } + + /** + *

Getter for the field templateClassLoader.

+ * + * @return a {@link java.lang.ClassLoader} object. + */ + public ClassLoader getTemplateClassLoader() + { + return templateClassLoader; + } + + /** + *

Setter for the field templateClassLoader.

+ * + * @param templateClassLoader a {@link java.lang.ClassLoader} object. + */ + public void setTemplateClassLoader( ClassLoader templateClassLoader ) + { + this.templateClassLoader = templateClassLoader; + } + + /** + *

Getter for the field templateProperties.

+ * + * @return a {@link java.util.Map} object. + */ + public Map getTemplateProperties() + { + return templateProperties; + } + + /** + *

Setter for the field templateProperties.

+ * + * @param templateProperties a {@link java.util.Map} object. + */ + public void setTemplateProperties( Map templateProperties ) + { + this.templateProperties = Collections.unmodifiableMap( templateProperties ); + } + + /** + *

Getter for the field locale.

+ * + * @return a {@link java.util.Locale} object. + */ + public Locale getLocale() + { + return locale; + } + + /** + *

Setter for the field locale.

+ * + * @param locale a {@link java.util.Locale} object. + */ + public void setLocale( Locale locale ) + { + this.locale = locale; + } + + /** + *

Getter for the field siteLocales - + * a list of locales available for this site context.

+ * + * @return a {@link java.util.List} object with {@link java.util.Locale} objects. + */ + public List getSiteLocales() + { + return siteLocales; + } + + /** + *

Adds passed locales to the list of site locales.

+ * + * @param locales List of {@link java.util.Locale} objects to add to the site locales list. + */ + public void addSiteLocales( List locales ) + { + siteLocales.addAll( locales ); + } + + /** + *

Getter for the field decoration.

+ * + * @return a {@link org.apache.maven.doxia.site.decoration.DecorationModel} object. + */ + public DecorationModel getDecoration() + { + return decoration; + } + + /** + *

Setter for the field decoration.

+ * + * @param decoration a {@link org.apache.maven.doxia.site.decoration.DecorationModel} object. + */ + public void setDecoration( DecorationModel decoration ) + { + this.decoration = decoration; + } + + /** + *

Setter for the field defaultWindowTitle.

+ * + * @param defaultWindowTitle a {@link java.lang.String} object. + */ + public void setDefaultWindowTitle( String defaultWindowTitle ) + { + this.defaultWindowTitle = defaultWindowTitle; + } + + /** + *

Getter for the field defaultWindowTitle.

+ * + * @return a {@link java.lang.String} object. + */ + public String getDefaultWindowTitle() + { + return defaultWindowTitle; + } + + /** + *

Getter for the field skin.

+ * + * @return a {@link Artifact} object. + */ + public Artifact getSkin() + { + return skin; + } + + /** + *

Setter for the field skinJarFile.

+ * + * @param skin an {@link Artifact} object. + */ + public void setSkin( Artifact skin ) + { + this.skin = skin; + } + + /** + *

Getter for the field skinModel.

+ * + * @return a {@link SkinModel} object. + */ + public SkinModel getSkinModel() + { + return skinModel; + } + + /** + *

Setter for the field skinModel.

+ * + * @param skinModel a {@link SkinModel} object. + */ + public void setSkinModel( SkinModel skinModel ) + { + this.skinModel = skinModel; + } + + /** + *

Setter for the field templateName.

+ * + * @param templateName a {@link java.lang.String} object. + */ + public void setTemplateName( String templateName ) + { + this.templateName = templateName; + } + + /** + *

Setter for the field usingDefaultTemplate.

+ * + * @param usingDefaultTemplate a boolean. + */ + public void setUsingDefaultTemplate( boolean usingDefaultTemplate ) + { + this.usingDefaultTemplate = usingDefaultTemplate; + } + + /** + *

isUsingDefaultTemplate.

+ * + * @return a boolean. + */ + public boolean isUsingDefaultTemplate() + { + return usingDefaultTemplate; + } + + /** + * Add a site directory, expected to have a Doxia Site layout, ie one directory per Doxia parser module containing + * files with parser extension. Typical values are src/site or target/generated-site. + * + * @param siteDirectory a {@link java.io.File} object. + */ + public void addSiteDirectory( File siteDirectory ) + { + this.siteDirectories.add( siteDirectory ); + } + + /** + * Add a extra-module source directory: used for Maven 1.x ${basedir}/xdocs layout, which contains + * xdoc and fml. + * + * @param moduleBasedir The base directory for module's source files. + * @param moduleParserId module's Doxia parser id. + */ + public void addModuleDirectory( File moduleBasedir, String moduleParserId ) + { + this.modules.add( new ExtraDoxiaModuleReference( moduleParserId, moduleBasedir ) ); + } + + /** + *

Getter for the field siteDirectories.

+ * + * @return List of site directories files. + */ + public List getSiteDirectories() + { + return siteDirectories; + } + + /** + *

Getter for the field modules.

+ * + * @return a {@link java.util.List} object. + */ + public List getModules() + { + return modules; + } + + /** + *

Getter for the field moduleExcludes.

+ * + * @return a map defining exclude patterns (comma separated) by parser id. + */ + public Map getModuleExcludes() + { + return moduleExcludes; + } + + /** + *

Setter for the field moduleExcludes.

+ * + * @param moduleExcludes a {@link java.util.Map} object. + */ + public void setModuleExcludes( Map moduleExcludes ) + { + this.moduleExcludes = moduleExcludes; + } + + /** + *

Getter for the field inputEncoding.

+ * + * @return a {@link java.lang.String} object. + */ + public String getInputEncoding() + { + return inputEncoding; + } + + /** + *

Setter for the field inputEncoding.

+ * + * @param inputEncoding a {@link java.lang.String} object. + */ + public void setInputEncoding( String inputEncoding ) + { + this.inputEncoding = inputEncoding; + } + + /** + *

Getter for the field outputEncoding.

+ * + * @return a {@link java.lang.String} object. + */ + public String getOutputEncoding() + { + return outputEncoding; + } + + /** + *

Setter for the field outputEncoding.

+ * + * @param outputEncoding a {@link java.lang.String} object. + */ + public void setOutputEncoding( String outputEncoding ) + { + this.outputEncoding = outputEncoding; + } + + /** + *

If you want to specify a specific publish date instead of the current date.

+ * + * @return the publish date, can be {@code null} + */ + public Date getPublishDate() + { + return publishDate; + } + + /** + *

Specify a specific publish date instead of the current date.

+ * + * @param publishDate the publish date + */ + public void setPublishDate( Date publishDate ) + { + this.publishDate = publishDate; + } + + /** + * Directory where to save content after Velocity processing (*.vm), but before parsing it with Doxia. + * + * @return not null if the documents are to be saved + * @since 1.7 + */ + public File getProcessedContentOutput() + { + return processedContentOutput; + } + + /** + * Where to (eventually) save content after Velocity processing (*.vm), but before parsing it with + * Doxia? + * + * @param processedContentOutput not null if the documents are to be saved + * @since 1.7 + */ + public void setProcessedContentOutput( File processedContentOutput ) + { + this.processedContentOutput = processedContentOutput; + } + + /** + * Root directory, to calculate relative path to every site directories. + * Corresponds to the pom.xml directory for Maven build. + * + * @return the root directory + * @since 1.8 + */ + public File getRootDirectory() + { + return rootDirectory; + } + + /** + * Set the root directory. + * + * @param rootDirectory + * @since 1.8 + */ + public void setRootDirectory( File rootDirectory ) + { + this.rootDirectory = rootDirectory; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SkinResourceLoader.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SkinResourceLoader.java new file mode 100644 index 000000000..e8870de15 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/SkinResourceLoader.java @@ -0,0 +1,125 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.velocity.runtime.resource.Resource; +import org.apache.velocity.runtime.resource.loader.ResourceLoader; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.commons.collections.ExtendedProperties; +import org.codehaus.plexus.util.IOUtil; + +/** + * Skin resource loader: gets content from context classloader, which should contain skin artifact, + * and normalizes newlines + * (see DOXIASITETOOLS-87). + * + * @author Hervé Boutemy + */ +@Deprecated +public class SkinResourceLoader + extends ResourceLoader +{ + public void init( ExtendedProperties configuration ) + { + } + + public synchronized InputStream getResourceStream( String name ) + throws ResourceNotFoundException + { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + if ( name.startsWith( "/" ) ) + { + name = name.substring( 1 ); + } + + return normalizeNewline( classLoader.getResourceAsStream( name ) ); + } + + InputStream normalizeNewline( InputStream in ) + throws ResourceNotFoundException + { + if ( in == null ) + { + return null; + } + + try + { + byte[] content = IOUtil.toByteArray( in ); + + // following code based on org.apache.maven.doxia.sink.AbstractSink.unifyEOLs(String) + + byte[] eol = System.getProperty( "line.separator" ).getBytes(); + + final int size = content.length; + + ByteArrayOutputStream out = new ByteArrayOutputStream( size ); + + for ( int i = 0; i < size; i++ ) + { + byte b = content[i]; + + if ( b == '\r' ) + { + if ( ( i + 1 ) < size && content[i + 1] == '\n' ) + { + i++; + } + + out.write( eol ); + } + else if ( b == '\n' ) + { + out.write( eol ); + } + else + { + out.write( b ); + } + } + + return new ByteArrayInputStream( out.toByteArray() ); + } + catch ( IOException ioe ) + { + throw new ResourceNotFoundException( "cannot read resource", ioe ); + } + finally + { + IOUtil.close( in ); + } + } + + public boolean isSourceModified( Resource resource ) + { + return false; + } + + public long getLastModified( Resource resource ) + { + return 0; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java new file mode 100644 index 000000000..91a349b10 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java @@ -0,0 +1,353 @@ +package org.apache.maven.doxia.siterenderer.sink; + +/* + * 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. + */ + +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.text.html.HTML.Attribute; + +import org.apache.maven.doxia.markup.HtmlMarkup; +import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.siterenderer.DocumentContent; +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.apache.maven.doxia.util.HtmlTools; +import org.codehaus.plexus.util.StringUtils; + +/** + * Sink for site rendering of a document, to allow later merge document's output with a template. + * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating + * into skin or template: title, date, authors, head, body + * + * @author Emmanuel Venisse + */ +@SuppressWarnings( "checkstyle:methodname" ) +public class SiteRendererSink + extends Xhtml5Sink + implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent +{ + private String date = ""; + + private String title = ""; + + private List authors = new ArrayList(); + + private final StringWriter headWriter; + + private StringBuilder sectionTitleBuffer; + + private StringBuilder sectionTitleWriteBuffer; + + private boolean sectionHasID; + + private boolean isSectionTitle; + + private Set anchorsInSectionTitle; + + private final Writer writer; + + private RenderingContext renderingContext; + + /** + * Construct a new SiteRendererSink for a document. + * + * @param renderingContext the document's RenderingContext. + */ + public SiteRendererSink( RenderingContext renderingContext ) + { + this( new StringWriter(), renderingContext ); + } + + /** + * Construct a new SiteRendererSink for a document. + * + * @param writer the writer for the sink. + * @param renderingContext the document's RenderingContext. + */ + private SiteRendererSink( StringWriter writer, RenderingContext renderingContext ) + { + super( writer ); + + this.writer = writer; + this.headWriter = new StringWriter(); + this.renderingContext = renderingContext; + + /* the template is expected to have used the main tag, which can be used only once */ + super.contentStack.push( HtmlMarkup.MAIN ); + } + + /** {@inheritDoc} */ + @Override + public void title_() + { + if ( getTextBuffer().length() > 0 ) + { + title = getTextBuffer().toString(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Reset text buffer, since text content before title mustn't be in title. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title() + */ + @Override + public void title() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author_() + { + if ( getTextBuffer().length() > 0 ) + { + String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); + text = StringUtils.replace( text, "&#", "&#" ); + authors.add( text.trim() ); + } + + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date_() + { + if ( getTextBuffer().length() > 0 ) + { + date = getTextBuffer().toString().trim(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_() + */ + @Override + public void body_() + { + // nop + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body() + */ + @Override + public void body() + { + // nop + } + + /** {@inheritDoc} */ + @Override + public void head_() + { + setHeadFlag( false ); + } + + /** {@inheritDoc} */ + @Override + public void head() + { + setHeadFlag( true ); + } + + /** {@inheritDoc} */ + @Override + public void anchor( String name, SinkEventAttributes attributes ) + { + super.anchor( name, attributes ); + if ( isSectionTitle ) + { + if ( anchorsInSectionTitle == null ) + { + anchorsInSectionTitle = new HashSet(); + } + anchorsInSectionTitle.add( name ); + } + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle( int depth, SinkEventAttributes attributes ) + { + sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) ); + isSectionTitle = true; + + super.onSectionTitle( depth, attributes ); + + this.sectionTitleBuffer = new StringBuilder(); + this.sectionTitleWriteBuffer = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle_( int depth ) + { + String sectionTitle = sectionTitleBuffer.toString(); + this.sectionTitleBuffer = null; + String sectionWriteTitle = sectionTitleWriteBuffer.toString(); + this.sectionTitleWriteBuffer = null; + + if ( !StringUtils.isEmpty( sectionTitle ) ) + { + if ( sectionHasID ) + { + sectionHasID = false; + } + else + { + String id = HtmlTools.encodeId( sectionTitle ); + if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) ) + { + anchor( id ); + anchor_(); + } + } + } + + super.write( sectionWriteTitle ); + + this.isSectionTitle = false; + anchorsInSectionTitle = null; + super.onSectionTitle_( depth ); + } + + /** {@inheritDoc} */ + @Override + public void text( String text ) + { + if ( sectionTitleBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleBuffer.append( text ); + } + + super.text( text ); + } + + /** {@inheritDoc} */ + @Override + protected void write( String text ) + { + String txt = text; + + if ( isHeadFlag() ) + { + headWriter.write( unifyEOLs( txt ) ); + + return; + } + + if ( renderingContext != null ) + { + String relativePathToBasedir = renderingContext.getRelativePath(); + + if ( relativePathToBasedir == null ) + { + txt = StringUtils.replace( txt, "$relativePath", "." ); + } + else + { + txt = StringUtils.replace( txt, "$relativePath", relativePathToBasedir ); + } + } + + if ( sectionTitleWriteBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleWriteBuffer.append( txt ); + } + else + { + super.write( txt ); + } + } + + // DocumentContent interface + + /** {@inheritDoc} */ + public String getTitle() + { + return title; + } + + /** {@inheritDoc} */ + public List getAuthors() + { + return authors; + } + + /** {@inheritDoc} */ + public String getDate() + { + return date; + } + + /** {@inheritDoc} */ + public String getBody() + { + return writer.toString(); + } + + /** {@inheritDoc} */ + public String getHead() + { + return headWriter.toString(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/maven-base.css b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/maven-base.css new file mode 100644 index 000000000..322efae73 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/maven-base.css @@ -0,0 +1,168 @@ +/* + * 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. + */ + +body { + margin: 0px; + padding: 0px; +} +table { + padding:0px; + width: 100%; + margin-left: -2px; + margin-right: -2px; +} +acronym { + cursor: help; + border-bottom: 1px dotted #feb; +} +table.bodyTable th, table.bodyTable td { + padding: 2px 4px 2px 4px; + vertical-align: top; +} +div.clear{ + clear:both; + visibility: hidden; +} +div.clear hr{ + display: none; +} +#bannerLeft, #bannerRight { + font-size: xx-large; + font-weight: bold; +} +#bannerLeft img, #bannerRight img { + margin: 0px; +} +.xleft, #bannerLeft img { + float:left; +} +.xright, #bannerRight { + float:right; +} +#banner { + padding: 0px; +} +#breadcrumbs { + padding: 3px 10px 3px 10px; +} +#leftColumn { + width: 170px; + float:left; + overflow: auto; +} +#bodyColumn { + margin-right: 1.5em; + margin-left: 197px; +} +#legend { + padding: 8px 0 8px 0; +} +#navcolumn { + padding: 8px 4px 0 8px; +} +#navcolumn h5 { + margin: 0; + padding: 0; + font-size: small; +} +#navcolumn ul { + margin: 0; + padding: 0; + font-size: small; +} +#navcolumn li { + list-style-type: none; + background-image: none; + background-repeat: no-repeat; + background-position: 0 0.4em; + padding-left: 16px; + list-style-position: outside; + line-height: 1.2em; + font-size: smaller; +} +#navcolumn li.expanded { + background-image: url(../images/expanded.gif); +} +#navcolumn li.collapsed { + background-image: url(../images/collapsed.gif); +} +#navcolumn li.none { + text-indent: -1em; + margin-left: 1em; +} +#poweredBy { + text-align: center; +} +#navcolumn img { + margin-top: 10px; + margin-bottom: 3px; +} +#poweredBy img { + display:block; + margin: 20px 0 20px 17px; +} +#search img { + margin: 0px; + display: block; +} +#search #q, #search #btnG { + border: 1px solid #999; + margin-bottom:10px; +} +#search form { + margin: 0px; +} +#lastPublished { + font-size: x-small; +} +.navSection { + margin-bottom: 2px; + padding: 8px; +} +.navSectionHead { + font-weight: bold; + font-size: x-small; +} +.section { + padding: 4px; +} +#footer { + padding: 3px 10px 3px 10px; + font-size: x-small; +} +#breadcrumbs { + font-size: x-small; + margin: 0pt; +} +.source { + padding: 12px; + margin: 1em 7px 1em 7px; +} +.source pre { + margin: 0px; + padding: 0px; +} +#navcolumn img.imageLink, .imageLink { + padding-left: 0px; + padding-bottom: 0px; + padding-top: 0px; + padding-right: 2px; + border: 0px; + margin: 0px; +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/print.css b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/print.css new file mode 100644 index 000000000..18fcbad70 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/css/print.css @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#banner, #footer, #leftcol, #breadcrumbs, .docs #toc, .docs .courtesylinks, #leftColumn, #navColumn { + display: none !important; +} +#bodyColumn, body.docs div.docs { + margin: 0 !important; + border: none !important +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site-macros.vm b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site-macros.vm new file mode 100644 index 000000000..3f55b30cd --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site-macros.vm @@ -0,0 +1,494 @@ +## 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. +## +#macro ( link $href $name $target $img $position $alt $border $width $height $title ) +#**##set ( $linkTitle = ' title="' + $name + '"' ) +#**##if( $target ) +#* *##set ( $linkTarget = ' target="' + $target + '"' ) +#**##else +#* *##set ( $linkTarget = "" ) +#**##end +#**##if ( $decoration.isLink( $href ) ) +#* *##set ( $linkClass = ' class="externalLink"' ) +#**##else +#* *##set ( $linkClass = "" ) +#**##end +#**##if ( $img ) +#* *##if ( $position == "left" ) +#* *##image($img $alt $border $width $height $title)$name## +#* *##else +#* *#$name #image($img $alt $border $width $height $title)## +#* *##end +#**##else +#* *#$name## +#**##end +#end +## +#macro ( image $img $alt $border $width $height $title ) +#**##if( $img ) +#* *##if ( !$decoration.isLink( $img ) ) +#* *##set ( $imgSrc = $PathTool.calculateLink( $img, $relativePath ) ) +#* *##set ( $imgSrc = $imgSrc.replaceAll( '\\', '/' ) ) +#* *##set ( $imgSrc = ' src="' + $imgSrc + '"' ) +#* *##else +#* *##set ( $imgSrc = ' src="' + $img + '"' ) +#* *##end +#* *##if( $alt ) +#* *##set ( $imgAlt = ' alt="' + $alt + '"' ) +#* *##else +#* *##set ( $imgAlt = ' alt=""' ) +#* *##end +#* *##if( $border ) +#* *##set ( $imgBorder = ' border="' + $border + '"' ) +#* *##else +#* *##set ( $imgBorder = "" ) +#* *##end +#* *##if( $width ) +#* *##set ( $imgWidth = ' width="' + $width + '"' ) +#* *##else +#* *##set ( $imgWidth = "" ) +#* *##end +#* *##if( $height ) +#* *##set ( $imgHeight = ' height="' + $height + '"' ) +#* *##else +#* *##set ( $imgHeight = "" ) +#* *##end +#* *##if( $title ) +#* *##set ( $imgTitle = ' title="' + $title + '"' ) +#* *##else +#* *##set ( $imgTitle = "" ) +#* *##end +#* *### +#**##end +#end +## +#macro ( banner $banner $id ) +#**##if ( $banner ) +#* *##if( $banner.href ) +#* *##set ( $hrf = $banner.href ) +#* *##if ( !$decoration.isLink( $hrf ) ) +#* *##set ( $hrf = $PathTool.calculateLink( $hrf, $relativePath ) ) +#* *##set ( $hrf = $hrf.replaceAll( '\\', '/' ) ) +#* *##if ( ( $hrf == '' ) ) +#* *##set ( $hrf = './' ) +#* *##end +#* *##end +#* *### +#* *##else +#* *# +#* *##end +#**##end +#end +## +#macro ( links $links ) +#**##set ( $counter = 0 ) +#**##foreach( $item in $links ) +#* *##set ( $counter = $counter + 1 ) +#* *##set ( $currentItemHref = $PathTool.calculateLink( $item.href, $relativePath ) ) +#* *##set ( $currentItemHref = $currentItemHref.replaceAll( '\\', '/' ) ) +#* *##link( $currentItemHref $item.name $item.target $item.img $item.position $item.alt $item.border $item.width $item.height $item.title ) +#* *##if ( $links.size() > $counter ) +#* *# | +#* *##end +#**##end +#end +## +#macro ( breadcrumbs $breadcrumbs ) +#**##foreach( $item in $breadcrumbs ) +#* *##set ( $currentItemHref = $PathTool.calculateLink( $item.href, $relativePath ) ) +#* *##set ( $currentItemHref = $currentItemHref.replaceAll( '\\', '/' ) ) +#* *##if ( ( $currentItemHref == '' ) ) +#* *##set ( $currentItemHref = './' ) +#* *##end +## +#* *##link( $currentItemHref $item.name $item.target $item.img $item.position $item.alt $item.border $item.width $item.height $item.title ) +#**# > +#**##end +#**#$shortTitle +#**##if( $decoration.edit && $docRenderingContext.editable ) +#* *# [edit] +#**##end +#end +## +#macro ( displayTree $display $item ) +#**##if ( $item && $item.items && $item.items.size() > 0 ) +#* *##foreach( $subitem in $item.items ) +#* *##set ( $subitemHref = $PathTool.calculateLink( $subitem.href, $relativePath ) ) +#* *##set ( $subitemHref = $subitemHref.replaceAll( '\\', '/' ) ) +## +#* *##if ( $alignedFileName == $subitemHref ) +#* *##set ( $display = true ) +#* *##end +## +#* *##displayTree( $display $subitem ) +#* *##end +#**##end +#end +## +#macro ( menuItem $item $indent ) +#**##set ( $collapseClass = "none" ) +#**##set ( $currentItemHref = $PathTool.calculateLink( $item.href, $relativePath ) ) +#**##set ( $currentItemHref = $currentItemHref.replaceAll( '\\', '/' ) ) +## +#**##if ( $item && $item.items && $item.items.size() > 0 ) +#* *##if ( $item.collapse == false ) +#* *##set ( $collapseClass = "expanded" ) +#* *##else +#* *### By default collapsed +#* *##set ( $collapseClass = "collapsed" ) +#* *##end +## +#* *##set ( $display = false ) +#* *##displayTree( $display $item ) +## +#* *##if ( $alignedFileName == $currentItemHref || $display ) +#* *##set ( $collapseClass = "expanded" ) +#* *##end +#**##end +$indent
  • ## +#**##if ( $item.img ) +#* *##if ( $item.position == "left" ) +#* *##if ( $alignedFileName == $currentItemHref ) +#* *##image($item.img $item.alt $item.border $item.width $item.height $item.title) $item.name +#* *##else +#* *##link($currentItemHref $item.name $item.target $item.img $item.position $item.alt $item.border $item.width $item.height $item.title) +#* *##end +#* *##else +#* *##if ( $alignedFileName == $currentItemHref ) +#* *#$item.name #image($item.img $item.alt $item.border $item.width $item.height $item.title) +#* *##else +#* *##link($currentItemHref $item.name $item.target $item.img $item.position $item.alt $item.border $item.width $item.height $item.title) +#* *##end +#* *##end +#**##else +#* *##if ( $alignedFileName == $currentItemHref ) +#* *#$item.name## +#* *##else +#* *##link( $currentItemHref $item.name $item.target $item.img $item.position $item.alt $item.border $item.width $item.height $item.title ) +#* *##end +#**##end +#**##if ( $item && $item.items && $item.items.size() > 0 ) +#* *##if ( $collapseClass == "expanded" ) + +$indent
      +#* *##foreach( $subitem in $item.items ) +#* *##menuItem( $subitem "$indent " ) +#* *##end +$indent
    ## +#* *##end +#**##end +#**#
  • +#end +## +#macro ( mainMenu $menus ) +#**##foreach( $menu in $menus ) +#* *##if ( $menu.name ) +#* *##if ( $menu.img ) +#* *##if( $menu.position ) +#* *##set ( $position = $menu.position ) +#* *##else +#* *##set ( $position = "left" ) +#* *##end +## +#* *##if ( !$decoration.isLink( $menu.img ) ) +#* *##set ( $src = $PathTool.calculateLink( $menu.img, $relativePath ) ) +#* *##set ( $src = $src.replaceAll( '\\', '/' ) ) +#* *##set ( $src = ' src="' + $src + '"' ) +#* *##else +#* *##set ( $src = ' src="' + $menu.img + '"' ) +#* *##end +## +#* *##if( $menu.alt ) +#* *##set ( $alt = ' alt="' + $menu.alt + '"' ) +#* *##else +#* *##set ( $alt = ' alt="' + $menu.name + '"' ) +#* *##end +## +#* *##if( $menu.border ) +#* *##set ( $border = ' border="' + $menu.border + '"' ) +#* *##else +#* *##set ( $border = ' border="0"' ) +#* *##end +## +#* *##if( $menu.width ) +#* *##set ( $width = ' width="' + $menu.width + '"' ) +#* *##else +#* *##set ( $width = "" ) +#* *##end +#* *##if( $menu.height ) +#* *##set ( $height = ' height="' + $menu.height + '"' ) +#* *##else +#* *##set ( $height = "" ) +#* *##end +#* *##if( $menu.title ) +#* *##set ( $title = ' title="' + $menu.title + '"' ) +#* *##else +#* *##set ( $title = "" ) +#* *##end +## +#* *##set ( $img = '" ) +## +#* *##if ( $position == "left" ) +
    $img $menu.name
    +#* *##else +
    $menu.name $img
    +#* *##end +#* *##else +
    $menu.name
    +#* *##end +#* *##end +#* *##if ( $menu.items && $menu.items.size() > 0 ) +
      +#* *##foreach( $item in $menu.items ) +#* *##menuItem( $item '' ) +#* *##end +
    +#* *##end +#**##end +#end +## +#macro ( copyright ) +#**##if ( $project ) +#* *##if ( ${project.organization} && ${project.organization.name} ) +#* *##set ( $period = "" ) +#* *##else +#* *##set ( $period = "." ) +#* *##end +## +#* *##set ( $currentYear = ${currentDate.year} + 1900 ) +## +#* *##if ( ${project.inceptionYear} && ( ${project.inceptionYear} != ${currentYear.toString()} ) ) + ${project.inceptionYear}–${currentYear}${period}## +#* *##else + ${currentYear}${period}## +#* *##end +## +#* *##if ( ${project.organization} ) +#* *##if ( ${project.organization.name} && ${project.organization.url} ) +#* *#${project.organization.name}. +#* *##elseif ( ${project.organization.name} ) +#* *#${project.organization.name}. +#* *##end +#* *##end +#**##end +#end +## +#macro ( publishDate $position $decorationPublishDate $version ) +#**##if ( $publishDate ) +#* *##set ( $dateValue = $dateFormat.format( $publishDate ) ) +#**##elseif ( $decoration.custom.getChild( 'publishDate' ) ) +#* *##set ( $dateValue = $decoration.custom.getChild( 'publishDate' ).getValue() ) +#**##else +#* *##set ( $dateValue = $dateFormat.format( $currentDate ) ) +#**##end +## +#**##set ( $datePosition = $decorationPublishDate.position ) +#**##set ( $versionPosition = $version.position ) +## +#**##set ( $breadcrumbs = $decoration.body.breadcrumbs ) +#**##set ( $links = $decoration.body.links ) +## +#**##if ( $datePosition.equalsIgnoreCase( "right" ) && $links && $links.size() > 0 ) +#* *##set ( $prefix = " |" ) +#**##else +#* *##set ( $prefix = "" ) +#**##end +## +#**##if ( $datePosition.equalsIgnoreCase( $position ) ) +#* *##if ( ( $datePosition.equalsIgnoreCase( "right" ) ) || ( $datePosition.equalsIgnoreCase( "bottom" ) ) ) + $prefix $i18n.getString( "site-renderer", $locale, "template.lastpublished" ): $dateValue +#* *##if ( $versionPosition.equalsIgnoreCase( $position ) ) +  | $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +#* *##end +#* *##elseif ( ( $datePosition.equalsIgnoreCase( "navigation-bottom" ) ) || ( $datePosition.equalsIgnoreCase( "navigation-top" ) ) ) +
    + $i18n.getString( "site-renderer", $locale, "template.lastpublished" ): $dateValue +#* *##if ( $versionPosition.equalsIgnoreCase( $position ) ) +  | $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +#* *##end +
    +#* *##elseif ( $datePosition.equalsIgnoreCase( "left" ) ) +
    + $i18n.getString( "site-renderer", $locale, "template.lastpublished" ): $dateValue +#* *##if ( $versionPosition.equalsIgnoreCase( $position ) ) +  | $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +#* *##end +#* *##if ( $breadcrumbs && $breadcrumbs.size() > 0 ) + | #breadcrumbs( $breadcrumbs ) +#* *##end +
    +#* *##end +#**##elseif ( $versionPosition.equalsIgnoreCase( $position ) ) +#* *##if ( ( $versionPosition.equalsIgnoreCase( "right" ) ) || ( $versionPosition.equalsIgnoreCase( "bottom" ) ) ) + $prefix $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +#* *##elseif ( ( $versionPosition.equalsIgnoreCase( "navigation-bottom" ) ) || ( $versionPosition.equalsIgnoreCase( "navigation-top" ) ) ) +
    + $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +
    +#* *##elseif ( $versionPosition.equalsIgnoreCase( "left" ) ) +
    + $i18n.getString( "site-renderer", $locale, "template.version" ): ${project.version} +#* *##if ( $breadcrumbs && $breadcrumbs.size() > 0 ) + | #breadcrumbs( $breadcrumbs ) +#* *##end +
    +#* *##end +#**##elseif ( $position.equalsIgnoreCase( "left" ) ) +#* *##if ( $breadcrumbs && $breadcrumbs.size() > 0 ) +
    +#* *##breadcrumbs( $breadcrumbs ) +
    +#* *##end +#**##end +#end +## +#macro ( poweredByLogo $poweredBy ) +#**##if( $poweredBy ) +#* *##foreach ($item in $poweredBy) +#* *##if( $item.href ) +#* *##set ( $href = $PathTool.calculateLink( $item.href, $relativePath ) ) +#* *##set ( $href = $href.replaceAll( '\\', '/' ) ) +#* *##else +#* *##set ( $href="https://maven.apache.org/" ) +#* *##end +## +#* *##if( $item.name ) +#* *##set ( $name = $item.name ) +#* *##else +#* *##set ( $name = $i18n.getString( "site-renderer", $locale, "template.builtby" ) ) +#* *##set ( $name = "${name} Maven" ) +#* *##end +## +#* *##if( $item.img ) +#* *##set ( $img = $item.img ) +#* *##else +#* *##set ( $img = "images/logos/maven-feather.png" ) +#* *##end +## +#* *##if ( !$decoration.isLink( $img ) ) +#* *##set ( $img = $PathTool.calculateLink( $img, $relativePath ) ) +#* *##set ( $img = $img.replaceAll( '\\', '/' ) ) +#* *##end +## +#* *##if( $item.alt ) +#* *##set ( $alt = ' alt="' + $item.alt + '"' ) +#* *##else +#* *##set ( $alt = ' alt="' + $name + '"' ) +#* *##end +## +#* *##if( $item.border ) +#* *##set ( $border = ' border="' + $item.border + '"' ) +#* *##else +#* *##set ( $border = "" ) +#* *##end +## +#* *##if( $item.width ) +#* *##set ( $width = ' width="' + $item.width + '"' ) +#* *##else +#* *##set ( $width = "" ) +#* *##end +#* *##if( $item.height ) +#* *##set ( $height = ' height="' + $item.height + '"' ) +#* *##else +#* *##set ( $height = "" ) +#* *##end +#* *##if( $item.title ) +#* *##set ( $title = ' title="' + $item.title + '"' ) +#* *##else +#* *##set ( $title = "" ) +#* *##end +## + + + +#* *##end +#* *##if( $poweredBy.isEmpty() ) + + $i18n.getString( + +#* *##end +#**##else + + $i18n.getString( + +#**##end +#end +## +#macro ( googleAnalytics $accountId ) +#**##if( $accountId && $accountId != "" ) + + +#**##end +#end +## +#macro( generatedBy ) +Apache Maven Doxia Site Renderer#if( $doxiaSiteRendererVersion ) $doxiaSiteRendererVersion#end## +#end +## \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site.vm b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site.vm new file mode 100644 index 000000000..6f5e76d2c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/default-site.vm @@ -0,0 +1,100 @@ + +## 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. +## +#parse( "default-site-macros.vm" ) + +## + + + + +## put meta together + +#foreach( $author in $authors ) + +#end +#if ( $documentDate ) + +#end + $title + + + + +#**##if( $decoration.body.head ) +#* *#$render.eval( $decoration.body.head ) +#**##end +#**##if( $headContent )$headContent#end +#**##googleAnalytics( $decoration.googleAnalyticsAccountId ) + + + + +
    + +
    +
    +
    +#* *#$bodyContent +
    +
    +
    +
    +
    + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/collapsed.gif b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/collapsed.gif new file mode 100644 index 0000000000000000000000000000000000000000..6e710840640c1bfd9dd76ce7fef56f1004092508 GIT binary patch literal 53 ycmZ?wbhEHbWM^P!XkdT>#h)yUTnvm1Iv_qshJlI4r7uBZ*YkPFU8d4p4Aua}2?(?R literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/expanded.gif b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/expanded.gif new file mode 100644 index 0000000000000000000000000000000000000000..0fef3d89e0df1f8bc49a0cd827f2607c7d7fd2f0 GIT binary patch literal 52 xcmZ?wbhEHbWM^P!XkdT>#h)yUTnvm1Iv_qshJlH@g}+fUi&t{amUB!D)&R0C2fzRT literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-black.png b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-black.png new file mode 100644 index 0000000000000000000000000000000000000000..919fd0f66a7f713920dd7422035db1c9d484351d GIT binary patch literal 2294 zcmVKOG`!VuDc=fnx$+R6#>c^>b&wcOS?|$!`a}U6ptjU_J zlBA}l*3{J0)YMd0R~Hr*dU$xO^ie1jhYlTLS+=C4#MRYRCX#twGUSD6Il$6AA+=UAlkY(ZF;m4037Yc>v&!1mPsNXdliHV74&z>zUEv=}iC@U)i zfc^XTJ3BiAKvYyzczAd~K){|od(ip)`}f`5-HnZnv$L~Hzqq=(y7Kb!>gsAwPfu@e z@3gcu0LabFC4?{xBNPh18Fpy3+Tr2hfq{Yc_V$w}PjVdhGtMTH$zU){PfznaPmK)? z4KH52=;-KZX=#a#jlFZ{PF7YH!!Q{c8Taqs=Xt)UsK{tE{@>vc{2Hgh!NL0adH}e0 z@19Df^78Tm0ES@zz{SO7Zf@=upJ1_AP_bIAgpih&mWqmsojZ4GG#a&9{f)&Au~_Wm z<0F^L4;(mPHk)-io!M*-3JMa7#VIK%EBy%}_$g6IPEM9cBvPp~K0f}{t5+6_rMbEJ z(xpqcZ{G$0j^p<2+vnuu^bN3MdU`rLJ3Br;9ss7MrVbuFxUjHLQBhGX6WriQ5|M*_w z@5bUDdV71dTCG;AO-@dx@4a~OA{y)K>k+2N$jAo|9?w z?b_+nr`2k;!{M;o?Qh<^`R=>#RtFA0KR<`Vfh)Li;|5+X!otGn&U<@%H*VaBDU;Gf zr_<5=()7Iqfmk>yLj`}084`48Zf?d|M~)mpOHfeI{QNv2WMN?;Dk=&9GBY#LVzb%$ z`};Aq6GAK&OK4~)&U*g*IT{xh7M8K~%9SgtQ-;OG#ZeC5ym=F=X|vf(9h#b&K7RZN z05+S=X0xGjU|@g-%ePwl!GC`7t=5VDruDp`t9rXwq=tAb*88KQqo~N`a#V_oixKzA z%F4dJzL1cRy1F{CSUfW`qfjWeZ{Hpm7>H$yNF>V6&c<>vGBOgU_w@7}J9g~o(WA6z z#sgc0B0VlH4i&T6{Pyiz)FUDys6$s*7rnXCi!3z)!0DGJ5eITHyM2Q|E@qtti{QRD z*nbiZg+h^&lY>QINl6I+oH}*N-Q67kYHMqqoSd*@fE67^695Pa36aTU0HD+95)%{g zFw)c0Gcqy&K&4WxG906$qk6p_b=txpgmiazqaGF(M)NU+!{3cPsc^{*a`Ja$nXfZ@ zhsL%N4whw0OG`2M6&4oG&CQ8KBHBPHC@3f>C|I^a>__(qFp!^RU zV`F0uhl6EVxm><`_ijATmoHz|)ztxjL?XdmSuB<(Po5A$mM!w}C3kdS~ef}W>dub-Hhz&fI`vJ#oXvTST@?6qsxN=r)tz|+%n^XARiL+I)0 z!HGL|?4Z?OC@z>ppO+fmk zEDIk1FgrV2R8&O&@;qNwR)+h@$;nZx)dqvXVzG2}b>-#d_4oHa!G&Dp59OYMg zd;9A2I}{29&+|ObzkB!Y^XJcKjE;^*({SomlT)I^E^_90Q{xPG;bvU;38ml zcng&pTZhKxAmAX-{xuvUBO`bZu-omWrKK8;X6fkl>(@`5I6;GyySuwkDCBv*tE;QE zwH1kg)0Ijk1~{Qms8A@Vadob6a=9D}VUx-9>C-1l1S|^dcDq`w#&Z*k#hB*+K%>#n z=0$)zo8T)X1Ujc}V+Omw8!O@%0GKp7%(fp1ER{;7QYogYiHQlT)w*&q5{X2iP;Ak literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-white.png b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/build-by-maven-white.png new file mode 100644 index 0000000000000000000000000000000000000000..7d44c9c2e5742bdf8649ad282f83208f1da9b982 GIT binary patch literal 2260 zcmV;_2rKuAP)4hTLUyOQ{PVbVY5&Y3g!&hN~bnR7}ZgkXUt ziC%zU0gf+&kEv>t|d$x|zXw1mS0D%1b{8z7DF%0wW-8(XBFc`A3vVI|O z^!N97baWg(eE86zLn4uA_wL=Zb@+UKU|=8sJb3V6XlSUctSl!dhm4xd=KJ^W|8h2q zR4NS%3yX+|NKQ`f?d=7Cf`Wo)&z=E5TU%REQIXYZefjbwRvsQ6zIyfQojZ3l8V#{v zv)R(q)39Vr2GBPsa+apV2%%fIZY3ln0Kl+1Y8c*(xe3X6sWFH9kH*UDDLl)ZN`}u~;f9D%P!A2LK5P2`MQl z(b3TuDUC++_U+qm01k;n!Z1u+TwGjS+}X2d^Yil+3Pn;B-~q z{Qdm_z{kf&EEb1^gw)j3R904!x}#RBj~+c578Vv16olc}xpQZGd;7k9`>@WHD_2M| z{%VB2fNVCK&1U^_rTW_bx`C@MK&%ZR^ybZ*=;&yb zN);0mV>X+~OA`|lRVtNAr7A8i#zL)DyJycHxm+$5izO0?QmM?$%p@6le0*H3R;yI1 z=;-LCrlu1oPI!8HIypHhmCA~Wig|;>WHON!GbSbmcN`jxhJ=GssnlpRR;zVzaF8J4 z>+3sJhW@0w{LH6-`(Afr<9kMWBXoSUM7Dox&JGJtojOI96z3EG z*uH)HWN?qO7x!`hzQnzLg5JL3Ui^ps%X$n4`+YK2S-yNZo>gC8kJmXUC#D?-i_a7IlwdR(Kkw#T>s)<( zJ!ZVTycREBO!{t;H9|r{F#q)FQ_`LjAsBnPnnKk2PZ;V3*7{M#@%jyBNObh|^_fg2 zd|f0I3eTTEPf=83VhUbHWgRft|{%MRRMp6H>seM7wV6&k5Vn7H0DDSDT_wn(;aaUDU zWi%QoiptK;CgqIWB$bwy78Mm?w@oI~&6_tPBO~$kExCLno}10)mX;RGM?^%-PjqOt zTFi(#=@4C7NJmxEVK7l6G0yhEp_Lq9)1fj}S-2%Mdrv$L~tStVt%xVSheDG9e5EX$6J zj8GIMm&=bIKaK;TqoYG05D0}r0!Kqb1E0?q2n1`_uAR{_f0E{OgnR$~y~Sd|+0n_# z2@6L?MsUQ^H0|QzLJoDKqobtlneyk|8`Sp{cp}PUC5RRQ^8?;2;Iss$eWk%*n3$Nr z(73v~e)3}s219#$yTM=(2n6o#?!LahxUO>?H!v`O%bZ*;$Ideh!!Qg0h{fVXix$lf i91DLtEx@rr0RIK2cl{g~?Z1Nn0000}s literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/maven-feather.png b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/images/logos/maven-feather.png new file mode 100644 index 0000000000000000000000000000000000000000..b5ada836e9eb4af4db810f648b013933e72c8fbe GIT binary patch literal 3330 zcmX9>c{JN;_x~o5Ac>t)`_^PEV{L6MNl>(?QcG&7ly=N-Xep}HlEki6%d`xGQff?J zZ3V5?nxMK^TW!%rlc2Oi#TE&YeBaFbd(OGfJqdI` zc>}=J0{}qD0)QP*?7suRWeWiKhXeo)6#$?b`+NA18vvk_kGT^3lRrj~)ZiX~E=7&X z2SKm_0zsnO+$cbVdd$U-?NJjv4pVQ1Nhjly1q-WLl67`_;z%v-QHPc;g_!S~IRE^{ z!-r;4Azogl1_mw!0>pbvoPqVZ9U2s5dwy6sHa1p4L7^@xJ3CvqEtc6=V;Sjo`SKw` zH=oaUc5x93g$)f2RLqLwrQCI9Ez?$q{#(_7txem8O7-r(E=u3NrnVzb>g3;N!E`D4 z$F(MEarBhUUxI^!j~_>3u~Bhx7JsSR*w|dSa6vbc*_R&srRM|ftV?XHdFb}1C$WrQ zvCqw{t=r+KeZT{28=Et|SGiR|Ew_)PCPc7HL$FRx^tIjT!gS^&HZAG+)pJ^j_L!yB z-&JbQI5tJZ0TS}9l}GV-#=yY9@UZdW!+Wo8V)3OP+M~kh8Cox&UgiEXkb|OHrtnt7 z^5^7qoPgd(mzSp^UljFw^Ea1#($jleS~zn<*Qt%~?;g8p7T$+e1_e6_0RivD9i_fn zntBj|S0D{TF>ZC0BjrC=O}^<#pa0LS&uvarfWzp2`pUd__f_%7YV~7dt=r6SgMYpk zjT&tozdBVDfMU+}3PBKu{I@a0eE%y;<26%LfpraXnsz78oRL+ASlucsJ9Ov}^-cnR z?X0S*D(PH#SsA1;IVGjHr-u@pc=<9LQ|*-QU~8*d0k5yGUszbEsHmW5uYUjj;c@h| zc=i>Ql~f4Q{2jFogTeH_k#4q)N#10=x?L3lT5fn+n;f?)a5}#)D(b9?5F`jW*8R2B zY10|kzu50Yt-pEkr?pP=J)v#j+39IETXnv??EKOqdr`^I$PR$!&#+i*wr^07q=V|W zRr`cRLkwol7wvCgY>XVWV#HBVP$e>vs8#}bhe8j(d*@G*O1g5TCFF^jnVIZQvS`z% z5v0FEpQe3XqLbN{Z+4@!!}?n1jYn$VqUAWElr$a=d)NRcr?dxiBP0c$a4eq)C6kW} zg`-#3YZthl;XEcu_;g!xn!}4v15@n5*WxOpB14=8A8Dk>`K z>FLRD7bsziv>lNxci1YB3`T!HV#jF&kvayv7^9-Sg&l|eQ^qB(FU%g~JDx-!K6@(Waovi+Tc$s`@s@Sv* z9p0C*!~5#c{h1>d>@N5DL);Ea=d|PU4}@o zGdG0Ng%R<9V_jn-yfB3nD7kxXb8!sMIXlJ1WeD*5?60hT&XSa)+yVTVl9iP_o8v^w8_0650v?-3$V0uILqsvdAu+2y6|YCewgNhga^h4Y-lNq0Cah}ivo zpoq6EpmWSceZAoF%B5UfVPU3op{AfPhFM{FSFJMU!)c~SDTMch@trf6$~-E;5xn-d z<8`e~UPj0w%vDYVje(iQii)`c=wzHbR6^djAF^dnW5A}!CD-JMWyVHEkW;BwukLPq z9nsR%B=!TuB0vQ|DPO#J@zkle(n^?>&z)~)XSMt|Ks2+uT9af6QEqK-hanLX5&&xP z-l-<%m`WTuBR<~hh#iYkQxoQNXtTFvX)i0JF_1Iu5Wn+7^XJlfPFX+T%IM9_7+4B=%5Y=a!X6S`QV)~knSitusE`|vEgD?+D*SdgtN-v z@2!tnPsQ$W9OoldXg5!7EGfyuKEmbk%8!pz518D&%P>a8*ji>n+N5Y15QI!N3aw76 zk?~TlC_r^z21V(@jrIB2O=fW{*e;OxLwTOl%b7{65NYoUzv46uU?y1WK`h1$gXk#s zGM!NC1T6)2&vea(*Gjoe-Y0OseT68UKVi7GtWs>+{mTm3?9wmCl9JqVL7fcIg7PHy zS|uV8fd^!W2I;)j*_@ml#-BrjgIWH)bTI&Jf1fXAax!YjYcdmoW44Np%MhjRZR?D*fO!{1UqRj~p#EAohT=T-17$$k6AmQb( zr9h0V!aUsY=NL_BPmf|~=n=+2*+gqRK=3w1+z;yxltfUx%}G^AqM7qBoD>Zu#))>h z(O-H}7=Go_Xv&X~RNksk#{u}JDqbNyJIauD&lJ!>cpV`%&T(-`&1Vx}= z8{BIG$r-+Li5}_#{j}s%FlGk$jM1|WKp=Pv|*T=m!~I+rUjJ3F@7W!gumQD8RFwVZryr0 zG6IWssk0)%eJuVTRDtKPo&xDaOWF|RzCnozye=JYW-)oDFHKrbK}AL7sWkcH57B~D zWIZ`=QNK#g)SEJB!`69JGO3P=r08pDX))Bb6t@_;R!2TlYhv>Ek*cIBeDucB zNbDTV5C(L01Ze7}3Kc7OC~(zLdAV~G`9N+1xB3ie(wD=k6U z@g3gU065J9XPq{lyp>keB&(ixxdnV8$%i$asL6b0O)JUdYtCpuubGB*DbEFHXlQtp zXgMTG%@{+j0dI{Adnj6-$)BcQylA>}r~l(e_1pE-*`Eac5PAGF#EWMIO6;2ECZAeo ziPF85kd7Ft6f{I>ZQIUbf5YND4#d%gJpKl~IaM@Xl!bUvZj*0lQRvUOOhugnVG zMF7OiLdS5a+otCLNQI8V^8vu3ka8NP_S>32`v3S)2n{Pe(fRVLdLST=H+AiBqCTY3 zZWI=>Zsgp=`Z%jG=8)QMYZO=@1A#!)z2kiwpnq3DhkpUGZV&>CeaB0vA>Y6+Mrd+| zrA52d@P7Qe=6m=0Lz-`5yrGM(x*9Y0sP7_5T2*v`@~JgS7L3#>yY-7x_MJ+9`9JqyEa*$Q0 ziiL%hken<6A7+&3D;!0f@qP3TvIRVoufv)c8?&aw&B~1Y(02aUpDjK7B)cSkx8QDV zQMj_M+x+$UXOfa)nmweB@KP^Xm2R7$9(p;LCnufvW}*eG4R>Eak)Ei}%-KE8gsec^ zj=HuX z(qyBjd`DTC3ZeF2!np?{CKA-DtE=Op^zuqOJMFU}UTntQB1KKp81%{!bT~6heKA2v zt?`kF-Zi+k^YcNCz>V!+^RbV}r|Gp2j0+=crL`N5t}4tX=Ugo&7+C6ua?F4oX!wQ+)83@^vkY zDLFc>n(A(&_r09T&@t7l6XQ+b#6#=gA#14-D;h1Uq<(+=C8$D8`D^qmZ z9NOcdL`OIEho{GDl585|eQ0-*j0e6Rr=PNtyozBAqJr literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/resources.txt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/resources.txt new file mode 100644 index 000000000..0384226cc --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/org/apache/maven/doxia/siterenderer/resources/resources.txt @@ -0,0 +1,24 @@ +# 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. + +images/expanded.gif +images/collapsed.gif +images/logos/maven-feather.png +images/logos/build-by-maven-white.png +images/logos/build-by-maven-black.png +css/maven-base.css +css/print.css diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer.properties new file mode 100644 index 000000000..3dc6ce342 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Last Published +template.version=Version +template.builtby=Built by diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_de.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_de.properties new file mode 100644 index 000000000..7620924ca --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_de.properties @@ -0,0 +1,19 @@ +# 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. + +template.lastpublished=Zuletzt ver\u00F6ffentlicht +template.builtby=Erstellt von diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_en.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_en.properties new file mode 100644 index 000000000..0c479bc64 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_en.properties @@ -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. + +# NOTE: +# This bundle is intentionally empty because English strings are provided by the base bundle via the parent chain. It +# must be provided nevertheless such that a request for locale "en" will not errorneously pick up the bundle for the +# JVM's default locale (which need not be "en"). See the method javadoc about +# ResourceBundle.getBundle(String, Locale, ClassLoader) +# for a full description of the lookup strategy. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_es.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_es.properties new file mode 100644 index 000000000..00c73d808 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_es.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Publicado el +template.version=Versión +template.builtby=Generado por diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_fr.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_fr.properties new file mode 100644 index 000000000..5281316a2 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_fr.properties @@ -0,0 +1,19 @@ +# 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. + +template.lastpublished=Dernière publication +template.builtby=Produit par diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_it.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_it.properties new file mode 100644 index 000000000..7eb73cdc9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_it.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Ultima Pubblicazione +template.version=Versione +template.builtby=Costruito da diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_ja.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_ja.properties new file mode 100644 index 000000000..3414d000a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_ja.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=\u6700\u7d42\u66f4\u65b0 +template.version=\u30d0\u30fc\u30b8\u30e7\u30f3 +template.builtby=Built by diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_nl.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_nl.properties new file mode 100644 index 000000000..d77251cea --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_nl.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Meest recente publicatie +template.version=Versie +template.builtby=Gemaakt door diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pl.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pl.properties new file mode 100644 index 000000000..d7f2e6a06 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pl.properties @@ -0,0 +1,19 @@ +# 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. + +template.lastpublished=Ostatnio opublikowano +template.builtby=Skompilowane przez diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pt_BR.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pt_BR.properties new file mode 100644 index 000000000..6dd269b26 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_pt_BR.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Última atualização +template.version=Versão +template.builtby=Construído por diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_sv.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_sv.properties new file mode 100644 index 000000000..1a21ec17c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_sv.properties @@ -0,0 +1,20 @@ +# 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. + +template.lastpublished=Senast publicerat +template.version=Version +template.builtby=Byggt av diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_zh_CN.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_zh_CN.properties new file mode 100644 index 000000000..25aedebba --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/main/resources/site-renderer_zh_CN.properties @@ -0,0 +1,20 @@ +# 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. + +template.builtby = \u6784\u5EFA\u4F9D\u9760 +template.lastpublished = \u6700\u8FD1\u66F4\u65B0 +template.version = \u7248\u672C diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/doxia-site-renderer.odg b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/doxia-site-renderer.odg new file mode 100644 index 0000000000000000000000000000000000000000..2a22fbfcf911f361d0b2dfdc8061a37bdc3a82fe GIT binary patch literal 13307 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 literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/index.apt.vm b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/index.apt.vm new file mode 100644 index 000000000..c8eb0ccf9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/apt/index.apt.vm @@ -0,0 +1,193 @@ + ----- + Introduction + ----- + Hervé Boutemy + Dennis Lundberg + ------ + 2015-12-20 + ------ + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Doxia Sitetools - Site Renderer + + The Site Renderer handles the rendering of sites, assembling a common site decoration template (also called ) + with a collection of documents. + + Documents can be dynamically generated with {{{/doxia/doxia/doxia-sink-api/}Doxia Sink API}}, like Maven reports, + or simply read from static files written in {{{/doxia/references/index.html}markup supported by Doxia Parsers}}, + eventually processed by {{{http://velocity.apache.org/engine/1.7/}Velocity}} + if their file names end in <<<.vm>>>. + +[doxia-site-renderer.png] + +* Doxia Site Skins + + A default site decoration template is included (see <<>>), but other decoration templates can be used at will, + either as a standalone template or packaged in a {{{../doxia-skin-model/}<> artifact}}. + + Maven team provides {{{/skins/}a collection of skins}} for projects use. + + Some documentation is available on {{{/plugins/maven-site-plugin/examples/creatingskins.html}how to create a new skin}}. + +* Velocity processing + + Site decoration and documents with file names ending in <<<.vm>>> are processed by Velocity. + + The Velocity context contains some variables related to rendering context that you can use: + +*---------------------------------+----------------------+-------------------------------+ +|| Variable || Type || Description || +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The file name of the (HTML) document being rendered, relative to the document being rendered. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | {{{../doxia-decoration-model/apidocs/org/apache/maven/doxia/site/decoration/DecorationModel.html}<<>>}} | This is a model that represents the data in your {{{../doxia-decoration-model/decoration.html}<<>>}}. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | <>: use <<>> or <<>>. The date when the site is rendered. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The file name of the (HTML) document being rendered, relative to the site root. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | <>: use <<>>, <<>>, or <<>>. An instance of the date format as defined in <<>> (default: An instance of the default date format for the locale of the document being rendered. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | <>: same as <<>>. The date when the site is rendered, in the format "yyyyMMdd". | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The version of the Doxia Site Renderer in use. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The locale for the document being rendered. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | An optional hardcoded publish date that has been set programmatically. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The path to the site root from the document being rendered. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>>> | The list of locales that the site will contain. | +*---------------------------------+----------------------+-------------------------------+ + + There are also some tools for general use: + +#set( $plexus = "http://codehaus-plexus.github.io/plexus-utils/apidocs/org/codehaus/plexus" ) +*---------------------------------+------------------------------------------------------+-------------------------------+ +|| Variable || Type || Description || +*---------------------------------+------------------------------------------------------+-------------------------------+ +| <<>> | {{{$plexus/util/FileUtils.html}<<>>}} | <> | +*---------------------------------+------------------------------------------------------+-------------------------------+ +| <<>> | {{{$plexus/i18n/I18N.html}<<>>}} | <>: use <<>>. | +*---------------------------------+------------------------------------------------------+-------------------------------+ +| <<>> | {{{$plexus/util/PathTool.html}<<>>}} | | +*---------------------------------+------------------------------------------------------+-------------------------------+ +| <<>> | {{{$plexus/util/StringUtils.html}<<>>}} | | +*---------------------------------+------------------------------------------------------+-------------------------------+ +| <<>> | {{{http://git.eclipse.org/c/sisu/org.eclipse.sisu.plexus.git/tree/org.eclipse.sisu.plexus/src/org/codehaus/plexus/PlexusContainer.java}<<>>}} | | +*---------------------------------+------------------------------------------------------+-------------------------------+ + + Additionally, there are {{{http://velocity.apache.org/tools/releases/2.0/generic.html} Velocity Generic Tools}} populated + with the site locale, the decoration model's date format, and site renderer's resource bundle: + +#set( $generic = "http://velocity.apache.org/tools/releases/2.0/javadoc/org/apache/velocity/tools/generic" ) +*------------------+----------------------------------------------------------+-------------------------------+ +|| Variable || Type || Description || +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/AlternatorTool.html}AlternatorTool}} | For creating alternators to easily alternate over a set of values. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/ClassTool.html}ClassTool}} | For simplifying reflective lookup of information about classes and their fields, methods and constructors. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/ContextTool.html}ContextTool}} | For convenient access to context data and metadata. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/ConversionTool.html}ConversionTool}} | For converting String values to richer object Types. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/ComparisonDateTool.html}ComparisonDateTool}} | For manipulating, formatting, and comparing dates. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/DisplayTool.html}DisplayTool}} | For controlling display of references (e.g., truncating values, "pretty printing" lists, and displaying alternates when a reference is null). +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/EscapeTool.html}EscapeTool}} | For common escaping needs in Velocity templates (e.g. escaping html, xml, javascript etc.). +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/FieldTool.html}FieldTool}} | For (easy) access to static fields in a class, such as string constants. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/LinkTool.html}LinkTool}} | For creating and manipulating URIs and URLs. The API for this tool is designed to closely resemble that of the VelocityView tool of the same name. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/LoopTool.html}LoopTool}} | A convenience tool to use with \#foreach loops. It wraps a list with a custom iterator to provide greater control, allowing loops to end early, skip ahead and more. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/MathTool.html}MathTool}} | For performing math functions. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/NumberTool.html}NumberTool}} | For formatting and converting numbers. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/RenderTool.html}RenderTool}} | To evaluate and render arbitrary strings of VTL, including recursive rendering. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/ResourceTool.html}ResourceTool}} | For simplified access to resource bundles for internationalization or other dynamic content needs. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/SortTool.html}SortTool}} | Used to sort collections (or arrays, iterators, etc) on any arbitary set of properties exposed by the objects contained within the collection. +*------------------+----------------------------------------------------------+-------------------------------+ +| <<>> | {{{$generic/XmlTool.html}XmlTool}} | For reading/navigating XML files. This uses dom4j under the covers and provides complete XPath support. +*------------------+----------------------------------------------------------+-------------------------------+ + + If you intend to use custom Velocity tools, add them to the Maven Site Plugin's dependency list and make sure + that they have a bundled configuration file in <<>>. + + See <<<{{{./xref/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.html\#L488}DefaultSiteRenderer.createToolManagedVelocityContext(...)}}>>> + source for more details and the {{{http://velocity.apache.org/tools/devel/summary.html}tools usage summary}}. + +** Maven Site Plugin + + When <<>> is used by <<>>, the following template properties are defined: + +*---------------------------------+----------------------+-------------------------------+ +|| Variable || Type || Description || +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | {{{/ref/current/maven-core/apidocs/org/apache/maven/project/MavenProject.html}<<>>}} | The current project. | +*---------------------------------+----------------------+-------------------------------+ +| | <<>> | Properties defined in POM are directly available. | +*---------------------------------+----------------------+-------------------------------+ + + See <<<{{{/plugins/maven-site-plugin/apidocs/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.html\#createSiteRenderingContext(java.util.Locale)}AbstractSiteRenderingMojo.createSiteRenderingContext(...)}}>>> + source for more details. + +** Site Template + + When Velocity is processing the site template (be it from skin or local template), there are a few complementary variables + available that give access to information taken from document being merged into the template: + +*---------------------------------+----------------------+-------------------------------+ +|| Variable || Type || Description || +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>>> | A list of authors from the source document. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | HTML body content of the Doxia generated output. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | <>: use <<>>. The date specified in the source document, in the format "yyyyMMdd". | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | <>: use <<>>. The date specified in the source document. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The date specified in the source document: semantics has to be chosen by document writer (document creation date, or document last modification date, or ...), and format is not enforced. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | HTML head content of the Doxia generated output. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The title of the document, excluding the project or site name. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | <<>> | The title of the document, including the project or site name. | +*---------------------------------+----------------------+-------------------------------+ +| <<>> | {{{./apidocs/org/apache/maven/doxia/siterenderer/RenderingContext.html}<<>>}} | (since 1.8) The document rendering context. | +*---------------------------------+----------------------+-------------------------------+ + + See <<<{{{./xref/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.html\#L616}DefaultSiteRenderer.createSiteTemplateVelocityContext(...)}}>>> + source for more details. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/resources/doxia-site-renderer.png b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/resources/doxia-site-renderer.png new file mode 100644 index 0000000000000000000000000000000000000000..5c1eebf324d38ff97a71d6169b7b0a1f9524d531 GIT binary patch literal 40267 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 literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/site.xml new file mode 100644 index 000000000..ebe51fe63 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/site/site.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AbstractVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AbstractVerifier.java new file mode 100644 index 000000000..b52fd9924 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AbstractVerifier.java @@ -0,0 +1,70 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import java.io.File; + +import org.codehaus.plexus.PlexusTestCase; + +/** + * Abstract base class for verifiers. + * + * @author ltheussl + */ +public abstract class AbstractVerifier + extends PlexusTestCase +{ + /** + * Get a HtmlPage from a file. + * + * @param htmlFile the file to parse. + * + * @return a HtmlPage. + * + * @throws Exception if something goes wrong. + */ + protected HtmlPage htmlPage( String htmlFile ) + throws Exception + { + File file = getTestFile( htmlFile ); + assertNotNull( file ); + assertTrue( file.exists() ); + + // HtmlUnit + try ( WebClient webClient = new WebClient() ) { + webClient.getOptions().setCssEnabled( false ); + + return (HtmlPage) webClient.getPage( file.toURI().toURL() ); + } + } + + /** + * Verify a HtmlPage. + * + * @param file the file to verify. + * + * @throws java.lang.Exception if something goes wrong + */ + public abstract void verify( String file ) + throws Exception; +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AptVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AptVerifier.java new file mode 100644 index 000000000..b8369ae2f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AptVerifier.java @@ -0,0 +1,180 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlBold; +import com.gargoylesoftware.htmlunit.html.HtmlCode; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlHeading3; +import com.gargoylesoftware.htmlunit.html.HtmlItalic; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlSection; + +import java.util.Iterator; + + +/** + * Verifies apt transformations. + * + * @author ltheussl + */ +public class AptVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "Links", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Links", a.getAttribute( "name" ) ); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + // Expected log: [APT Parser] Ambiguous link: 'cdc.html'. If this is a local link, prepend "./"! + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Anchor", a.getAttribute( "name" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "cdc.html", a.getAttribute( "name" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Anchor", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Anchor", a.getAttribute( "href" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Anchor_with_space", a.getAttribute( "name" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Anchor_with_space", a.getAttribute( "href" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "http://maven.apache.org/", a.getAttribute( "href" ) ); + assertEquals( "externalLink", a.getAttribute( "class" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "http://maven.apache.org/", a.getAttribute( "href" ) ); + assertEquals( "externalLink", a.getAttribute( "class" ) ); + + // Expected log: [APT Parser] Ambiguous link: 'cdc.html'. If this is a local link, prepend "./"! + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "./cdc.html", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#cdc.html", a.getAttribute( "href" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "/index.html", a.getAttribute( "href" ) ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + // Note: htmlunit strips the white space, actual result is ok + assertEquals( "Section formatting: italic bold mono", h2.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Section_formatting:_italic_bold_mono", a.getAttribute( "name" ) ); + + HtmlItalic italic = (HtmlItalic) elementIterator.next(); + assertEquals( "i", italic.getTagName() ); + assertEquals( "italic", italic.asText().trim() ); + + HtmlBold bold = (HtmlBold) elementIterator.next(); + assertEquals( "b", bold.getTagName() ); + assertEquals( "bold", bold.asText().trim() ); + + HtmlCode code = (HtmlCode) elementIterator.next(); + assertEquals( "code", code.getTagName() ); + assertEquals( "mono", code.asText().trim() ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading3 h3 = (HtmlHeading3) elementIterator.next(); + assertNotNull( h3 ); + // Note: htmlunit strips the white space, actual result is ok + assertEquals( "SubSection formatting: italic bold mono", h3.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "SubSection_formatting:_italic_bold_mono", a.getAttribute( "name" ) ); + + italic = (HtmlItalic) elementIterator.next(); + assertEquals( "i", italic.getTagName() ); + assertEquals( "italic", italic.asText().trim() ); + + bold = (HtmlBold) elementIterator.next(); + assertEquals( "b", bold.getTagName() ); + assertEquals( "bold", bold.asText().trim() ); + + code = (HtmlCode) elementIterator.next(); + assertEquals( "code", code.getTagName() ); + assertEquals( "mono", code.asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + italic = (HtmlItalic) elementIterator.next(); + assertEquals( "i", italic.getTagName() ); + assertEquals( "italic", italic.asText().trim() ); + + bold = (HtmlBold) elementIterator.next(); + assertEquals( "b", bold.getTagName() ); + assertEquals( "bold", bold.asText().trim() ); + + code = (HtmlCode) elementIterator.next(); + assertEquals( "code", code.getTagName() ); + assertEquals( "mono", code.asText().trim() ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "No Default Anchor in Section Title with Explicit Anchor", h2.asText().trim() ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "No_Default_Anchor_in_Section_Title_with_Explicit_Anchor", a.getAttribute( "name" ) ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AttributesVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AttributesVerifier.java new file mode 100644 index 000000000..0865d9227 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/AttributesVerifier.java @@ -0,0 +1,246 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlBold; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlHeading3; +import com.gargoylesoftware.htmlunit.html.HtmlImage; +import com.gargoylesoftware.htmlunit.html.HtmlItalic; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlPreformattedText; +import com.gargoylesoftware.htmlunit.html.HtmlS; +import com.gargoylesoftware.htmlunit.html.HtmlSection; +import com.gargoylesoftware.htmlunit.html.HtmlSubscript; +import com.gargoylesoftware.htmlunit.html.HtmlSuperscript; +import com.gargoylesoftware.htmlunit.html.HtmlTable; +import com.gargoylesoftware.htmlunit.html.HtmlTableDataCell; +import com.gargoylesoftware.htmlunit.html.HtmlTableHeaderCell; +import com.gargoylesoftware.htmlunit.html.HtmlTableRow; +import com.gargoylesoftware.htmlunit.html.HtmlUnderlined; + +import java.util.Iterator; + + +/** + * + * + * @author ltheussl + */ +public class AttributesVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "section", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "section", a.getAttribute( "name" ) ); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + assertEquals( "ID", p.getAttribute( "id" ) ); + assertEquals( "CLASS", p.getAttribute( "class" ) ); + assertEquals( "TITLE", p.getAttribute( "title" ) ); + assertEquals( "STYLE", p.getAttribute( "style" ) ); + assertEquals( "LANG", p.getAttribute( "lang" ) ); + + HtmlImage img = (HtmlImage) elementIterator.next(); + assertNotNull( img ); + + assertEquals( "project.png", img.getAttribute( "src" ) ); + assertEquals( "150", img.getAttribute( "width" ) ); + assertEquals( "93", img.getAttribute( "height" ) ); + assertEquals( "border: 1px solid silver", img.getAttribute( "style" ) ); + assertEquals( "Project", img.getAttribute( "alt" ) ); + + // test object identity to distinguish the case ATTRIBUTE_VALUE_EMPTY + assertSame( img.getAttribute( "dummy" ), HtmlElement.ATTRIBUTE_NOT_DEFINED ); + + HtmlTable table = (HtmlTable) elementIterator.next(); + assertEquals( "1", table.getAttribute( "border" ) ); + assertEquals( "none", table.getAttribute( "class" ) ); + + element = elementIterator.next(); + // this is a htmlunit bug + assertEquals( "tbody", element.getTagName() ); + + HtmlTableRow tr = (HtmlTableRow) elementIterator.next(); + HtmlTableHeaderCell th = (HtmlTableHeaderCell) elementIterator.next(); + + th = (HtmlTableHeaderCell) elementIterator.next(); + assertEquals( "center", th.getAttribute( "align" ) ); + assertEquals( "2", th.getAttribute( "colspan" ) ); + assertEquals( "50%", th.getAttribute( "width" ) ); + + tr = (HtmlTableRow) elementIterator.next(); + + th = (HtmlTableHeaderCell) elementIterator.next(); + assertEquals( "2", th.getAttribute( "rowspan" ) ); + assertEquals( "middle", th.getAttribute( "valign" ) ); + + HtmlTableDataCell td = (HtmlTableDataCell) elementIterator.next(); + td = (HtmlTableDataCell) elementIterator.next(); + tr = (HtmlTableRow) elementIterator.next(); + td = (HtmlTableDataCell) elementIterator.next(); + td = (HtmlTableDataCell) elementIterator.next(); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + HtmlUnderlined u = (HtmlUnderlined) elementIterator.next(); + assertEquals( "u", u.getTagName() ); + HtmlS s = (HtmlS) elementIterator.next(); + assertEquals( "s", s.getTagName() ); + HtmlSubscript sub = (HtmlSubscript) elementIterator.next(); + assertEquals( "sub", sub.getTagName() ); + HtmlSuperscript sup = (HtmlSuperscript) elementIterator.next(); + assertEquals( "sup", sup.getTagName() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + HtmlBold b = (HtmlBold) elementIterator.next(); + assertEquals( "b", b.getTagName() ); + HtmlItalic i = (HtmlItalic) elementIterator.next(); + assertEquals( "i", i.getTagName() ); + i = (HtmlItalic) elementIterator.next(); + assertEquals( "i", i.getTagName() ); + b = (HtmlBold) elementIterator.next(); + assertEquals( "b", b.getTagName() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "color: red; margin-left: 20px", p.getAttribute( "style" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Anchor", a.getAttribute( "name" ) ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Anchor", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Anchor", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "http://maven.apache.org/", a.getAttribute( "href" ) ); + assertEquals( "externalLink", a.getAttribute( "class" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "./cdc.html", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "cdc.html", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "cdc.pdf", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "./cdc.txt", a.getAttribute( "href" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "/index.html", a.getAttribute( "href" ) ); + + HtmlDivision div = (HtmlDivision) elementIterator.next(); + assertEquals( "source", div.getAttribute( "class" ) ); + HtmlPreformattedText pre = (HtmlPreformattedText) elementIterator.next(); + assertEquals( "pretty", pre.getAttribute( "class" ) ); + + div = (HtmlDivision) elementIterator.next(); + assertEquals( "source", div.getAttribute( "class" ) ); + assertEquals( "", div.getAttribute( "id" ) ); + pre = (HtmlPreformattedText) elementIterator.next(); + assertEquals( "pretty", pre.getAttribute( "id" ) ); + + section = (HtmlSection) elementIterator.next(); + h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Section without id", h2.asText().trim() ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Section_without_id", a.getAttribute( "name" ) ); + + section = (HtmlSection) elementIterator.next(); + HtmlHeading3 h3 = (HtmlHeading3) elementIterator.next(); + assertEquals( "Subsection without id", h3.asText().trim() ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Subsection_without_id", a.getAttribute( "name" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "section-id", a.getAttribute( "name" ) ); + section = (HtmlSection) elementIterator.next(); + h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Section with id", h2.asText().trim() ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Section_with_id", a.getAttribute( "name" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "subsection-id", a.getAttribute( "name" ) ); + section = (HtmlSection) elementIterator.next(); + h3 = (HtmlHeading3) elementIterator.next(); + assertEquals( "Subsection with id", h3.asText().trim() ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Subsection_with_id", a.getAttribute( "name" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "foo", a.getAttribute( "name" ) ); + section = (HtmlSection) elementIterator.next(); + assertEquals( "bar", section.getAttribute( "class" ) ); + assertEquals( "foo", section.getAttribute( "id" ) ); + h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Section name", h2.asText().trim() ); + assertEquals( "", h2.getAttribute( "class" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Section_name", a.getAttribute( "name" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "subfoo", a.getAttribute( "name" ) ); + section = (HtmlSection) elementIterator.next(); + assertEquals( "subbar", section.getAttribute( "class" ) ); + assertEquals( "subfoo", section.getAttribute( "id" ) ); + h3 = (HtmlHeading3) elementIterator.next(); + assertEquals( "Subsection name", h3.asText().trim() ); + assertEquals( "", h3.getAttribute( "class" ) ); + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Subsection_name", a.getAttribute( "name" ) ); + + assertFalse( elementIterator.hasNext() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/CommentsVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/CommentsVerifier.java new file mode 100644 index 000000000..82c48e6b5 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/CommentsVerifier.java @@ -0,0 +1,41 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; + +import org.codehaus.plexus.util.FileUtils; + +/** + * Verifies that there are no DOXIASITETOOLS-146 comments + */ +public class CommentsVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + String content = FileUtils.fileRead( new File( file ), "UTF-8" ); + + assertTrue( file + " should not contain 'DOXIASITETOOLS-146' text in comments", + content.indexOf( "DOXIASITETOOLS-146" ) < 0 ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java new file mode 100644 index 000000000..9570bcb21 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/DefaultSiteRendererTest.java @@ -0,0 +1,671 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.xsd.AbstractXmlValidator; +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.ReflectionUtils; +import org.codehaus.plexus.util.StringUtils; +import org.mockito.Mockito; +import org.xml.sax.EntityResolver; + +/** + * @author Vincent Siveton + * @author Emmanuel Venisse + */ +public class DefaultSiteRendererTest + extends PlexusTestCase +{ + /** + * All output produced by this test will go here. + */ + private static final String OUTPUT = "target/output"; + + /** + * The renderer used to produce output. + */ + private Renderer renderer; + + /** + * The locale before executing tests. + */ + private Locale oldLocale; + + private File skinJar = new File( getBasedir(), "target/test-classes/skin.jar" ); + + /** + * @throws java.lang.Exception if something goes wrong. + * @see org.codehaus.plexus.PlexusTestCase#setUp() + */ + @Override + protected void setUp() + throws Exception + { + super.setUp(); + + renderer = (Renderer) lookup( Renderer.ROLE ); + + // copy the default-site.vm and default-site-macros.vm + copyVm( "default-site.vm", "\n\n\n\r\n\r\n\r\n" ); + copyVm( "default-site-macros.vm", "" ); + + InputStream skinIS = this.getResourceAsStream( "velocity-toolmanager.vm" ); + JarOutputStream jarOS = new JarOutputStream( new FileOutputStream( skinJar ) ); + try + { + jarOS.putNextEntry( new ZipEntry( "META-INF/maven/site.vm" ) ); + IOUtil.copy( skinIS, jarOS ); + jarOS.closeEntry(); + } + finally + { + IOUtil.close( skinIS ); + IOUtil.close( jarOS ); + } + + oldLocale = Locale.getDefault(); + Locale.setDefault( Locale.ENGLISH ); + } + + private void copyVm( String filename, String append ) + throws IOException + { + InputStream is = this.getResourceAsStream( "/org/apache/maven/doxia/siterenderer/resources/" + filename ); + assertNotNull( is ); + OutputStream os = new FileOutputStream( new File( getBasedir(), "target/test-classes/" + filename ) ); + try + { + IOUtil.copy( is, os ); + os.write( append.getBytes( "ISO-8859-1" ) ); + } + finally + { + IOUtil.close( is ); + IOUtil.close( os ); + } + } + + /** + * @throws java.lang.Exception if something goes wrong. + * @see org.codehaus.plexus.PlexusTestCase#tearDown() + */ + @Override + protected void tearDown() + throws Exception + { + release( renderer ); + super.tearDown(); + + Locale.setDefault( oldLocale ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void testRenderExceptionMessageWhenLineNumberIsNotAvailable() + throws Exception + { + final File testBasedir = getTestFile( "src/test/resources/site/xdoc" ); + final String testDocumentName = "head.xml"; + final String exceptionMessage = "parse error occurred"; + + Doxia doxiaInstance = (Doxia) lookup( Doxia.class ); + Doxia doxiaSpy = spy( doxiaInstance ); + Mockito.doThrow( new ParseException( exceptionMessage ) ) + .when( doxiaSpy ) + .parse( Mockito.any(), Mockito.anyString(), Mockito.any() ); + Renderer renderer = (Renderer) lookup( Renderer.class ); + ReflectionUtils.setVariableValueInObject( renderer, "doxia", doxiaSpy ); + + RenderingContext renderingContext = new RenderingContext( testBasedir, "", testDocumentName, "xdoc", "", + false ); + + try + { + renderer.renderDocument( null, renderingContext, new SiteRenderingContext() ); + fail( "should fail with exception" ); + } + catch ( RendererException e ) + { + assertEquals( + String.format( "Error parsing '%s%s%s': %s", + testBasedir.getAbsolutePath(), File.separator, testDocumentName, exceptionMessage ), + e.getMessage() ); + } + } + + /** + * @throws Exception if something goes wrong. + */ + public void testRenderExceptionMessageWhenLineNumberIsAvailable() + throws Exception + { + final File testBasedir = getTestFile( "src/test/resources/site/xdoc" ); + final String testDocumentName = "head.xml"; + final String exceptionMessage = "parse error occurred"; + + Doxia doxiaInstance = (Doxia) lookup( Doxia.class ); + Doxia doxiaSpy = spy( doxiaInstance ); + Mockito.doThrow( new ParseException( exceptionMessage, 42, 36 ) ) + .when( doxiaSpy ) + .parse( Mockito.any(), Mockito.anyString(), Mockito.any() ); + Renderer renderer = (Renderer) lookup( Renderer.class ); + ReflectionUtils.setVariableValueInObject( renderer, "doxia", doxiaSpy ); + + RenderingContext renderingContext = new RenderingContext( testBasedir, "", testDocumentName, "xdoc", "", + false ); + + try + { + renderer.renderDocument( null, renderingContext, new SiteRenderingContext() ); + fail( "should fail with exception" ); + } + catch ( RendererException e ) + { + assertEquals( + String.format( "Error parsing '%s%s%s': line [42] %s", + testBasedir.getAbsolutePath(), File.separator, testDocumentName, exceptionMessage ), + e.getMessage() ); + } + } + + /** + * @throws Exception if something goes wrong. + */ + public void testRender() + throws Exception + { + // Safety + FileUtils.deleteDirectory( getTestFile( OUTPUT ) ); + + // ---------------------------------------------------------------------- + // Render the site from src/test/resources/site to OUTPUT + // ---------------------------------------------------------------------- + DecorationModel decoration = new DecorationXpp3Reader() + .read( new FileReader( getTestFile( "src/test/resources/site/site.xml" ) ) ); + + SiteRenderingContext ctxt = getSiteRenderingContext( decoration, "src/test/resources/site", false ); + ctxt.setRootDirectory( getTestFile( "" ) ); + renderer.render( renderer.locateDocumentFiles( ctxt, true ).values(), ctxt, getTestFile( OUTPUT ) ); + + ctxt = getSiteRenderingContext( decoration, "src/test/resources/site-validate", true ); + ctxt.setRootDirectory( getTestFile( "" ) ); + renderer.render( renderer.locateDocumentFiles( ctxt, true ).values(), ctxt, getTestFile( OUTPUT ) ); + + // ---------------------------------------------------------------------- + // Verify specific pages + // ---------------------------------------------------------------------- + verifyHeadPage(); + verifyCdcPage(); + verifyNestedItemsPage(); + verifyMultipleBlock(); + verifyMacro(); + verifyEntitiesPage(); + verifyJavascriptPage(); + verifyFaqPage(); + verifyAttributes(); + verifyMisc(); + verifyDocbookPageExists(); + verifyApt(); + verifyExtensionInFilename(); + verifyNewlines(); + + // ---------------------------------------------------------------------- + // Validate the rendering pages + // ---------------------------------------------------------------------- + validatePages(); + } + + public void testExternalReport() + throws Exception + { + DocumentRenderer docRenderer = mock( DocumentRenderer.class ); + when( docRenderer.isExternalReport() ).thenReturn( true ); + when( docRenderer.getOutputName() ).thenReturn( "external/index" ); + when( docRenderer.getRenderingContext() ).thenReturn( new RenderingContext( new File( "" ), "index.html", + "generator:external" ) ); + + SiteRenderingContext context = new SiteRenderingContext(); + + renderer.render( Collections.singletonList( docRenderer ), context, new File( "target/output" ) ); + + verify( docRenderer ).renderDocument( isNull( Writer.class ), eq( renderer ), eq( context ) ); + } + + public void testVelocityToolManager() + throws Exception + { + StringWriter writer = new StringWriter(); + + SiteRenderingContext siteRenderingContext = new SiteRenderingContext(); + siteRenderingContext.setDecoration( new DecorationModel() ); + + Map attributes = new HashMap(); + + /* + * We need to add doxiaSiteRendererVersion manually because version property from pom.properties + * is not available at test time in some cases. + */ + attributes.put( "doxiaSiteRendererVersion", "1.7-bogus" ); + + siteRenderingContext.setTemplateProperties( attributes ); + + siteRenderingContext.setTemplateName( "org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm" ); + RenderingContext context = new RenderingContext( new File( "" ), "document.html", "generator" ); + SiteRendererSink sink = new SiteRendererSink( context ); + renderer.mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + + String renderResult = writer.toString(); + String expectedResult = + IOUtils.toString( + getClass().getResourceAsStream( "velocity-toolmanager.expected.txt" ), + StandardCharsets.UTF_8 ); + expectedResult = StringUtils.unifyLineSeparators( expectedResult ); + assertEquals( expectedResult, renderResult ); + } + + public void testVelocityToolManagerForTemplate() + throws Exception + { + StringWriter writer = new StringWriter(); + + File templateFile = + new File( getBasedir(), "target/test-classes/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm" ); + Map attributes = new HashMap(); + + /* + * We need to add doxiaSiteRendererVersion manually because version property from pom.properties + * is not available at test time in some cases. + */ + attributes.put( "doxiaSiteRendererVersion", "1.7-bogus" ); + + SiteRenderingContext siteRenderingContext = + renderer.createContextForTemplate( templateFile, attributes, new DecorationModel(), + "defaultWindowTitle", Locale.ENGLISH ); + RenderingContext context = new RenderingContext( new File( "" ), "document.html", "generator" ); + SiteRendererSink sink = new SiteRendererSink( context ); + renderer.mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + + String renderResult = writer.toString(); + InputStream in = getClass().getResourceAsStream( "velocity-toolmanager.expected.txt" ); + String expectedResult = StringUtils.unifyLineSeparators( IOUtils.toString( in, StandardCharsets.UTF_8 ) ); + assertEquals( expectedResult, renderResult ); + } + + public void testVelocityToolManagerForSkin() + throws Exception + { + StringWriter writer = new StringWriter(); + + File skinFile = skinJar; + + Map attributes = new HashMap(); + + /* + * We need to add doxiaSiteRendererVersion manually because version property from pom.properties + * is not available at test time in some cases. + */ + attributes.put( "doxiaSiteRendererVersion", "1.7-bogus" ); + + Artifact skin = new DefaultArtifact( "org.group", "artifact", "1.1", null, "jar", "", null ); + skin.setFile( skinFile ); + SiteRenderingContext siteRenderingContext = + renderer.createContextForSkin( skin, attributes, new DecorationModel(), "defaultWindowTitle", + Locale.ENGLISH ); + RenderingContext context = new RenderingContext( new File( "" ), "document.html", "generator" ); + SiteRendererSink sink = new SiteRendererSink( context ); + renderer.mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + String renderResult = writer.toString(); + String expectedResult = StringUtils.unifyLineSeparators( + IOUtils.toString( + getClass().getResourceAsStream( "velocity-toolmanager.expected.txt" ), + StandardCharsets.UTF_8 ) ); + assertEquals( expectedResult, renderResult ); + } + + public void testMatchVersion() + throws Exception + { + DefaultSiteRenderer r = (DefaultSiteRenderer) renderer; + assertTrue( r.matchVersion( "1.7", "1.7" ) ); + assertFalse( r.matchVersion( "1.7", "1.8" ) ); + } + + private SiteRenderingContext getSiteRenderingContext( DecorationModel decoration, String siteDir, boolean validate ) + { + SiteRenderingContext ctxt = new SiteRenderingContext(); + ctxt.setTemplateName( "default-site.vm" ); + ctxt.setTemplateClassLoader( getClassLoader() ); + ctxt.setUsingDefaultTemplate( true ); + final Map templateProp = new HashMap(); + templateProp.put( "outputEncoding", "UTF-8" ); + ctxt.setTemplateProperties( templateProp ); + ctxt.setDecoration( decoration ); + ctxt.addSiteDirectory( getTestFile( siteDir ) ); + ctxt.setValidate( validate ); + + return ctxt; + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyHeadPage() + throws Exception + { + new HeadVerifier().verify( "target/output/head.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyCdcPage() + throws Exception + { + File nestedItems = getTestFile( "target/output/cdc.html" ); + assertNotNull( nestedItems ); + assertTrue( nestedItems.exists() ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyNestedItemsPage() + throws Exception + { + NestedItemsVerifier verifier = new NestedItemsVerifier(); + verifier.verify( "target/output/nestedItems.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyMultipleBlock() + throws Exception + { + MultipleBlockVerifier verifier = new MultipleBlockVerifier(); + verifier.verify( "target/output/multipleblock.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyMacro() + throws Exception + { + File macro = getTestFile( "target/output/macro.html" ); + assertNotNull( macro ); + assertTrue( macro.exists() ); + + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( macro ); + String content = IOUtil.toString( reader ); + assertEquals( content.indexOf( "" ), -1 ); + } + finally + { + IOUtil.close( reader ); + } + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyEntitiesPage() + throws Exception + { + EntitiesVerifier verifier = new EntitiesVerifier(); + verifier.verify( "target/output/entityTest.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyJavascriptPage() + throws Exception + { + JavascriptVerifier verifier = new JavascriptVerifier(); + verifier.verify( "target/output/javascript.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyFaqPage() + throws Exception + { + FaqVerifier verifier = new FaqVerifier(); + verifier.verify( "target/output/faq.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyAttributes() + throws Exception + { + AttributesVerifier verifier = new AttributesVerifier(); + verifier.verify( "target/output/attributes.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyMisc() + throws Exception + { + AbstractVerifier verifier = new MiscVerifier(); + verifier.verify( "target/output/misc.html" ); + + verifier = new CommentsVerifier(); + verifier.verify( "target/output/misc.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyDocbookPageExists() + throws Exception + { + File output = getTestFile( "target/output/docbook.html" ); + assertNotNull( output ); + assertTrue( output.exists() ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyApt() + throws Exception + { + AbstractVerifier verifier = new AptVerifier(); + verifier.verify( "target/output/apt.html" ); + + verifier = new CommentsVerifier(); + verifier.verify( "target/output/apt.html" ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyExtensionInFilename() + throws Exception + { + File output = getTestFile( "target/output/extension.apt.not.at.end.html" ); + assertNotNull( output ); + assertTrue( output.exists() ); + } + + /** + * @throws Exception if something goes wrong. + */ + public void verifyNewlines() + throws Exception + { + /* apt */ + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/apt.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/cdc.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/interpolation.html" ), "ISO-8859-1" ) ); + /* confluence */ + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/confluence/anchor.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/confluence/code.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/confluence/table.html" ), "ISO-8859-1" ) ); + /* docbook */ + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/docbook.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/sdocbook_full.html" ), "ISO-8859-1" ) ); + /* fml */ + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/faq.html" ), "ISO-8859-1" ) ); + /* xdoc */ + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/attributes.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/javascript.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/head.html" ), "ISO-8859-1" ) ); + checkNewlines( FileUtils.fileRead( getTestFile( "target/output/macro.html" ), "ISO-8859-1" ) ); + } + + private void checkNewlines( String content ) + { + int cr = StringUtils.countMatches( content, "\r" ); + int lf = StringUtils.countMatches( content, "\n" ); + assertTrue( "Should contain only Windows or Unix newlines: cr = " + cr + ", lf = " + lf, ( cr == 0 ) + || ( cr == lf ) ); + } + + /** + * Validate the generated pages. + * + * @throws Exception if something goes wrong. + * @since 1.1.1 + */ + public void validatePages() + throws Exception + { + new Xhtml5ValidatorTest().validateGeneratedPages(); + } + + protected static class Xhtml5ValidatorTest + extends AbstractXmlValidator + { + /** + * Validate the generated documents. + * + * @throws Exception + */ + public void validateGeneratedPages() + throws Exception + { + setValidate( false ); + setUp(); + testValidateFiles(); + tearDown(); + } + + private static String[] getIncludes() + { + return new String[] { "**/*.html" }; + } + + /** {@inheritDoc} */ + protected String addNamespaces( String content ) + { + return content; + } + + /** {@inheritDoc} */ + protected EntityResolver getEntityResolver() + { + /* HTML5 restricts use of entities to XML only */ + return null; + } + + /** {@inheritDoc} */ + protected Map getTestDocuments() + throws IOException + { + Map testDocs = new HashMap(); + + File dir = new File( getBasedir(), "target/output" ); + + List l = + FileUtils.getFileNames( dir, getIncludes()[0], FileUtils.getDefaultExcludesAsString(), true ); + + for ( String file : l ) + { + file = StringUtils.replace( file, "\\", "/" ); + + Reader reader = ReaderFactory.newXmlReader( new File( file ) ); + try + { + testDocs.put( file, IOUtil.toString( reader ) ); + } + finally + { + IOUtil.close( reader ); + } + } + + return testDocs; + } + + /** {@inheritDoc} */ + @Override + protected boolean isFailErrorMessage( String message ) + { + return true; + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/EntitiesVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/EntitiesVerifier.java new file mode 100644 index 000000000..0195bd238 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/EntitiesVerifier.java @@ -0,0 +1,189 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlHeading3; +import com.gargoylesoftware.htmlunit.html.HtmlHeading4; +import com.gargoylesoftware.htmlunit.html.HtmlMeta; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlPreformattedText; +import com.gargoylesoftware.htmlunit.html.HtmlSection; + +import java.util.Iterator; + +/** + * Verify the site/xdoc/entityTest.xml + * + * @author ltheussl + */ +public class EntitiesVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlMeta author = (HtmlMeta) page.getElementsByName( "author" ).get( 0 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "Ligature \u00C6" ) > 0 ); + assertEquals( "Ligature \u00C6", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 1 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "Ampersand &" ) > 0 ); + assertEquals( "Ampersand &", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 2 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "Less than <" ) > 0 ); + assertEquals( "Less than <", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 3 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "Greater than >" ) > 0 ); + assertEquals( "Greater than >", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 4 ); + assertNotNull( author ); + assertEquals( "Apostrophe '", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 5 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "Quote "" ) > 0 ); + assertEquals( "Quote \"", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 6 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "test@email.com" ) > 0 ); + assertEquals( "test@email.com", author.getContentAttribute() ); + + author = (HtmlMeta) page.getElementsByName( "author" ).get( 7 ); + assertNotNull( author ); + assertTrue( author.toString().indexOf( "test\u00A9email.com" ) > 0 ); + assertEquals( "test\u00A9email.com", author.getContentAttribute() ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( h2.asText().trim(), "section name with entities: '&' '\u0391' ' ' '\uD835\uDFED'" ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "section_name_with_entities:_.27.26.27_.27.CE.91.27_.27.C2.A0.27_.27.3F.3F.27", + a.getAttribute( "name" ) ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading4 h4 = (HtmlHeading4) elementIterator.next(); + assertNotNull( h4 ); + assertEquals( "Entities", h4.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "Entities", a.getAttribute( "name" ) ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading3 h3 = (HtmlHeading3) elementIterator.next(); + assertNotNull( h3 ); + assertEquals( "Generic Entities: '&' '<' '>' '\"' '''", h3.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "'&' '<' '>' '\"' '''", p.asText().trim() ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h3 = (HtmlHeading3) elementIterator.next(); + assertNotNull( h3 ); + assertEquals( "Local Entities: '\u0391' '\u0392' '\u0393' '\uD835\uDFED'", h3.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "'\u0391' '\u0392' '\u0393' '\uD835\uDFED\uD835\uDFED' '\u0159\u0159' '\u0159'", p.asText().trim() ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h3 = (HtmlHeading3) elementIterator.next(); + assertNotNull( h3 ); + assertEquals( "DTD Entities: ' ' '\u00A1' '\u00A2'", h3.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "' ' '\u00A1' '\u00A2'", p.asText().trim() ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h4 = (HtmlHeading4) elementIterator.next(); + assertNotNull( h4 ); + assertEquals( "CDATA", h4.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "CDATA", a.getAttribute( "name" ) ); + + HtmlDivision div = (HtmlDivision) elementIterator.next(); + assertNotNull( div ); + + HtmlPreformattedText pre = (HtmlPreformattedText) elementIterator.next(); + assertNotNull( pre ); + assertEquals( "", pre.asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "' ' '¡'", p.asText().trim() ); + + assertFalse( elementIterator.hasNext() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/FaqVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/FaqVerifier.java new file mode 100644 index 000000000..39a25bb44 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/FaqVerifier.java @@ -0,0 +1,198 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionDescription; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionList; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionTerm; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlListItem; +import com.gargoylesoftware.htmlunit.html.HtmlOrderedList; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlPreformattedText; +import com.gargoylesoftware.htmlunit.html.HtmlSection; + +import java.util.Iterator; + +/** + * + * @author ltheussl + */ +public class FaqVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Oft Asked Questions", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertEquals( a.getAttribute( "name" ), "Oft_Asked_Questions" ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "top", a.getAttribute( "name" ) ); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + element = elementIterator.next(); + assertEquals( "b", element.getTagName() ); + assertEquals( "Contributing", element.asText().trim() ); + + HtmlOrderedList ol = (HtmlOrderedList) elementIterator.next(); + assertEquals( "One stupid question & a silly answer?", ol.getFirstElementChild().asText().trim() ); + + HtmlListItem li = (HtmlListItem) elementIterator.next(); + assertEquals( "One stupid question & a silly answer?", li.getFirstElementChild().asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#stupid-question", a.getAttribute( "href" ) ); + + element = elementIterator.next(); + assertEquals( "b", element.getTagName() ); + assertEquals( "stupid", element.asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + element = elementIterator.next(); + assertEquals( "b", element.getTagName() ); + assertEquals( "Using Maven", element.asText().trim() ); + + ol = (HtmlOrderedList) elementIterator.next(); + assertEquals( "How do I disable a report on my site?", ol.getFirstElementChild().asText().trim() ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "How do I disable a report on my site?", li.getFirstElementChild().asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#disable-reports", a.getAttribute( "href" ) ); + + section = (HtmlSection) elementIterator.next(); + + h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Contributing", h2.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Contributing", a.getAttribute( "name" ) ); + + HtmlDefinitionList dl = (HtmlDefinitionList) elementIterator.next(); + + HtmlDefinitionTerm dt = (HtmlDefinitionTerm) elementIterator.next(); + assertEquals( "One stupid question & a silly answer?", dt.getFirstChild().asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "stupid-question", a.getAttribute( "name" ) ); + + element = elementIterator.next(); + assertEquals( "b", element.getTagName() ); + assertEquals( "stupid", element.asText().trim() ); + + HtmlDefinitionDescription dd = (HtmlDefinitionDescription) elementIterator.next(); + + p = (HtmlParagraph) elementIterator.next(); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#Using_Maven", a.getAttribute( "href" ) ); + assertEquals( "local link", a.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "./cdc.html", a.getAttribute( "href" ) ); + assertEquals( "source document", a.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "http://maven.apache.org/?l=a&m=b", a.getAttribute( "href" ) ); + assertEquals( "external link", a.asText().trim() ); + + element = elementIterator.next(); + assertEquals( "i", element.getTagName() ); + assertEquals( "italic", element.asText().trim() ); + + element = elementIterator.next(); + assertEquals( "b", element.getTagName() ); + assertEquals( "non-US-ASCII characters: àéèç", element.asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertEquals( "right", p.getAttribute( "align" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#top", a.getAttribute( "href" ) ); + assertEquals( "[top]", a.asText().trim() ); + + + section = (HtmlSection) elementIterator.next(); + + h2 = (HtmlHeading2) elementIterator.next(); + assertEquals( "Using Maven", h2.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "Using_Maven", a.getAttribute( "name" ) ); + + dl = (HtmlDefinitionList) elementIterator.next(); + + dt = (HtmlDefinitionTerm) elementIterator.next(); + assertEquals( "How do I disable a report on my site?", dt.getFirstChild().asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "disable-reports", a.getAttribute( "name" ) ); + + dd = (HtmlDefinitionDescription) elementIterator.next(); + + p = (HtmlParagraph) elementIterator.next(); + + element = elementIterator.next(); + assertEquals( "code", element.getTagName() ); + assertEquals( "", element.asText().trim() ); + + HtmlDivision div = (HtmlDivision) elementIterator.next(); + assertEquals( "source", div.getAttribute( "class" ) ); + + HtmlPreformattedText pre = (HtmlPreformattedText) elementIterator.next(); + assertEquals( "1.5", pre.asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertEquals( "right", p.getAttribute( "align" ) ); + + a = (HtmlAnchor) elementIterator.next(); + assertEquals( "#top", a.getAttribute( "href" ) ); + assertEquals( "[top]", a.asText().trim() ); + + assertFalse( elementIterator.hasNext() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/HeadVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/HeadVerifier.java new file mode 100644 index 000000000..babd6e1f8 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/HeadVerifier.java @@ -0,0 +1,102 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlBase; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlLink; +import com.gargoylesoftware.htmlunit.html.HtmlMeta; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlTitle; + +import java.util.Iterator; +import java.util.List; + +/** + * Verify correct rendering of site/xdoc/head.xml. + * + * @author ltheussl + */ +public class HeadVerifier + extends AbstractVerifier +{ + + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement html = page.getDocumentElement(); + assertNotNull( html ); + + assertEquals( "en", html.getAttribute( "lang" ) ); + + List heads = html.getElementsByTagName( "head" ); + assertEquals( 1, heads.size() ); + HtmlElement head = heads.get( 0 ); + assertNotNull( head ); + + Iterator elementIterator = head.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlMeta meta = (HtmlMeta) elementIterator.next(); + assertEquals( "UTF-8", meta.getAttribute( "charset" ) ); + + // Skip viewport + elementIterator.next(); + + meta = (HtmlMeta) elementIterator.next(); + assertEquals( "Unexpected meta entry found generated resource " + file, "generator", meta.getAttribute( "name" ) ); + String generator = meta.getAttribute("content"); + assertEquals("Unexpected value found for generator meta entry in generated resource " + file, "Apache Maven Doxia Site Renderer", generator); + + meta = (HtmlMeta) elementIterator.next(); + assertEquals( "author", meta.getAttribute( "name" ) ); + assertEquals( "John Doe", meta.getAttribute( "content" ).trim() ); + + HtmlTitle title = (HtmlTitle) elementIterator.next(); + assertNotNull( title ); + + HtmlLink link = (HtmlLink) elementIterator.next(); + assertNotNull( link ); + link = (HtmlLink) elementIterator.next(); + assertNotNull( link ); + link = (HtmlLink) elementIterator.next(); + assertNotNull( link ); + link = (HtmlLink) elementIterator.next(); + assertNotNull( link ); + + meta = (HtmlMeta) elementIterator.next(); + assertEquals( "description", meta.getAttribute( "name" ) ); + assertEquals( "Free Web tutorials", meta.getAttribute( "content" ) ); + + meta = (HtmlMeta) elementIterator.next(); + assertEquals( "keywords", meta.getAttribute( "name" ) ); + assertEquals( "HTML,CSS,XML,JavaScript", meta.getAttribute( "content" ) ); + + HtmlBase base = (HtmlBase) elementIterator.next(); + assertEquals( "http://maven.apache.org/", base.getAttribute( "href" ) ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/JavascriptVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/JavascriptVerifier.java new file mode 100644 index 000000000..bbc38d60c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/JavascriptVerifier.java @@ -0,0 +1,106 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.CollectingAlertHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlScript; +import com.gargoylesoftware.htmlunit.html.HtmlSection; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + + +/** + * Verify javascript code. + * + * @author ltheussl + */ +public class JavascriptVerifier + extends AbstractVerifier +{ + /** + * Verifies a HtmlPage. + * + * @param file the file to verify. + * + * @throws Exception if something goes wrong. + */ + public void verify( String file ) + throws Exception + { + File jsTest = getTestFile( "target/output/javascript.html" ); + assertNotNull( jsTest ); + assertTrue( jsTest.exists() ); + + // HtmlUnit + try ( WebClient webClient = new WebClient() ) { + webClient.getOptions().setCssEnabled( false ); + + final List collectedAlerts = new ArrayList( 4 ); + webClient.setAlertHandler( new CollectingAlertHandler( collectedAlerts ) ); + + HtmlPage page = (HtmlPage) webClient.getPage( jsTest.toURI().toURL() ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "Test", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "Test", a.getAttribute( "name" ) ); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "You should see a JavaScript alert...", p.asText().trim() ); + + HtmlScript script = (HtmlScript) elementIterator.next(); + assertNotNull( script ); + assertEquals( "text/javascript", script.getAttribute( "type" ) ); + assertEquals( "", script.asText().trim() ); + List expectedAlerts = Collections.singletonList( "Hello!" ); + assertEquals( expectedAlerts, collectedAlerts ); + } + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MiscVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MiscVerifier.java new file mode 100644 index 000000000..e1664d13b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MiscVerifier.java @@ -0,0 +1,64 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParameter; +import com.gargoylesoftware.htmlunit.html.HtmlUnknownElement; + +import java.util.Iterator; + + +/** + * + * @author ltheussl + */ +public class MiscVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // + // ---------------------------------------------------------------------- + + HtmlUnknownElement applet = (HtmlUnknownElement) elementIterator.next(); + assertEquals( "org.micro.applet.Main", applet.getAttribute( "code" ) ); + assertEquals( "micro-applet.jar", applet.getAttribute( "archive" ) ); + + HtmlParameter param = (HtmlParameter) elementIterator.next(); + assertEquals( "midlet", param.getAttribute( "name" ) ); + assertEquals( "org.micro.applet.SimpleDemoMIDlet", param.getAttribute( "value" ) ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MultipleBlockVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MultipleBlockVerifier.java new file mode 100644 index 000000000..3820e533f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/MultipleBlockVerifier.java @@ -0,0 +1,132 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlListItem; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlSection; +import com.gargoylesoftware.htmlunit.html.HtmlUnorderedList; + +import java.util.Iterator; + +/** + * + * @author ltheussl + */ +public class MultipleBlockVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // Verify link + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "section name", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "section_name", a.getAttribute( "name" ) ); + + // ---------------------------------------------------------------------- + // Paragraph + // ---------------------------------------------------------------------- + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "text", p.asText().trim() ); + + // ---------------------------------------------------------------------- + // Unordered list + // ---------------------------------------------------------------------- + + HtmlUnorderedList ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + HtmlListItem li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "list1", li.getFirstChild().asText().trim() ); + + // ---------------------------------------------------------------------- + // Paragraph + // ---------------------------------------------------------------------- + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "text2", p.asText().trim() ); + + // ---------------------------------------------------------------------- + // Unordered list + // ---------------------------------------------------------------------- + + ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "list1", li.getFirstChild().asText().trim() ); + + // ---------------------------------------------------------------------- + // Paragraph + // ---------------------------------------------------------------------- + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "text3", p.asText().trim() ); + + // ---------------------------------------------------------------------- + // Unordered list + // ---------------------------------------------------------------------- + + ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "list1", p.getFirstChild().asText().trim() ); + + assertFalse( elementIterator.hasNext() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/NestedItemsVerifier.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/NestedItemsVerifier.java new file mode 100644 index 000000000..92b64723b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/NestedItemsVerifier.java @@ -0,0 +1,320 @@ +package org.apache.maven.doxia.siterenderer; + +import java.util.Iterator; + +/* + * 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. + */ + +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionDescription; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionList; +import com.gargoylesoftware.htmlunit.html.HtmlDefinitionTerm; +import com.gargoylesoftware.htmlunit.html.HtmlDivision; +import com.gargoylesoftware.htmlunit.html.HtmlElement; +import com.gargoylesoftware.htmlunit.html.HtmlHeading2; +import com.gargoylesoftware.htmlunit.html.HtmlHeading4; +import com.gargoylesoftware.htmlunit.html.HtmlListItem; +import com.gargoylesoftware.htmlunit.html.HtmlOrderedList; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlParagraph; +import com.gargoylesoftware.htmlunit.html.HtmlSection; +import com.gargoylesoftware.htmlunit.html.HtmlUnorderedList; + +/** + * + * @author ltheussl + */ +public class NestedItemsVerifier + extends AbstractVerifier +{ + /** {@inheritDoc} */ + public void verify( String file ) + throws Exception + { + HtmlPage page = htmlPage( file ); + assertNotNull( page ); + + HtmlElement element = page.getHtmlElementById( "contentBox" ); + assertNotNull( element ); + HtmlDivision division = (HtmlDivision) element; + assertNotNull( division ); + + Iterator elementIterator = division.getHtmlElementDescendants().iterator(); + + // ---------------------------------------------------------------------- + // Verify link + // ---------------------------------------------------------------------- + + HtmlSection section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading2 h2 = (HtmlHeading2) elementIterator.next(); + assertNotNull( h2 ); + assertEquals( "List Section", h2.asText().trim() ); + + HtmlAnchor a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "List_Section", a.getAttribute( "name" ) ); + + // ---------------------------------------------------------------------- + // Unordered lists + // ---------------------------------------------------------------------- + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + HtmlHeading4 h4 = (HtmlHeading4) elementIterator.next(); + assertNotNull( h4 ); + assertEquals( "Unordered lists", h4.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "Unordered_lists", a.getAttribute( "name" ) ); + + HtmlParagraph p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Below is an unordered list, followed by six paragraphs.", p.asText().trim() ); + + HtmlUnorderedList ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + HtmlListItem li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 1.", li.getFirstChild().asText().trim() ); + + ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Item 11.", p.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Item 12.", p.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 13.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 14.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 2.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 3.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 4.", li.getFirstChild().asText().trim() ); + + ul = (HtmlUnorderedList) elementIterator.next(); + assertNotNull( ul ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 41.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 42.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 43.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 44.", li.getFirstChild().asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 1 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 2 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 3 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 4 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 5 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 6 below list.", p.asText().trim() ); + + // ---------------------------------------------------------------------- + // Ordered lists + // ---------------------------------------------------------------------- + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h4 = (HtmlHeading4) elementIterator.next(); + assertNotNull( h4 ); + assertEquals( "Ordered lists", h4.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "Ordered_lists", a.getAttribute( "name" ) ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Below is an ordered list, followed by six paragraphs.", p.asText().trim() ); + + HtmlOrderedList ol = (HtmlOrderedList) elementIterator.next(); + assertNotNull( ol ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 1.", li.getFirstChild().asText().trim() ); + + ol = (HtmlOrderedList) elementIterator.next(); + assertNotNull( ol ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 11.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 12.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 13.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 14.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 2.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 3.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 4.", li.getFirstChild().asText().trim() ); + + ol = (HtmlOrderedList) elementIterator.next(); + assertNotNull( ol ); + + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 41.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 42.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 43.", li.getFirstChild().asText().trim() ); + li = (HtmlListItem) elementIterator.next(); + assertNotNull( li ); + assertEquals( "Item 44.", li.getFirstChild().asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 1 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 2 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 3 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 4 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 5 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 6 below list.", p.asText().trim() ); + + // ---------------------------------------------------------------------- + // Definition lists + // ---------------------------------------------------------------------- + + section = (HtmlSection) elementIterator.next(); + assertNotNull( section ); + + h4 = (HtmlHeading4) elementIterator.next(); + assertNotNull( h4 ); + assertEquals( "Definition lists", h4.asText().trim() ); + + a = (HtmlAnchor) elementIterator.next(); + assertNotNull( a ); + assertEquals( "Definition_lists", a.getAttribute( "name" ) ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Below is a definition list, followed by six paragraphs.", p.asText().trim() ); + + HtmlDefinitionList dl = (HtmlDefinitionList) elementIterator.next(); + assertNotNull( dl ); + + HtmlDefinitionTerm dt = (HtmlDefinitionTerm) elementIterator.next(); + assertNotNull( dt ); + assertEquals( "Term 1.", dt.getFirstChild().asText().trim() ); + HtmlDefinitionDescription dd = (HtmlDefinitionDescription) elementIterator.next(); + assertNotNull( dd ); + assertEquals( "Description 1.", dd.getFirstChild().asText().trim() ); + + dt = (HtmlDefinitionTerm) elementIterator.next(); + assertNotNull( dt ); + assertEquals( "Term 2.", dt.getFirstChild().asText().trim() ); + dd = (HtmlDefinitionDescription) elementIterator.next(); + assertNotNull( dd ); + assertEquals( "Description 2.", dd.getFirstChild().asText().trim() ); + + dl = (HtmlDefinitionList) elementIterator.next(); + assertNotNull( dl ); + dt = (HtmlDefinitionTerm) elementIterator.next(); + assertNotNull( dt ); + assertEquals( "Term 21.", dt.getFirstChild().asText().trim() ); + dd = (HtmlDefinitionDescription) elementIterator.next(); + assertNotNull( dd ); + assertEquals( "Description 21.", dd.getFirstChild().asText().trim() ); + + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 1 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 2 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 3 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 4 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 5 below list.", p.asText().trim() ); + p = (HtmlParagraph) elementIterator.next(); + assertNotNull( p ); + assertEquals( "Paragraph 6 below list.", p.asText().trim() ); + + assertFalse( elementIterator.hasNext() ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java new file mode 100644 index 000000000..2435847e5 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/RenderingContextTest.java @@ -0,0 +1,76 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.File; + +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.codehaus.plexus.PlexusTestCase; + +/** + * @author olamy + * @since 20 oct. 07 + */ +public class RenderingContextTest + extends PlexusTestCase +{ + + /** + * Test getRelativePath() with various file names. + * + * @throws java.lang.Exception if any. + */ + public void testFileNameWithDot() + throws Exception + { + File baseDir = new File( getBasedir() + File.separatorChar + "test" + File.separatorChar + "resources" ); + String docName = "file.with.dot.in.name.xml"; + + RenderingContext renderingContext = new RenderingContext( baseDir, "test", docName, "", "xml", false ); + assertEquals( "file.with.dot.in.name.html", renderingContext.getOutputName() ); + assertEquals( ".", renderingContext.getRelativePath() ); + + renderingContext = new RenderingContext( baseDir, docName, "generator" ); // not Doxia source + assertEquals( "file.with.dot.in.name.html", renderingContext.getOutputName() ); + assertEquals( ".", renderingContext.getRelativePath() ); + + docName = "index.xml.vm"; + + renderingContext = new RenderingContext( baseDir, "test", docName, "", "xml", false ); + assertEquals( "index.html", renderingContext.getOutputName() ); + assertEquals( ".", renderingContext.getRelativePath() ); + + docName = "download.apt.vm"; + + renderingContext = new RenderingContext( baseDir, "test", docName, "", "apt", false ); + assertEquals( "download.html", renderingContext.getOutputName() ); + assertEquals( ".", renderingContext.getRelativePath() ); + + docName = "path/file.apt"; + renderingContext = new RenderingContext( baseDir, "test", docName, "", "apt", false ); + assertEquals( "path/file.html", renderingContext.getOutputName() ); + assertEquals( "..", renderingContext.getRelativePath() ); + + renderingContext = new RenderingContext( baseDir, docName, "generator" ); + assertEquals( "path/file.html", renderingContext.getOutputName() ); + assertEquals( "..", renderingContext.getRelativePath() ); + } + +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/SkinResourceLoaderTest.java b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/SkinResourceLoaderTest.java new file mode 100644 index 000000000..6f7a5e976 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/java/org/apache/maven/doxia/siterenderer/SkinResourceLoaderTest.java @@ -0,0 +1,63 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.maven.doxia.sink.impl.AbstractSink; +import org.codehaus.plexus.util.IOUtil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SkinResourceLoaderTest +{ + private SkinResourceLoader skinResourceLoader = new SkinResourceLoader(); + + @Test + public void testNormalizeNewline() throws Exception + { + String EOL = AbstractSink.EOL; + String EOL_MACOS9 = "\r"; + String EOL_UNIX = "\n"; + String EOL_WIN = "\r\n"; + + assertEquals( "Hello " + EOL + " world", normalizeNewline( "Hello " + EOL_MACOS9 + " world" ) ); + assertEquals( "Hello " + EOL + " world", normalizeNewline( "Hello " + EOL_UNIX + " world" ) ); + assertEquals( "Hello " + EOL + " world", normalizeNewline( "Hello " + EOL_WIN + " world" ) ); + + assertEquals( "Hello world" + EOL, normalizeNewline( "Hello world" + EOL_MACOS9 ) ); + assertEquals( "Hello world" + EOL, normalizeNewline( "Hello world" + EOL_UNIX ) ); + assertEquals( "Hello world" + EOL, normalizeNewline( "Hello world" + EOL_WIN ) ); + + assertEquals( EOL + "Hello world", normalizeNewline( EOL_MACOS9 + "Hello world" ) ); + assertEquals( EOL + "Hello world", normalizeNewline( EOL_UNIX + "Hello world" ) ); + assertEquals( EOL + "Hello world", normalizeNewline( EOL_WIN + "Hello world" ) ); + } + + private String normalizeNewline( String text ) throws IOException + { + InputStream in = new ByteArrayInputStream( text.getBytes() ); + InputStream out = skinResourceLoader.normalizeNewline( in ); + return IOUtil.toString( out ); + } +} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-lat1.ent b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-lat1.ent new file mode 100644 index 000000000..ffee223eb --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-lat1.ent @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-special.ent b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-special.ent new file mode 100644 index 000000000..ca358b2fe --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-special.ent @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-symbol.ent b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-symbol.ent new file mode 100644 index 000000000..63c2abfa6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml-symbol.ent @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml1-transitional.dtd b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml1-transitional.dtd new file mode 100644 index 000000000..c62c02a5f --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/dtd/xhtml1-transitional.dtd @@ -0,0 +1,1201 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/log4j.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/log4j.properties new file mode 100644 index 000000000..45cd5b389 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/log4j.properties @@ -0,0 +1,24 @@ +# 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. + +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%c{1}:%L] %d{ISO8601} - %m%n + +log4j.logger.com.gargoylesoftware.htmlunit=ERROR diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.expected.txt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.expected.txt new file mode 100644 index 000000000..eb6fed940 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.expected.txt @@ -0,0 +1,42 @@ +Maven 2 & 3 +This is... + +We have the following keys in the context: + 1. FileUtils + 2. PathTool + 3. StringUtils + 4. alignedFileName + 5. alternator + 6. authors + 7. bodyContent + 8. class + 9. context + 10. convert + 11. currentDate + 12. currentFileName + 13. date + 14. dateFormat + 15. dateRevision + 16. decoration + 17. display + 18. docRenderingContext + 19. doxiaSiteRendererVersion + 20. esc + 21. field + 22. headContent + 23. i18n + 24. link + 25. locale + 26. loop + 27. math + 28. number + 29. plexus + 30. publishDate + 31. relativePath + 32. render + 33. shortTitle + 34. sorter + 35. supportedLocales + 36. text + 37. title + 38. xml diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm new file mode 100644 index 000000000..64a430af3 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm @@ -0,0 +1,7 @@ +$esc.html('Maven 2 & 3') +$display.truncate("This is a long string.", 10) + +We have the following keys in the context: +#foreach( $key in $sorter.sort( $context.keys ) ) + $display.printf( "%2s", ${foreach.count} ). $key +#end diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-plugin.properties b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-plugin.properties new file mode 100644 index 000000000..9e19dc3f7 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-plugin.properties @@ -0,0 +1,19 @@ +# 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. + +template.lastpublished=Last Published +template.builtby=Built by diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/entityTest.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/entityTest.xml new file mode 100644 index 000000000..48460b7d8 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/entityTest.xml @@ -0,0 +1,71 @@ + + + + + +%HTMLlat1; + + + + + + + +]> + + + Test entities, cdatas and comments + + Ligature Æ + Ampersand & + Less than < + Greater than > + Apostrophe ' + Quote " + test@email.com + test©email.com + + + + +
    + +

    Entities

    +

    Generic Entities: '&' '<' '>' '"' '''

    +

    '&' '<' '>' '"' '''

    + +

    Local Entities: 'Α' 'Β' 'Γ' '&tritPos;'

    +

    'Α' 'Β' 'Γ' '&tritPos;𝟭' '&flo;ř' '&myCustomEntity;'

    + +

    DTD Entities: ' ' '¡' '¢'

    +

    ' ' '¡' '¢'

    + +

    CDATA

    + ]]> +

    + +
    + + + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/xdoc-2.0.xsd b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/xdoc-2.0.xsd new file mode 100644 index 000000000..065b90e53 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site-validate/xdoc/xdoc-2.0.xsd @@ -0,0 +1,2963 @@ + + + + + + + + + Xdoc 2.0 XML Schema. + + This is based on: Extensible HTML version 1.0 Transitional XML Schema + http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd + + For further information, see: + http://maven.apache.org/doxia/references/xdoc-format.html + + + + + + + + ================ Character mnemonic entities ========================= + + XHTML entity sets are identified by the PUBLIC and SYSTEM identifiers: + + PUBLIC "-//W3C//ENTITIES Latin 1 for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent" + + PUBLIC "-//W3C//ENTITIES Special for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent" + + PUBLIC "-//W3C//ENTITIES Symbols for XHTML//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent" + + + + + + ================== Imported Names ==================================== + + + + + + + media type, as per [RFC2045] + + + + + + + + + comma-separated list of media types, as per [RFC2045] + + + + + + + + + a character encoding, as per [RFC2045] + + + + + + + + + a space separated list of character encodings, as per [RFC2045] + + + + + + + + + a language code, as per [RFC3066] + + + + + + + + + a single character, as per section 2.2 of [XML] + + + + + + + + + + + one or more digits + + + + + + + + + + + tabindex attribute specifies the position of the current element + in the tabbing order for the current document. This value must be + a number between 0 and 32767. User agents should ignore leading zeros. + + + + + + + + + + + + space-separated list of link types + + + + + + + + + single or comma-separated list of media descriptors + + + + + + + + + + + a Uniform Resource Identifier, see [RFC2396] + + + + + + + + + a space separated list of Uniform Resource Identifiers + + + + + + + + + date and time information. ISO date format + + + + + + + + + script expression + + + + + + + + + style sheet data + + + + + + + + + used for titles etc. + + + + + + + + + render in this frame + + + + + + + + + + + nn for pixels or nn% for percentage length + + + + + + + + + + + pixel, percentage, or relative + + + + + + + + + + + integer representing length in pixels + + + + + + + + these are used for image maps + + + + + + + + + + + + + + + + comma separated list of lengths + + + + + + + + + + + used for object, applet, img, input and iframe + + + + + + + + + + + + + + + a color using sRGB: #RRGGBB as Hex values + + There are also 16 widely known color names with their sRGB values: + + Black = #000000 Green = #008000 + Silver = #C0C0C0 Lime = #00FF00 + Gray = #808080 Olive = #808000 + White = #FFFFFF Yellow = #FFFF00 + Maroon = #800000 Navy = #000080 + Red = #FF0000 Blue = #0000FF + Purple = #800080 Teal = #008080 + Fuchsia= #FF00FF Aqua = #00FFFF + + + + + + + + + + =================== Generic Attributes =============================== + + + + + + + core attributes common to most elements + id document-wide unique id + class space separated list of classes + style associated style info + title advisory title/amplification + + + + + + + + + + + + internationalization attributes + lang language code (backwards compatible) + xml:lang language code (as per XML 1.0 spec) + dir direction for weak/neutral text + + + + + + + + + + + + + + + + + + attributes for common UI events + onclick a pointer button was clicked + ondblclick a pointer button was double clicked + onmousedown a pointer button was pressed down + onmouseup a pointer button was released + onmousemove a pointer was moved onto the element + onmouseout a pointer was moved away from the element + onkeypress a key was pressed and released + onkeydown a key was pressed down + onkeyup a key was released + + + + + + + + + + + + + + + + + + attributes for elements that can get the focus + accesskey accessibility key character + tabindex position in tabbing order + onfocus the element got the focus + onblur the element lost the focus + + + + + + + + + + + + + + + + + + text alignment for p, div, h1-h6. The default is + align="left" for ltr headings, "right" for rtl + + + + + + + + + + + + + + + + + =================== Text Elements ==================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + these can only occur at block level + + + + + + + + + + + + + + + + + + + + + + "Inline" covers inline or "text-level" element + + + + + + + + + + + ================== Block level elements ============================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Flow" mixes block and inline and is used for list items etc. + + + + + + + + + + + + + ================== Content models for exclusions ===================== + + + + + + + a elements use "Inline" excluding a + + + + + + + + + + + + + + + pre uses "Inline" excluding img, object, applet, big, small, + font, or basefont + + + + + + + + + + + + + + + + form uses "Flow" excluding form + + + + + + + + + + + + + button uses "Flow" but excludes a, form, form controls, iframe + + + + + + + + + + + + + + + + + + + + + + + + + ================ Document Head ======================================= + + + + + + + + + + + + + + + + + + + + content model is "head.misc" combined with a single + title and an optional base element in any order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The title element is not considered part of the flow of text. + It should be displayed, for example as the page header or + window title. Exactly one title is required per document. + + + + + + + + + + + + document base URI + + + + + + + + + + + + + generic metainformation + + + + + + + + + + + + + + + + Relationship values can be used in principle: + + a) for document specific toolbars/menus when used + with the link element in document head e.g. + start, contents, previous, next, index, end, help + b) to link to a separate style sheet (rel="stylesheet") + c) to make a link to a script (rel="script") + d) by stylesheets to control how collections of + html nodes are rendered into printed documents + e) to make a link to a printable version of this document + e.g. a PostScript or PDF version (rel="alternate" media="print") + + + + + + + + + + + + + + + + + + + style info, which may include CDATA sections + + + + + + + + + + + + + + + + script statements, which may include CDATA sections + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non script-based rendering + + + + + + + + + + + + + + ======================= Frames ======================================= + + + + + + + inline subwindow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alternate content container for non frame-based rendering + + + + + + + + + + + + + + =================== Document Body ==================================== + + + + + + + generic language/style container + + + + + + + + + + + + + + + =================== Paragraphs ======================================= + + + + + + + + + + + + + + + + + =================== Headings ========================================= + + There are six levels of headings from h1 (the most important) + to h6 (the least important). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Lists ============================================ + + + + + + + Unordered list bullet styles + + + + + + + + + + + + + Unordered list + + + + + + + + + + + + + + + + + + + + + + Ordered list numbering style + + 1 arabic numbers 1, 2, 3, ... + a lower alpha a, b, c, ... + A upper alpha A, B, C, ... + i lower roman i, ii, iii, ... + I upper roman I, II, III, ... + + The style is applied to the sequence number which by default + is reset to 1 for the first list item in an ordered list. + + + + + + + + + Ordered (numbered) list + + + + + + + + + + + + + + + + + + + + + + + single column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + multiple column list (DEPRECATED) + + + + + + + + + + + + + + + + + + + + + LIStyle is constrained to: "(ULStyle|OLStyle)" + + + + + + + + + list item + + + + + + + + + + + + + + + + definition lists - dt for term, dd for its definition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Address ========================================== + + + + + + + information on author + + + + + + + + + + + + + + + =================== Horizontal Rule ================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Preformatted Text ================================ + + + + + + + content is "Inline" excluding + "img|object|applet|big|small|sub|sup|font|basefont" + + + + + + + + + + + + + + + + =================== Block-like Quotes ================================ + + + + + + + + + + + + + + + + + =================== Text alignment =================================== + + + + + + + center content + + + + + + + + + + + + + + =================== Inserted/Deleted Text ============================ + + ins/del are allowed in block and inline content, but its + inappropriate to include block content within an ins element + occurring in inline content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================== The Anchor Element ================================ + + + + + + + content is "Inline" except that anchors shouldn't be nested + + + + + + + + + + + + + + + + + + + + + + + + + ===================== Inline Elements ================================ + + + + + + + generic language/style container + + + + + + + + + + + + + + + I18N BiDi over-ride + + + + + + + + + + + + + + + + + + + + + + + + + + forced line break + + + + + + + + + + + + + + + + + + + + + emphasis + + + + + + + + + + + + + + + strong emphasis + + + + + + + + + + + + + + + definitional + + + + + + + + + + + + + + + program code + + + + + + + + + + + + + + + sample + + + + + + + + + + + + + + + something user would type + + + + + + + + + + + + + + + variable + + + + + + + + + + + + + + + citation + + + + + + + + + + + + + + + abbreviation + + + + + + + + + + + + + + + acronym + + + + + + + + + + + + + + + inlined quote + + + + + + + + + + + + + + + + subscript + + + + + + + + + + + + + + + superscript + + + + + + + + + + + + + + + fixed pitch font + + + + + + + + + + + + + + + italic font + + + + + + + + + + + + + + + bold font + + + + + + + + + + + + + + + bigger font + + + + + + + + + + + + + + + smaller font + + + + + + + + + + + + + + + underline + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + strike-through + + + + + + + + + + + + + + + base font size + + + + + + + + + + + + + + local change to font + + + + + + + + + + + + + + + + + + ==================== Object ====================================== + + object is used to embed objects as part of HTML pages. + param elements should precede other content. Parameters + can also be expressed as attribute/value pairs on the + object element itself when brevity is desired. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + param is used to supply a named property value. + In XML it would seem natural to follow RDF and support an + abbreviated syntax where the param elements are replaced + by attribute value pairs on the object start tag. + + + + + + + + + + + + + + + + + + + + + + =================== Java applet ================================== + + One of code or object attributes must be present. + Place param elements before other content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + =================== Images =========================================== + + To avoid accessibility problems for people who aren't + able to see the image, you should provide a text + description using the alt and longdesc attributes. + In addition, avoid the use of server-side image maps. + + + + + + + + + + + + + + + + usemap points to a map element which may be in this document + or an external document, although the latter is not widely supported + + + + + + + + + + + + + + + + + + + + ================== Client-side image maps ============================ + + These can be placed in the same document or grouped in a + separate document although this isn't yet widely supported + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================ Forms =============================================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Each label must not contain more than ONE field + Label elements shouldn't be nested. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + form control + + + + + + + + + + the name attribute is required for all but submit & reset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option selector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + option group + + + + + + + + + + + + + + + + + + + + + + selectable choice + + + + + + + + + + + + + + + + + + + + + + + + + + + multi-line text field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The fieldset element is used to group form fields. + Only one legend element should occur in the content + and if present should only be preceded by whitespace. + + NOTE: this content model is different from the XHTML 1.0 DTD, + closer to the intended content model in HTML4 DTD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fieldset label + + + + + + + + + + + + + + + + + Content is "Flow" excluding a, form and form controls + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single-line text input control (DEPRECATED) + + + + + + + + + + + + ======================= Tables ======================================= + + Derived from IETF HTML table standard, see [RFC1942] + + + + + + + The border attribute sets the thickness of the frame around the + table. The default units are screen pixels. + + The frame attribute specifies which parts of the frame around + the table should be rendered. The values are not the same as + CALS to avoid a name clash with the valign attribute. + + + + + + + + + + + + + + + + + + + The rules attribute defines which rules to draw between cells: + + If rules is absent then assume: + "none" if border is absent or border="0" otherwise "all" + + + + + + + + + + + + + + + horizontal placement of table relative to document + + + + + + + + + + + + + horizontal alignment attributes for cell contents + + char alignment char, e.g. char=':' + charoff offset for alignment char + + + + + + + + + + + + + + + + + + + + + vertical alignment attributes for cell contents + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use thead to duplicate headers when breaking table + across page boundaries, or for static headers when + tbody sections are rendered in scrolling panel. + + Use tfoot to duplicate footers when breaking table + across page boundaries, or for static footers when + tbody sections are rendered in scrolling panel. + + Use multiple tbody sections when rules are needed + between groups of table rows. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + colgroup groups a set of col elements. It allows you to group + several semantically related columns together. + + + + + + + + + + + + + + + + + + col elements define the alignment properties for cells in + one or more columns. + + The width attribute specifies the width of the columns, e.g. + + width=64 width in screen pixels + width=0.5* relative width of 0.5 + + The span attribute causes the attributes of one + col element to apply to more than one column. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Scope is simpler than headers attribute for common tables + + + + + + + + + + + + + th is for headers, td for data and for cells acting as both + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ================== Xdoc Specific ===================================== + + + + + + ================ Document Structure ================================== + + + + + + 2.0.0 + + The <document/> element is the root of the Xdoc descriptor. + + + + + + + 2.0.0 + + Optional properties element for this document element. + + + + + + 2.0.0 + + Optional head element for this document element. + + + + + + 2.0.0 + + Required body element for this document element. + + + + + + + + 2.0.0 + + The identifier of this document element. + + + + + + + + + ================ Document Properties ================================= + + + + + + + + + 2.0.0 + + Required title element for the document element. + + + + + + 2.0.0 + + Optional author element for the document element. + + + + + + 2.0.0 + + Optional creation/last updated date for the document element. + + + + + + + + + + 2.0.0 + + An author element. + + + + + + 2.0.0 + + The email attribute for the author element. + + + + + + + + + 2.0.0 + + An date element. The date is recommended (but it is not a requirement) to be align to + the ISO-8601 standard, i.e.: YYYY-MM-DD. + + + + + + + + =================== Document Body ==================================== + + + + + + 2.0.0 + + The body element. + + + + + + + 2.0.0 + + A section in the body element. + + + + + + + + + + + 2.0.0 + + A section element. + + + + + + + + + + + 2.0.0 + + A subsection in the section element. + + + + + + + + + + + + 2.0.0 + + A subsection element. + + + + + + + + + + + + + + + + + ================ Document Addons ===================================== + See "special.extra" group. + + + + + + 2.0.0 + + A source element. + + + + + + + + + + + + 2.0.0 + + A macro element. + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/apt.apt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/apt.apt new file mode 100644 index 000000000..77a214ffc --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/apt.apt @@ -0,0 +1,61 @@ + ----- + Anchors / Links in APT + ----- + Lukas Theussl + ----- + May 2008 + ----- + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Links + + {Anchor} + {cdc.html} + Link to {{Anchor}}. + Link to anchor {{{Anchor}showing alternate text}}. + {Anchor with space} and {{{Anchor_with_space}link to it}}. + Link to {{http://maven.apache.org/}}. + Link to {{{http://maven.apache.org/}Maven home page}}. + Link to {{{./cdc.html}other document}} and within {{{cdc.html}same document}}. + Link to {{{/index.html}root document}}. + +Section formatting: <> <<>> + +* SubSection formatting: <> <<>> + + <> <<>> + +{No Default Anchor in Section Title with Explicit Anchor} + +~~ DOXIASITETOOLS-146 comments are not rendered + +TOC Macro + +%{toc} + +Echo Macro + +%{echo|param1=value1|param2=value2} + +Snippet Macro + +%{snippet|id=macrotest|file=src/test/resources/site/site.xml} diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/cdc.apt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/cdc.apt new file mode 100644 index 000000000..b61ce893c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/cdc.apt @@ -0,0 +1,107 @@ + ----- + Plexus Component Descriptor Creator + ----- + Trygve Laugstøl + ----- + 25th May 2005 + ----- + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Plexus Component Descriptor Creator + + <> + + The Component Descriptor Creator (or CDC for short) is a tool that will create + the <<>> file from your Java code. It uses a set of JavaDoc + tags to gather the information it needs to create the component descriptor. + +* Component Tags + +*-------------------------------*--------------*---------------------------------------------* +| <> | <> | <> | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.component | y | Marker tag to mark a class as a Plexus component. | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.role | y | The role of the component. | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.version | n | The component version. | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.lifecycle-handler | n | The lifecycle handler of the component. | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.instatiation-strategy | n | The instantiation strategy of the component. | +*-------------------------------*---*--------------------------------------------------------* + ++---+ +/** + * @plexus.component + * @plexus.lifecycle-handler plexus-configurable + */ +public class DefaultActionManager + extends AbstractLogEnabled + implements ActionManager, Serviceable +{ ++---+ +~~ [example-class-tags] Example Class Tags + +* Requirement Tags + + These tags are used on fields to state requirements. + +*-------------------------------*----------------*-------------------------------------------* +| <> | <> | <> | +*-------------------------------*-----*------------------------------------------------------* +| @plexus.requirement | y | Marker tag to mark this field as requirement | +*-------------------------------*-----*------------------------------------------------------* +| @plexus.role | y/n | Only required if the field is a List or Map | +*-------------------------------*-----*------------------------------------------------------* +| @plexus.role-hint | n | | +*-------------------------------*-----*------------------------------------------------------* + ++---+ +/** + * @plexus.requirement + */ +private ActionManager actionManager; ++---+ +~~ [example-requirement-tags] Example Requirement Tags + +* Configuration Tags + + Note that the default value tag won't be required in the future when these + tags will be used to generate a separate configuration declaration section. + +*-------------------------------*--------------*---------------------------------------------* +| <> | <> | <> | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.configuration | y | Marker tag to mark this field as configureable | +*-------------------------------*---*--------------------------------------------------------* +| @plexus.default-value | y | Sets the default value for the configuration field | +*-------------------------------*---*--------------------------------------------------------* + ++---+ +/** + * @plexus.requirement + * @plexus.role-hint velocity + */ +private Renderer renderer; ++---+ +~~ [example-requirement-tags] Example Requirement Tags diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/extension.apt.not.at.end.apt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/extension.apt.not.at.end.apt new file mode 100644 index 000000000..01472896b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/extension.apt.not.at.end.apt @@ -0,0 +1,32 @@ +----- + extension in filename + ----- + Hervé Boutemy + ----- + 2015-12-19 + ----- + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +RenderingContext + + When transforming source file name into output file name, it is important to use last ".apt" and not first one + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/interpolation.apt b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/interpolation.apt new file mode 100644 index 000000000..1c938bb5a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/apt/interpolation.apt @@ -0,0 +1,33 @@ + ----- + Project goodies that are available in the project + ----- + Maven Monkey + ----- + + ----- + +~~ 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. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Things you can do with Velocity + + * project = ${project.name} ${project.version} + * $currentFileName + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/anchor.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/anchor.confluence new file mode 100644 index 000000000..425dcbabd --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/anchor.confluence @@ -0,0 +1,7 @@ +h1. Section Title + +{anchor:start}Simple paragraph. + +Simple paragraph{anchor:end} + +Simple {anchor:middle} paragraph. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/code.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/code.confluence new file mode 100644 index 000000000..d4f73d53a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/code.confluence @@ -0,0 +1,21 @@ +Simple paragraph. + +{code:title=Cat.java} +public class Cat { + public void sitOn(Mat mat) { + // ... code here + } +} +{code} + +Another paragraph (the title of the code block is ignored). + +Simple paragraph with embedded code +{code} +public class Cat { + public void sitOn(Mat mat) { + // ... code here + } +} +{code} +in the same paragraph (this didn't work until DOXIA-181). See also DOXIA-302. \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/escapes.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/escapes.confluence new file mode 100644 index 000000000..82e453369 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/escapes.confluence @@ -0,0 +1,6 @@ +Simple paragraph with asterisk \* and underline \_. + +Simple paragraph with asterisk \*not bold\* and underline \_not italic\_. + +Simple paragraph with normal \character escaped. + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/figure.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/figure.confluence new file mode 100644 index 000000000..5a35dca45 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/figure.confluence @@ -0,0 +1,22 @@ +Simple paragraph. + +!images/build-by-maven-white.png! + +Simple paragraph with attempted inline !image.jpg! (should fail). + +DOXIA-361: + +!images/build-by-maven-white.png! With caption on same line + +!images/build-by-maven-white.png!\\ +With caption underneath and linebreak + +!images/build-by-maven-white.png! +With caption underneath and no linebreak + +!images/build-by-maven-white.png! +With *bold* caption underneath + +DOXIA-303: + +!images/build-by-maven-white.png|align=right, vspace=4! \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/linebreak.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/linebreak.confluence new file mode 100644 index 000000000..d076a6dae --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/linebreak.confluence @@ -0,0 +1,5 @@ +Line\\ +break. + +Line\\with 2\\inline\\ +Breaks. \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/link.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/link.confluence new file mode 100644 index 000000000..7d543680e --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/link.confluence @@ -0,0 +1,5 @@ +Line with [middle] link + +Line with link at the [end] + +Line with [link#anchor] and [#simple] anchor and [alias|link] \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-format.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-format.confluence new file mode 100644 index 000000000..9dbbefb5b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-format.confluence @@ -0,0 +1,6 @@ + +A paragraph with *_bold italic_* _*italic bold*_ *{{bold monospaced}}* + +A paragraph with *bold _italic_* _italic *bold*_ *bold {{monospaced}}* + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list-heterogenous.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list-heterogenous.confluence new file mode 100644 index 000000000..37884187c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list-heterogenous.confluence @@ -0,0 +1,4 @@ +* A top level list item +*# A nested list item +*# Another nested list item +* Back at the top level diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list.confluence new file mode 100644 index 000000000..2a4e719aa --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/nested-list.confluence @@ -0,0 +1,8 @@ + +A paragraph + +* A top level list item +** A nested list item +** Another nested list item +* Back at the top level + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/note-tip-info.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/note-tip-info.confluence new file mode 100644 index 000000000..75e83a8d5 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/note-tip-info.confluence @@ -0,0 +1,27 @@ +Simple paragraph. + +{note:title=Be Careful} +The body of the note here.. +{note} + +Tip + +{tip:title=Guess What?} +The body of the tip here.. +{tip} + +Info + +{info:title=Some +Info} +The body of the info here.. +{info} + +Quote + +{quote:title=Simon Says} +The body of the *quote* here.. +{quote} + +(In all cases there is no way to reverse the Doxia output +back to confluence because they are all rendered as a defitionList.) \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-figure.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-figure.confluence new file mode 100644 index 000000000..6d3b02756 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-figure.confluence @@ -0,0 +1,6 @@ + +A paragraph +!images/logo.png! +with a figure + +Another paragraph diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-header.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-header.confluence new file mode 100644 index 000000000..5467b0bef --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-header.confluence @@ -0,0 +1,6 @@ + +A paragraph +h2. A header +with a header + +Another paragraph diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-list.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-list.confluence new file mode 100644 index 000000000..df0d9f87d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/paragraph-list.confluence @@ -0,0 +1,8 @@ + +A paragraph +* A nested list item +* Another nested list item +with two lines + +Back at the top level + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/section.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/section.confluence new file mode 100644 index 000000000..d28fe4ccf --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/section.confluence @@ -0,0 +1,9 @@ +h1. Section1 + +h2. Section2 +h3. Section3 + +h4. Section4 +h5. Section5 + +h1. TitleWithLeadingSpace diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-list.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-list.confluence new file mode 100644 index 000000000..7b213a408 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-list.confluence @@ -0,0 +1,14 @@ +* Simple paragraph with *bold* and _italic_ text. +* Here is a link to [JIRA|http://jira.codehaus.org] +* Here is a link with no text [http://jira.codehaus.org] +* This is some {{monospaced}} text. +* Item with no formatting + +Paragraph + +* One bullet + +* A list item with +more than one line + +*bold text, not a list!* \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-paragraph.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-paragraph.confluence new file mode 100644 index 000000000..00d15f231 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/simple-paragraph.confluence @@ -0,0 +1,8 @@ +Simple paragraph with *bold* and _italic_ text. + +Here is a link to [JIRA|http://jira.codehaus.org] + +Here is a link with no text [http://jira.codehaus.org] + +This is some {{monospaced}} text. + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table-link.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table-link.confluence new file mode 100644 index 000000000..cdab3a2ae --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table-link.confluence @@ -0,0 +1,4 @@ +|| Version || Download || Format || +|0.1.1 |[Download|http://example.com/release.0.1.1/ex-win32-win32.x86.zip]| 12-12-2008 | zip | +|0.1.2 |[http://example.com/release.0.1.2/ex-win32-win32.x86.zip]| 04-12-2008 | zip | +|0.1.3 |[Download|http://example.com/release.0.1.3/ex-win32-win32.x86.zip]| 03-11-2008 | zip | \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table.confluence new file mode 100644 index 000000000..227a8e0fc --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/table.confluence @@ -0,0 +1,13 @@ + +Simple table: + +||heading 1||heading 2||heading 3|| +|col A1|col A2|col A3| +|col B1|col B2|col B3| + +Table with links (DOXIA-301): + +|| Version || Download || Date || Format || +|0.1.1 | [Download|http://example.com/release.0.1.1/ex-win32-win32.x86.zip] | 12-12-2008 | zip | +|0.1.2 | [Download|http://example.com/release.0.1.2/ex-win32-win32.x86.zip] | 04-12-2008 | zip | +|0.1.3 | [Download|http://example.com/release.0.1.3/ex-win32-win32.x86.zip] | 03-11-2008 | zip | \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/test.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/test.confluence new file mode 100644 index 000000000..3d5daeac1 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/test.confluence @@ -0,0 +1,98 @@ +This the way that we would like to *translate* sites that are primarily +authored in confluence while at the same time having the site be rendered in +a _standard way on a static website_. + +---- + +Here is a link to [JIRA|http://jira.codehaus.org] + +Here is a link with no text [http://jira.codehaus.org] + +This is some {{monospaced}} text. + +* item one +** foo +** bar +* item two +* item three + +Some more text + +# number one +# number two +# number three + +||one||two||three|| +|foo|bar|baz| + +h1. I am h1 + +this is how you would code a mojo! + +{code} +public class MyMojo + extends AbstractMojo +{ + /** + * @parameter expression="${plugin.artifacts}" + * @required + */ + private List pluginArtifacts; + + public void execute() + throws MojoExecutionException + { + ... + for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); ) + { + Artifact pluginArtifact = (Artifact) i.next(); + } + ... + } +} +{code} + +h2. I am h2 + +this is the way of the world + +{noformat} +public class MyMojo + extends AbstractMojo +{ + /** + * @parameter expression="${plugin.artifacts}" + * @required + */ + private List pluginArtifacts; + + public void execute() + throws MojoExecutionException + { + ... + for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); ) + { + Artifact pluginArtifact = (Artifact) i.next(); + } + ... + } +} +{noformat} + +h3. I am h3 + +this is the way of the world + +h4. I am h4 + +this is the way of the world + +h5. I am h5 + +this is the way of the world + +h1. Answered Questions + +h3. What can I do to get the Maven love? + +Well, you just have to be calm and the maven love will come your way. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/unknown-macro.confluence b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/unknown-macro.confluence new file mode 100644 index 000000000..3b84a0c6c --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/confluence/confluence/unknown-macro.confluence @@ -0,0 +1,5 @@ +{unknown:start}Simple paragraph. + +Simple paragraph{unknown:end}. + +Simple {unknown:middle} paragraph. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/docbook.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/docbook.xml new file mode 100644 index 000000000..585096525 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/docbook.xml @@ -0,0 +1,31 @@ + + + + +
    + Title + +
    + Section title + + Some content +
    +
    \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/sdocbook_full.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/sdocbook_full.xml new file mode 100644 index 000000000..085eeda36 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/docbook/sdocbook_full.xml @@ -0,0 +1,546 @@ + + + + + + +
    + + + + Title + Or How I Learned to Live + citetitle + + Configuring Menus + + CorpAuthor + + Date + + Assn. + + + articleinfo Abstract Title + articleinfo Abstract Content + + + + PhD + Main + Author + Sr. + D + + Director of Cooperative Efforts + Cooperative Engineering + + + Title + super hero. + + + + Dr + Peter + Doe + Sr. + Spiderman + + Director of Cooperative Efforts + Cooperative Engineering + + + Title + super hero. + + + + Authorgroup CorpAuthor + + + Dr + Authorgroup + Author + Sr. + D + + Director of Cooperative Efforts + Cooperative Engineering + + + authorblurb title + He's a super hero in his spare time. + + + + + Dr + Authorgroup + Editor + Sr. + Spiderman + + Director of Cooperative Efforts + Cooperative Engineering + + + authorblurb title + He's a super hero in his spare time. + + + + + Dr + Authorgroup + OtherCredit + Sr. + AlterEgo + + Director of Cooperative Efforts + Cooperative Engineering + + + authorblurb title + He's a super hero in his spare time. + + + + + + + + 1996 + 1997 + me + + + Edition + + + Dr + Peter + Parker + Sr. + Spiderman + + Director of Cooperative Efforts + Cooperative Engineering + + + Title + Peter's a super hero in his spare time. + + + + + key1 + key2 + + + + The Eiffel Tower + + + + Dr + John + Doe + Sr. + Spiderman + + Director of Cooperative Efforts + Cooperative Engineering + + + Title + Peter's a super hero in his spare time. + + + + 1994 + The TeX User's Group + beta + 15 + 3 + No notice is required. + + + + 0.91 + 11 Dec 1996 + ndw + Bug fixes + + + 0.90 + 30 Nov 1996 + ndw + long write up + + + + + Electronic Publishing + SGML (Computer program language) + + + + + + + Abstract Title + In brief. + + +
    + + A trivial example of recursive sections. + + Section title +
    Sub-section title +
    Sub-sub-section title +
    Sub-sub-sub-section title +
    Sub-sub-sub-sub-section title + + + William Shakespeare + Epigraph: What say you? + + + + The Assn. of Computing Machinery + has published Developing SGML DTDs. + + + + In UNIX command ls + with the option is used to get a directory listing. + + + + The output from the date command is eg + Sun Nov 16, 1997 21:03:29. + + + + An email address is eg yo@tu.com. + + + + The symbolic constants for error numbers are defined in + errno.h in + /usr/include/sys. + + + + This software is provided as is. + + + + Execute the command with + command + + filename. + + + + This is hosted at helio.com. + The name pipipo is traded. + The name pipipo is registered. + The name pipipo is copyrighted. + The name pipipo is serviced. + The name pipipo is traded. + + + + At the system prompt, enter xyzzy. + + + + A Sidebar + Sidebar content. + + + + A link to an element with an XRefLabel: . + A straight link generates the cross-reference text: . + + + + + Phaser sound effect + + + A sound effect. + + + + + + + + Italic font. + Bold font. + Monospaced font. + + + + A Real Function + + real function()Error! + + + +
    + William Shakespeare + Blockquote: What say you? +
    + + + An annual percentage rate of + 3.27%The prime rate. + will be charged on all balances. + + + + List item 1. + List item 2.Paragraph contained in list item 2. + + Sub-list item 1. + Sub-list item 2. + + + List item 3. Force end of list: + + + Verbatim text not contained in list item 3 + + + Numbered item 1. + + Numbered item A. + Numbered item B. + + + Numbered item 2. + + + List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]]. + + + Defined term 1of definition list. + Defined term 2of definition list.Verbatim text + in a box + + +
    + Figure title + + + Figure caption + +
    + + + Here is an inline image: . + + + + + No grid, no caption: + + + + + + + cell + cell + + + cell + cell + + + + + + + + + + + header + header + + + + + cell + cell + + + + + + + + + + + + + + Horizontal Span + a3a4a5 + + + + + f1f2f3f4f5 + + + + + b1b2b3b4 + + Vertical Span + + + c1 + Span Both + c4 + + + d1d4d5 + + + + + + + + + + + + + + + + a1a2a5 + + + b1b2b3b4b5 + + + c1c2c5 + + + + + + + + + + a1b1c1 + + + a2 + + + + b2a1b2b1b2c1 + + + b2a2b2b2b2c2 + + + b2a3b2b3b2c3 + + + + c2 + + + a3b3c3 + + + + + + + Anchor. + Link to Anchor. + Link to http://www.pixware.fr. + Link to showing alternate text. + Link to Pixware home page. + + + + Upcoming Changes + Future versions of this feature may not be backward-compatible. + +
    +
    +
    +
    +
    + + + A Test Bibliography + Books + + + The World Wide Web Journal + 21. + O'Reilly & Associates, Inc. and + Winter, 1996. + + + + + + Demonstration Appendix + This appendix demonstrates an appendix. + + +
    \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/fml/faq.fml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/fml/faq.fml new file mode 100644 index 000000000..a6843c541 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/fml/faq.fml @@ -0,0 +1,60 @@ + + + + + + + + Contributing + + + One stupid question & a silly answer? + +

    + A paragraph with a local link, + a link to another source document, + an external link + with entities, an italic text and non-US-ASCII characters: àéèç. +

    +
    +
    + +
    + + + Using Maven + + + How do I disable a report on my site? + +

    + Test nested <source></source> tags (DOXIA-16): +

    + 1.5]]> +
    +
    + +
    + +
    + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/resources/confluence/images/build-by-maven-white.png b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/resources/confluence/images/build-by-maven-white.png new file mode 100644 index 0000000000000000000000000000000000000000..7d44c9c2e5742bdf8649ad282f83208f1da9b982 GIT binary patch literal 2260 zcmV;_2rKuAP)4hTLUyOQ{PVbVY5&Y3g!&hN~bnR7}ZgkXUt ziC%zU0gf+&kEv>t|d$x|zXw1mS0D%1b{8z7DF%0wW-8(XBFc`A3vVI|O z^!N97baWg(eE86zLn4uA_wL=Zb@+UKU|=8sJb3V6XlSUctSl!dhm4xd=KJ^W|8h2q zR4NS%3yX+|NKQ`f?d=7Cf`Wo)&z=E5TU%REQIXYZefjbwRvsQ6zIyfQojZ3l8V#{v zv)R(q)39Vr2GBPsa+apV2%%fIZY3ln0Kl+1Y8c*(xe3X6sWFH9kH*UDDLl)ZN`}u~;f9D%P!A2LK5P2`MQl z(b3TuDUC++_U+qm01k;n!Z1u+TwGjS+}X2d^Yil+3Pn;B-~q z{Qdm_z{kf&EEb1^gw)j3R904!x}#RBj~+c578Vv16olc}xpQZGd;7k9`>@WHD_2M| z{%VB2fNVCK&1U^_rTW_bx`C@MK&%ZR^ybZ*=;&yb zN);0mV>X+~OA`|lRVtNAr7A8i#zL)DyJycHxm+$5izO0?QmM?$%p@6le0*H3R;yI1 z=;-LCrlu1oPI!8HIypHhmCA~Wig|;>WHON!GbSbmcN`jxhJ=GssnlpRR;zVzaF8J4 z>+3sJhW@0w{LH6-`(Afr<9kMWBXoSUM7Dox&JGJtojOI96z3EG z*uH)HWN?qO7x!`hzQnzLg5JL3Ui^ps%X$n4`+YK2S-yNZo>gC8kJmXUC#D?-i_a7IlwdR(Kkw#T>s)<( zJ!ZVTycREBO!{t;H9|r{F#q)FQ_`LjAsBnPnnKk2PZ;V3*7{M#@%jyBNObh|^_fg2 zd|f0I3eTTEPf=83VhUbHWgRft|{%MRRMp6H>seM7wV6&k5Vn7H0DDSDT_wn(;aaUDU zWi%QoiptK;CgqIWB$bwy78Mm?w@oI~&6_tPBO~$kExCLno}10)mX;RGM?^%-PjqOt zTFi(#=@4C7NJmxEVK7l6G0yhEp_Lq9)1fj}S-2%Mdrv$L~tStVt%xVSheDG9e5EX$6J zj8GIMm&=bIKaK;TqoYG05D0}r0!Kqb1E0?q2n1`_uAR{_f0E{OgnR$~y~Sd|+0n_# z2@6L?MsUQ^H0|QzLJoDKqobtlneyk|8`Sp{cp}PUC5RRQ^8?;2;Iss$eWk%*n3$Nr z(73v~e)3}s219#$yTM=(2n6o#?!LahxUO>?H!v`O%bZ*;$Ideh!!Qg0h{fVXix$lf i91DLtEx@rr0RIK2cl{g~?Z1Nn0000}s literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/site.xml new file mode 100644 index 000000000..d18c154a9 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/site.xml @@ -0,0 +1,57 @@ + + + + + + + + + + Plexus + http://plexus.codehaus.org/images/plexus-logo.png + http://plexus.codehaus.org + + + http://media.codehaus.org/images/unity-codehaus-logo.png + http://www.codehaus.org + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/attributes.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/attributes.xml new file mode 100644 index 000000000..efbee99e5 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/attributes.xml @@ -0,0 +1,93 @@ + + + + + + +
    +

    + + Project +

    + + + + + + + + + + + + + + + +
    NameTelephone
    Telephone555 77 854555 77 855
    555 77 854555 77 855
    +

    + + underline strike-through subscript superscript +

    +

    + + bold-italic italic-bold +

    + +

    + + + +

    + Link to anchor. + Link to anchor showing alternate text. + Link to Maven home page. + Link to other document. + Link to other document. + Link to other document. + Link to other document. + Link to root document. +

    + + + some source + some source + +
    + + +
    + + +
    + +
    + + +
    + + +
    + + +
    + + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/head.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/head.xml new file mode 100644 index 000000000..dccf3526a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/head.xml @@ -0,0 +1,39 @@ + + + + + + + + Page Title + John Doe + + + + + + + + + +

    Oi!

    + + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/javascript.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/javascript.xml new file mode 100644 index 000000000..16114a46a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/javascript.xml @@ -0,0 +1,45 @@ + + + + + + + Hello + + + + +
    + +

    You should see a JavaScript alert...

    + + + +
    + + + +
    \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/macro.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/macro.xml new file mode 100644 index 000000000..b57d9c50b --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/macro.xml @@ -0,0 +1,75 @@ + + + + + + Test DOXIA-96 + Vincent Siveton + + + + +
    + +
    + +
    + + + + +
    + + +
    + + + + +
    + +
    +

    bla bla...

    +

    bla bla...

    +

    SUB SUB TITLE 1.2.1

    +
    +

    bla bla...

    +
    + +
    +

    bla bla...

    +

    bla bla...

    +

    SUB SUB TITLE 2.2.1

    +
    +

    bla bla...

    +
    + +
    +

    bla bla...

    +

    bla bla...

    +

    SUB SUB TITLE 3.2.1

    +
    +

    bla bla...

    +
    + + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/misc.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/misc.xml new file mode 100644 index 000000000..352f24be2 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/multipleblock.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/multipleblock.xml new file mode 100644 index 000000000..b9cc6d7ec --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/multipleblock.xml @@ -0,0 +1,44 @@ + + + + + + Test DOXIA-93 + Vincent Siveton + + +
    +

    text

    +
      +
    • list1
    • +
    +

    text2

    +
      +
    • list1
    • +
    +

    text3

    +
      +
    • list1

    • +
    +
    + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/nestedItems.xml b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/nestedItems.xml new file mode 100644 index 000000000..5ffa9b83d --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/site/xdoc/nestedItems.xml @@ -0,0 +1,195 @@ + + + + + + Test List Issue + + + +
    +

    Unordered lists

    +

    + Below is an unordered list, followed by six paragraphs. +

    +
      +
    • + Item 1. +
        +
      • +

        Item 11.

        +
      • +
      • +

        Item 12.

        +
      • +
      • + Item 13. +
      • +
      • + Item 14. +
      • +
      +
    • +
    • + Item 2. +
    • +
    • + Item 3. +
    • +
    • + Item 4. +
        +
      • + Item 41. +
      • +
      • + Item 42. +
      • +
      • + Item 43. +
      • +
      • + Item 44. +
      • +
      +
    • +
    +

    + Paragraph 1 below list. +

    +

    + Paragraph 2 below list. +

    +

    + Paragraph 3 below list. +

    +

    + Paragraph 4 below list. +

    +

    + Paragraph 5 below list. +

    +

    + Paragraph 6 below list. +

    + +

    Ordered lists

    +

    + Below is an ordered list, followed by six paragraphs. +

    +
      +
    1. + Item 1. +
        +
      1. + Item 11. +
      2. +
      3. + Item 12. +
      4. +
      5. + Item 13. +
      6. +
      7. + Item 14. +
      8. +
      +
    2. +
    3. + Item 2. +
    4. +
    5. + Item 3. +
    6. +
    7. + Item 4. +
        +
      1. + Item 41. +
      2. +
      3. + Item 42. +
      4. +
      5. + Item 43. +
      6. +
      7. + Item 44. +
      8. +
      +
    8. +
    +

    + Paragraph 1 below list. +

    +

    + Paragraph 2 below list. +

    +

    + Paragraph 3 below list. +

    +

    + Paragraph 4 below list. +

    +

    + Paragraph 5 below list. +

    +

    + Paragraph 6 below list. +

    + +

    Definition lists

    +

    + Below is a definition list, followed by six paragraphs. +

    +
    +
    Term 1.
    +
    Description 1.
    +
    Term 2.
    +
    Description 2. +
    +
    Term 21.
    +
    Description 21.
    +
    +
    +
    +

    + Paragraph 1 below list. +

    +

    + Paragraph 2 below list. +

    +

    + Paragraph 3 below list. +

    +

    + Paragraph 4 below list. +

    +

    + Paragraph 5 below list. +

    +

    + Paragraph 6 below list. +

    +
    + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/xhtml-lat1.ent b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/xhtml-lat1.ent new file mode 100644 index 000000000..ffee223eb --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-site-renderer/src/test/resources/xhtml-lat1.ent @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-skin-model/pom.xml b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/pom.xml new file mode 100644 index 000000000..1ea9e4e06 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/pom.xml @@ -0,0 +1,88 @@ + + + + + + 4.0.0 + + + org.apache.maven.doxia + doxia-sitetools + 1.9.3-SNAPSHOT + ../pom.xml + + + doxia-skin-model + + Doxia Sitetools :: Skin Model + The Skin Model defines metadata for Doxia Sitetools skins. + + + + org.codehaus.plexus + plexus-utils + + + + + + + org.codehaus.modello + modello-maven-plugin + + + src/main/mdo/skin.mdo + + + 1.7.0 + 1.7.0 + + + + descriptor + generate-sources + + java + xpp3-reader + xsd + + + + descriptor-xdoc + pre-site + + xdoc + + + + descriptor-xsd + pre-site + + xsd + + + ${project.build.directory}/generated-site/resources/xsd + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/main/mdo/skin.mdo b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/main/mdo/skin.mdo new file mode 100644 index 000000000..99b7eeb28 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/main/mdo/skin.mdo @@ -0,0 +1,101 @@ + + + + + + skin + Skin + This is a reference for the skin descriptor used in Doxia Sitetools, stored as META-INF/maven/skin.xml.

    +

    An XSD is available at:

    + + ]]>
    + + + + package + org.apache.maven.doxia.site.skin + + + + + + SkinModel + <skin> element is the root of the skin descriptor. + ]]> + 1.7.0+ + + + + + + + prerequisites + 1.7.0+ + Describes the prerequisites in the build environment for using this skin. + + Prerequisites + + + + encoding + 1.7.0+ + Encoding of text content, like the Velocity template itself. + String + false + + + + + 1.7.0+ + + + + + + + + Prerequisites + 1.7.0+ + Describes the prerequisites a skin can have. + + + doxiaSitetools + 1.7.0+ + String + 1.7 + + + false + + + + +
    diff --git a/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/apt/index.apt b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/apt/index.apt new file mode 100644 index 000000000..0aa95a5ef --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/apt/index.apt @@ -0,0 +1,54 @@ + ----- + Doxia Sitetools Skin Model + ----- + Hervé Boutemy + ----- + 2016-02-07 + ----- + + ~~ 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. + + ~~ NOTE: For help with the syntax of this file, see: + ~~ http://maven.apache.org/doxia/references/apt-format.html + +Doxia Sitetools Skin Model + + This is strictly the model for Doxia Sitetools Skin Model, used in skins in <<>>. + + The following are generated from this model: + + * {{{./apidocs/index.html}Java sources}} with Reader for the Xpp3 XML parser + + * A {{{./skin.html}Descriptor Reference}} + + * An XSD referenced in the {{{./skin.html}Descriptor Reference}}. + +* Doxia Sitetools Skin + + A Doxia Sitetools skin must contain a Velocity template named <<>>: it will be called + by {{{../doxia-site-renderer/index.html}Doxia Sitetools – Site Renderer}} with additional variables about the + rendered document as documented in the {{{../doxia-site-renderer/index.html#Site_Template}Site Template section}}, + the main variable being <<>>. + + Maven team provides {{{/skins/}a collection of skins}} for projects use. + + Some documentation is available on {{{/plugins/maven-site-plugin/examples/creatingskins.html}how to create a new skin}}, + by copying other skins to benefit from examples of breadcrumbs or menu generation from + {{{../doxia-decoration-model/index.html}decoration model}}. + + Since Doxia Sitetools 1.7, a skin descriptor can be added in <<>>. diff --git a/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/site.xml b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/site.xml new file mode 100644 index 000000000..4e1193a07 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/doxia-skin-model/src/site/site.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/pom.xml b/Java-base/maven-doxia-sitetools/src/pom.xml new file mode 100644 index 000000000..f1cf8c081 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/pom.xml @@ -0,0 +1,411 @@ + + + + + + 4.0.0 + + + org.apache.maven + maven-parent + 34 + ../../pom/maven/pom.xml + + + org.apache.maven.doxia + doxia-sitetools + 1.9.3-SNAPSHOT + pom + + Doxia Sitetools + Doxia Sitetools generates sites, consisting of static and dynamic content that was generated by Doxia. + https://maven.apache.org/doxia/doxia-sitetools/ + 2005 + + + doxia-decoration-model + doxia-skin-model + doxia-integration-tools + doxia-site-renderer + doxia-doc-renderer + + + + scm:git:https://gitbox.apache.org/repos/asf/maven-doxia-sitetools.git + scm:git:https://gitbox.apache.org/repos/asf/maven-doxia-sitetools.git + https://github.com/apache/maven-doxia-sitetools/tree/${project.scm.tag} + master + + + jira + https://issues.apache.org/jira/browse/DOXIASITETOOLS + + + Jenkins + https://builds.apache.org/job/doxia-sitetools/ + + + + apache.website + scm:svn:https://svn.apache.org/repos/asf/maven/doxia/website/components/${maven.site.path} + + + + + 7 + 1.9.1 + doxia-sitetools-archives/doxia-sitetools-LATEST + 2020-02-19T07:03:09Z + + + + + + + org.apache.maven.doxia + doxia-logging-api + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-sink-api + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-core + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-apt + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-confluence + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-docbook-simple + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-fml + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-markdown + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-xdoc + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-xhtml + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-module-xhtml5 + ${doxiaVersion} + + + org.apache.maven.doxia + doxia-decoration-model + ${project.version} + + + org.apache.maven.doxia + doxia-skin-model + ${project.version} + + + + commons-io + commons-io + 2.6 + + + + org.codehaus.plexus + plexus-container-default + 1.0-alpha-30 + + + org.codehaus.plexus + plexus-i18n + 1.0-beta-10 + + + org.codehaus.plexus + plexus-component-api + + + + + org.codehaus.plexus + plexus-velocity + 1.2 + + + org.codehaus.plexus + plexus-utils + 3.0.22 + + + + org.apache.velocity + velocity + 1.7 + + + + junit + junit + 4.13 + + + + + + + + src/main/resources + + + ${project.build.directory}/generated-site/xsd + + **/*.xsd + + + + + + + + org.codehaus.mojo + clirr-maven-plugin + + 1.1 + + + + org.apache.maven.plugins + maven-site-plugin + + scm:svn:https://svn.apache.org/repos/asf/maven/doxia/website/components/${maven.site.path} + + + + org.apache.maven.plugins + maven-scm-publish-plugin + + ${maven.site.cache}/doxia/${maven.site.path} + + + + org.apache.rat + apache-rat-plugin + + + src/test/resources/site/confluence/confluence/*.confluence + src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.vm + src/test/resources/org/apache/maven/doxia/siterenderer/velocity-toolmanager.expected.txt + src/test/resources/dtd/xhtml-*.ent + src/test/resources/dtd/xhtml1-transitional.dtd + src/test/resources/xhtml-lat1.ent + + + + + + + + org.codehaus.plexus + plexus-component-metadata + + + + generate-metadata + + + + + + org.codehaus.mojo + clirr-maven-plugin + + + verify + + check + + + + org/apache/maven/doxia/site/decoration/* + org/apache/maven/doxia/site/decoration/inheritance/*URLContainer + + org/apache/maven/doxia/siterenderer/*Renderer + org/apache/maven/doxia/siterenderer/sink/* + + org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer + org/apache/maven/doxia/docrenderer/itext/AbstractITextRender + + org/apache/maven/doxia/siterenderer/SiteRenderingContext + + + + + + + maven-enforcer-plugin + + + enforce-bytecode-version + + enforce + + + + + ${maven.compiler.target} + + + true + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.2 + compile + + + + + + + + + reporting + + + + org.apache.maven.plugins + maven-changes-plugin + 2.6 + false + + Type,Key,Summary,Resolution,Assignee + 1000 + true + Key + + + + + jira-report + + + + + + org.apache.maven.plugins + maven-jxr-plugin + + + non-aggregate + + jxr + + + + aggregate + + aggregate + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + Doxia Site Renderer + org.apache.maven.doxia.siterenderer* + + + Doxia Decoration Model + org.apache.maven.doxia.site.decoration* + + + Doxia Skin Model + org.apache.maven.doxia.site.skin* + + + Doxia Integration Tools + org.apache.maven.doxia.tools* + + + Doxia Document Renderer + org.apache.maven.doxia.docrenderer* + + + + + + non-aggregate + + javadoc + + + + aggregate + + aggregate + + + + + + + + + jigsaw + + [1.9,) + + + true + + + + diff --git a/Java-base/maven-doxia-sitetools/src/src/site/resources/download.cgi b/Java-base/maven-doxia-sitetools/src/src/site/resources/download.cgi new file mode 100644 index 000000000..1b178d2e6 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/src/site/resources/download.cgi @@ -0,0 +1,22 @@ +#!/bin/sh +# +# 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. +# +# Just call the standard mirrors.cgi script. It will use download.html +# as the input template. +exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $* \ No newline at end of file diff --git a/Java-base/maven-doxia-sitetools/src/src/site/resources/images/doxia-sitetools-deps.png b/Java-base/maven-doxia-sitetools/src/src/site/resources/images/doxia-sitetools-deps.png new file mode 100644 index 0000000000000000000000000000000000000000..410e33a76ac25e28c849cfbfedc3a6ad5238753f GIT binary patch literal 55078 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 P00000NkvXXu0mjfc% + + + + + + + Doxia + https://maven.apache.org/doxia/images/doxia-logo.png + https://maven.apache.org/doxia/ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-doxia-sitetools/src/src/site/xdoc/download.xml.vm b/Java-base/maven-doxia-sitetools/src/src/site/xdoc/download.xml.vm new file mode 100644 index 000000000..252fdae39 --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/src/site/xdoc/download.xml.vm @@ -0,0 +1,126 @@ + + + + + + + Download ${project.name} Source + + +
    + +

    ${project.name} ${project.version} is distributed in source format. Use a source archive if you intend to build + ${project.name} yourself. Otherwise, simply use the ready-made binary artifacts from central repository.

    + +

    You will be prompted for a mirror - if the file is not found on yours, please be patient, as it may take 24 + hours to reach all mirrors.

    + +

    In order to guard against corrupted downloads/installations, it is highly recommended to + verify the signature + of the release bundles against the public KEYS used by the Apache Maven + developers.

    + +

    ${project.name} is distributed under the Apache License, version 2.0.

    + +

    We strongly encourage our users to configure a Maven repository mirror closer to their location, please read How to Use Mirrors for Repositories.

    + + + + +

    + [if-any logo] + + logo + + [end] + The currently selected mirror is + [preferred]. + If you encounter a problem with this mirror, + please select another mirror. + If all mirrors are failing, there are + backup + mirrors + (at the end of the mirrors list) that should be available. +

    + +
    + Other mirrors: + + +
    + +

    + You may also consult the + complete list of + mirrors. +

    + + + + + +

    This is the current stable version of ${project.name}.

    + + + + + + + + + + + + + + + + + + +
    LinkChecksumSignature
    ${project.name} ${project.version} (Source zip)maven/doxia/${project.artifactId}-${project.version}-source-release.zipmaven/doxia/${project.artifactId}-${project.version}-source-release.zip.sha512maven/doxia/${project.artifactId}-${project.version}-source-release.zip.asc
    +
    + + + +

    Older non-recommended releases can be found on our archive site.

    + +
    +
    + +
    + diff --git a/Java-base/maven-doxia-sitetools/src/src/site/xdoc/doxia-sitetools-deps.odg b/Java-base/maven-doxia-sitetools/src/src/site/xdoc/doxia-sitetools-deps.odg new file mode 100644 index 0000000000000000000000000000000000000000..af3825b448ea472727eaddf221e95c230ff9dd87 GIT binary patch literal 14097 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 literal 0 HcmV?d00001 diff --git a/Java-base/maven-doxia-sitetools/src/src/site/xdoc/index.xml b/Java-base/maven-doxia-sitetools/src/src/site/xdoc/index.xml new file mode 100644 index 000000000..818a2a65a --- /dev/null +++ b/Java-base/maven-doxia-sitetools/src/src/site/xdoc/index.xml @@ -0,0 +1,64 @@ + + + + + + + + Doxia Sitetools + Hervé Boutemy + + + + +
    + +

    Doxia Sitetools is an extension of base Doxia component that generates:

    +
      +
    • either HTML sites, adding decoration (header, footer, navigation bar, menu, ...) to content that was generated by Doxia: + a decoration is defined in a site skin,
    • +
    • or documents like RTF or PDF.
    • +
    +

    In addition, Doxia Sitetools processes files with extra .vm extension with Velocity.

    + +

    + Doxia Sitetools Dependencies + + Doxia Site Renderer + Doxia Integration Tools + Doxia Decoration Model + Doxia Skin Model + Doxia Document Renderer + Plexus Velocity + Velocity + Doxia Module XHTML + Doxia Module FO + Doxia Module iText + Doxia Modules + +

    + +
    + + + +
    diff --git a/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/Dockerfile b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/buggy.java b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/buggy.java new file mode 100644 index 000000000..707694137 --- /dev/null +++ b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/buggy.java @@ -0,0 +1,713 @@ +package org.apache.maven.doxia.docrenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.io.xpp3.DocumentXpp3Reader; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.util.XmlValidator; + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.context.Context; + +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.logging.AbstractLogEnabled; + +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.SiteResourceLoader; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + * Abstract document renderer. + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +public abstract class AbstractDocumentRenderer + extends AbstractLogEnabled + implements DocumentRenderer +{ + @Requirement + protected ParserModuleManager parserModuleManager; + + @Requirement + protected Doxia doxia; + + @Requirement + private VelocityComponent velocity; + + /** + * The common base directory of source files. + */ + private String baseDir; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @deprecated since 1.1.2, use {@link #render(Map, File, DocumentModel, DocumentRendererContext)} + */ + public abstract void render( Map filesToProcess, File outputDirectory, + DocumentModel documentModel ) + throws DocumentRendererException, IOException; + + //-------------------------------------------- + // + //-------------------------------------------- + + /** {@inheritDoc} */ + public void render( Collection files, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( files ), outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, documentModel, null ); + } + + /** + * Render an aggregate document from the files found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the aggregate document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + */ + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render a document from the files found in a source directory, depending on a rendering context. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentModel the document model, containing all the metadata, etc. + * If the model contains a TOC, only the files found in this TOC are rendered, + * otherwise all files found under baseDirectory will be processed. + * If the model is null, render all files from baseDirectory individually. + * @param context the rendering context when processing files. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void render( File baseDirectory, File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + render( getFilesToProcess( baseDirectory ), outputDirectory, documentModel, context ); + } + + /** + * Render a document from the files found in baseDirectory. This just forwards to + * {@link #render(File,File,DocumentModel)} with a new DocumentModel. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File, DocumentModel) + */ + public void render( File baseDirectory, File outputDirectory ) + throws DocumentRendererException, IOException + { + render( baseDirectory, outputDirectory, (DocumentModel) null ); + } + + /** + * Render a document from the files found in baseDirectory. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @param outputDirectory the output directory where the document should be generated. + * @param documentDescriptor a file containing the document model. + * If this file does not exist or is null, some default settings will be used. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @see #render(File, File) if documentDescriptor does not exist or is null + * @see #render(Map, File, DocumentModel) otherwise + */ + public void render( File baseDirectory, File outputDirectory, File documentDescriptor ) + throws DocumentRendererException, IOException + { + if ( ( documentDescriptor == null ) || ( !documentDescriptor.exists() ) ) + { + getLogger().warn( "No documentDescriptor found: using default settings!" ); + + render( baseDirectory, outputDirectory ); + } + else + { + render( getFilesToProcess( baseDirectory ), outputDirectory, readDocumentModel( documentDescriptor ), + null ); + } + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.1 + * @deprecated since 1.1.2, use {@link #renderIndividual(Map, File, DocumentRendererContext)} + */ + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Render documents separately for each file found in a Map. + * + * @param filesToProcess the Map of Files to process. The Map should contain as keys the paths of the + * source files (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * @param outputDirectory the output directory where the documents should be generated. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + * @throws java.io.IOException if any + * @since 1.1.2 + */ + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + // nop + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param baseDirectory the directory containing the source files. + * This should follow the standard Maven convention, ie containing all the site modules. + * @return a Map of files to process. + * @throws java.io.IOException in case of a problem reading the files under baseDirectory. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException if any + */ + public Map getFilesToProcess( File baseDirectory ) + throws IOException, DocumentRendererException + { + if ( !baseDirectory.isDirectory() ) + { + getLogger().warn( "No files found to process!" ); + + return new HashMap(); + } + + setBaseDir( baseDirectory.getAbsolutePath() ); + + Map filesToProcess = new LinkedHashMap(); + Map duplicatesFiles = new LinkedHashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( baseDirectory, module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + // TODO: handle in/excludes + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", null, false ); + + String[] extensions = getExtensions( module ); + List docs = new LinkedList( allFiles ); + // Take care of extension case + for ( Iterator it = docs.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, extensions ) ) + { + it.remove(); + } + } + + String[] vmExtensions = new String[extensions.length]; + for ( int i = 0; i < extensions.length; i++ ) + { + vmExtensions[i] = extensions[i] + ".vm"; + } + List velocityFiles = new LinkedList( allFiles ); + // *.xml.vm + for ( Iterator it = velocityFiles.iterator(); it.hasNext(); ) + { + String name = it.next().trim(); + + if ( !endsWithIgnoreCase( name, vmExtensions ) ) + { + it.remove(); + } + } + docs.addAll( velocityFiles ); + + for ( String filePath : docs ) + { + filePath = filePath.trim(); + + if ( filePath.lastIndexOf( '.' ) > 0 ) + { + String key = filePath.substring( 0, filePath.lastIndexOf( '.' ) ); + + if ( duplicatesFiles.containsKey( key ) ) + { + throw new DocumentRendererException( "Files '" + module.getSourceDirectory() + + File.separator + filePath + "' clashes with existing '" + + duplicatesFiles.get( key ) + "'." ); + } + + duplicatesFiles.put( key, module.getSourceDirectory() + File.separator + filePath ); + } + + filesToProcess.put( filePath, module ); + } + } + } + + return filesToProcess; + } + + protected static String[] getExtensions( ParserModule module ) + { + String[] extensions = new String[module.getExtensions().length]; + for ( int i = module.getExtensions().length - 1; i >= 0; i-- ) + { + extensions[i] = '.' + module.getExtensions()[i]; + } + return extensions; + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + protected static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + protected static boolean endsWithIgnoreCase( String str, String[] searchStrs ) + { + for ( String searchStr : searchStrs ) + { + if ( endsWithIgnoreCase( str, searchStr ) ) + { + return true; + } + } + return false; + } + + /** + * Returns a Map of files to process. The Map contains as keys the paths of the source files + * (relative to {@link #getBaseDir() baseDir}), and the corresponding ParserModule as values. + * + * @param files The Collection of source files. + * @return a Map of files to process. + */ + public Map getFilesToProcess( Collection files ) + { + // ---------------------------------------------------------------------- + // Map all the file names to parser ids + // ---------------------------------------------------------------------- + + Map filesToProcess = new HashMap(); + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + String[] extensions = getExtensions( module ); + + String sourceDirectory = File.separator + module.getSourceDirectory() + File.separator; + + for ( String file : files ) + { + // first check if the file path contains one of the recognized source dir identifiers + // (there's trouble if a pathname contains 2 identifiers), then match file extensions (not unique). + + if ( file.indexOf( sourceDirectory ) != -1 ) + { + filesToProcess.put( file, module ); + } + else + { + // don't overwrite if it's there already + if ( endsWithIgnoreCase( file, extensions ) && !filesToProcess.containsKey( file ) ) + { + filesToProcess.put( file, module ); + } + } + } + } + + return filesToProcess; + } + + /** {@inheritDoc} */ + public DocumentModel readDocumentModel( File documentDescriptor ) + throws DocumentRendererException, IOException + { + DocumentModel documentModel; + + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( documentDescriptor ); + documentModel = new DocumentXpp3Reader().read( reader ); + } + catch ( XmlPullParserException e ) + { + throw new DocumentRendererException( "Error parsing document descriptor", e ); + } + finally + { + IOUtil.close( reader ); + } + + return documentModel; + } + + /** + * Sets the current base directory. + * + * @param newDir the absolute path to the base directory to set. + */ + public void setBaseDir( String newDir ) + { + this.baseDir = newDir; + } + + /** + * Return the current base directory. + * + * @return the current base directory. + */ + public String getBaseDir() + { + return this.baseDir; + } + + //-------------------------------------------- + // + //-------------------------------------------- + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + * @deprecated since 1.1.2, use {@link #parse(String, String, Sink, DocumentRendererContext)} + */ + protected void parse( String fullDocPath, String parserId, Sink sink ) + throws DocumentRendererException, IOException + { + parse( fullDocPath, parserId, sink, null ); + } + + /** + * Parse a source document into a sink. + * + * @param fullDocPath absolute path to the source document. + * @param parserId determines the parser to use. + * @param sink the sink to receive the events. + * @param context the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException in case of a parsing error. + * @throws java.io.IOException if the source document cannot be opened. + */ +/** + * Parse a source document into a sink. + * + * @param fullDocPath + * absolute path to the source document. + * @param parserId + * determines the parser to use. + * @param sink + * the sink to receive the events. + * @param context + * the rendering context. + * @throws org.apache.maven.doxia.docrenderer.DocumentRendererException + * in case of a parsing error. + * @throws java.io.IOException + * if the source document cannot be opened. + */ +protected void parse(java.lang.String fullDocPath, java.lang.String parserId, org.apache.maven.doxia.sink.Sink sink, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + if (getLogger().isDebugEnabled()) { + getLogger().debug("Parsing file " + fullDocPath); + } + java.io.Reader reader = null; + try { + java.io.File f = new java.io.File(fullDocPath); + org.apache.maven.doxia.parser.Parser parser = doxia.getParser(parserId); + switch (parser.getType()) { + case org.apache.maven.doxia.parser.Parser.XML_TYPE : + reader = org.codehaus.plexus.util.ReaderFactory.newXmlReader(f); + if (org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer.isVelocityFile(f)) { + reader = getVelocityReader(f, ((org.codehaus.plexus.util.xml.XmlStreamReader) (reader)).getEncoding(), context); + } + if ((context != null) && java.lang.Boolean.TRUE.equals(((java.lang.Boolean) (context.get("validate"))))) { + reader = validate(reader, fullDocPath); + } + break; + case org.apache.maven.doxia.parser.Parser.TXT_TYPE : + case org.apache.maven.doxia.parser.Parser.UNKNOWN_TYPE : + default : + if (org.apache.maven.doxia.docrenderer.AbstractDocumentRenderer.isVelocityFile(f)) { + reader = getVelocityReader(f, context == null ? org.codehaus.plexus.util.ReaderFactory.FILE_ENCODING : context.getInputEncoding(), context); + } else { + { + reader = org.codehaus.plexus.util.ReaderFactory.newReader(f, /* NPEX_NULL_EXP */ + context.getInputEncoding()); + } + } + } + sink.enableLogging(new org.apache.maven.doxia.logging.PlexusLoggerWrapper(getLogger())); + doxia.parse(reader, parserId, sink); + } catch (org.apache.maven.doxia.parser.manager.ParserNotFoundException e) { + throw new org.apache.maven.doxia.docrenderer.DocumentRendererException((((("No parser '" + parserId) + "' found for ") + fullDocPath) + ": ") + e.getMessage(), e); + } catch (org.apache.maven.doxia.parser.ParseException e) { + throw new org.apache.maven.doxia.docrenderer.DocumentRendererException((("Error parsing " + fullDocPath) + ": ") + e.getMessage(), e); + } finally { + org.codehaus.plexus.util.IOUtil.close(reader); + sink.flush(); + } +} + + /** + * Copies the contents of the resource directory to an output folder. + * + * @param outputDirectory the destination folder. + * @throws java.io.IOException if any. + */ + protected void copyResources( File outputDirectory ) + throws IOException + { + File resourcesDirectory = new File( getBaseDir(), "resources" ); + + if ( !resourcesDirectory.isDirectory() ) + { + return; + } + + if ( !outputDirectory.exists() ) + { + outputDirectory.mkdirs(); + } + + copyDirectory( resourcesDirectory, outputDirectory ); + } + + /** + * Copy content of a directory, excluding scm-specific files. + * + * @param source directory that contains the files and sub-directories to be copied. + * @param destination destination folder. + * @throws java.io.IOException if any. + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.isDirectory() && destination.isDirectory() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + /** + * @param documentModel not null + * @return the output name defined in the documentModel without the output extension. If the output name is not + * defined, return target by default. + * @since 1.1.1 + * @see org.apache.maven.doxia.document.DocumentModel#getOutputName() + * @see #getOutputExtension() + */ + protected String getOutputName( DocumentModel documentModel ) + { + String outputName = documentModel.getOutputName(); + if ( outputName == null ) + { + getLogger().info( "No outputName is defined in the document descriptor. Using 'target'" ); + + documentModel.setOutputName( "target" ); + } + + outputName = outputName.trim(); + if ( outputName.toLowerCase( Locale.ENGLISH ).endsWith( "." + getOutputExtension() ) ) + { + outputName = + outputName.substring( 0, outputName.toLowerCase( Locale.ENGLISH ) + .lastIndexOf( "." + getOutputExtension() ) ); + } + documentModel.setOutputName( outputName ); + + return documentModel.getOutputName(); + } + + /** + * TODO: DOXIA-111: we need a general filter here that knows how to alter the context + * + * @param f the file to process, not null + * @param encoding the wanted encoding, not null + * @param context the current render document context not null + * @return a reader with + * @throws DocumentRendererException + */ + private Reader getVelocityReader( File f, String encoding, DocumentRendererContext context ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Velocity render for " + f.getAbsolutePath() ); + } + + SiteResourceLoader.setResource( f.getAbsolutePath() ); + + Context velocityContext = new VelocityContext(); + + if ( context.getKeys() != null ) + { + for ( int i = 0; i < context.getKeys().length; i++ ) + { + String key = (String) context.getKeys()[i]; + + velocityContext.put( key, context.get( key ) ); + } + } + + StringWriter sw = new StringWriter(); + try + { + velocity.getEngine().mergeTemplate( f.getAbsolutePath(), encoding, velocityContext, sw ); + } + catch ( Exception e ) + { + throw new DocumentRendererException( "Error whenn parsing Velocity file " + f.getAbsolutePath() + ": " + + e.getMessage(), e ); + } + + return new StringReader( sw.toString() ); + } + + /** + * @param f not null + * @return true if file has a vm extension, false otherwise. + */ + private static boolean isVelocityFile( File f ) + { + return FileUtils.getExtension( f.getAbsolutePath() ).toLowerCase( Locale.ENGLISH ).endsWith( "vm" ); + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } +} diff --git a/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/metadata.json b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/metadata.json new file mode 100644 index 000000000..0a228777a --- /dev/null +++ b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-AbstractDocumentRenderer_532", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java", + "line": 534, + "npe_method": "parse", + "deref_field": "context", + "npe_class": "AbstractDocumentRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "AbstractDocumentRenderer_532" + } +} diff --git a/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/npe.json b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/npe.json new file mode 100644 index 000000000..e5d1ce951 --- /dev/null +++ b/Java/maven-doxia-sitetools-AbstractDocumentRenderer_532/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/AbstractDocumentRenderer.java", + "line": 534, + "npe_method": "parse", + "deref_field": "context", + "npe_class": "AbstractDocumentRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DecorationUtils_113/Dockerfile b/Java/maven-doxia-sitetools-DecorationUtils_113/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_113/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DecorationUtils_113/buggy.java b/Java/maven-doxia-sitetools-DecorationUtils_113/buggy.java new file mode 100644 index 000000000..3aa767149 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_113/buggy.java @@ -0,0 +1,127 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ + public static Xpp3Dom getCustomChild( Xpp3Dom custom, String path ) + { + String[] elements = path.split( "\\." ); + for ( String element : elements ) + { + if ( custom == null ) + { + return null; + } + custom = custom.getChild( element ); + } + return custom; + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? null : custom.getValue(); + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @param defaultValue + * default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ +public static java.lang.String getCustomValue(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path, java.lang.String defaultValue) { + custom = org.apache.maven.doxia.site.decoration.DecorationUtils.getCustomChild(/* NPEX_NULL_EXP */ + custom, path); + return custom.getValue(); +} +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_113/metadata.json b/Java/maven-doxia-sitetools-DecorationUtils_113/metadata.json new file mode 100644 index 000000000..7ec86e191 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_113/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DecorationUtils_113", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 124, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils", + "repo": "maven-doxia-sitetools", + "bug_id": "DecorationUtils_113" + } +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_113/npe.json b/Java/maven-doxia-sitetools-DecorationUtils_113/npe.json new file mode 100644 index 000000000..1bc58a283 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_113/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 124, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DecorationUtils_78/Dockerfile b/Java/maven-doxia-sitetools-DecorationUtils_78/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_78/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DecorationUtils_78/buggy.java b/Java/maven-doxia-sitetools-DecorationUtils_78/buggy.java new file mode 100644 index 000000000..75dd00cef --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_78/buggy.java @@ -0,0 +1,122 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ +public static org.codehaus.plexus.util.xml.Xpp3Dom getCustomChild(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path) { + java.lang.String[] elements = path.split("\\."); + for (java.lang.String element : elements) { + { + custom = /* NPEX_NULL_EXP */ + custom.getChild(element); + } + } + return custom; +} + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? null : custom.getValue(); + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path, String defaultValue ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? defaultValue : custom.getValue(); + } +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_78/metadata.json b/Java/maven-doxia-sitetools-DecorationUtils_78/metadata.json new file mode 100644 index 000000000..2db8d40c3 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_78/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DecorationUtils_78", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 88, + "npe_method": "getCustomChild", + "deref_field": "custom", + "npe_class": "DecorationUtils", + "repo": "maven-doxia-sitetools", + "bug_id": "DecorationUtils_78" + } +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_78/npe.json b/Java/maven-doxia-sitetools-DecorationUtils_78/npe.json new file mode 100644 index 000000000..94ad4391a --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_78/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 88, + "npe_method": "getCustomChild", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DecorationUtils_98/Dockerfile b/Java/maven-doxia-sitetools-DecorationUtils_98/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_98/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DecorationUtils_98/buggy.java b/Java/maven-doxia-sitetools-DecorationUtils_98/buggy.java new file mode 100644 index 000000000..b829f8b02 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_98/buggy.java @@ -0,0 +1,125 @@ +package org.apache.maven.doxia.site.decoration; + +/* + * 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. + */ + +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Decoration model utilities. + * + * @since 1.7 + */ +public class DecorationUtils +{ + public static boolean isLink( String href ) + { + return StringUtils.isNotBlank( href ) + && ( startsWithAnyIgnoreCase( href, "http:/", "https:/", "ftp:/", "mailto:", "file:/" ) + || href.contains( "://" ) ); + } + + private static boolean startsWithIgnoreCase( String str, String prefix ) + { + if ( str == null || prefix == null ) + { + return ( str == null && prefix == null ); + } + if ( prefix.length() > str.length() ) + { + return false; + } + return str.regionMatches( true, 0, prefix, 0, prefix.length() ); + } + + public static boolean startsWithAnyIgnoreCase( String string, String... searchStrings ) + { + for ( int i = 0; i < searchStrings.length; i++ ) + { + String searchString = searchStrings[i]; + if ( startsWithIgnoreCase( string, searchString ) ) + { + return true; + } + } + return false; + } + + /** + * Helper to get decoration custom DOM element by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return null if any element in the path does not exist + * @since 1.8 + */ + public static Xpp3Dom getCustomChild( Xpp3Dom custom, String path ) + { + String[] elements = path.split( "\\." ); + for ( String element : elements ) + { + if ( custom == null ) + { + return null; + } + custom = custom.getChild( element ); + } + return custom; + } + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ +/** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom + * the custom DOM element + * @param path + * the dotted path to the child + * @return the element value or null if any element in the path does not exist + * @since 1.8 + */ +public static java.lang.String getCustomValue(org.codehaus.plexus.util.xml.Xpp3Dom custom, java.lang.String path) { + custom = org.apache.maven.doxia.site.decoration.DecorationUtils.getCustomChild(/* NPEX_NULL_EXP */ + custom, path); + return custom.getValue(); +} + + /** + * Helper to get decoration custom DOM element value by simply specifying a dotted path. + * + * @param custom the custom DOM element + * @param path the dotted path to the child + * @param defaultValue default value + * @return the element value or the default value if any element in the path does not exist + * @since 1.8 + */ + public static String getCustomValue( Xpp3Dom custom, String path, String defaultValue ) + { + custom = getCustomChild( custom, path ); + return ( custom == null ) ? defaultValue : custom.getValue(); + } +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_98/metadata.json b/Java/maven-doxia-sitetools-DecorationUtils_98/metadata.json new file mode 100644 index 000000000..219c6f8bd --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_98/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DecorationUtils_98", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 107, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils", + "repo": "maven-doxia-sitetools", + "bug_id": "DecorationUtils_98" + } +} diff --git a/Java/maven-doxia-sitetools-DecorationUtils_98/npe.json b/Java/maven-doxia-sitetools-DecorationUtils_98/npe.json new file mode 100644 index 000000000..43859cec3 --- /dev/null +++ b/Java/maven-doxia-sitetools-DecorationUtils_98/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/DecorationUtils.java", + "line": 107, + "npe_method": "getCustomValue", + "deref_field": "custom", + "npe_class": "DecorationUtils" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/Dockerfile b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/buggy.java b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/buggy.java new file mode 100644 index 000000000..6f5f43f73 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + { + relativizeBannerPaths(/* NPEX_NULL_EXP */ + decoration.getBannerLeft(), baseUrl); + } + if (decoration.getBannerRight() != null) { + relativizeBannerPaths(decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + if (decoration.getBody() != null) { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/metadata.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/metadata.json new file mode 100644 index 000000000..e80ce3e1f --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 128, + "npe_method": "resolvePaths", + "deref_field": "getBannerLeft", + "npe_class": "DefaultDecorationModelInheritanceAssembler", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultDecorationModelInheritanceAssembler_126" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/npe.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/npe.json new file mode 100644 index 000000000..9b36f9053 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_126/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 128, + "npe_method": "resolvePaths", + "deref_field": "getBannerLeft", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/Dockerfile b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/buggy.java b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/buggy.java new file mode 100644 index 000000000..31a154fe2 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + if (decoration.getBannerLeft() != null) { + relativizeBannerPaths(decoration.getBannerLeft(), baseUrl); + } + { + relativizeBannerPaths(/* NPEX_NULL_EXP */ + decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + if (decoration.getBody() != null) { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/metadata.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/metadata.json new file mode 100644 index 000000000..461f9e52b --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 131, + "npe_method": "resolvePaths", + "deref_field": "getBannerRight", + "npe_class": "DefaultDecorationModelInheritanceAssembler", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultDecorationModelInheritanceAssembler_131" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/npe.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/npe.json new file mode 100644 index 000000000..a41cc7848 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_131/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 131, + "npe_method": "resolvePaths", + "deref_field": "getBannerRight", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/Dockerfile b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/buggy.java b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/buggy.java new file mode 100644 index 000000000..e02a26aff --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/buggy.java @@ -0,0 +1,458 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.Body; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.LinkItem; +import org.apache.maven.doxia.site.decoration.Logo; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Manage inheritance of the decoration model. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + */ +@Component( role = DecorationModelInheritanceAssembler.class ) +public class DefaultDecorationModelInheritanceAssembler + implements DecorationModelInheritanceAssembler +{ + /** {@inheritDoc} */ + public void assembleModelInheritance( String name, DecorationModel child, DecorationModel parent, + String childBaseUrl, String parentBaseUrl ) + { + if ( parent == null || !child.isMergeParent() ) + { + return; + } + + child.setCombineSelf( parent.getCombineSelf() ); + + URLRebaser urlContainer = new URLRebaser( parentBaseUrl, childBaseUrl ); + + if ( child.getBannerLeft() == null && parent.getBannerLeft() != null ) + { + child.setBannerLeft( parent.getBannerLeft().clone() ); + rebaseBannerPaths( child.getBannerLeft(), urlContainer ); + } + + if ( child.getBannerRight() == null && parent.getBannerRight() != null ) + { + child.setBannerRight( parent.getBannerRight().clone() ); + rebaseBannerPaths( child.getBannerRight(), urlContainer ); + } + + if ( child.isDefaultPublishDate() && parent.getPublishDate() != null ) + { + child.setPublishDate( parent.getPublishDate().clone() ); + } + + if ( child.isDefaultVersion() && parent.getVersion() != null ) + { + child.setVersion( parent.getVersion().clone() ); + } + + if ( child.getEdit() == null && parent.getEdit() != null ) + { + child.setEdit( parent.getEdit() ); + } + + if ( child.getSkin() == null && parent.getSkin() != null ) + { + child.setSkin( parent.getSkin().clone() ); + } + + child.setPoweredBy( mergePoweredByLists( child.getPoweredBy(), parent.getPoweredBy(), urlContainer ) ); + + if ( parent.getLastModified() > child.getLastModified() ) + { + child.setLastModified( parent.getLastModified() ); + } + + if ( child.getGoogleAdSenseClient() == null && parent.getGoogleAdSenseClient() != null ) + { + child.setGoogleAdSenseClient( parent.getGoogleAdSenseClient() ); + } + + if ( child.getGoogleAdSenseSlot() == null && parent.getGoogleAdSenseSlot() != null ) + { + child.setGoogleAdSenseSlot( parent.getGoogleAdSenseSlot() ); + } + + if ( child.getGoogleAnalyticsAccountId() == null && parent.getGoogleAnalyticsAccountId() != null ) + { + child.setGoogleAnalyticsAccountId( parent.getGoogleAnalyticsAccountId() ); + } + + assembleBodyInheritance( name, child, parent, urlContainer ); + + assembleCustomInheritance( child, parent ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void resolvePaths(final org.apache.maven.doxia.site.decoration.DecorationModel decoration, final java.lang.String baseUrl) { + if (baseUrl == null) { + return; + } + if (decoration.getBannerLeft() != null) { + relativizeBannerPaths(decoration.getBannerLeft(), baseUrl); + } + if (decoration.getBannerRight() != null) { + relativizeBannerPaths(decoration.getBannerRight(), baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Logo logo : decoration.getPoweredBy()) { + relativizeLogoPaths(logo, baseUrl); + } + { + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : /* NPEX_NULL_EXP */ + decoration.getBody().getLinks()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.LinkItem linkItem : decoration.getBody().getBreadcrumbs()) { + relativizeLinkItemPaths(linkItem, baseUrl); + } + for (org.apache.maven.doxia.site.decoration.Menu menu : decoration.getBody().getMenus()) { + relativizeMenuPaths(menu.getItems(), baseUrl); + } + } +} + + /** + * Resolves all relative paths between the elements in a banner. The banner element might contain relative paths + * to the oldBaseUrl, these are changed to the newBannerUrl. + * + * @param banner + * @param baseUrl + */ + private void relativizeBannerPaths( final Banner banner, final String baseUrl ) + { + // banner has been checked to be not null, both href and src may be empty or null + banner.setHref( relativizeLink( banner.getHref(), baseUrl ) ); + banner.setSrc( relativizeLink( banner.getSrc(), baseUrl ) ); + } + + private void rebaseBannerPaths( final Banner banner, final URLRebaser urlContainer ) + { + if ( banner.getHref() != null ) // it may be empty + { + banner.setHref( urlContainer.rebaseLink( banner.getHref() ) ); + } + + if ( banner.getSrc() != null ) + { + banner.setSrc( urlContainer.rebaseLink( banner.getSrc() ) ); + } + } + + private void assembleCustomInheritance( final DecorationModel child, final DecorationModel parent ) + { + if ( child.getCustom() == null ) + { + child.setCustom( parent.getCustom() ); + } + else + { + child.setCustom( Xpp3Dom.mergeXpp3Dom( (Xpp3Dom) child.getCustom(), (Xpp3Dom) parent.getCustom() ) ); + } + } + + private void assembleBodyInheritance( final String name, final DecorationModel child, final DecorationModel parent, + final URLRebaser urlContainer ) + { + Body cBody = child.getBody(); + Body pBody = parent.getBody(); + + if ( cBody != null || pBody != null ) + { + if ( cBody == null ) + { + cBody = new Body(); + child.setBody( cBody ); + } + + if ( pBody == null ) + { + pBody = new Body(); + } + + if ( cBody.getHead() == null && pBody.getHead() != null ) + { + cBody.setHead( pBody.getHead() ); + } + + cBody.setLinks( mergeLinkItemLists( cBody.getLinks(), pBody.getLinks(), urlContainer, false ) ); + + if ( cBody.getBreadcrumbs().isEmpty() && !pBody.getBreadcrumbs().isEmpty() ) + { + LinkItem breadcrumb = new LinkItem(); + breadcrumb.setName( name ); + breadcrumb.setHref( "index.html" ); + cBody.getBreadcrumbs().add( breadcrumb ); + } + cBody.setBreadcrumbs( mergeLinkItemLists( cBody.getBreadcrumbs(), pBody.getBreadcrumbs(), urlContainer, + true ) ); + + cBody.setMenus( mergeMenus( cBody.getMenus(), pBody.getMenus(), urlContainer ) ); + + if ( cBody.getFooter() == null && pBody.getFooter() != null ) + { + cBody.setFooter( pBody.getFooter() ); + } + } + } + + private List mergeMenus( final List childMenus, final List parentMenus, + final URLRebaser urlContainer ) + { + List menus = new ArrayList( childMenus.size() + parentMenus.size() ); + + for ( Menu menu : childMenus ) + { + menus.add( menu ); + } + + int topCounter = 0; + for ( Menu menu : parentMenus ) + { + if ( "top".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( topCounter, clone ); + topCounter++; + } + else if ( "bottom".equals( menu.getInherit() ) ) + { + final Menu clone = menu.clone(); + + rebaseMenuPaths( clone.getItems(), urlContainer ); + + menus.add( clone ); + } + } + + return menus; + } + + private void relativizeMenuPaths( final List items, final String baseUrl ) + { + for ( MenuItem item : items ) + { + relativizeLinkItemPaths( item, baseUrl ); + relativizeMenuPaths( item.getItems(), baseUrl ); + } + } + + private void rebaseMenuPaths( final List items, final URLRebaser urlContainer ) + { + for ( MenuItem item : items ) + { + rebaseLinkItemPaths( item, urlContainer ); + rebaseMenuPaths( item.getItems(), urlContainer ); + } + } + + private void relativizeLinkItemPaths( final LinkItem item, final String baseUrl ) + { + item.setHref( relativizeLink( item.getHref(), baseUrl ) ); + } + + private void rebaseLinkItemPaths( final LinkItem item, final URLRebaser urlContainer ) + { + item.setHref( urlContainer.rebaseLink( item.getHref() ) ); + } + + private void relativizeLogoPaths( final Logo logo, final String baseUrl ) + { + logo.setImg( relativizeLink( logo.getImg(), baseUrl ) ); + relativizeLinkItemPaths( logo, baseUrl ); + } + + private void rebaseLogoPaths( final Logo logo, final URLRebaser urlContainer ) + { + logo.setImg( urlContainer.rebaseLink( logo.getImg() ) ); + rebaseLinkItemPaths( logo, urlContainer ); + } + + private List mergeLinkItemLists( final List childList, final List parentList, + final URLRebaser urlContainer, boolean cutParentAfterDuplicate ) + { + List items = new ArrayList( childList.size() + parentList.size() ); + + for ( LinkItem item : parentList ) + { + if ( !items.contains( item ) && !childList.contains( item ) ) + { + final LinkItem clone = item.clone(); + + rebaseLinkItemPaths( clone, urlContainer ); + + items.add( clone ); + } + else if ( cutParentAfterDuplicate ) + { + // if a parent item is found in child, ignore next items (case for breadcrumbs) + // merge ( "B > E", "A > B > C > D" ) -> "A > B > E" (notice missing "C > D") + // see https://issues.apache.org/jira/browse/DOXIASITETOOLS-62 + break; + } + } + + for ( LinkItem item : childList ) + { + if ( !items.contains( item ) ) + { + items.add( item ); + } + } + + return items; + } + + private List mergePoweredByLists( final List childList, final List parentList, + final URLRebaser urlContainer ) + { + List logos = new ArrayList( childList.size() + parentList.size() ); + + for ( Logo logo : parentList ) + { + if ( !logos.contains( logo ) ) + { + final Logo clone = logo.clone(); + + rebaseLogoPaths( clone, urlContainer ); + + logos.add( clone ); + } + } + + for ( Logo logo : childList ) + { + if ( !logos.contains( logo ) ) + { + logos.add( logo ); + } + } + + return logos; + } + + // relativize only affects absolute links, if the link has the same scheme, host and port + // as the base, it is made into a relative link as viewed from the base + private String relativizeLink( final String link, final String baseUri ) + { + if ( link == null || baseUri == null ) + { + return link; + } + + // this shouldn't be necessary, just to swallow mal-formed hrefs + try + { + final URIPathDescriptor path = new URIPathDescriptor( baseUri, link ); + + return path.relativizeLink().toString(); + } + catch ( IllegalArgumentException e ) + { + return link; + } + } + + /** + * URL rebaser: based on an old and a new path, can rebase a link based on old path to a value based on the new + * path. + */ + private static class URLRebaser + { + + private final String oldPath; + + private final String newPath; + + /** + * Construct a URL rebaser. + * + * @param oldPath the old path. + * @param newPath the new path. + */ + URLRebaser( final String oldPath, final String newPath ) + { + this.oldPath = oldPath; + this.newPath = newPath; + } + + /** + * Get the new path. + * + * @return the new path. + */ + public String getNewPath() + { + return this.newPath; + } + + /** + * Get the old path. + * + * @return the old path. + */ + public String getOldPath() + { + return this.oldPath; + } + + /** + * Rebase only affects relative links, a relative link wrt an old base gets translated, + * so it points to the same location as viewed from a new base + */ + public String rebaseLink( final String link ) + { + if ( link == null || getOldPath() == null ) + { + return link; + } + + if ( link.contains( "${project." ) ) + { + throw new IllegalArgumentException( "site.xml late interpolation ${project.*} expression found" + + " in link: '" + link + "'. Use early interpolation ${this.*}" ); + } + + final URIPathDescriptor oldPath = new URIPathDescriptor( getOldPath(), link ); + + return oldPath.rebaseLink( getNewPath() ).toString(); + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/metadata.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/metadata.json new file mode 100644 index 000000000..2bd209274 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 137, + "npe_method": "resolvePaths", + "deref_field": "getBody", + "npe_class": "DefaultDecorationModelInheritanceAssembler", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultDecorationModelInheritanceAssembler_141" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/npe.json b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/npe.json new file mode 100644 index 000000000..19914fc51 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultDecorationModelInheritanceAssembler_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/DefaultDecorationModelInheritanceAssembler.java", + "line": 137, + "npe_method": "resolvePaths", + "deref_field": "getBody", + "npe_class": "DefaultDecorationModelInheritanceAssembler" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/buggy.java new file mode 100644 index 000000000..2a14c2ead --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/buggy.java @@ -0,0 +1,1178 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public java.util.Map locateDocumentFiles(org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext, boolean editable) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + java.util.Map files = new java.util.LinkedHashMap(); + java.util.Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + // look in every site directory (in general src/site or target/generated-site) + for (java.io.File siteDirectory : siteRenderingContext.getSiteDirectories()) { + if (siteDirectory.exists()) { + java.util.Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for (org.apache.maven.doxia.parser.module.ParserModule module : modules) { + java.io.File moduleBasedir = new java.io.File(siteDirectory, module.getSourceDirectory()); + java.lang.String excludes = /* NPEX_NULL_EXP */ + moduleExcludes.get(module.getParserId()); + addModuleFiles(siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, editable); + } + } + } + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for (org.apache.maven.doxia.siterenderer.ExtraDoxiaModuleReference module : siteRenderingContext.getModules()) { + try { + org.apache.maven.doxia.parser.module.ParserModule parserModule = parserModuleManager.getParserModule(module.getParserId()); + java.lang.String excludes = (moduleExcludes == null) ? null : moduleExcludes.get(module.getParserId()); + addModuleFiles(siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, files, editable); + } catch (org.apache.maven.doxia.parser.module.ParserModuleNotFoundException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Unable to find module: " + e.getMessage(), e); + } + } + return files; +} + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/metadata.json new file mode 100644 index 000000000..09fc32fdc --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_190", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 186, + "npe_method": "locateDocumentFiles", + "deref_field": "moduleExcludes", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_190" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/npe.json new file mode 100644 index 000000000..d7a4ae3bd --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_190/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 186, + "npe_method": "locateDocumentFiles", + "deref_field": "moduleExcludes", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/buggy.java new file mode 100644 index 000000000..acfd6afc5 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/buggy.java @@ -0,0 +1,1177 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ +/** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext + * the site rendering context + * @return a Velocity tools managed context + */ +protected org.apache.velocity.context.Context createToolManagedVelocityContext(org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) { + java.util.Locale locale = siteRenderingContext.getLocale(); + java.lang.String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + org.apache.velocity.tools.config.EasyFactoryConfiguration config = new org.apache.velocity.tools.config.EasyFactoryConfiguration(false); + config.property("safeMode", java.lang.Boolean.FALSE); + config.toolbox(org.apache.velocity.tools.Scope.REQUEST).tool(org.apache.velocity.tools.generic.ContextTool.class).tool(org.apache.velocity.tools.generic.LinkTool.class).tool(org.apache.velocity.tools.generic.LoopTool.class).tool(org.apache.velocity.tools.generic.RenderTool.class); + config.toolbox(org.apache.velocity.tools.Scope.APPLICATION).property("locale", locale).tool(org.apache.velocity.tools.generic.AlternatorTool.class).tool(org.apache.velocity.tools.generic.ClassTool.class).tool(org.apache.velocity.tools.generic.ComparisonDateTool.class).property("format", dateFormat).tool(org.apache.velocity.tools.generic.ConversionTool.class).property("dateFormat", dateFormat).tool(org.apache.velocity.tools.generic.DisplayTool.class).tool(org.apache.velocity.tools.generic.EscapeTool.class).tool(org.apache.velocity.tools.generic.FieldTool.class).tool(org.apache.velocity.tools.generic.MathTool.class).tool(org.apache.velocity.tools.generic.NumberTool.class).tool(org.apache.velocity.tools.generic.ResourceTool.class).property("bundles", new java.lang.String[]{ "site-renderer" }).tool(org.apache.velocity.tools.generic.SortTool.class).tool(org.apache.velocity.tools.generic.XmlTool.class); + org.apache.velocity.tools.config.FactoryConfiguration customConfig = org.apache.velocity.tools.config.ConfigurationUtils.findInClasspath(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.TOOLS_LOCATION); + { + config.addConfiguration(/* NPEX_NULL_EXP */ + customConfig); + } + org.apache.velocity.tools.ToolManager manager = new org.apache.velocity.tools.ToolManager(false, false); + manager.configure(config); + return manager.createContext(); +} + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/metadata.json new file mode 100644 index 000000000..cd8e8c2b8 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_525", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 514, + "npe_method": "createToolManagedVelocityContext", + "deref_field": "customConfig", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_525" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/npe.json new file mode 100644 index 000000000..58e29ad93 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_525/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 514, + "npe_method": "createToolManagedVelocityContext", + "deref_field": "customConfig", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/buggy.java new file mode 100644 index 000000000..5f9eeeebf --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/buggy.java @@ -0,0 +1,1180 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ +/** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content + * the document content to be merged into the template + * @param siteRenderingContext + * the site rendering context + * @return + */ +protected org.apache.velocity.context.Context createSiteTemplateVelocityContext(org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) { + // first get the context from document + org.apache.velocity.context.Context context = createDocumentVelocityContext(content.getRenderingContext(), siteRenderingContext); + // then add data objects from rendered document + // Add infos from document + context.put("authors", content.getAuthors()); + context.put("shortTitle", content.getTitle()); + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + java.lang.String title = ""; + if ((siteRenderingContext.getDecoration() != null) && (siteRenderingContext.getDecoration().getName() != null)) { + title = siteRenderingContext.getDecoration().getName(); + } else { + { + title = /* NPEX_NULL_EXP */ + siteRenderingContext.getDefaultWindowTitle(); + } + } + if (title.length() > 0) { + title += " – ";// Symbol Name: En Dash, Html Entity: – + + } + title += content.getTitle(); + context.put("title", title); + context.put("headContent", content.getHead()); + context.put("bodyContent", content.getBody()); + // document date (got from Doxia Sink date() API) + java.lang.String documentDate = content.getDate(); + if (org.codehaus.plexus.util.StringUtils.isNotEmpty(documentDate)) { + context.put("documentDate", documentDate); + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try { + // we support only ISO 8601 date + java.util.Date creationDate = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(documentDate); + context.put("creationDate", creationDate); + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMdd"); + context.put("dateCreation", sdf.format(creationDate)); + } catch (java.text.ParseException e) { + getLogger().warn(((("Could not parse date '" + documentDate) + "' from ") + content.getRenderingContext().getInputName()) + " (expected yyyy-MM-dd format), ignoring!"); + } + } + // document rendering context, to get eventual inputName + context.put("docRenderingContext", content.getRenderingContext()); + return context; +} + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/metadata.json new file mode 100644 index 000000000..16e1a372f --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_647", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 651, + "npe_method": "createSiteTemplateVelocityContext", + "deref_field": "getDefaultWindowTitle", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_647" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/npe.json new file mode 100644 index 000000000..34b3d9d45 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_647/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 651, + "npe_method": "createSiteTemplateVelocityContext", + "deref_field": "getDefaultWindowTitle", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/buggy.java new file mode 100644 index 000000000..53c640474 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = /* NPEX_NULL_EXP */ + skinModel.getEncoding(); + template = (encoding == null) ? velocity.getEngine().getTemplate(templateName) : velocity.getEngine().getTemplate(templateName, encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + if (old != null) { + java.lang.Thread.currentThread().setContextClassLoader(old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/metadata.json new file mode 100644 index 000000000..5fe659bda --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_736", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 725, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "skinModel", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_736" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/npe.json new file mode 100644 index 000000000..02e49157f --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_736/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 725, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "skinModel", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/buggy.java new file mode 100644 index 000000000..fdaf4cd5f --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = (skinModel == null) ? null : skinModel.getEncoding(); + template = velocity.getEngine().getTemplate(templateName, /* NPEX_NULL_EXP */ + encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + if (old != null) { + java.lang.Thread.currentThread().setContextClassLoader(old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/metadata.json new file mode 100644 index 000000000..d12df53a6 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_738", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 726, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "encoding", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_738" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/npe.json new file mode 100644 index 000000000..c87c1ad1a --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_738/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 726, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "encoding", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/buggy.java new file mode 100644 index 000000000..0515ad48f --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/buggy.java @@ -0,0 +1,1161 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public void mergeDocumentIntoSite(java.io.Writer writer, org.apache.maven.doxia.siterenderer.DocumentContent content, org.apache.maven.doxia.siterenderer.SiteRenderingContext siteRenderingContext) throws org.apache.maven.doxia.siterenderer.RendererException { + java.lang.String templateName = siteRenderingContext.getTemplateName(); + getLogger().debug((("Processing Velocity for template " + templateName) + " on ") + content.getRenderingContext().getInputName()); + org.apache.velocity.context.Context context = createSiteTemplateVelocityContext(content, siteRenderingContext); + java.lang.ClassLoader old = null; + if (siteRenderingContext.getTemplateClassLoader() != null) { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + old = java.lang.Thread.currentThread().getContextClassLoader(); + java.lang.Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader()); + } + try { + org.apache.velocity.Template template; + org.apache.maven.artifact.Artifact skin = siteRenderingContext.getSkin(); + try { + org.apache.maven.doxia.site.skin.SkinModel skinModel = siteRenderingContext.getSkinModel(); + java.lang.String encoding = (skinModel == null) ? null : skinModel.getEncoding(); + template = (encoding == null) ? velocity.getEngine().getTemplate(templateName) : velocity.getEngine().getTemplate(templateName, encoding); + } catch (org.apache.velocity.exception.ParseErrorException pee) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity parsing error while reading the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), pee); + } catch (org.apache.velocity.exception.ResourceNotFoundException rnfe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Could not find the site decoration template " + (skin == null ? ("'" + templateName) + "'" : ("from " + skin.getId()) + " skin"), rnfe); + } + try { + java.io.StringWriter sw = new java.io.StringWriter(); + template.merge(context, sw); + writer.write(sw.toString().replaceAll("\r?\n", org.apache.commons.lang3.SystemUtils.LINE_SEPARATOR)); + } catch (org.apache.velocity.exception.VelocityException ve) { + throw new org.apache.maven.doxia.siterenderer.RendererException("Velocity error while merging site decoration template.", ve); + } catch (java.io.IOException ioe) { + throw new org.apache.maven.doxia.siterenderer.RendererException("IO exception while merging site decoration template.", ioe); + } + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + { + java.lang.Thread.currentThread().setContextClassLoader(/* NPEX_NULL_EXP */ + old); + } + } +} + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ + public SiteRenderingContext createContextForSkin( Artifact skin, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws IOException, RendererException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setSkin( skin ); + + ZipFile zipFile = getZipFile( skin.getFile() ); + InputStream in = null; + + try + { + if ( zipFile.getEntry( SKIN_TEMPLATE_LOCATION ) != null ) + { + context.setTemplateName( SKIN_TEMPLATE_LOCATION ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{skin.getFile().toURI().toURL()} ) ); + } + else + { + context.setTemplateName( DEFAULT_TEMPLATE ); + context.setTemplateClassLoader( getClass().getClassLoader() ); + context.setUsingDefaultTemplate( true ); + } + + ZipEntry skinDescriptorEntry = zipFile.getEntry( SkinModel.SKIN_DESCRIPTOR_LOCATION ); + if ( skinDescriptorEntry != null ) + { + in = zipFile.getInputStream( skinDescriptorEntry ); + + SkinModel skinModel = new SkinXpp3Reader().read( in ); + context.setSkinModel( skinModel ); + + String toolsPrerequisite = + skinModel.getPrerequisites() == null ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + + Package p = DefaultSiteRenderer.class.getPackage(); + String current = ( p == null ) ? null : p.getImplementationVersion(); + + if ( StringUtils.isNotBlank( toolsPrerequisite ) && ( current != null ) + && !matchVersion( current, toolsPrerequisite ) ) + { + throw new RendererException( "Cannot use skin: has " + toolsPrerequisite + + " Doxia Sitetools prerequisite, but current is " + current ); + } + } + } + catch ( XmlPullParserException e ) + { + throw new RendererException( "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + + " skin descriptor from " + skin.getId() + " skin", e ); + } + finally + { + IOUtil.close( in ); + closeZipFile( zipFile ); + } + + return context; + } + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/metadata.json new file mode 100644 index 000000000..a3bbb6bfd --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_773", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 744, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "old", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_773" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/npe.json new file mode 100644 index 000000000..55be19965 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_773/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 744, + "npe_method": "mergeDocumentIntoSite", + "deref_field": "old", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/buggy.java new file mode 100644 index 000000000..c771b6653 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(/* NPEX_NULL_EXP */ + skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = (skinModel.getPrerequisites() == null) ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = (p == null) ? null : p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/metadata.json new file mode 100644 index 000000000..2dd084792 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_821", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 814, + "npe_method": "createContextForSkin", + "deref_field": "skinDescriptorEntry", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_821" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/npe.json new file mode 100644 index 000000000..76c2d1eb4 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_821/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 814, + "npe_method": "createContextForSkin", + "deref_field": "skinDescriptorEntry", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/buggy.java new file mode 100644 index 000000000..1add25b38 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = /* NPEX_NULL_EXP */ + skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = (p == null) ? null : p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/metadata.json new file mode 100644 index 000000000..74677f136 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_828", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 817, + "npe_method": "createContextForSkin", + "deref_field": "getPrerequisites", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_828" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/npe.json new file mode 100644 index 000000000..0a6d2c958 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_828/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 817, + "npe_method": "createContextForSkin", + "deref_field": "getPrerequisites", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/buggy.java new file mode 100644 index 000000000..e680c8ab9 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/buggy.java @@ -0,0 +1,1169 @@ +package org.apache.maven.doxia.siterenderer; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.Restriction; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.Doxia; +import org.apache.maven.doxia.logging.PlexusLoggerWrapper; +import org.apache.maven.doxia.parser.ParseException; +import org.apache.maven.doxia.parser.Parser; +import org.apache.maven.doxia.parser.manager.ParserNotFoundException; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.PublishDate; +import org.apache.maven.doxia.site.skin.SkinModel; +import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.maven.doxia.parser.module.ParserModuleManager; +import org.apache.maven.doxia.parser.module.ParserModuleNotFoundException; +import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; +import org.apache.maven.doxia.util.XmlValidator; +import org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.tools.Scope; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.tools.config.ConfigurationUtils; +import org.apache.velocity.tools.config.EasyFactoryConfiguration; +import org.apache.velocity.tools.config.FactoryConfiguration; +import org.apache.velocity.tools.generic.AlternatorTool; +import org.apache.velocity.tools.generic.ClassTool; +import org.apache.velocity.tools.generic.ComparisonDateTool; +import org.apache.velocity.tools.generic.ContextTool; +import org.apache.velocity.tools.generic.ConversionTool; +import org.apache.velocity.tools.generic.DisplayTool; +import org.apache.velocity.tools.generic.EscapeTool; +import org.apache.velocity.tools.generic.FieldTool; +import org.apache.velocity.tools.generic.LinkTool; +import org.apache.velocity.tools.generic.LoopTool; +import org.apache.velocity.tools.generic.MathTool; +import org.apache.velocity.tools.generic.NumberTool; +import org.apache.velocity.tools.generic.RenderTool; +import org.apache.velocity.tools.generic.ResourceTool; +import org.apache.velocity.tools.generic.SortTool; +import org.apache.velocity.tools.generic.XmlTool; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.PathTool; +import org.codehaus.plexus.util.PropertyUtils; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.codehaus.plexus.velocity.VelocityComponent; + +/** + *

    DefaultSiteRenderer class.

    + * + * @author Emmanuel Venisse + * @author Vincent Siveton + * @since 1.0 + */ +@Component( role = Renderer.class ) +public class DefaultSiteRenderer + extends AbstractLogEnabled + implements Renderer +{ + // ---------------------------------------------------------------------- + // Requirements + // ---------------------------------------------------------------------- + + @Requirement + private VelocityComponent velocity; + + @Requirement + private ParserModuleManager parserModuleManager; + + @Requirement + private Doxia doxia; + + @Requirement + private I18N i18n; + + @Requirement + private PlexusContainer plexus; + + private static final String RESOURCE_DIR = "org/apache/maven/doxia/siterenderer/resources"; + + private static final String DEFAULT_TEMPLATE = RESOURCE_DIR + "/default-site.vm"; + + private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm"; + + private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml"; + + // ---------------------------------------------------------------------- + // Renderer implementation + // ---------------------------------------------------------------------- + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext ) + throws IOException, RendererException + { + return locateDocumentFiles( siteRenderingContext, false ); + } + + /** {@inheritDoc} */ + public Map locateDocumentFiles( SiteRenderingContext siteRenderingContext, + boolean editable ) + throws IOException, RendererException + { + Map files = new LinkedHashMap(); + Map moduleExcludes = siteRenderingContext.getModuleExcludes(); + + // look in every site directory (in general src/site or target/generated-site) + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + if ( siteDirectory.exists() ) + { + Collection modules = parserModuleManager.getParserModules(); + // use every Doxia parser module + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( siteDirectory, module.getSourceDirectory() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, + editable ); + } + } + } + + // look in specific modules directories (used for old Maven 1.x site layout: xdoc and fml docs in /xdocs) + for ( ExtraDoxiaModuleReference module : siteRenderingContext.getModules() ) + { + try + { + ParserModule parserModule = parserModuleManager.getParserModule( module.getParserId() ); + + String excludes = ( moduleExcludes == null ) ? null : moduleExcludes.get( module.getParserId() ); + + addModuleFiles( siteRenderingContext.getRootDirectory(), module.getBasedir(), parserModule, excludes, + files, editable ); + } + catch ( ParserModuleNotFoundException e ) + { + throw new RendererException( "Unable to find module: " + e.getMessage(), e ); + } + } + return files; + } + + private List filterExtensionIgnoreCase( List fileNames, String extension ) + { + List filtered = new LinkedList( fileNames ); + for ( Iterator it = filtered.iterator(); it.hasNext(); ) + { + String name = it.next(); + + // Take care of extension case + if ( !endsWithIgnoreCase( name, extension ) ) + { + it.remove(); + } + } + return filtered; + } + + private void addModuleFiles( File rootDir, File moduleBasedir, ParserModule module, String excludes, + Map files, boolean editable ) + throws IOException, RendererException + { + if ( !moduleBasedir.exists() || ArrayUtils.isEmpty( module.getExtensions() ) ) + { + return; + } + + String moduleRelativePath = + PathTool.getRelativeFilePath( rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath() ); + + List allFiles = FileUtils.getFileNames( moduleBasedir, "**/*.*", excludes, false ); + + for ( String extension : module.getExtensions() ) + { + String fullExtension = "." + extension; + + List docs = filterExtensionIgnoreCase( allFiles, fullExtension ); + + // *..vm + List velocityFiles = filterExtensionIgnoreCase( allFiles, fullExtension + ".vm" ); + + docs.addAll( velocityFiles ); + + for ( String doc : docs ) + { + RenderingContext context = new RenderingContext( moduleBasedir, moduleRelativePath, doc, + module.getParserId(), extension, editable ); + + // TODO: DOXIA-111: we need a general filter here that knows how to alter the context + if ( endsWithIgnoreCase( doc, ".vm" ) ) + { + context.setAttribute( "velocity", "true" ); + } + + String key = context.getOutputName(); + key = StringUtils.replace( key, "\\", "/" ); + + if ( files.containsKey( key ) ) + { + DocumentRenderer renderer = files.get( key ); + + RenderingContext originalContext = renderer.getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + doc + + "' clashes with existing '" + originalDoc + "'." ); + } + // ----------------------------------------------------------------------- + // Handle key without case differences + // ----------------------------------------------------------------------- + for ( Map.Entry entry : files.entrySet() ) + { + if ( entry.getKey().equalsIgnoreCase( key ) ) + { + RenderingContext originalContext = entry.getValue().getRenderingContext(); + + File originalDoc = new File( originalContext.getBasedir(), originalContext.getInputName() ); + + if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + { + throw new RendererException( "File '" + module.getSourceDirectory() + File.separator + + doc + "' clashes with existing '" + originalDoc + "'." ); + } + + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "File '" + module.getSourceDirectory() + File.separator + doc + + "' could clash with existing '" + originalDoc + "'." ); + } + } + } + + files.put( key, new DoxiaDocumentRenderer( context ) ); + } + } + } + + /** {@inheritDoc} */ + public void render( Collection documents, SiteRenderingContext siteRenderingContext, + File outputDirectory ) + throws RendererException, IOException + { + for ( DocumentRenderer docRenderer : documents ) + { + RenderingContext renderingContext = docRenderer.getRenderingContext(); + + File outputFile = new File( outputDirectory, docRenderer.getOutputName() ); + + File inputFile = new File( renderingContext.getBasedir(), renderingContext.getInputName() ); + + boolean modified = !outputFile.exists() || ( inputFile.lastModified() > outputFile.lastModified() ) + || ( siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified() ); + + if ( modified || docRenderer.isOverwrite() ) + { + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating " + outputFile ); + } + + Writer writer = null; + try + { + if ( !docRenderer.isExternalReport() ) + { + writer = WriterFactory.newWriter( outputFile, siteRenderingContext.getOutputEncoding() ); + } + docRenderer.renderDocument( writer, this, siteRenderingContext ); + } + finally + { + IOUtil.close( writer ); + } + } + else + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( inputFile + " unchanged, not regenerating..." ); + } + } + } + } + + /** {@inheritDoc} */ + public void renderDocument( Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext ) + throws RendererException, FileNotFoundException, UnsupportedEncodingException + { + SiteRendererSink sink = new SiteRendererSink( docRenderingContext ); + + File doc = new File( docRenderingContext.getBasedir(), docRenderingContext.getInputName() ); + + Reader reader = null; + try + { + String resource = doc.getAbsolutePath(); + + Parser parser = doxia.getParser( docRenderingContext.getParserId() ); + // DOXIASITETOOLS-146 don't render comments from source markup + parser.setEmitComments( false ); + + // TODO: DOXIA-111: the filter used here must be checked generally. + if ( docRenderingContext.getAttribute( "velocity" ) != null ) + { + getLogger().debug( "Processing Velocity for " + docRenderingContext.getDoxiaSourcePath() ); + try + { + Context vc = createDocumentVelocityContext( docRenderingContext, siteContext ); + + StringWriter sw = new StringWriter(); + + velocity.getEngine().mergeTemplate( resource, siteContext.getInputEncoding(), vc, sw ); + + String doxiaContent = sw.toString(); + + if ( siteContext.getProcessedContentOutput() != null ) + { + // save Velocity processing result, ie the Doxia content that will be parsed after + saveVelocityProcessedContent( docRenderingContext, siteContext, doxiaContent ); + } + + reader = new StringReader( doxiaContent ); + } + catch ( VelocityException e ) + { + throw new RendererException( "Error parsing " + docRenderingContext.getDoxiaSourcePath() + + " as a Velocity template: " + e.getMessage(), e ); + } + + if ( parser.getType() == Parser.XML_TYPE && siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + } + else + { + switch ( parser.getType() ) + { + case Parser.XML_TYPE: + reader = ReaderFactory.newXmlReader( doc ); + if ( siteContext.isValidate() ) + { + reader = validate( reader, resource ); + } + break; + + case Parser.TXT_TYPE: + case Parser.UNKNOWN_TYPE: + default: + reader = ReaderFactory.newReader( doc, siteContext.getInputEncoding() ); + } + } + sink.enableLogging( new PlexusLoggerWrapper( getLogger() ) ); + + doxia.parse( reader, docRenderingContext.getParserId(), sink ); + } + catch ( ParserNotFoundException e ) + { + throw new RendererException( "Error getting a parser for '" + doc + "': " + e.getMessage(), e ); + } + catch ( ParseException e ) + { + StringBuilder errorMsgBuilder = new StringBuilder(); + errorMsgBuilder.append( "Error parsing '" ).append( doc ).append( "': " ); + if ( e.getLineNumber() > 0 ) + { + errorMsgBuilder.append( "line [" ).append( e.getLineNumber() ).append( "] " ); + } + errorMsgBuilder.append( e.getMessage() ); + throw new RendererException( errorMsgBuilder.toString(), e ); + } + catch ( IOException e ) + { + throw new RendererException( "IOException when processing '" + doc + "'", e ); + } + finally + { + sink.flush(); + + sink.close(); + + IOUtil.close( reader ); + } + + mergeDocumentIntoSite( writer, (DocumentContent) sink, siteContext ); + } + + private void saveVelocityProcessedContent( RenderingContext docRenderingContext, SiteRenderingContext siteContext, + String doxiaContent ) + throws IOException + { + if ( !siteContext.getProcessedContentOutput().exists() ) + { + siteContext.getProcessedContentOutput().mkdirs(); + } + + String input = docRenderingContext.getInputName(); + File outputFile = new File( siteContext.getProcessedContentOutput(), + input.substring( 0, input.length() - 3 ) ); + + File outputParent = outputFile.getParentFile(); + if ( !outputParent.exists() ) + { + outputParent.mkdirs(); + } + + FileUtils.fileWrite( outputFile, siteContext.getInputEncoding(), doxiaContent ); + } + + /** + * Creates a Velocity Context with all generic tools configured wit the site rendering context. + * + * @param siteRenderingContext the site rendering context + * @return a Velocity tools managed context + */ + protected Context createToolManagedVelocityContext( SiteRenderingContext siteRenderingContext ) + { + Locale locale = siteRenderingContext.getLocale(); + String dateFormat = siteRenderingContext.getDecoration().getPublishDate().getFormat(); + + EasyFactoryConfiguration config = new EasyFactoryConfiguration( false ); + config.property( "safeMode", Boolean.FALSE ); + config.toolbox( Scope.REQUEST ) + .tool( ContextTool.class ) + .tool( LinkTool.class ) + .tool( LoopTool.class ) + .tool( RenderTool.class ); + config.toolbox( Scope.APPLICATION ).property( "locale", locale ) + .tool( AlternatorTool.class ) + .tool( ClassTool.class ) + .tool( ComparisonDateTool.class ).property( "format", dateFormat ) + .tool( ConversionTool.class ).property( "dateFormat", dateFormat ) + .tool( DisplayTool.class ) + .tool( EscapeTool.class ) + .tool( FieldTool.class ) + .tool( MathTool.class ) + .tool( NumberTool.class ) + .tool( ResourceTool.class ).property( "bundles", new String[] { "site-renderer" } ) + .tool( SortTool.class ) + .tool( XmlTool.class ); + + FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath( TOOLS_LOCATION ); + + if ( customConfig != null ) + { + config.addConfiguration( customConfig ); + } + + ToolManager manager = new ToolManager( false, false ); + manager.configure( config ); + + return manager.createContext(); + } + + /** + * Create a Velocity Context for a Doxia document, containing every information about rendered document. + * + * @param sink the site renderer sink for the document + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createDocumentVelocityContext( RenderingContext renderingContext, + SiteRenderingContext siteRenderingContext ) + { + Context context = createToolManagedVelocityContext( siteRenderingContext ); + // ---------------------------------------------------------------------- + // Data objects + // ---------------------------------------------------------------------- + + context.put( "relativePath", renderingContext.getRelativePath() ); + + String currentFileName = renderingContext.getOutputName().replace( '\\', '/' ); + context.put( "currentFileName", currentFileName ); + + context.put( "alignedFileName", PathTool.calculateLink( currentFileName, renderingContext.getRelativePath() ) ); + + context.put( "decoration", siteRenderingContext.getDecoration() ); + + Locale locale = siteRenderingContext.getLocale(); + context.put( "locale", locale ); + context.put( "supportedLocales", Collections.unmodifiableList( siteRenderingContext.getSiteLocales() ) ); + + context.put( "currentDate", new Date() ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateRevision", sdf.format( new Date() ) ); + + context.put( "publishDate", siteRenderingContext.getPublishDate() ); + + PublishDate publishDate = siteRenderingContext.getDecoration().getPublishDate(); + DateFormat dateFormat = new SimpleDateFormat( publishDate.getFormat(), locale ); + context.put( "dateFormat", dateFormat ); + + // doxiaSiteRendererVersion + InputStream inputStream = this.getClass().getResourceAsStream( "/META-INF/" + + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties" ); + Properties properties = PropertyUtils.loadProperties( inputStream ); + if ( inputStream == null ) + { + getLogger().debug( "pom.properties for doxia-site-renderer could not be found." ); + } + else if ( properties == null ) + { + getLogger().debug( "Failed to load pom.properties, so doxiaVersion is not available" + + " in the Velocity context." ); + } + else + { + context.put( "doxiaSiteRendererVersion", properties.getProperty( "version" ) ); + } + + // Add user properties + Map templateProperties = siteRenderingContext.getTemplateProperties(); + + if ( templateProperties != null ) + { + for ( Map.Entry entry : templateProperties.entrySet() ) + { + context.put( entry.getKey(), entry.getValue() ); + } + } + + // ---------------------------------------------------------------------- + // Tools + // ---------------------------------------------------------------------- + + context.put( "PathTool", new PathTool() ); + + context.put( "FileUtils", new FileUtils() ); + + context.put( "StringUtils", new StringUtils() ); + + context.put( "i18n", i18n ); + + context.put( "plexus", plexus ); + return context; + } + + /** + * Create a Velocity Context for the site template decorating the document. In addition to all the informations + * from the document, this context contains data gathered in {@link SiteRendererSink} during document rendering. + * + * @param content the document content to be merged into the template + * @param siteRenderingContext the site rendering context + * @return + */ + protected Context createSiteTemplateVelocityContext( DocumentContent content, + SiteRenderingContext siteRenderingContext ) + { + // first get the context from document + Context context = createDocumentVelocityContext( content.getRenderingContext(), siteRenderingContext ); + + // then add data objects from rendered document + + // Add infos from document + context.put( "authors", content.getAuthors() ); + + context.put( "shortTitle", content.getTitle() ); + + // DOXIASITETOOLS-70: Prepend the project name to the title, if any + String title = ""; + if ( siteRenderingContext.getDecoration() != null + && siteRenderingContext.getDecoration().getName() != null ) + { + title = siteRenderingContext.getDecoration().getName(); + } + else if ( siteRenderingContext.getDefaultWindowTitle() != null ) + { + title = siteRenderingContext.getDefaultWindowTitle(); + } + + if ( title.length() > 0 ) + { + title += " – "; // Symbol Name: En Dash, Html Entity: – + } + title += content.getTitle(); + + context.put( "title", title ); + + context.put( "headContent", content.getHead() ); + + context.put( "bodyContent", content.getBody() ); + + // document date (got from Doxia Sink date() API) + String documentDate = content.getDate(); + if ( StringUtils.isNotEmpty( documentDate ) ) + { + context.put( "documentDate", documentDate ); + + // deprecated variables that rework the document date, suppose one semantics over others + // (ie creation date, while it may be last modification date if the document writer decided so) + // see DOXIASITETOOLS-20 for the beginning and DOXIASITETOOLS-164 for the end of this story + try + { + // we support only ISO 8601 date + Date creationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( documentDate ); + + context.put( "creationDate", creationDate ); + SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd" ); + context.put( "dateCreation", sdf.format( creationDate ) ); + } + catch ( java.text.ParseException e ) + { + getLogger().warn( "Could not parse date '" + documentDate + "' from " + + content.getRenderingContext().getInputName() + + " (expected yyyy-MM-dd format), ignoring!" ); + } + } + + // document rendering context, to get eventual inputName + context.put( "docRenderingContext", content.getRenderingContext() ); + + return context; + } + + /** {@inheritDoc} */ + public void generateDocument( Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext ) + throws RendererException + { + mergeDocumentIntoSite( writer, sink, siteRenderingContext ); + } + + /** {@inheritDoc} */ + public void mergeDocumentIntoSite( Writer writer, DocumentContent content, + SiteRenderingContext siteRenderingContext ) + throws RendererException + { + String templateName = siteRenderingContext.getTemplateName(); + + getLogger().debug( "Processing Velocity for template " + templateName + " on " + + content.getRenderingContext().getInputName() ); + + Context context = createSiteTemplateVelocityContext( content, siteRenderingContext ); + + ClassLoader old = null; + + if ( siteRenderingContext.getTemplateClassLoader() != null ) + { + // ------------------------------------------------------------------------- + // If no template classloader was set we'll just use the context classloader + // ------------------------------------------------------------------------- + + old = Thread.currentThread().getContextClassLoader(); + + Thread.currentThread().setContextClassLoader( siteRenderingContext.getTemplateClassLoader() ); + } + + try + { + Template template; + Artifact skin = siteRenderingContext.getSkin(); + + try + { + SkinModel skinModel = siteRenderingContext.getSkinModel(); + String encoding = ( skinModel == null ) ? null : skinModel.getEncoding(); + + template = ( encoding == null ) ? velocity.getEngine().getTemplate( templateName ) + : velocity.getEngine().getTemplate( templateName, encoding ); + } + catch ( ParseErrorException pee ) + { + throw new RendererException( "Velocity parsing error while reading the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + pee ); + } + catch ( ResourceNotFoundException rnfe ) + { + throw new RendererException( "Could not find the site decoration template " + + ( ( skin == null ) ? ( "'" + templateName + "'" ) : ( "from " + skin.getId() + " skin" ) ), + rnfe ); + } + + try + { + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + writer.write( sw.toString().replaceAll( "\r?\n", SystemUtils.LINE_SEPARATOR ) ); + } + catch ( VelocityException ve ) + { + throw new RendererException( "Velocity error while merging site decoration template.", ve ); + } + catch ( IOException ioe ) + { + throw new RendererException( "IO exception while merging site decoration template.", ioe ); + } + } + finally + { + IOUtil.close( writer ); + + if ( old != null ) + { + Thread.currentThread().setContextClassLoader( old ); + } + } + } + + private SiteRenderingContext createSiteRenderingContext( Map attributes, DecorationModel decoration, + String defaultWindowTitle, Locale locale ) + { + SiteRenderingContext context = new SiteRenderingContext(); + + context.setTemplateProperties( attributes ); + context.setLocale( locale ); + context.setDecoration( decoration ); + context.setDefaultWindowTitle( defaultWindowTitle ); + + return context; + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.siterenderer.SiteRenderingContext createContextForSkin(org.apache.maven.artifact.Artifact skin, java.util.Map attributes, org.apache.maven.doxia.site.decoration.DecorationModel decoration, java.lang.String defaultWindowTitle, java.util.Locale locale) throws java.io.IOException, org.apache.maven.doxia.siterenderer.RendererException { + org.apache.maven.doxia.siterenderer.SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultWindowTitle, locale); + context.setSkin(skin); + java.util.zip.ZipFile zipFile = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.getZipFile(skin.getFile()); + java.io.InputStream in = null; + try { + if (zipFile.getEntry(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION) != null) { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.SKIN_TEMPLATE_LOCATION); + context.setTemplateClassLoader(new java.net.URLClassLoader(new java.net.URL[]{ skin.getFile().toURI().toURL() })); + } else { + context.setTemplateName(org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.DEFAULT_TEMPLATE); + context.setTemplateClassLoader(getClass().getClassLoader()); + context.setUsingDefaultTemplate(true); + } + java.util.zip.ZipEntry skinDescriptorEntry = zipFile.getEntry(org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION); + { + in = zipFile.getInputStream(skinDescriptorEntry); + org.apache.maven.doxia.site.skin.SkinModel skinModel = new org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader().read(in); + context.setSkinModel(skinModel); + java.lang.String toolsPrerequisite = (skinModel.getPrerequisites() == null) ? null : skinModel.getPrerequisites().getDoxiaSitetools(); + java.lang.Package p = org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.class.getPackage(); + java.lang.String current = /* NPEX_NULL_EXP */ + p.getImplementationVersion(); + if ((org.codehaus.plexus.util.StringUtils.isNotBlank(toolsPrerequisite) && (current != null)) && (!matchVersion(current, toolsPrerequisite))) { + throw new org.apache.maven.doxia.siterenderer.RendererException((("Cannot use skin: has " + toolsPrerequisite) + " Doxia Sitetools prerequisite, but current is ") + current); + } + } + } catch (org.codehaus.plexus.util.xml.pull.XmlPullParserException e) { + throw new org.apache.maven.doxia.siterenderer.RendererException(((("Failed to parse " + org.apache.maven.doxia.site.skin.SkinModel.SKIN_DESCRIPTOR_LOCATION) + " skin descriptor from ") + skin.getId()) + " skin", e); + } finally { + org.codehaus.plexus.util.IOUtil.close(in); + org.apache.maven.doxia.siterenderer.DefaultSiteRenderer.closeZipFile(zipFile); + } + return context; +} + + boolean matchVersion( String current, String prerequisite ) + throws RendererException + { + try + { + ArtifactVersion v = new DefaultArtifactVersion( current ); + VersionRange vr = VersionRange.createFromVersionSpec( prerequisite ); + + boolean matched = false; + ArtifactVersion recommendedVersion = vr.getRecommendedVersion(); + if ( recommendedVersion == null ) + { + List restrictions = vr.getRestrictions(); + for ( Restriction restriction : restrictions ) + { + if ( restriction.containsVersion( v ) ) + { + matched = true; + break; + } + } + } + else + { + // only singular versions ever have a recommendedVersion + @SuppressWarnings( "unchecked" ) + int compareTo = recommendedVersion.compareTo( v ); + matched = ( compareTo <= 0 ); + } + + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current + + ", matched = " + matched ); + } + + return matched; + } + catch ( InvalidVersionSpecificationException e ) + { + throw new RendererException( "Invalid skin doxia-sitetools prerequisite: " + prerequisite, e ); + } + } + + /** {@inheritDoc} */ + @Deprecated + public SiteRenderingContext createContextForTemplate( File templateFile, Map attributes, + DecorationModel decoration, String defaultWindowTitle, + Locale locale ) + throws MalformedURLException + { + SiteRenderingContext context = createSiteRenderingContext( attributes, decoration, defaultWindowTitle, locale ); + + context.setTemplateName( templateFile.getName() ); + context.setTemplateClassLoader( new URLClassLoader( new URL[]{templateFile.getParentFile().toURI().toURL()} ) ); + + return context; + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File resourcesDirectory, + File outputDirectory ) + throws IOException + { + throw new AssertionError( "copyResources( SiteRenderingContext, File, File ) is deprecated." ); + } + + /** {@inheritDoc} */ + public void copyResources( SiteRenderingContext siteRenderingContext, File outputDirectory ) + throws IOException + { + if ( siteRenderingContext.getSkin() != null ) + { + ZipFile file = getZipFile( siteRenderingContext.getSkin().getFile() ); + + try + { + for ( Enumeration e = file.entries(); e.hasMoreElements(); ) + { + ZipEntry entry = e.nextElement(); + + if ( !entry.getName().startsWith( "META-INF/" ) ) + { + File destFile = new File( outputDirectory, entry.getName() ); + if ( !entry.isDirectory() ) + { + if ( destFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + destFile.getParentFile().mkdirs(); + + copyFileFromZip( file, entry, destFile ); + } + else + { + destFile.mkdirs(); + } + } + } + } + finally + { + closeZipFile( file ); + } + } + + if ( siteRenderingContext.isUsingDefaultTemplate() ) + { + InputStream resourceList = getClass().getClassLoader() + .getResourceAsStream( RESOURCE_DIR + "/resources.txt" ); + + if ( resourceList != null ) + { + Reader r = null; + LineNumberReader reader = null; + try + { + r = ReaderFactory.newReader( resourceList, ReaderFactory.UTF_8 ); + reader = new LineNumberReader( r ); + + String line; + + while ( ( line = reader.readLine() ) != null ) + { + if ( line.startsWith( "#" ) || line.trim().length() == 0 ) + { + continue; + } + + InputStream is = getClass().getClassLoader().getResourceAsStream( RESOURCE_DIR + "/" + line ); + + if ( is == null ) + { + throw new IOException( "The resource " + line + " doesn't exist." ); + } + + File outputFile = new File( outputDirectory, line ); + + if ( outputFile.exists() ) + { + // don't override existing content: avoids extra rewrite with same content or extra site + // resource + continue; + } + + if ( !outputFile.getParentFile().exists() ) + { + outputFile.getParentFile().mkdirs(); + } + + OutputStream os = null; + try + { + // for the images + os = new FileOutputStream( outputFile ); + IOUtil.copy( is, os ); + } + finally + { + IOUtil.close( os ); + } + + IOUtil.close( is ); + } + } + finally + { + IOUtil.close( reader ); + IOUtil.close( r ); + } + } + } + + // Copy extra site resources + for ( File siteDirectory : siteRenderingContext.getSiteDirectories() ) + { + File resourcesDirectory = new File( siteDirectory, "resources" ); + + if ( resourcesDirectory != null && resourcesDirectory.exists() ) + { + copyDirectory( resourcesDirectory, outputDirectory ); + } + } + + // Check for the existence of /css/site.css + File siteCssFile = new File( outputDirectory, "/css/site.css" ); + if ( !siteCssFile.exists() ) + { + // Create the subdirectory css if it doesn't exist, DOXIA-151 + File cssDirectory = new File( outputDirectory, "/css/" ); + boolean created = cssDirectory.mkdirs(); + if ( created && getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created." ); + } + + // If the file is not there - create an empty file, DOXIA-86 + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( + "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file." ); + } + Writer writer = null; + try + { + writer = WriterFactory.newWriter( siteCssFile, siteRenderingContext.getOutputEncoding() ); + //DOXIA-290...the file should not be 0 bytes. + writer.write( "/* You can override this file with your own styles */" ); + } + finally + { + IOUtil.close( writer ); + } + } + } + + private static void copyFileFromZip( ZipFile file, ZipEntry entry, File destFile ) + throws IOException + { + FileOutputStream fos = new FileOutputStream( destFile ); + + try + { + IOUtil.copy( file.getInputStream( entry ), fos ); + } + finally + { + IOUtil.close( fos ); + } + } + + /** + * Copy the directory + * + * @param source source file to be copied + * @param destination destination file + * @throws java.io.IOException if any + */ + protected void copyDirectory( File source, File destination ) + throws IOException + { + if ( source.exists() ) + { + DirectoryScanner scanner = new DirectoryScanner(); + + String[] includedResources = {"**/**"}; + + scanner.setIncludes( includedResources ); + + scanner.addDefaultExcludes(); + + scanner.setBasedir( source ); + + scanner.scan(); + + List includedFiles = Arrays.asList( scanner.getIncludedFiles() ); + + for ( String name : includedFiles ) + { + File sourceFile = new File( source, name ); + + File destinationFile = new File( destination, name ); + + FileUtils.copyFile( sourceFile, destinationFile ); + } + } + } + + private Reader validate( Reader source, String resource ) + throws ParseException, IOException + { + getLogger().debug( "Validating: " + resource ); + + try + { + String content = IOUtil.toString( new BufferedReader( source ) ); + + new XmlValidator( new PlexusLoggerWrapper( getLogger() ) ).validate( content ); + + return new StringReader( content ); + } + finally + { + IOUtil.close( source ); + } + } + + // TODO replace with StringUtils.endsWithIgnoreCase() from maven-shared-utils 0.7 + static boolean endsWithIgnoreCase( String str, String searchStr ) + { + if ( str.length() < searchStr.length() ) + { + return false; + } + + return str.regionMatches( true, str.length() - searchStr.length(), searchStr, 0, searchStr.length() ); + } + + private static ZipFile getZipFile( File file ) + throws IOException + { + if ( file == null ) + { + throw new IOException( "Error opening ZipFile: null" ); + } + + try + { + // TODO: plexus-archiver, if it could do the excludes + return new ZipFile( file ); + } + catch ( ZipException ex ) + { + IOException ioe = new IOException( "Error opening ZipFile: " + file.getAbsolutePath() ); + ioe.initCause( ex ); + throw ioe; + } + } + + private static void closeZipFile( ZipFile zipFile ) + { + // TODO: move to plexus utils + try + { + zipFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/metadata.json new file mode 100644 index 000000000..bd9473953 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteRenderer_832", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 819, + "npe_method": "createContextForSkin", + "deref_field": "p", + "npe_class": "DefaultSiteRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteRenderer_832" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/npe.json b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/npe.json new file mode 100644 index 000000000..3fb36cc22 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteRenderer_832/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/DefaultSiteRenderer.java", + "line": 819, + "npe_method": "createContextForSkin", + "deref_field": "p", + "npe_class": "DefaultSiteRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_1036/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_1036/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/buggy.java new file mode 100644 index 000000000..2f4af0b05 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/buggy.java @@ -0,0 +1,1539 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ +// ---------------------------------------------------------------------- +// Protected methods +// ---------------------------------------------------------------------- +/** + * + * + * @param path + * could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ +protected static java.lang.String getNormalizedPath(java.lang.String path) { + java.lang.String normalized = org.apache.commons.io.FilenameUtils.normalize(path); + if (/* NPEX_NULL_EXP */ + normalized == null) { + normalized = path; + } + return normalized.replace('\\', '/'); +} + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_1036/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/metadata.json new file mode 100644 index 000000000..b22351593 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_1036", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 1043, + "npe_method": "getNormalizedPath", + "deref_field": "normalized", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_1036" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_1036/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/npe.json new file mode 100644 index 000000000..6ccc98492 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_1036/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 1043, + "npe_method": "getNormalizedPath", + "deref_field": "normalized", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_349/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_349/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_349/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_349/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_349/buggy.java new file mode 100644 index 000000000..0088db730 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_349/buggy.java @@ -0,0 +1,1529 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public java.io.File getSiteDescriptor(java.io.File siteDirectory, java.util.Locale locale) { + checkNotNull("siteDirectory", siteDirectory); + final java.util.Locale llocale = /* NPEX_NULL_EXP */ + locale; + java.io.File siteDescriptor = new java.io.File(siteDirectory, ("site_" + llocale.getLanguage()) + ".xml"); + if (!siteDescriptor.isFile()) { + siteDescriptor = new java.io.File(siteDirectory, "site.xml"); + } + return siteDescriptor; +} + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_349/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_349/metadata.json new file mode 100644 index 000000000..d5ec2ec24 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_349/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_349", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 352, + "npe_method": "getSiteDescriptor", + "deref_field": "locale", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_349" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_349/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_349/npe.json new file mode 100644 index 000000000..a61ad53a0 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_349/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 352, + "npe_method": "getSiteDescriptor", + "deref_field": "locale", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_492/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_492/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_492/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_492/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_492/buggy.java new file mode 100644 index 000000000..26e43d495 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_492/buggy.java @@ -0,0 +1,1503 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.doxia.site.decoration.DecorationModel getDecorationModel(java.io.File siteDirectory, java.util.Locale locale, org.apache.maven.project.MavenProject project, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository, java.util.List repositories) throws org.apache.maven.doxia.tools.SiteToolException { + checkNotNull("project", project); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + checkNotNull("repositories", repositories); + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + getLogger().debug((("Computing decoration model of " + project.getId()) + " for locale ") + llocale); + java.util.Map.Entry result = getDecorationModel(0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories); + org.apache.maven.doxia.site.decoration.DecorationModel decorationModel = result.getKey(); + org.apache.maven.project.MavenProject parentProject = result.getValue(); + if (decorationModel == null) { + getLogger().debug("Using default site descriptor"); + java.lang.String siteDescriptorContent; + java.io.Reader reader = null; + try { + // Note the default is not a super class - it is used when nothing else is found + reader = org.codehaus.plexus.util.ReaderFactory.newXmlReader(getClass().getResourceAsStream("/default-site.xml")); + siteDescriptorContent = readSiteDescriptor(reader, "default-site.xml"); + } catch (java.io.IOException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Error reading default site descriptor: " + e.getMessage(), e); + } finally { + org.codehaus.plexus.util.IOUtil.close(reader); + } + decorationModel = readDecorationModel(siteDescriptorContent); + } + // DecorationModel back to String to interpolate, then go back to DecorationModel + java.lang.String siteDescriptorContent = decorationModelToString(decorationModel); + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent(project, siteDescriptorContent, false); + decorationModel = readDecorationModel(siteDescriptorContent); + { + populateParentMenu(decorationModel, llocale, project, /* NPEX_NULL_EXP */ + parentProject, true); + } + try { + populateModulesMenu(decorationModel, llocale, project, reactorProjects, localRepository, true); + } catch (java.io.IOException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Error while populating modules menu: " + e.getMessage(), e); + } + if (decorationModel.getBannerLeft() == null) { + // extra default to set + org.apache.maven.doxia.site.decoration.Banner banner = new org.apache.maven.doxia.site.decoration.Banner(); + banner.setName(project.getName()); + decorationModel.setBannerLeft(banner); + } + return decorationModel; +} + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_492/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_492/metadata.json new file mode 100644 index 000000000..c3aceedf9 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_492/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_492", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 475, + "npe_method": "getDecorationModel", + "deref_field": "parentProject", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_492" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_492/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_492/npe.json new file mode 100644 index 000000000..119293c86 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_492/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 475, + "npe_method": "getDecorationModel", + "deref_field": "parentProject", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_587/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_587/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_587/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_587/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_587/buggy.java new file mode 100644 index 000000000..d79a3fb55 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_587/buggy.java @@ -0,0 +1,1491 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ +/** + * {@inheritDoc } + */ +public org.apache.maven.project.MavenProject getParentProject(org.apache.maven.project.MavenProject aProject, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository) { + checkNotNull("aProject", aProject); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + if (org.apache.maven.doxia.tools.DefaultSiteTool.isMaven3OrMore()) { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + org.apache.maven.project.MavenProject parentProject = null; + org.apache.maven.project.MavenProject origParent = aProject.getParent(); + { + for (org.apache.maven.project.MavenProject reactorProject : reactorProjects) { + if ((reactorProject.getGroupId().equals(/* NPEX_NULL_EXP */ + origParent.getGroupId()) && reactorProject.getArtifactId().equals(origParent.getArtifactId())) && reactorProject.getVersion().equals(origParent.getVersion())) { + parentProject = reactorProject; + getLogger().debug(("Parent project " + origParent.getId()) + " picked from reactor"); + break; + } + } + if (((parentProject == null) && (aProject.getBasedir() != null)) && org.codehaus.plexus.util.StringUtils.isNotEmpty(aProject.getModel().getParent().getRelativePath())) { + try { + java.lang.String relativePath = aProject.getModel().getParent().getRelativePath(); + java.io.File pomFile = new java.io.File(aProject.getBasedir(), relativePath); + if (pomFile.isDirectory()) { + pomFile = new java.io.File(pomFile, "pom.xml"); + } + pomFile = new java.io.File(org.apache.maven.doxia.tools.DefaultSiteTool.getNormalizedPath(pomFile.getPath())); + if (pomFile.isFile()) { + org.apache.maven.project.MavenProject mavenProject = mavenProjectBuilder.build(pomFile, localRepository, null); + if ((mavenProject.getGroupId().equals(origParent.getGroupId()) && mavenProject.getArtifactId().equals(origParent.getArtifactId())) && mavenProject.getVersion().equals(origParent.getVersion())) { + parentProject = mavenProject; + getLogger().debug((("Parent project " + origParent.getId()) + " loaded from a relative path: ") + relativePath); + } + } + } catch (org.apache.maven.project.ProjectBuildingException e) { + getLogger().info((("Unable to load parent project " + origParent.getId()) + " from a relative path: ") + e.getMessage()); + } + } + if (parentProject == null) { + try { + parentProject = mavenProjectBuilder.buildFromRepository(aProject.getParentArtifact(), aProject.getRemoteArtifactRepositories(), localRepository); + getLogger().debug(("Parent project " + origParent.getId()) + " loaded from repository"); + } catch (org.apache.maven.project.ProjectBuildingException e) { + getLogger().warn((("Unable to load parent project " + origParent.getId()) + " from repository: ") + e.getMessage()); + } + } + if (parentProject == null) { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + parentProject = origParent; + getLogger().debug(("Parent project " + origParent.getId()) + " picked from original value"); + } + } + return parentProject; +} + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_587/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_587/metadata.json new file mode 100644 index 000000000..dd62d3610 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_587/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_587", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 587, + "npe_method": "getParentProject", + "deref_field": "origParent", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_587" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_587/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_587/npe.json new file mode 100644 index 000000000..07e55beb0 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_587/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 587, + "npe_method": "getParentProject", + "deref_field": "origParent", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_686/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_686/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_686/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_686/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_686/buggy.java new file mode 100644 index 000000000..fbcecbbd7 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_686/buggy.java @@ -0,0 +1,1520 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ +/** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel + * the Doxia Sitetools DecorationModel, not null. + * @param locale + * the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project + * a Maven project, not null. + * @param parentProject + * a Maven parent project, not null. + * @param keepInheritedRefs + * used for inherited references. + */ +private void populateParentMenu(org.apache.maven.doxia.site.decoration.DecorationModel decorationModel, java.util.Locale locale, org.apache.maven.project.MavenProject project, org.apache.maven.project.MavenProject parentProject, boolean keepInheritedRefs) { + checkNotNull("decorationModel", decorationModel); + checkNotNull("project", project); + checkNotNull("parentProject", parentProject); + org.apache.maven.doxia.site.decoration.Menu menu = decorationModel.getMenuRef("parent"); + { + if (keepInheritedRefs && /* NPEX_NULL_EXP */ + menu.isInheritAsRef()) { + return; + } + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + java.lang.String parentUrl = org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(parentProject); + if (parentUrl != null) { + if (parentUrl.endsWith("/")) { + parentUrl += "index.html"; + } else { + parentUrl += "/index.html"; + } + parentUrl = getRelativePath(parentUrl, org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(project)); + } else { + // parent has no url, assume relative path is given by site structure + java.io.File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if (parentBasedir != null) { + // Try to find the relative path to the parent via the file system + java.lang.String parentPath = parentBasedir.getAbsolutePath(); + java.lang.String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath(parentPath, projectPath) + "/index.html"; + } + } + // Only add the parent menu if we were able to find a URL for it + if (parentUrl == null) { + getLogger().warn("Unable to find a URL to the parent project. The parent menu will NOT be added."); + } else { + if (menu.getName() == null) { + menu.setName(i18n.getString("site-tool", llocale, "decorationModel.menu.parentproject")); + } + org.apache.maven.doxia.site.decoration.MenuItem item = new org.apache.maven.doxia.site.decoration.MenuItem(); + item.setName(parentProject.getName()); + item.setHref(parentUrl); + menu.addItem(item); + } + } +} + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ + private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + boolean keepInheritedRefs ) + throws SiteToolException, IOException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "decorationModel", decorationModel ); + + Menu menu = decorationModel.getMenuRef( "modules" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; + + // we require child modules and reactors to process module menu + if ( project.getModules().size() > 0 ) + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); + } + + for ( String module : (List) project.getModules() ) + { + MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); + + if ( moduleProject == null ) + { + getLogger().warn( "Module " + module + + " not found in reactor: loading locally" ); + + File f = new File( project.getBasedir(), module + "/pom.xml" ); + if ( f.exists() ) + { + try + { + moduleProject = mavenProjectBuilder.build( f, localRepository, null ); + } + catch ( ProjectBuildingException e ) + { + throw new SiteToolException( "Unable to read local module-POM", e ); + } + } + else + { + getLogger().warn( "No filesystem module-POM available" ); + + moduleProject = new MavenProject(); + moduleProject.setName( module ); + moduleProject.setDistributionManagement( new DistributionManagement() ); + moduleProject.getDistributionManagement().setSite( new Site() ); + moduleProject.getDistributionManagement().getSite().setUrl( module ); + } + } + + String siteUrl = getDistMgmntSiteUrl( moduleProject ); + String itemName = + ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); + + appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); + } + } + else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) + { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef( "modules" ); + } + } + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_686/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_686/metadata.json new file mode 100644 index 000000000..77a38cd3b --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_686/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_686", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 699, + "npe_method": "populateParentMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_686" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_686/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_686/npe.json new file mode 100644 index 000000000..62b60039b --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_686/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 699, + "npe_method": "populateParentMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_771/Dockerfile b/Java/maven-doxia-sitetools-DefaultSiteTool_771/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_771/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_771/buggy.java b/Java/maven-doxia-sitetools-DefaultSiteTool_771/buggy.java new file mode 100644 index 000000000..f98f1f211 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_771/buggy.java @@ -0,0 +1,1519 @@ +package org.apache.maven.doxia.tools; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.apache.commons.io.FilenameUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.doxia.site.decoration.Banner; +import org.apache.maven.doxia.site.decoration.DecorationModel; +import org.apache.maven.doxia.site.decoration.Menu; +import org.apache.maven.doxia.site.decoration.MenuItem; +import org.apache.maven.doxia.site.decoration.Skin; +import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; +import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Site; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.reporting.MavenReport; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.logging.AbstractLogEnabled; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.ObjectBasedValueSource; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Default implementation of the site tool. + * + * @author Vincent Siveton + */ +@Component( role = SiteTool.class ) +public class DefaultSiteTool + extends AbstractLogEnabled + implements SiteTool +{ + // ---------------------------------------------------------------------- + // Components + // ---------------------------------------------------------------------- + + /** + * The component that is used to resolve additional artifacts required. + */ + @Requirement + private ArtifactResolver artifactResolver; + + /** + * The component used for creating artifact instances. + */ + @Requirement + private ArtifactFactory artifactFactory; + + /** + * Internationalization. + */ + @Requirement + protected I18N i18n; + + /** + * The component for assembling inheritance. + */ + @Requirement + protected DecorationModelInheritanceAssembler assembler; + + /** + * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid + * issues like DOXIASITETOOLS-166) + */ + @Requirement + protected MavenProjectBuilder mavenProjectBuilder; + + // ---------------------------------------------------------------------- + // Public methods + // ---------------------------------------------------------------------- + + public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, + List remoteArtifactRepositories, + DecorationModel decoration ) + throws SiteToolException + { + checkNotNull( "localRepository", localRepository ); + checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); + checkNotNull( "decoration", decoration ); + + Skin skin = decoration.getSkin(); + + if ( skin == null ) + { + skin = Skin.getDefaultSkin(); + } + + String version = skin.getVersion(); + Artifact artifact; + try + { + if ( version == null ) + { + version = Artifact.RELEASE_VERSION; + } + VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); + artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, + "jar", null, null ); + + artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version + + "' is not valid: " + e.getMessage(), e ); + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); + } + catch ( ArtifactNotFoundException e ) + { + throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); + } + + return artifact; + } + + public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, + List remoteArtifactRepositories ) + throws SiteToolException + { + return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); + } + + /** + * This method is not implemented according to the URI specification and has many weird + * corner cases where it doesn't do the right thing. Please consider using a better + * implemented method from a different library such as org.apache.http.client.utils.URIUtils#resolve. + */ + @Deprecated + public String getRelativePath( String to, String from ) + { + checkNotNull( "to", to ); + checkNotNull( "from", from ); + + if ( to.contains( ":" ) && from.contains( ":" ) ) + { + String toScheme = to.substring( 0, to.lastIndexOf( ':' ) ); + String fromScheme = from.substring( 0, from.lastIndexOf( ':' ) ); + if ( !toScheme.equals( fromScheme ) ) + { + return to; + } + } + + URL toUrl = null; + URL fromUrl = null; + + String toPath = to; + String fromPath = from; + + try + { + toUrl = new URL( to ); + } + catch ( MalformedURLException e ) + { + try + { + toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); + return to; + } + } + + try + { + fromUrl = new URL( from ); + } + catch ( MalformedURLException e ) + { + try + { + fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); + } + catch ( MalformedURLException e1 ) + { + getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); + return to; + } + } + + if ( toUrl != null && fromUrl != null ) + { + // URLs, determine if they share protocol and domain info + + if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) + && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) + && ( toUrl.getPort() == fromUrl.getPort() ) ) + { + // shared URL domain details, use URI to determine relative path + + toPath = toUrl.getFile(); + fromPath = fromUrl.getFile(); + } + else + { + // don't share basic URL information, no relative available + + return to; + } + } + else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) + { + // one is a URL and the other isn't, no relative available. + + return to; + } + + // either the two locations are not URLs or if they are they + // share the common protocol and domain info and we are left + // with their URI information + + String relativePath = getRelativeFilePath( fromPath, toPath ); + + if ( relativePath == null ) + { + relativePath = to; + } + + if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) + { + getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); + } + + return relativePath; + } + + private static String getRelativeFilePath( final String oldPath, final String newPath ) + { + // normalize the path delimiters + + String fromPath = new File( oldPath ).getPath(); + String toPath = new File( newPath ).getPath(); + + // strip any leading slashes if its a windows path + if ( toPath.matches( "^\\[a-zA-Z]:" ) ) + { + toPath = toPath.substring( 1 ); + } + if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) + { + fromPath = fromPath.substring( 1 ); + } + + // lowercase windows drive letters. + if ( fromPath.startsWith( ":", 1 ) ) + { + fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); + } + if ( toPath.startsWith( ":", 1 ) ) + { + toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); + } + + // check for the presence of windows drives. No relative way of + // traversing from one to the other. + + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) + && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + { + // they both have drive path element but they don't match, no + // relative path + + return null; + } + + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) + || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + { + + // one has a drive path element and the other doesn't, no relative + // path. + + return null; + + } + + final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); + + return relativePath.toString(); + } + + /** {@inheritDoc} */ + public File getSiteDescriptor( File siteDirectory, Locale locale ) + { + checkNotNull( "siteDirectory", siteDirectory ); + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); + + if ( !siteDescriptor.isFile() ) + { + siteDescriptor = new File( siteDirectory, "site.xml" ); + } + return siteDescriptor; + } + + /** + * Get a site descriptor from one of the repositories. + * + * @param project the Maven project, not null. + * @param localRepository the Maven local repository, not null. + * @param repositories the Maven remote repositories, not null. + * @param locale the locale wanted for the site descriptor. If not null, searching for + * site_localeLanguage.xml, otherwise searching for site.xml. + * @return the site descriptor into the local repository after download of it from repositories or null if not + * found in repositories. + * @throws SiteToolException if any + */ + File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; + + try + { + return resolveSiteDescriptor( project, localRepository, repositories, llocale ); + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); + return null; + } + catch ( ArtifactResolutionException e ) + { + throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); + } + } + + /** + * Read site descriptor content from Reader, adding support for deprecated ${reports}, + * ${parentProject} and ${modules} tags. + * + * @param reader + * @return the input content interpolated with deprecated tags + * @throws IOException + */ + private String readSiteDescriptor( Reader reader, String projectId ) + throws IOException + { + String siteDescriptorContent = IOUtil.toString( reader ); + + // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. + Properties props = new Properties(); + props.put( "reports", "" ); + props.put( "modules", "" ); + props.put( "parentProject", "" ); + + // warn if interpolation required + for ( Object prop : props.keySet() ) + { + if ( siteDescriptorContent.contains( "$" + prop ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop + + ": should be replaced with " + props.getProperty( (String) prop ) ); + } + if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) + { + getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop + + "}: should be replaced with " + props.getProperty( (String) prop ) ); + } + } + + return StringUtils.interpolate( siteDescriptorContent, props ); + } + + /** {@inheritDoc} */ + public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, + List reactorProjects, ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + checkNotNull( "project", project ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + checkNotNull( "repositories", repositories ); + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); + + Map.Entry result = + getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); + DecorationModel decorationModel = result.getKey(); + MavenProject parentProject = result.getValue(); + + if ( decorationModel == null ) + { + getLogger().debug( "Using default site descriptor" ); + + String siteDescriptorContent; + + Reader reader = null; + try + { + // Note the default is not a super class - it is used when nothing else is found + reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); + siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); + } + finally + { + IOUtil.close( reader ); + } + + decorationModel = readDecorationModel( siteDescriptorContent ); + } + + // DecorationModel back to String to interpolate, then go back to DecorationModel + String siteDescriptorContent = decorationModelToString( decorationModel ); + + // "classical" late interpolation, after full inheritance + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); + + decorationModel = readDecorationModel( siteDescriptorContent ); + + if ( parentProject != null ) + { + populateParentMenu( decorationModel, llocale, project, parentProject, true ); + } + + try + { + populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); + } + + if ( decorationModel.getBannerLeft() == null ) + { + // extra default to set + Banner banner = new Banner(); + banner.setName( project.getName() ); + decorationModel.setBannerLeft( banner ); + } + + return decorationModel; + } + + /** {@inheritDoc} */ + public String getInterpolatedSiteDescriptorContent( Map props, MavenProject aProject, + String siteDescriptorContent ) + throws SiteToolException + { + checkNotNull( "props", props ); + + // "classical" late interpolation + return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); + } + + private String getInterpolatedSiteDescriptorContent( MavenProject aProject, + String siteDescriptorContent, boolean isEarly ) + throws SiteToolException + { + checkNotNull( "aProject", aProject ); + checkNotNull( "siteDescriptorContent", siteDescriptorContent ); + + RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); + + if ( isEarly ) + { + interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); + interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); + } + else + { + interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); + interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); + + try + { + interpolator.addValueSource( new EnvarBasedValueSource() ); + } + catch ( IOException e ) + { + // Prefer logging? + throw new SiteToolException( "IOException: cannot interpolate environment properties: " + + e.getMessage(), e ); + } + } + + try + { + // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 + return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); + } + catch ( InterpolationException e ) + { + throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + public MavenProject getParentProject( MavenProject aProject, List reactorProjects, + ArtifactRepository localRepository ) + { + checkNotNull( "aProject", aProject ); + checkNotNull( "reactorProjects", reactorProjects ); + checkNotNull( "localRepository", localRepository ); + + if ( isMaven3OrMore() ) + { + // no need to make voodoo with Maven 3: job already done + return aProject.getParent(); + } + + MavenProject parentProject = null; + + MavenProject origParent = aProject.getParent(); + if ( origParent != null ) + { + for ( MavenProject reactorProject : reactorProjects ) + { + if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) + && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) + && reactorProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = reactorProject; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); + break; + } + } + + if ( parentProject == null && aProject.getBasedir() != null + && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) + { + try + { + String relativePath = aProject.getModel().getParent().getRelativePath(); + + File pomFile = new File( aProject.getBasedir(), relativePath ); + + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + } + pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); + + if ( pomFile.isFile() ) + { + MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); + + if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) + && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) + && mavenProject.getVersion().equals( origParent.getVersion() ) ) + { + parentProject = mavenProject; + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " + + relativePath ); + } + } + } + catch ( ProjectBuildingException e ) + { + getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + try + { + parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject + .getRemoteArtifactRepositories(), localRepository ); + + getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); + } + catch ( ProjectBuildingException e ) + { + getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " + + e.getMessage() ); + } + } + + if ( parentProject == null ) + { + // fallback to original parent, which may contain uninterpolated value (still need a unit test) + + parentProject = origParent; + + getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); + } + } + return parentProject; + } + + /** + * Populate the pre-defined parent menu of the decoration model, + * if used through <menu ref="parent"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param parentProject a Maven parent project, not null. + * @param keepInheritedRefs used for inherited references. + */ + private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, + MavenProject parentProject, boolean keepInheritedRefs ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "project", project ); + checkNotNull( "parentProject", parentProject ); + + Menu menu = decorationModel.getMenuRef( "parent" ); + + if ( menu == null ) + { + return; + } + + if ( keepInheritedRefs && menu.isInheritAsRef() ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + String parentUrl = getDistMgmntSiteUrl( parentProject ); + + if ( parentUrl != null ) + { + if ( parentUrl.endsWith( "/" ) ) + { + parentUrl += "index.html"; + } + else + { + parentUrl += "/index.html"; + } + + parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); + } + else + { + // parent has no url, assume relative path is given by site structure + File parentBasedir = parentProject.getBasedir(); + // First make sure that the parent is available on the file system + if ( parentBasedir != null ) + { + // Try to find the relative path to the parent via the file system + String parentPath = parentBasedir.getAbsolutePath(); + String projectPath = project.getBasedir().getAbsolutePath(); + parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; + } + } + + // Only add the parent menu if we were able to find a URL for it + if ( parentUrl == null ) + { + getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); + } + else + { + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); + } + + MenuItem item = new MenuItem(); + item.setName( parentProject.getName() ); + item.setHref( parentUrl ); + menu.addItem( item ); + } + } + + /** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel the Doxia Sitetools DecorationModel, not null. + * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project a Maven project, not null. + * @param reactorProjects the Maven reactor projects, not null. + * @param localRepository the Maven local repository, not null. + * @param keepInheritedRefs used for inherited references. + * @throws SiteToolException if any + * @throws IOException + */ +/** + * Populate the pre-defined modules menu of the decoration model, + * if used through <menu ref="modules"/>. + * + * @param decorationModel + * the Doxia Sitetools DecorationModel, not null. + * @param locale + * the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. + * @param project + * a Maven project, not null. + * @param reactorProjects + * the Maven reactor projects, not null. + * @param localRepository + * the Maven local repository, not null. + * @param keepInheritedRefs + * used for inherited references. + * @throws SiteToolException + * if any + * @throws IOException + * + */ +private void populateModulesMenu(org.apache.maven.doxia.site.decoration.DecorationModel decorationModel, java.util.Locale locale, org.apache.maven.project.MavenProject project, java.util.List reactorProjects, org.apache.maven.artifact.repository.ArtifactRepository localRepository, boolean keepInheritedRefs) throws org.apache.maven.doxia.tools.SiteToolException, java.io.IOException { + checkNotNull("project", project); + checkNotNull("reactorProjects", reactorProjects); + checkNotNull("localRepository", localRepository); + checkNotNull("decorationModel", decorationModel); + org.apache.maven.doxia.site.decoration.Menu menu = decorationModel.getMenuRef("modules"); + { + if (keepInheritedRefs && /* NPEX_NULL_EXP */ + menu.isInheritAsRef()) { + return; + } + final java.util.Locale llocale = (locale == null) ? java.util.Locale.getDefault() : locale; + // we require child modules and reactors to process module menu + if (project.getModules().size() > 0) { + if (menu.getName() == null) { + menu.setName(i18n.getString("site-tool", llocale, "decorationModel.menu.projectmodules")); + } + for (java.lang.String module : ((java.util.List) (project.getModules()))) { + org.apache.maven.project.MavenProject moduleProject = org.apache.maven.doxia.tools.DefaultSiteTool.getModuleFromReactor(project, reactorProjects, module); + if (moduleProject == null) { + getLogger().warn(("Module " + module) + " not found in reactor: loading locally"); + java.io.File f = new java.io.File(project.getBasedir(), module + "/pom.xml"); + if (f.exists()) { + try { + moduleProject = mavenProjectBuilder.build(f, localRepository, null); + } catch (org.apache.maven.project.ProjectBuildingException e) { + throw new org.apache.maven.doxia.tools.SiteToolException("Unable to read local module-POM", e); + } + } else { + getLogger().warn("No filesystem module-POM available"); + moduleProject = new org.apache.maven.project.MavenProject(); + moduleProject.setName(module); + moduleProject.setDistributionManagement(new org.apache.maven.model.DistributionManagement()); + moduleProject.getDistributionManagement().setSite(new org.apache.maven.model.Site()); + moduleProject.getDistributionManagement().getSite().setUrl(module); + } + } + java.lang.String siteUrl = org.apache.maven.doxia.tools.DefaultSiteTool.getDistMgmntSiteUrl(moduleProject); + java.lang.String itemName = (moduleProject.getName() == null) ? moduleProject.getArtifactId() : moduleProject.getName(); + appendMenuItem(project, menu, itemName, siteUrl, moduleProject.getArtifactId()); + } + } else if (decorationModel.getMenuRef("modules").getInherit() == null) { + // only remove if project has no modules AND menu is not inherited, see MSHARED-174 + decorationModel.removeMenuRef("modules"); + } + } +} + + private static MavenProject getModuleFromReactor( MavenProject project, List reactorProjects, + String module ) + throws IOException + { + File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); + + for ( MavenProject reactorProject : reactorProjects ) + { + if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) + { + return reactorProject; + } + } + + // module not found in reactor + return null; + } + + /** {@inheritDoc} */ + public void populateReportsMenu( DecorationModel decorationModel, Locale locale, + Map> categories ) + { + checkNotNull( "decorationModel", decorationModel ); + checkNotNull( "categories", categories ); + + Menu menu = decorationModel.getMenuRef( "reports" ); + + if ( menu == null ) + { + return; + } + + final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; + + if ( menu.getName() == null ) + { + menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); + } + + boolean found = false; + if ( menu.getItems().isEmpty() ) + { + List categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = createCategoryMenu( + i18n.getString( "site-tool", llocale, + "decorationModel.menu.projectinformation" ), + "/project-info.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + + categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); + if ( !isEmptyList( categoryReports ) ) + { + MenuItem item = + createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), + "/project-reports.html", categoryReports, llocale ); + menu.getItems().add( item ); + found = true; + } + } + if ( !found ) + { + decorationModel.removeMenuRef( "reports" ); + } + } + + /** {@inheritDoc} */ + public List getSiteLocales( String locales ) + { + if ( locales == null ) + { + return Collections.singletonList( DEFAULT_LOCALE ); + } + + String[] localesArray = StringUtils.split( locales, "," ); + List localesList = new ArrayList( localesArray.length ); + + for ( String localeString : localesArray ) + { + Locale locale = codeToLocale( localeString ); + + if ( locale == null ) + { + continue; + } + + if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale defined by '" + locale + + "' is not available in this Java Virtual Machine (" + + System.getProperty( "java.version" ) + + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); + } + continue; + } + + // Default bundles are in English + if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) + && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() + .equals( locale.getLanguage() ) ) ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) + + ") is not currently supported by Maven Site - IGNORING." + + "\nContributions are welcome and greatly appreciated!" + + "\nIf you want to contribute a new translation, please visit " + + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); + } + + continue; + } + + localesList.add( locale ); + } + + if ( localesList.isEmpty() ) + { + localesList = Collections.singletonList( DEFAULT_LOCALE ); + } + + return localesList; + } + + /** + * Converts a locale code like "en", "en_US" or "en_US_win" to a java.util.Locale + * object. + *

    If localeCode = default, return the current value of the default locale for this instance + * of the Java Virtual Machine.

    + * + * @param localeCode the locale code string. + * @return a java.util.Locale object instanced or null if errors occurred + * @see java.util.Locale#getDefault() + */ + private Locale codeToLocale( String localeCode ) + { + if ( localeCode == null ) + { + return null; + } + + if ( "default".equalsIgnoreCase( localeCode ) ) + { + return Locale.getDefault(); + } + + String language = ""; + String country = ""; + String variant = ""; + + StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); + final int maxTokens = 3; + if ( tokenizer.countTokens() > maxTokens ) + { + if ( getLogger().isWarnEnabled() ) + { + getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); + } + return null; + } + + if ( tokenizer.hasMoreTokens() ) + { + language = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + country = tokenizer.nextToken(); + if ( tokenizer.hasMoreTokens() ) + { + variant = tokenizer.nextToken(); + } + } + } + + return new Locale( language, country, variant ); + } + + // ---------------------------------------------------------------------- + // Protected methods + // ---------------------------------------------------------------------- + + /** + * @param path could be null. + * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. + * @see FilenameUtils#normalize(String) + */ + protected static String getNormalizedPath( String path ) + { + String normalized = FilenameUtils.normalize( path ); + if ( normalized == null ) + { + normalized = path; + } + return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); + } + + // ---------------------------------------------------------------------- + // Private methods + // ---------------------------------------------------------------------- + + /** + * @param project not null + * @param localRepository not null + * @param repositories not null + * @param locale not null + * @return the resolved site descriptor + * @throws IOException if any + * @throws ArtifactResolutionException if any + * @throws ArtifactNotFoundException if any + */ + private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, + List repositories, Locale locale ) + throws IOException, ArtifactResolutionException, ArtifactNotFoundException + { + File result; + + // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? + Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), + project.getArtifactId(), + project.getVersion(), "xml", + "site_" + locale.getLanguage() ); + + boolean found = false; + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() > 0 ) + { + found = true; + } + else + { + getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " + + locale.getLanguage() + ", trying without locale..." ); + } + } + catch ( ArtifactNotFoundException e ) + { + getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); + + // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote + // repository, because the parent was already released (and snapshots are updated automatically if changed) + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + } + + if ( !found ) + { + artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), + project.getVersion(), "xml", "site" ); + try + { + artifactResolver.resolve( artifact, repositories, localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + // see above regarding this zero length file + result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); + result.getParentFile().mkdirs(); + result.createNewFile(); + + throw e; + } + + result = artifact.getFile(); + + // we use zero length files to avoid re-resolution (see below) + if ( result.length() == 0 ) + { + getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); + result = null; + } + } + + return result; + } + + /** + * @param depth depth of project + * @param siteDirectory, can be null if project.basedir is null, ie POM from repository + * @param locale not null + * @param project not null + * @param reactorProjects not null + * @param localRepository not null + * @param repositories not null + * @param origProps not null + * @return the decoration model depending the locale and the parent project + * @throws SiteToolException if any + */ + private Map.Entry getDecorationModel( int depth, File siteDirectory, Locale locale, + MavenProject project, + List reactorProjects, + ArtifactRepository localRepository, + List repositories ) + throws SiteToolException + { + // 1. get site descriptor File + File siteDescriptor; + if ( project.getBasedir() == null ) + { + // POM is in the repository: look into the repository for site descriptor + try + { + siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); + } + catch ( SiteToolException e ) + { + throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " + + e.getMessage(), e ); + } + } + else + { + // POM is in build directory: look for site descriptor as local file + siteDescriptor = getSiteDescriptor( siteDirectory, locale ); + } + + // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) + DecorationModel decoration = null; + Reader siteDescriptorReader = null; + try + { + if ( siteDescriptor != null && siteDescriptor.exists() ) + { + getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + + " site descriptor from " + siteDescriptor ); + + siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); + + String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); + + // interpolate ${this.*} = early interpolation + siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); + + decoration = readDecorationModel( siteDescriptorContent ); + decoration.setLastModified( siteDescriptor.lastModified() ); + } + else + { + getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); + } + } + catch ( IOException e ) + { + throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " + + siteDescriptor, e ); + } + finally + { + IOUtil.close( siteDescriptorReader ); + } + + // 3. look for parent project + MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); + + // 4. merge with parent project DecorationModel + if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) + { + depth++; + getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " + + parentProject.getId() ); + + File parentSiteDirectory = null; + if ( parentProject.getBasedir() != null ) + { + // extrapolate parent project site directory + String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), + siteDescriptor.getParentFile().getAbsolutePath() ); + + parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); + // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin + // has different configuration. But this is a rare case (this only has impact if parent is from reactor) + } + + DecorationModel parentDecoration = + getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, + repositories ).getKey(); + + // MSHARED-116 requires an empty decoration model (instead of a null one) + // MSHARED-145 requires us to do this only if there is a parent to merge it with + if ( decoration == null && parentDecoration != null ) + { + // we have no site descriptor: merge the parent into an empty one + decoration = new DecorationModel(); + } + + String name = project.getName(); + if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) + { + name = decoration.getName(); + } + + // Merge the parent and child DecorationModels + String projectDistMgmnt = getDistMgmntSiteUrl( project ); + String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth + + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " + + parentDistMgmnt ); + } + assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, + parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); + } + + return new AbstractMap.SimpleEntry( decoration, parentProject ); + } + + /** + * @param siteDescriptorContent not null + * @return the decoration model object + * @throws SiteToolException if any + */ + private DecorationModel readDecorationModel( String siteDescriptorContent ) + throws SiteToolException + { + try + { + return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); + } + catch ( XmlPullParserException e ) + { + throw new SiteToolException( "Error parsing site descriptor", e ); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + } + + private String decorationModelToString( DecorationModel decoration ) + throws SiteToolException + { + StringWriter writer = new StringWriter(); + + try + { + new DecorationXpp3Writer().write( writer, decoration ); + return writer.toString(); + } + catch ( IOException e ) + { + throw new SiteToolException( "Error reading site descriptor", e ); + } + finally + { + IOUtil.close( writer ); + } + } + + private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) + { + // use tokenizer to traverse paths and for lazy checking + StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + int count = 0; + + // walk along the to path looking for divergence from the from path + while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) + { + if ( separatorChar == '\\' ) + { + if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) + { + break; + } + } + else + { + if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) + { + break; + } + } + + count++; + } + + // reinitialize the tokenizers to count positions to retrieve the + // gobbled token + + toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); + fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); + + while ( count-- > 0 ) + { + fromTokeniser.nextToken(); + toTokeniser.nextToken(); + } + + StringBuilder relativePath = new StringBuilder(); + + // add back refs for the rest of from location. + while ( fromTokeniser.hasMoreTokens() ) + { + fromTokeniser.nextToken(); + + relativePath.append( ".." ); + + if ( fromTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + + if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + + // add fwd fills for whatever's left of to. + while ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( toTokeniser.nextToken() ); + + if ( toTokeniser.hasMoreTokens() ) + { + relativePath.append( separatorChar ); + } + } + return relativePath.toString(); + } + + /** + * @param project not null + * @param menu not null + * @param name not null + * @param href could be null + * @param defaultHref not null + */ + private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) + { + String selectedHref = href; + + if ( selectedHref == null ) + { + selectedHref = defaultHref; + } + + MenuItem item = new MenuItem(); + item.setName( name ); + + String baseUrl = getDistMgmntSiteUrl( project ); + if ( baseUrl != null ) + { + selectedHref = getRelativePath( selectedHref, baseUrl ); + } + + if ( selectedHref.endsWith( "/" ) ) + { + item.setHref( selectedHref + "index.html" ); + } + else + { + item.setHref( selectedHref + "/index.html" ); + } + menu.addItem( item ); + } + + /** + * @param name not null + * @param href not null + * @param categoryReports not null + * @param locale not null + * @return the menu item object + */ + private MenuItem createCategoryMenu( String name, String href, List categoryReports, Locale locale ) + { + MenuItem item = new MenuItem(); + item.setName( name ); + item.setCollapse( true ); + item.setHref( href ); + + // MSHARED-172, allow reports to define their order in some other way? + //Collections.sort( categoryReports, new ReportComparator( locale ) ); + + for ( MavenReport report : categoryReports ) + { + MenuItem subitem = new MenuItem(); + subitem.setName( report.getName( locale ) ); + subitem.setHref( report.getOutputName() + ".html" ); + item.getItems().add( subitem ); + } + + return item; + } + + // ---------------------------------------------------------------------- + // static methods + // ---------------------------------------------------------------------- + + /** + * Convenience method. + * + * @param list could be null + * @return true if the list is null or empty + */ + private static boolean isEmptyList( List list ) + { + return list == null || list.isEmpty(); + } + + /** + * Return distributionManagement.site.url if defined, null otherwise. + * + * @param project not null + * @return could be null + */ + private static String getDistMgmntSiteUrl( MavenProject project ) + { + return getDistMgmntSiteUrl( project.getDistributionManagement() ); + } + + private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) + { + if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) + { + return urlEncode( distMgmnt.getSite().getUrl() ); + } + + return null; + } + + private static String urlEncode( final String url ) + { + if ( url == null ) + { + return null; + } + + try + { + return new File( url ).toURI().toURL().toExternalForm(); + } + catch ( MalformedURLException ex ) + { + return url; // this will then throw somewhere else + } + } + + private void checkNotNull( String name, Object value ) + { + if ( value == null ) + { + throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); + } + } + + /** + * Check the current Maven version to see if it's Maven 3.0 or newer. + */ + private static boolean isMaven3OrMore() + { + return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; + } + + private static String getMavenVersion() + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + final Properties properties = new Properties(); + final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; + final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); + try + { + properties.load( in ); + } + catch ( IOException ioe ) + { + return ""; + } + finally + { + IOUtil.close( in ); + } + + return properties.getProperty( "version" ).trim(); + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_771/metadata.json b/Java/maven-doxia-sitetools-DefaultSiteTool_771/metadata.json new file mode 100644 index 000000000..1f74f489d --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_771/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-DefaultSiteTool_771", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 788, + "npe_method": "populateModulesMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool", + "repo": "maven-doxia-sitetools", + "bug_id": "DefaultSiteTool_771" + } +} diff --git a/Java/maven-doxia-sitetools-DefaultSiteTool_771/npe.json b/Java/maven-doxia-sitetools-DefaultSiteTool_771/npe.json new file mode 100644 index 000000000..4a81867d6 --- /dev/null +++ b/Java/maven-doxia-sitetools-DefaultSiteTool_771/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-integration-tools/src/main/java/org/apache/maven/doxia/tools/DefaultSiteTool.java", + "line": 788, + "npe_method": "populateModulesMenu", + "deref_field": "menu", + "npe_class": "DefaultSiteTool" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-FoPdfRenderer_90/Dockerfile b/Java/maven-doxia-sitetools-FoPdfRenderer_90/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-FoPdfRenderer_90/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-FoPdfRenderer_90/buggy.java b/Java/maven-doxia-sitetools-FoPdfRenderer_90/buggy.java new file mode 100644 index 000000000..3b4d030c8 --- /dev/null +++ b/Java/maven-doxia-sitetools-FoPdfRenderer_90/buggy.java @@ -0,0 +1,325 @@ +package org.apache.maven.doxia.docrenderer.pdf.fo; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.xml.transform.TransformerException; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOC; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.fo.FoAggregateSink; +import org.apache.maven.doxia.module.fo.FoSink; +import org.apache.maven.doxia.module.fo.FoSinkFactory; +import org.apache.maven.doxia.module.fo.FoUtils; +import org.apache.maven.doxia.parser.module.ParserModule; + +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; + +import org.xml.sax.SAXParseException; + +/** + * PDF renderer that uses Doxia's FO module. + * + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "fo" ) +public class FoPdfRenderer + extends AbstractPdfRenderer +{ + /** + * {@inheritDoc} + * @see org.apache.maven.doxia.module.fo.FoUtils#convertFO2PDF(File, File, String) + */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + // Should take care of the document model for the metadata... + generatePdf( inputFile, pdfFile, null ); + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void render(java.util.Map filesToProcess, java.io.File outputDirectory, org.apache.maven.doxia.document.DocumentModel documentModel, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + // copy resources, images, etc. + copyResources(outputDirectory); + { + java.lang.String outputName = getOutputName(/* NPEX_NULL_EXP */ + documentModel); + java.io.File outputFOFile = new java.io.File(outputDirectory, outputName + ".fo"); + if (!outputFOFile.getParentFile().exists()) { + outputFOFile.getParentFile().mkdirs(); + } + java.io.File pdfOutputFile = new java.io.File(outputDirectory, outputName + ".pdf"); + if (!pdfOutputFile.getParentFile().exists()) { + pdfOutputFile.getParentFile().mkdirs(); + } + java.io.Writer writer = null; + try { + writer = org.codehaus.plexus.util.WriterFactory.newXmlWriter(outputFOFile); + org.apache.maven.doxia.module.fo.FoAggregateSink sink = new org.apache.maven.doxia.module.fo.FoAggregateSink(writer); + java.io.File fOConfigFile = new java.io.File(outputDirectory, "pdf-config.xml"); + if (fOConfigFile.exists()) { + sink.load(fOConfigFile); + getLogger().debug("Loaded pdf config file: " + fOConfigFile.getAbsolutePath()); + } + java.lang.String generateTOC = ((context != null) && (context.get("generateTOC") != null)) ? context.get("generateTOC").toString().trim() : "start"; + int tocPosition = 0; + if ("start".equalsIgnoreCase(generateTOC)) { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_START; + } else if ("end".equalsIgnoreCase(generateTOC)) { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_END; + } else { + tocPosition = org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_NONE; + } + sink.setDocumentModel(documentModel, tocPosition); + sink.beginDocument(); + sink.coverPage(); + if (tocPosition == org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_START) { + sink.toc(); + } + if ((documentModel.getToc() == null) || (documentModel.getToc().getItems() == null)) { + getLogger().info("No TOC is defined in the document descriptor. Merging all documents."); + mergeAllSources(filesToProcess, sink, context); + } else { + getLogger().debug("Using TOC defined in the document descriptor."); + mergeSourcesFromTOC(documentModel.getToc(), sink, context); + } + if (tocPosition == org.apache.maven.doxia.module.fo.FoAggregateSink.TOC_END) { + sink.toc(); + } + sink.endDocument(); + } finally { + org.codehaus.plexus.util.IOUtil.close(writer); + } + generatePdf(outputFOFile, pdfOutputFile, documentModel); + } +} + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputFOFile = new File( outputDirectory, output + ".fo" ); + if ( !outputFOFile.getParentFile().exists() ) + { + outputFOFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + FoSink sink = + (FoSink) new FoSinkFactory().createSink( outputFOFile.getParentFile(), outputFOFile.getName() ); + sink.beginDocument(); + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + sink.endDocument(); + + generatePdf( outputFOFile, pdfOutputFile, null ); + } + } + + private void mergeAllSources( Map filesToProcess, FoAggregateSink sink, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + sink.setDocumentName( key ); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + } + + private void mergeSourcesFromTOC( DocumentTOC toc, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + parseTocItems( toc.getItems(), sink, context ); + } + + private void parseTocItems( List items, FoAggregateSink sink, DocumentRendererContext context ) + throws IOException, DocumentRendererException + { + for ( DocumentTOCItem tocItem : items ) + { + if ( tocItem.getRef() == null ) + { + if ( getLogger().isInfoEnabled() ) + { + getLogger().info( "No ref defined for tocItem " + tocItem.getName() ); + } + + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + renderModules( href, sink, tocItem, context ); + + if ( tocItem.getItems() != null ) + { + parseTocItems( tocItem.getItems(), sink, context ); + } + } + } + + private void renderModules( String href, FoAggregateSink sink, DocumentTOCItem tocItem, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + sink.setDocumentName( doc ); + sink.setDocumentTitle( tocItem.getName() ); + + parse( source.getPath(), module.getParserId(), sink, context ); + } + } + } + } + } + + /** + * @param inputFile + * @param pdfFile + * @param documentModel could be null + * @throws DocumentRendererException if any + * @since 1.1.1 + */ + private void generatePdf( File inputFile, File pdfFile, DocumentModel documentModel ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating: " + pdfFile ); + } + + try + { + FoUtils.convertFO2PDF( inputFile, pdfFile, null, documentModel ); + } + catch ( TransformerException e ) + { + if ( ( e.getCause() != null ) && ( e.getCause() instanceof SAXParseException ) ) + { + SAXParseException sax = (SAXParseException) e.getCause(); + + StringBuilder sb = new StringBuilder(); + sb.append( "Error creating PDF from " ).append( inputFile.getAbsolutePath() ).append( ":" ) + .append( sax.getLineNumber() ).append( ":" ).append( sax.getColumnNumber() ).append( "\n" ); + sb.append( e.getMessage() ); + + throw new DocumentRendererException( sb.toString() ); + } + + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage() ); + } + } +} diff --git a/Java/maven-doxia-sitetools-FoPdfRenderer_90/metadata.json b/Java/maven-doxia-sitetools-FoPdfRenderer_90/metadata.json new file mode 100644 index 000000000..5a02dd3d6 --- /dev/null +++ b/Java/maven-doxia-sitetools-FoPdfRenderer_90/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-FoPdfRenderer_90", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java", + "line": 91, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "FoPdfRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "FoPdfRenderer_90" + } +} diff --git a/Java/maven-doxia-sitetools-FoPdfRenderer_90/npe.json b/Java/maven-doxia-sitetools-FoPdfRenderer_90/npe.json new file mode 100644 index 000000000..45abac0f4 --- /dev/null +++ b/Java/maven-doxia-sitetools-FoPdfRenderer_90/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/fo/FoPdfRenderer.java", + "line": 91, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "FoPdfRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-ITextPdfRenderer_152/Dockerfile b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-ITextPdfRenderer_152/buggy.java b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/buggy.java new file mode 100644 index 000000000..af95bb63a --- /dev/null +++ b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/buggy.java @@ -0,0 +1,669 @@ +package org.apache.maven.doxia.docrenderer.pdf.itext; + +/* + * 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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +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.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.maven.doxia.docrenderer.DocumentRendererContext; +import org.apache.maven.doxia.docrenderer.DocumentRendererException; +import org.apache.maven.doxia.docrenderer.pdf.AbstractPdfRenderer; +import org.apache.maven.doxia.docrenderer.pdf.PdfRenderer; +import org.apache.maven.doxia.document.DocumentCover; +import org.apache.maven.doxia.document.DocumentMeta; +import org.apache.maven.doxia.document.DocumentModel; +import org.apache.maven.doxia.document.DocumentTOCItem; +import org.apache.maven.doxia.module.itext.ITextSink; +import org.apache.maven.doxia.module.itext.ITextSinkFactory; +import org.apache.maven.doxia.module.itext.ITextUtil; +import org.apache.maven.doxia.parser.module.ParserModule; +import org.apache.xml.utils.DefaultErrorHandler; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.lowagie.text.ElementTags; + +/** + * Abstract document render with the iText framework + * + * @author Vincent Siveton + * @author ltheussl + * @since 1.1 + */ +@Component( role = PdfRenderer.class, hint = "itext" ) +public class ITextPdfRenderer + extends AbstractPdfRenderer +{ + /** The xslt style sheet used to transform a Document to an iText file. */ + private static final String XSLT_RESOURCE = "TOC.xslt"; + + /** The TransformerFactory. */ + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + /** The DocumentBuilderFactory. */ + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance(); + + /** The DocumentBuilder. */ + private static final DocumentBuilder DOCUMENT_BUILDER; + + static + { + TRANSFORMER_FACTORY.setErrorListener( new DefaultErrorHandler() ); + + try + { + DOCUMENT_BUILDER = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder(); + } + catch ( ParserConfigurationException e ) + { + throw new RuntimeException( "Error building document :" + e.getMessage() ); + } + } + + /** {@inheritDoc} */ + public void generatePdf( File inputFile, File pdfFile ) + throws DocumentRendererException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Generating : " + pdfFile ); + } + + try + { + ITextUtil.writePdf( new FileInputStream( inputFile ), new FileOutputStream( pdfFile ) ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( "Cannot create PDF from " + inputFile + ": " + e.getMessage(), e ); + } + catch ( RuntimeException e ) + { + throw new DocumentRendererException( "Error creating PDF from " + inputFile + ": " + e.getMessage(), e ); + } + } + + /** {@inheritDoc} */ + @Override + public void render( Map filesToProcess, File outputDirectory, DocumentModel documentModel ) + throws DocumentRendererException, IOException + { + render( filesToProcess, outputDirectory, documentModel, null ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void render(java.util.Map filesToProcess, java.io.File outputDirectory, org.apache.maven.doxia.document.DocumentModel documentModel, org.apache.maven.doxia.docrenderer.DocumentRendererContext context) throws org.apache.maven.doxia.docrenderer.DocumentRendererException, java.io.IOException { + // copy resources, images, etc. + copyResources(outputDirectory); + { + java.lang.String outputName = getOutputName(/* NPEX_NULL_EXP */ + documentModel); + java.io.File outputITextFile = new java.io.File(outputDirectory, outputName + ".xml"); + if (!outputITextFile.getParentFile().exists()) { + outputITextFile.getParentFile().mkdirs(); + } + java.io.File pdfOutputFile = new java.io.File(outputDirectory, outputName + ".pdf"); + if (!pdfOutputFile.getParentFile().exists()) { + pdfOutputFile.getParentFile().mkdirs(); + } + java.util.List iTextFiles; + if ((documentModel.getToc() == null) || (documentModel.getToc().getItems() == null)) { + getLogger().info("No TOC is defined in the document descriptor. Merging all documents."); + iTextFiles = parseAllFiles(filesToProcess, outputDirectory, context); + } else { + getLogger().debug("Using TOC defined in the document descriptor."); + iTextFiles = parseTOCFiles(outputDirectory, documentModel, context); + } + java.lang.String generateTOC = ((context != null) && (context.get("generateTOC") != null)) ? context.get("generateTOC").toString() : "start"; + java.io.File iTextFile = new java.io.File(outputDirectory, outputName + ".xml"); + java.io.File iTextOutput = new java.io.File(outputDirectory, (outputName + ".") + getOutputExtension()); + org.w3c.dom.Document document = generateDocument(iTextFiles); + transform(documentModel, document, iTextFile, generateTOC); + generatePdf(iTextFile, iTextOutput); + } +} + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory ) + throws DocumentRendererException, IOException + { + renderIndividual( filesToProcess, outputDirectory, null ); + } + + /** {@inheritDoc} */ + @Override + public void renderIndividual( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String output = key; + for ( String extension : module.getExtensions() ) + { + String lowerCaseExtension = extension.toLowerCase( Locale.ENGLISH ); + if ( output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) != -1 ) + { + output = + output.substring( 0, output.toLowerCase( Locale.ENGLISH ).indexOf( "." + lowerCaseExtension ) ); + } + } + + File outputITextFile = new File( outputDirectory, output + ".xml" ); + if ( !outputITextFile.getParentFile().exists() ) + { + outputITextFile.getParentFile().mkdirs(); + } + + File pdfOutputFile = new File( outputDirectory, output + ".pdf" ); + if ( !pdfOutputFile.getParentFile().exists() ) + { + pdfOutputFile.getParentFile().mkdirs(); + } + + parse( fullDoc, module, outputITextFile, context ); + + generatePdf( outputITextFile, pdfOutputFile ); + } + } + + //-------------------------------------------- + // + //-------------------------------------------- + + + /** + * Parse a source document and emit results into a sink. + * + * @param fullDocPath file to the source document. + * @param module the site module associated with the source document (determines the parser to use). + * @param iTextFile the resulting iText xml file. + * @throws DocumentRendererException in case of a parsing problem. + * @throws IOException if the source and/or target document cannot be opened. + */ + private void parse( File fullDoc, ParserModule module, File iTextFile, DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( "Parsing file " + fullDoc.getAbsolutePath() ); + } + + System.setProperty( "itext.basedir", iTextFile.getParentFile().getAbsolutePath() ); + + Writer writer = null; + ITextSink sink = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + sink = (ITextSink) new ITextSinkFactory().createSink( writer ); + + sink.setClassLoader( new URLClassLoader( new URL[] { iTextFile.getParentFile().toURI().toURL() } ) ); + + parse( fullDoc.getAbsolutePath(), module.getParserId(), sink, context ); + } + finally + { + if ( sink != null ) + { + sink.flush(); + sink.close(); + } + IOUtil.close( writer ); + System.getProperties().remove( "itext.basedir" ); + } + } + + /** + * Merge all iTextFiles to a single one. + * + * @param iTextFiles list of iText xml files. + * @return Document. + * @throws DocumentRendererException if any. + * @throws IOException if any. + */ + private Document generateDocument( List iTextFiles ) + throws DocumentRendererException, IOException + { + Document document = DOCUMENT_BUILDER.newDocument(); + document.appendChild( document.createElement( ElementTags.ITEXT ) ); // Used only to set a root + + for ( File iTextFile : iTextFiles ) + { + Document iTextDocument; + + try + { + iTextDocument = DOCUMENT_BUILDER.parse( iTextFile ); + } + catch ( SAXException e ) + { + throw new DocumentRendererException( "SAX Error : " + e.getMessage() ); + } + + // Only one chapter per doc + Node chapter = iTextDocument.getElementsByTagName( ElementTags.CHAPTER ).item( 0 ); + + try + { + document.getDocumentElement().appendChild( document.importNode( chapter, true ) ); + } + catch ( DOMException e ) + { + throw new DocumentRendererException( "Error appending chapter for " + + iTextFile + " : " + e.getMessage() ); + } + } + + return document; + } + + /** + * Initialize the transformer object. + * + * @return an instance of a transformer object. + * @throws DocumentRendererException if any. + */ + private Transformer initTransformer() + throws DocumentRendererException + { + try + { + Transformer transformer = TRANSFORMER_FACTORY.newTransformer( new StreamSource( ITextPdfRenderer.class + .getResourceAsStream( XSLT_RESOURCE ) ) ); + + transformer.setErrorListener( TRANSFORMER_FACTORY.getErrorListener() ); + + transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "false" ); + + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + + // No doctype since itext doctype is not up to date! + + return transformer; + } + catch ( TransformerConfigurationException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + catch ( IllegalArgumentException e ) + { + throw new DocumentRendererException( "Error configuring Transformer for " + XSLT_RESOURCE + ": " + + e.getMessage() ); + } + } + + /** + * Add transformer parameters from a DocumentModel. + * + * @param transformer the Transformer to set the parameters. + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param iTextFile the iTextFile not null for the relative paths. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + */ + private void addTransformerParameters( Transformer transformer, DocumentModel documentModel, File iTextFile, + String generateTOC ) + { + if ( documentModel == null ) + { + return; + } + + // TOC + addTransformerParameter( transformer, "toc.position", generateTOC ); + + // Meta parameters + boolean hasNullMeta = false; + if ( documentModel.getMeta() == null ) + { + hasNullMeta = true; + documentModel.setMeta( new DocumentMeta() ); + } + addTransformerParameter( transformer, "meta.author", documentModel.getMeta().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + addTransformerParameter( transformer, "meta.creator", documentModel.getMeta().getCreator(), + System.getProperty( "user.name", "null" ) ); + // see com.lowagie.text.Document#addCreationDate() + SimpleDateFormat sdf = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy" ); + addTransformerParameter( transformer, "meta.creationdate", documentModel.getMeta().getCreationdate(), + sdf.format( new Date() ) ); + addTransformerParameter( transformer, "meta.keywords", documentModel.getMeta().getAllKeyWords() ); + addTransformerParameter( transformer, "meta.pagesize", documentModel.getMeta().getPageSize(), + ITextUtil.getPageSize( ITextUtil.getDefaultPageSize() ) ); + addTransformerParameter( transformer, "meta.producer", documentModel.getMeta().getGenerator(), + "Apache Doxia iText" ); + addTransformerParameter( transformer, "meta.subject", documentModel.getMeta().getSubject(), + ( documentModel.getMeta().getTitle() != null ? documentModel.getMeta().getTitle() + : "" ) ); + addTransformerParameter( transformer, "meta.title", documentModel.getMeta().getTitle() ); + if ( hasNullMeta ) + { + documentModel.setMeta( null ); + } + + // cover parameter + boolean hasNullCover = false; + if ( documentModel.getCover() == null ) + { + hasNullCover = true; + documentModel.setCover( new DocumentCover() ); + } + addTransformerParameter( transformer, "cover.author", documentModel.getCover().getAllAuthorNames(), + System.getProperty( "user.name", "null" ) ); + String companyLogo = getLogoURL( documentModel.getCover().getCompanyLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.companyLogo", companyLogo ); + addTransformerParameter( transformer, "cover.companyName", documentModel.getCover().getCompanyName() ); + if ( documentModel.getCover().getCoverdate() == null ) + { + documentModel.getCover().setCoverDate( new Date() ); + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + documentModel.getCover().setCoverDate( null ); + } + else + { + addTransformerParameter( transformer, "cover.date", documentModel.getCover().getCoverdate() ); + } + addTransformerParameter( transformer, "cover.subtitle", documentModel.getCover().getCoverSubTitle() ); + addTransformerParameter( transformer, "cover.title", documentModel.getCover().getCoverTitle() ); + addTransformerParameter( transformer, "cover.type", documentModel.getCover().getCoverType() ); + addTransformerParameter( transformer, "cover.version", documentModel.getCover().getCoverVersion() ); + String projectLogo = getLogoURL( documentModel.getCover().getProjectLogo(), iTextFile.getParentFile() ); + addTransformerParameter( transformer, "cover.projectLogo", projectLogo ); + addTransformerParameter( transformer, "cover.projectName", documentModel.getCover().getProjectName() ); + if ( hasNullCover ) + { + documentModel.setCover( null ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @param defaultValue could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value, String defaultValue ) + { + if ( StringUtils.isEmpty( value ) ) + { + addTransformerParameter( transformer, name, defaultValue ); + } + else + { + addTransformerParameter( transformer, name, value ); + } + } + + /** + * @param transformer not null + * @param name not null + * @param value could be empty + * @since 1.1.1 + */ + private void addTransformerParameter( Transformer transformer, String name, String value ) + { + if ( StringUtils.isEmpty( value ) ) + { + return; + } + + transformer.setParameter( name, value ); + } + + /** + * Transform a document to an iTextFile. + * + * @param documentModel the DocumentModel to take the parameters from, could be null. + * @param document the Document to transform. + * @param iTextFile the resulting iText xml file. + * @param generateTOC not null, possible values are: 'none', 'start' and 'end'. + * @throws DocumentRendererException in case of a transformation error. + */ + private void transform( DocumentModel documentModel, Document document, File iTextFile, String generateTOC ) + throws DocumentRendererException + { + Transformer transformer = initTransformer(); + + addTransformerParameters( transformer, documentModel, iTextFile, generateTOC ); + + // need a writer for StreamResult to prevent FileNotFoundException when iTextFile contains spaces + Writer writer = null; + try + { + writer = WriterFactory.newXmlWriter( iTextFile ); + transformer.transform( new DOMSource( document ), new StreamResult( writer ) ); + } + catch ( TransformerException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + catch ( IOException e ) + { + throw new DocumentRendererException( + "Error transforming Document " + document + ": " + e.getMessage(), + e ); + } + finally + { + IOUtil.close( writer ); + } + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseAllFiles( Map filesToProcess, File outputDirectory, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Map.Entry entry : filesToProcess.entrySet() ) + { + String key = entry.getKey(); + ParserModule module = entry.getValue(); + File fullDoc = new File( getBaseDir(), module.getSourceDirectory() + File.separator + key ); + + String outputITextName = key.substring( 0, key.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( fullDoc, module, outputITextFileTmp, context ); + } + + return iTextFiles; + } + + /** + * @param filesToProcess not null + * @param outputDirectory not null + * @return a list of all parsed files. + * @throws DocumentRendererException if any + * @throws IOException if any + * @since 1.1.1 + */ + private List parseTOCFiles( File outputDirectory, DocumentModel documentModel, + DocumentRendererContext context ) + throws DocumentRendererException, IOException + { + List iTextFiles = new LinkedList(); + for ( Iterator it = documentModel.getToc().getItems().iterator(); it.hasNext(); ) + { + DocumentTOCItem tocItem = it.next(); + + if ( tocItem.getRef() == null ) + { + getLogger().debug( + "No ref defined for the tocItem '" + tocItem.getName() + + "' in the document descriptor. IGNORING" ); + continue; + } + + String href = StringUtils.replace( tocItem.getRef(), "\\", "/" ); + if ( href.lastIndexOf( '.' ) != -1 ) + { + href = href.substring( 0, href.lastIndexOf( '.' ) ); + } + + Collection modules = parserModuleManager.getParserModules(); + for ( ParserModule module : modules ) + { + File moduleBasedir = new File( getBaseDir(), module.getSourceDirectory() ); + + if ( moduleBasedir.exists() ) + { + for ( String extension : module.getExtensions() ) + { + String doc = href + "." + extension; + File source = new File( moduleBasedir, doc ); + + // Velocity file? + if ( !source.exists() ) + { + if ( href.indexOf( "." + extension ) != -1 ) + { + doc = href + ".vm"; + } + else + { + doc = href + "." + extension + ".vm"; + } + source = new File( moduleBasedir, doc ); + } + + if ( source.exists() ) + { + String outputITextName = doc.substring( 0, doc.lastIndexOf( '.' ) + 1 ) + "xml"; + File outputITextFileTmp = new File( outputDirectory, outputITextName ); + outputITextFileTmp.deleteOnExit(); + if ( !outputITextFileTmp.getParentFile().exists() ) + { + outputITextFileTmp.getParentFile().mkdirs(); + } + + iTextFiles.add( outputITextFileTmp ); + parse( source, module, outputITextFileTmp, context ); + } + } + } + } + } + + return iTextFiles; + } + + /** + * @param logo + * @param parentFile + * @return the logo url or null if unable to create it. + * @since 1.1.1 + */ + private String getLogoURL( String logo, File parentFile ) + { + if ( logo == null ) + { + return null; + } + + try + { + return new URL( logo ).toString(); + } + catch ( MalformedURLException e ) + { + try + { + File f = new File( parentFile, logo ); + if ( !f.exists() ) + { + getLogger().warn( "The logo " + f.getAbsolutePath() + " doesnt exist. IGNORING" ); + } + else + { + return f.toURI().toURL().toString(); + } + } + catch ( MalformedURLException e1 ) + { + getLogger().debug( "Failed to convert to URL: " + logo, e1 ); + } + } + + return null; + } +} diff --git a/Java/maven-doxia-sitetools-ITextPdfRenderer_152/metadata.json b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/metadata.json new file mode 100644 index 000000000..d87264b8d --- /dev/null +++ b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-ITextPdfRenderer_152", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java", + "line": 153, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "ITextPdfRenderer", + "repo": "maven-doxia-sitetools", + "bug_id": "ITextPdfRenderer_152" + } +} diff --git a/Java/maven-doxia-sitetools-ITextPdfRenderer_152/npe.json b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/npe.json new file mode 100644 index 000000000..438f06b81 --- /dev/null +++ b/Java/maven-doxia-sitetools-ITextPdfRenderer_152/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-doc-renderer/src/main/java/org/apache/maven/doxia/docrenderer/pdf/itext/ITextPdfRenderer.java", + "line": 153, + "npe_method": "render", + "deref_field": "documentModel", + "npe_class": "ITextPdfRenderer" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathDescriptor_118/Dockerfile b/Java/maven-doxia-sitetools-PathDescriptor_118/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_118/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathDescriptor_118/buggy.java b/Java/maven-doxia-sitetools-PathDescriptor_118/buggy.java new file mode 100644 index 000000000..81e127a37 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_118/buggy.java @@ -0,0 +1,248 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + +private static java.net.URL buildBaseUrl(final java.lang.String base) throws java.net.MalformedURLException { + { + try { + return new java.net.URL(/* NPEX_NULL_EXP */ + base); + } catch (java.net.MalformedURLException e) { + return new java.io.File(base).toURI().toURL(); + } + } +} + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_118/metadata.json b/Java/maven-doxia-sitetools-PathDescriptor_118/metadata.json new file mode 100644 index 000000000..44304e4a8 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_118/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathDescriptor_118", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 119, + "npe_method": "buildBaseUrl", + "deref_field": "base", + "npe_class": "PathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "PathDescriptor_118" + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_118/npe.json b/Java/maven-doxia-sitetools-PathDescriptor_118/npe.json new file mode 100644 index 000000000..949de8328 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_118/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 119, + "npe_method": "buildBaseUrl", + "deref_field": "base", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathDescriptor_136/Dockerfile b/Java/maven-doxia-sitetools-PathDescriptor_136/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_136/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathDescriptor_136/buggy.java b/Java/maven-doxia-sitetools-PathDescriptor_136/buggy.java new file mode 100644 index 000000000..6b42bf732 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_136/buggy.java @@ -0,0 +1,245 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + +private static java.net.URL buildUrl(final java.net.URL baseUrl, final java.lang.String path) throws java.net.MalformedURLException { + { + if (path == null) { + return /* NPEX_NULL_EXP */ + baseUrl; + } + if (baseUrl.getProtocol().equals("file")) { + return new java.io.File(baseUrl.getFile(), path).toURI().toURL(); + } + if (path.startsWith("/") && baseUrl.getPath().endsWith("/")) { + return new java.net.URL(baseUrl, path.substring(1)); + } + return new java.net.URL(baseUrl, path); + } +} + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_136/metadata.json b/Java/maven-doxia-sitetools-PathDescriptor_136/metadata.json new file mode 100644 index 000000000..4b862359d --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_136/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathDescriptor_136", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 137, + "npe_method": "buildUrl", + "deref_field": "baseUrl", + "npe_class": "PathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "PathDescriptor_136" + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_136/npe.json b/Java/maven-doxia-sitetools-PathDescriptor_136/npe.json new file mode 100644 index 000000000..03eb52e74 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_136/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 137, + "npe_method": "buildUrl", + "deref_field": "baseUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathDescriptor_141/Dockerfile b/Java/maven-doxia-sitetools-PathDescriptor_141/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_141/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathDescriptor_141/buggy.java b/Java/maven-doxia-sitetools-PathDescriptor_141/buggy.java new file mode 100644 index 000000000..0d15abb59 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_141/buggy.java @@ -0,0 +1,245 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + +private static java.net.URL buildUrl(final java.net.URL baseUrl, final java.lang.String path) throws java.net.MalformedURLException { + if (baseUrl == null) { + throw new java.net.MalformedURLException("Base is null!"); + } + { + if (baseUrl.getProtocol().equals("file")) { + return new java.io.File(baseUrl.getFile(), /* NPEX_NULL_EXP */ + path).toURI().toURL(); + } + if (path.startsWith("/") && baseUrl.getPath().endsWith("/")) { + return new java.net.URL(baseUrl, path.substring(1)); + } + return new java.net.URL(baseUrl, path); + } +} + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_141/metadata.json b/Java/maven-doxia-sitetools-PathDescriptor_141/metadata.json new file mode 100644 index 000000000..81dc0092a --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_141/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathDescriptor_141", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 140, + "npe_method": "buildUrl", + "deref_field": "path", + "npe_class": "PathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "PathDescriptor_141" + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_141/npe.json b/Java/maven-doxia-sitetools-PathDescriptor_141/npe.json new file mode 100644 index 000000000..5026743d7 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_141/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 140, + "npe_method": "buildUrl", + "deref_field": "path", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathDescriptor_206/Dockerfile b/Java/maven-doxia-sitetools-PathDescriptor_206/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_206/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathDescriptor_206/buggy.java b/Java/maven-doxia-sitetools-PathDescriptor_206/buggy.java new file mode 100644 index 000000000..dc33280df --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_206/buggy.java @@ -0,0 +1,252 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ +/** + * Get the path. + * + * @return the path. + */ +public java.lang.String getPath() { + { + if (isFile()) { + return org.codehaus.plexus.util.StringUtils.stripEnd(/* NPEX_NULL_EXP */ + getPathUrl().getPath(), "/"); + } else { + return getPathUrl().getPath(); + } + } +} + + /** + * Get the location for files. + * + * @return the location. + */ + public String getLocation() + { + if ( isFile() ) + { + if ( getPathUrl() != null ) + { + return StringUtils.stripEnd( getPathUrl().getFile(), "/" ); + } + else + { + return relativePath; + } + } + else + { + return getPathUrl().toExternalForm(); + } + } + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_206/metadata.json b/Java/maven-doxia-sitetools-PathDescriptor_206/metadata.json new file mode 100644 index 000000000..83f5881d7 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_206/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathDescriptor_206", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 213, + "npe_method": "getPath", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "PathDescriptor_206" + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_206/npe.json b/Java/maven-doxia-sitetools-PathDescriptor_206/npe.json new file mode 100644 index 000000000..44d7ca90e --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_206/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 213, + "npe_method": "getPath", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathDescriptor_232/Dockerfile b/Java/maven-doxia-sitetools-PathDescriptor_232/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_232/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathDescriptor_232/buggy.java b/Java/maven-doxia-sitetools-PathDescriptor_232/buggy.java new file mode 100644 index 000000000..b941fdd6d --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_232/buggy.java @@ -0,0 +1,252 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.StringUtils; + +/** + * This class holds an instance of a maven path. This consists of a relative path (e.g. images/maven-logo.png) and a + * base reference which can also be a relative path (e.g. '.' or '../doxia') or an URL that is used for an absolute + * anchor. + * + * @author Henning P. Schmiedehausen + * @deprecated use {@link URIPathDescriptor} instead. + */ + +public class PathDescriptor +{ + private final URL baseUrl; + + private final URL pathUrl; + + private final String relativePath; + + /** + * Construct a PathDescriptor from a path. + * + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String path ) + throws MalformedURLException + { + this( (URL) null, path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param base a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final String base, final String path ) + throws MalformedURLException + { + this( PathDescriptor.buildBaseUrl( base ), path ); + } + + /** + * Construct a PathDescriptor from a path and a base. + * + * @param baseUrl a base reference. + * @param path the path. + * @throws java.net.MalformedURLException if a URL cannot be formed from the path. + */ + public PathDescriptor( final URL baseUrl, final String path ) + throws MalformedURLException + { + this.baseUrl = baseUrl; + + URL pathURL = null; + String relPath = null; + + try + { + pathURL = new URL( path ); + } + catch ( MalformedURLException e ) + { + try + { + pathURL = buildUrl( baseUrl, path ); + } + catch ( MalformedURLException e2 ) + { + // If we got an absolute path passed in and end here, then the path + // is converted to relative because we have no reference URL anyway + // to which it has been anchored. + if ( path != null && path.startsWith( "/" ) ) + { + relPath = path.substring( 1 ); + } + else + { + relPath = path; + } + } + } + + this.pathUrl = pathURL; + this.relativePath = relPath; + } + + private static URL buildBaseUrl( final String base ) + throws MalformedURLException + { + if ( base == null ) + { + return null; + } + + try + { + return new URL( base ); + } + catch ( MalformedURLException e ) + { + return new File( base ).toURI().toURL(); + } + } + + private static URL buildUrl( final URL baseUrl, final String path ) + throws MalformedURLException + { + if ( baseUrl == null ) + { + throw new MalformedURLException( "Base is null!" ); + } + + if ( path == null ) + { + return baseUrl; + } + + if ( baseUrl.getProtocol().equals( "file" ) ) + { + return new File( baseUrl.getFile(), path ).toURI().toURL(); + } + + if ( path.startsWith( "/" ) && baseUrl.getPath().endsWith( "/" ) ) + { + return new URL( baseUrl, path.substring( 1 ) ); + } + + return new URL( baseUrl, path ); + } + + /** + * Check if this PathDescriptor describes a file. + * + * @return true for file, false otherwise. + */ + public boolean isFile() + { + return isRelative() || pathUrl.getProtocol().equals( "file" ); + } + + /** + * Check if this PathDescriptor describes a relative path. + * + * @return true if {@link #getPathUrl()} returns null. + */ + public boolean isRelative() + { + return pathUrl == null; + } + + /** + * Get the base URL. + * + * @return the base URL. + */ + public URL getBaseUrl() + { + return baseUrl; + } + + /** + * Get the path as a URL. + * + * @return the path as a URL. + */ + public URL getPathUrl() + { + return pathUrl; + } + + /** + * Get the path. + * + * @return the path. + */ + public String getPath() + { + if ( getPathUrl() != null ) + { + if ( isFile() ) + { + return StringUtils.stripEnd( getPathUrl().getPath(), "/" ); + } + else + { + return getPathUrl().getPath(); + } + } + else + { + return relativePath; + } + } + + /** + * Get the location for files. + * + * @return the location. + */ +/** + * Get the location for files. + * + * @return the location. + */ +public java.lang.String getLocation() { + if (isFile()) { + { + return org.codehaus.plexus.util.StringUtils.stripEnd(/* NPEX_NULL_EXP */ + getPathUrl().getFile(), "/"); + } + } else { + return getPathUrl().toExternalForm(); + } +} + + /** {@inheritDoc} */ + public String toString() + { + StringBuilder res = + new StringBuilder( ( StringUtils.isNotEmpty( relativePath ) ) ? relativePath : String.valueOf( pathUrl ) ); + res.append( " (Base: " ).append( baseUrl ).append( ") Location: " ).append( getLocation() ); + return res.toString(); + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_232/metadata.json b/Java/maven-doxia-sitetools-PathDescriptor_232/metadata.json new file mode 100644 index 000000000..a37a0f7e0 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_232/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathDescriptor_232", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 237, + "npe_method": "getLocation", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "PathDescriptor_232" + } +} diff --git a/Java/maven-doxia-sitetools-PathDescriptor_232/npe.json b/Java/maven-doxia-sitetools-PathDescriptor_232/npe.json new file mode 100644 index 000000000..79a9f5cc1 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathDescriptor_232/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathDescriptor.java", + "line": 237, + "npe_method": "getLocation", + "deref_field": "getPathUrl", + "npe_class": "PathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-PathUtils_57/Dockerfile b/Java/maven-doxia-sitetools-PathUtils_57/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathUtils_57/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-PathUtils_57/buggy.java b/Java/maven-doxia-sitetools-PathUtils_57/buggy.java new file mode 100644 index 000000000..718484ff7 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathUtils_57/buggy.java @@ -0,0 +1,151 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.MalformedURLException; +import java.net.URL; + +import org.codehaus.plexus.util.PathTool; + +/** + * Utilities that allow conversion of old and new pathes and URLs relative to each other. + * + * @author Brett Porter + * @author Henning P. Schmiedehausen + * @deprecated this only operates on deprecated classes, it is not used anymore. + */ +public abstract class PathUtils +{ + /** + * Private constructor. + */ + private PathUtils() + { + // do not instantiate + } + + /** + *

    convertPath.

    + * + * @param oldPath not null + * @param newPath not null + * @return a PathDescriptor converted by the new path + * @throws java.net.MalformedURLException if any + */ +/** + *

    convertPath.

    + * + * @param oldPath + * not null + * @param newPath + * not null + * @return a PathDescriptor converted by the new path + * @throws java.net.MalformedURLException + * if any + */ +public static final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor convertPath(final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor oldPath, final org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor newPath) throws java.net.MalformedURLException { + java.lang.String relative = org.apache.maven.doxia.site.decoration.inheritance.PathUtils.getRelativePath(oldPath, newPath); + { + return new org.apache.maven.doxia.site.decoration.inheritance.PathDescriptor(/* NPEX_NULL_EXP */ + relative); + } +} + + /** + *

    getRelativePath.

    + * + * @param oldPathDescriptor not null + * @param newPathDescriptor not null + * @return a relative path depending if PathDescriptor is a file or a web url. + * @see PathTool#getRelativeFilePath(String, String) + * @see PathTool#getRelativeWebPath(String, String) + */ + public static final String getRelativePath( final PathDescriptor oldPathDescriptor, + final PathDescriptor newPathDescriptor ) + { + // Cannot convert from URL to file. + if ( oldPathDescriptor.isFile() ) + { + if ( !newPathDescriptor.isFile() ) + { + // We want to convert from a file to an URL. This is normally not possible... + if ( oldPathDescriptor.isRelative() ) + { + // unless the old path is a relative path. Then we might convert an existing + // site into a new URL using resolvePaths()... + return oldPathDescriptor.getPath(); + } + + // The old path is not relative. Bail out. + return null; + } + else + { + // both are files, if either of them is relative, bail out + // see DOXIASITETOOLS-29, MSITE-404, PLXUTILS-116 + if ( oldPathDescriptor.isRelative() || newPathDescriptor.isRelative() ) + { + return null; + } + } + } + + // Don't optimize to else. This might also be old.isFile && new.isFile ... + if ( !oldPathDescriptor.isFile() ) + { + // URLs, determine if they share protocol and domain info + URL oldUrl = oldPathDescriptor.getPathUrl(); + URL newUrl = newPathDescriptor.getPathUrl(); + + if ( oldUrl == null || newUrl == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + if ( ( newUrl.getProtocol().equalsIgnoreCase( oldUrl.getProtocol() ) ) + && ( newUrl.getHost().equalsIgnoreCase( oldUrl.getHost() ) ) + && ( newUrl.getPort() == oldUrl.getPort() ) ) + { + // Both paths point to the same site. So we can use relative paths. + + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + return PathTool.getRelativeWebPath( newPath, oldPath ); + } + + // Different sites. No relative Path possible. + return null; + } + + // Both Descriptors point to an absolute path. We can build a relative path. + String oldPath = oldPathDescriptor.getPath(); + String newPath = newPathDescriptor.getPath(); + + if ( oldPath == null || newPath == null ) + { + // One of the sites has a strange URL. no relative path possible, bail out. + return null; + } + + return PathTool.getRelativeFilePath( oldPath, newPath ); + } +} diff --git a/Java/maven-doxia-sitetools-PathUtils_57/metadata.json b/Java/maven-doxia-sitetools-PathUtils_57/metadata.json new file mode 100644 index 000000000..923543313 --- /dev/null +++ b/Java/maven-doxia-sitetools-PathUtils_57/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-PathUtils_57", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java", + "line": 67, + "npe_method": "convertPath", + "deref_field": "relative", + "npe_class": "PathUtils", + "repo": "maven-doxia-sitetools", + "bug_id": "PathUtils_57" + } +} diff --git a/Java/maven-doxia-sitetools-PathUtils_57/npe.json b/Java/maven-doxia-sitetools-PathUtils_57/npe.json new file mode 100644 index 000000000..20dc3fb9b --- /dev/null +++ b/Java/maven-doxia-sitetools-PathUtils_57/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/PathUtils.java", + "line": 67, + "npe_method": "convertPath", + "deref_field": "relative", + "npe_class": "PathUtils" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_269/Dockerfile b/Java/maven-doxia-sitetools-SiteRendererSink_269/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_269/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_269/buggy.java b/Java/maven-doxia-sitetools-SiteRendererSink_269/buggy.java new file mode 100644 index 000000000..e3fb4826b --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_269/buggy.java @@ -0,0 +1,354 @@ +package org.apache.maven.doxia.siterenderer.sink; + +/* + * 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. + */ + +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.text.html.HTML.Attribute; + +import org.apache.maven.doxia.markup.HtmlMarkup; +import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.siterenderer.DocumentContent; +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.apache.maven.doxia.util.HtmlTools; +import org.codehaus.plexus.util.StringUtils; + +/** + * Sink for site rendering of a document, to allow later merge document's output with a template. + * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating + * into skin or template: title, date, authors, head, body + * + * @author Emmanuel Venisse + */ +@SuppressWarnings( "checkstyle:methodname" ) +public class SiteRendererSink + extends Xhtml5Sink + implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent +{ + private String date = ""; + + private String title = ""; + + private List authors = new ArrayList(); + + private final StringWriter headWriter; + + private StringBuilder sectionTitleBuffer; + + private StringBuilder sectionTitleWriteBuffer; + + private boolean sectionHasID; + + private boolean isSectionTitle; + + private Set anchorsInSectionTitle; + + private final Writer writer; + + private RenderingContext renderingContext; + + /** + * Construct a new SiteRendererSink for a document. + * + * @param renderingContext the document's RenderingContext. + */ + public SiteRendererSink( RenderingContext renderingContext ) + { + this( new StringWriter(), renderingContext ); + } + + /** + * Construct a new SiteRendererSink for a document. + * + * @param writer the writer for the sink. + * @param renderingContext the document's RenderingContext. + */ + private SiteRendererSink( StringWriter writer, RenderingContext renderingContext ) + { + super( writer ); + + this.writer = writer; + this.headWriter = new StringWriter(); + this.renderingContext = renderingContext; + + /* the template is expected to have used the main tag, which can be used only once */ + super.contentStack.push( HtmlMarkup.MAIN ); + } + + /** {@inheritDoc} */ + @Override + public void title_() + { + if ( getTextBuffer().length() > 0 ) + { + title = getTextBuffer().toString(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Reset text buffer, since text content before title mustn't be in title. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title() + */ + @Override + public void title() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author_() + { + if ( getTextBuffer().length() > 0 ) + { + String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); + text = StringUtils.replace( text, "&#", "&#" ); + authors.add( text.trim() ); + } + + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date_() + { + if ( getTextBuffer().length() > 0 ) + { + date = getTextBuffer().toString().trim(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_() + */ + @Override + public void body_() + { + // nop + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body() + */ + @Override + public void body() + { + // nop + } + + /** {@inheritDoc} */ + @Override + public void head_() + { + setHeadFlag( false ); + } + + /** {@inheritDoc} */ + @Override + public void head() + { + setHeadFlag( true ); + } + + /** {@inheritDoc} */ + @Override + public void anchor( String name, SinkEventAttributes attributes ) + { + super.anchor( name, attributes ); + if ( isSectionTitle ) + { + if ( anchorsInSectionTitle == null ) + { + anchorsInSectionTitle = new HashSet(); + } + anchorsInSectionTitle.add( name ); + } + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle( int depth, SinkEventAttributes attributes ) + { + sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) ); + isSectionTitle = true; + + super.onSectionTitle( depth, attributes ); + + this.sectionTitleBuffer = new StringBuilder(); + this.sectionTitleWriteBuffer = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle_( int depth ) + { + String sectionTitle = sectionTitleBuffer.toString(); + this.sectionTitleBuffer = null; + String sectionWriteTitle = sectionTitleWriteBuffer.toString(); + this.sectionTitleWriteBuffer = null; + + if ( !StringUtils.isEmpty( sectionTitle ) ) + { + if ( sectionHasID ) + { + sectionHasID = false; + } + else + { + String id = HtmlTools.encodeId( sectionTitle ); + if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) ) + { + anchor( id ); + anchor_(); + } + } + } + + super.write( sectionWriteTitle ); + + this.isSectionTitle = false; + anchorsInSectionTitle = null; + super.onSectionTitle_( depth ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +public void text(java.lang.String text) { + { + // this implies we're inside a section title, collect text events for anchor generation + /* NPEX_NULL_EXP */ + sectionTitleBuffer.append(text); + } + super.text(text); +} + + /** {@inheritDoc} */ + @Override + protected void write( String text ) + { + String txt = text; + + if ( isHeadFlag() ) + { + headWriter.write( unifyEOLs( txt ) ); + + return; + } + + if ( renderingContext != null ) + { + String relativePathToBasedir = renderingContext.getRelativePath(); + + if ( relativePathToBasedir == null ) + { + txt = StringUtils.replace( txt, "$relativePath", "." ); + } + else + { + txt = StringUtils.replace( txt, "$relativePath", relativePathToBasedir ); + } + } + + if ( sectionTitleWriteBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleWriteBuffer.append( txt ); + } + else + { + super.write( txt ); + } + } + + // DocumentContent interface + + /** {@inheritDoc} */ + public String getTitle() + { + return title; + } + + /** {@inheritDoc} */ + public List getAuthors() + { + return authors; + } + + /** {@inheritDoc} */ + public String getDate() + { + return date; + } + + /** {@inheritDoc} */ + public String getBody() + { + return writer.toString(); + } + + /** {@inheritDoc} */ + public String getHead() + { + return headWriter.toString(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } +} diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_269/metadata.json b/Java/maven-doxia-sitetools-SiteRendererSink_269/metadata.json new file mode 100644 index 000000000..0f902e4ac --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_269/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-SiteRendererSink_269", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 274, + "npe_method": "text", + "deref_field": "sectionTitleBuffer", + "npe_class": "SiteRendererSink", + "repo": "maven-doxia-sitetools", + "bug_id": "SiteRendererSink_269" + } +} diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_269/npe.json b/Java/maven-doxia-sitetools-SiteRendererSink_269/npe.json new file mode 100644 index 000000000..a97d8b44b --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_269/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 274, + "npe_method": "text", + "deref_field": "sectionTitleBuffer", + "npe_class": "SiteRendererSink" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_305/Dockerfile b/Java/maven-doxia-sitetools-SiteRendererSink_305/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_305/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_305/buggy.java b/Java/maven-doxia-sitetools-SiteRendererSink_305/buggy.java new file mode 100644 index 000000000..0cdc44736 --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_305/buggy.java @@ -0,0 +1,341 @@ +package org.apache.maven.doxia.siterenderer.sink; + +/* + * 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. + */ + +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.text.html.HTML.Attribute; + +import org.apache.maven.doxia.markup.HtmlMarkup; +import org.apache.maven.doxia.module.xhtml5.Xhtml5Sink; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.sink.SinkEventAttributes; +import org.apache.maven.doxia.siterenderer.DocumentContent; +import org.apache.maven.doxia.siterenderer.RenderingContext; +import org.apache.maven.doxia.util.HtmlTools; +import org.codehaus.plexus.util.StringUtils; + +/** + * Sink for site rendering of a document, to allow later merge document's output with a template. + * During raw Doxia rendering, content is stored in multiple fields for later use when incorporating + * into skin or template: title, date, authors, head, body + * + * @author Emmanuel Venisse + */ +@SuppressWarnings( "checkstyle:methodname" ) +public class SiteRendererSink + extends Xhtml5Sink + implements Sink, org.codehaus.doxia.sink.Sink, DocumentContent +{ + private String date = ""; + + private String title = ""; + + private List authors = new ArrayList(); + + private final StringWriter headWriter; + + private StringBuilder sectionTitleBuffer; + + private StringBuilder sectionTitleWriteBuffer; + + private boolean sectionHasID; + + private boolean isSectionTitle; + + private Set anchorsInSectionTitle; + + private final Writer writer; + + private RenderingContext renderingContext; + + /** + * Construct a new SiteRendererSink for a document. + * + * @param renderingContext the document's RenderingContext. + */ + public SiteRendererSink( RenderingContext renderingContext ) + { + this( new StringWriter(), renderingContext ); + } + + /** + * Construct a new SiteRendererSink for a document. + * + * @param writer the writer for the sink. + * @param renderingContext the document's RenderingContext. + */ + private SiteRendererSink( StringWriter writer, RenderingContext renderingContext ) + { + super( writer ); + + this.writer = writer; + this.headWriter = new StringWriter(); + this.renderingContext = renderingContext; + + /* the template is expected to have used the main tag, which can be used only once */ + super.contentStack.push( HtmlMarkup.MAIN ); + } + + /** {@inheritDoc} */ + @Override + public void title_() + { + if ( getTextBuffer().length() > 0 ) + { + title = getTextBuffer().toString(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Reset text buffer, since text content before title mustn't be in title. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#title() + */ + @Override + public void title() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void author_() + { + if ( getTextBuffer().length() > 0 ) + { + String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); + text = StringUtils.replace( text, "&#", "&#" ); + authors.add( text.trim() ); + } + + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date() + { + resetTextBuffer(); + } + + /** {@inheritDoc} */ + @Override + public void date_() + { + if ( getTextBuffer().length() > 0 ) + { + date = getTextBuffer().toString().trim(); + } + + resetTextBuffer(); + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body_() + */ + @Override + public void body_() + { + // nop + } + + /** + * {@inheritDoc} + * + * Do nothing. + * @see org.apache.maven.doxia.module.xhtml5.Xhtml5Sink#body() + */ + @Override + public void body() + { + // nop + } + + /** {@inheritDoc} */ + @Override + public void head_() + { + setHeadFlag( false ); + } + + /** {@inheritDoc} */ + @Override + public void head() + { + setHeadFlag( true ); + } + + /** {@inheritDoc} */ + @Override + public void anchor( String name, SinkEventAttributes attributes ) + { + super.anchor( name, attributes ); + if ( isSectionTitle ) + { + if ( anchorsInSectionTitle == null ) + { + anchorsInSectionTitle = new HashSet(); + } + anchorsInSectionTitle.add( name ); + } + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle( int depth, SinkEventAttributes attributes ) + { + sectionHasID = ( attributes != null && attributes.isDefined ( Attribute.ID.toString() ) ); + isSectionTitle = true; + + super.onSectionTitle( depth, attributes ); + + this.sectionTitleBuffer = new StringBuilder(); + this.sectionTitleWriteBuffer = new StringBuilder(); + } + + /** {@inheritDoc} */ + @Override + protected void onSectionTitle_( int depth ) + { + String sectionTitle = sectionTitleBuffer.toString(); + this.sectionTitleBuffer = null; + String sectionWriteTitle = sectionTitleWriteBuffer.toString(); + this.sectionTitleWriteBuffer = null; + + if ( !StringUtils.isEmpty( sectionTitle ) ) + { + if ( sectionHasID ) + { + sectionHasID = false; + } + else + { + String id = HtmlTools.encodeId( sectionTitle ); + if ( ( anchorsInSectionTitle == null ) || ( !anchorsInSectionTitle.contains( id ) ) ) + { + anchor( id ); + anchor_(); + } + } + } + + super.write( sectionWriteTitle ); + + this.isSectionTitle = false; + anchorsInSectionTitle = null; + super.onSectionTitle_( depth ); + } + + /** {@inheritDoc} */ + @Override + public void text( String text ) + { + if ( sectionTitleBuffer != null ) + { + // this implies we're inside a section title, collect text events for anchor generation + sectionTitleBuffer.append( text ); + } + + super.text( text ); + } + + /** {@inheritDoc} */ + @Override +/** + * {@inheritDoc } + */ +protected void write(java.lang.String text) { + java.lang.String txt = text; + if (isHeadFlag()) { + headWriter.write(org.apache.maven.doxia.sink.impl.AbstractSink.unifyEOLs(txt)); + return; + } + if (renderingContext != null) { + java.lang.String relativePathToBasedir = renderingContext.getRelativePath(); + if (relativePathToBasedir == null) { + txt = org.codehaus.plexus.util.StringUtils.replace(txt, "$relativePath", "."); + } else { + txt = org.codehaus.plexus.util.StringUtils.replace(txt, "$relativePath", relativePathToBasedir); + } + } + { + // this implies we're inside a section title, collect text events for anchor generation + /* NPEX_NULL_EXP */ + sectionTitleWriteBuffer.append(txt); + } +} + + // DocumentContent interface + + /** {@inheritDoc} */ + public String getTitle() + { + return title; + } + + /** {@inheritDoc} */ + public List getAuthors() + { + return authors; + } + + /** {@inheritDoc} */ + public String getDate() + { + return date; + } + + /** {@inheritDoc} */ + public String getBody() + { + return writer.toString(); + } + + /** {@inheritDoc} */ + public String getHead() + { + return headWriter.toString(); + } + + /** {@inheritDoc} */ + public RenderingContext getRenderingContext() + { + return renderingContext; + } +} diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_305/metadata.json b/Java/maven-doxia-sitetools-SiteRendererSink_305/metadata.json new file mode 100644 index 000000000..0d8ebac1b --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_305/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-SiteRendererSink_305", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 300, + "npe_method": "write", + "deref_field": "sectionTitleWriteBuffer", + "npe_class": "SiteRendererSink", + "repo": "maven-doxia-sitetools", + "bug_id": "SiteRendererSink_305" + } +} diff --git a/Java/maven-doxia-sitetools-SiteRendererSink_305/npe.json b/Java/maven-doxia-sitetools-SiteRendererSink_305/npe.json new file mode 100644 index 000000000..67b09b048 --- /dev/null +++ b/Java/maven-doxia-sitetools-SiteRendererSink_305/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-site-renderer/src/main/java/org/apache/maven/doxia/siterenderer/sink/SiteRendererSink.java", + "line": 300, + "npe_method": "write", + "deref_field": "sectionTitleWriteBuffer", + "npe_class": "SiteRendererSink" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_166/Dockerfile b/Java/maven-doxia-sitetools-URIPathDescriptor_166/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_166/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_166/buggy.java b/Java/maven-doxia-sitetools-URIPathDescriptor_166/buggy.java new file mode 100644 index 000000000..bfbad3cf4 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_166/buggy.java @@ -0,0 +1,267 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; +import java.net.URISyntaxException; + +import org.codehaus.plexus.util.PathTool; + +/** + * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptor +{ + private final URI baseURI; + private final URI link; + + /** + * A URIPathDescriptor consists of a base URI and a link. + * Both arguments to this constructor have to be parsable to URIs. + * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. + * + * Before being parsed to {@link URI}s, the arguments are modified to catch + * some common bad practices: first all Windows-style backslashes '\' are replaced by + * forward slashes '/'. + * If the baseURI does not end with '/', a slash is appended. + * If the link starts with a '/', the first character is stripped. + * + * @param baseURI The base URI. Has to be a valid absolute URI. + * In addition, the path of the URI should not have any file part, + * ie http://maven.apache.org/ is valid, + * http://maven.apache.org/index.html is not. + * Even though the latter form is accepted without warning, + * the methods in this class will not return what is probably expected, + * because a slash is appended during construction, as noted above. + * @param link the link. This may be a relative link or an absolute link. + * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. + * + * @throws IllegalArgumentException if either argument is not parsable as a URI, + * or if baseURI is not absolute. + */ + public URIPathDescriptor( final String baseURI, final String link ) + { + final String llink = sanitizeLink( link ); + final String bbase = sanitizeBase( baseURI ); + + this.baseURI = URI.create( bbase ).normalize(); + this.link = URI.create( llink ).normalize(); + + if ( !this.baseURI.isAbsolute() ) + { + throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); + } + } + + /** + * Return the base of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized base URI. + */ + public URI getBaseURI() + { + return baseURI; + } + + /** + * Return the link of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized link URI. + */ + public URI getLink() + { + return link; + } + + /** + * Resolve the link to the base. + * This always returns an absolute URI. If link is absolute, link is returned. + * + * @return the resolved link. This is equivalent to calling + * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). + */ + public URI resolveLink() + { + return baseURI.resolve( link ); + } + + /** + * Calculate the relative link with respect to the base. + * The original link is returned if either + * link is relative; + * or link and base do not share the {@link #sameSite(java.net.URI) same site}. + * + * @return the link as a relative URI. + */ + public URI relativizeLink() + { + return relativizeLink( baseURI.toString(), link ); + } + + // NOTE: URI.relativize does not work as expected, see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 + private static URI relativizeLink( final String base, final URI link ) + { + if ( !link.isAbsolute() ) + { + return link; + } + + final URI newBaseURI = URI.create( base ); + + if ( !sameSite( link, newBaseURI ) ) + { + return link; + } + + final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); + + return URI.create( correctRelativePath( relativePath ) ); + } + + /** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase the new base URI. Has to be parsable as a URI. + *. + * @return a new relative link or the original link {@link #resolveLink() resolved}, + * i.e. as an absolute link, if the link cannot be re-based. + */ +/** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase + * the new base URI. Has to be parsable as a URI. + * . + * @return a new relative link or the original link {@link #resolveLink() resolved}, +i.e. as an absolute link, if the link cannot be re-based. + */ +public java.net.URI rebaseLink(final java.lang.String newBase) { + if (link.isAbsolute()) { + return link; + } + { + final java.net.URI newBaseURI; + try { + newBaseURI = new java.net.URI(/* NPEX_NULL_EXP */ + newBase); + } catch (java.net.URISyntaxException ex) { + return resolveLink(); + } + if (!sameSite(newBaseURI)) { + return resolveLink(); + } + final java.lang.String relativeBasePath = org.codehaus.plexus.util.PathTool.getRelativeWebPath(newBaseURI.getPath(), baseURI.getPath()); + return java.net.URI.create(org.apache.maven.doxia.site.decoration.inheritance.URIPathDescriptor.correctRelativePath(relativeBasePath)).resolve(link); + } +} + + private static String correctRelativePath( final String relativePath ) + { + if ( "".equals( relativePath ) || "/".equals( relativePath ) ) + { + return "./"; + } + else + { + return relativePath; + } + } + + /** + * Check if this URIPathDescriptor lives on the same site as the given URI. + * + * @param uri a URI to compare with. + * May be null, in which case false is returned. + * + * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI + * where null values are allowed. + */ + public boolean sameSite( final URI uri ) + { + return ( uri != null ) && sameSite( this.baseURI, uri ); + } + + private static boolean sameSite( final URI baseURI, final URI newBaseURI ) + { + final boolean sameScheme = + ( newBaseURI.getScheme() == null ? false : baseURI.getScheme().equalsIgnoreCase( newBaseURI.getScheme() ) ); + final boolean sameHost = + ( baseURI.getHost() == null ? newBaseURI.getHost() == null + : baseURI.getHost().equalsIgnoreCase( newBaseURI.getHost() ) ); + final boolean samePort = ( baseURI.getPort() == newBaseURI.getPort() ); + + return ( sameScheme && samePort && sameHost ); + } + + /** + * Construct a string representation of this URIPathDescriptor. + * This is equivalent to calling {@link #resolveLink()}.toString(). + * + * @return this URIPathDescriptor as a String. + */ + @Override + public String toString() + { + return resolveLink().toString(); + } + + private static String sanitizeBase( final String base ) + { + String sane = base.replace( '\\', '/' ); + + if ( !sane.endsWith( "/" ) ) + { + sane += "/"; + } + + return sane; + } + + private static String sanitizeLink( final String link ) + { + String sane = link.replace( '\\', '/' ); + + if ( sane.startsWith( "/" ) ) + { + sane = sane.substring( 1 ); + } + + return sane; + } +} diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_166/metadata.json b/Java/maven-doxia-sitetools-URIPathDescriptor_166/metadata.json new file mode 100644 index 000000000..06adfddb8 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_166/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-URIPathDescriptor_166", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 182, + "npe_method": "rebaseLink", + "deref_field": "newBase", + "npe_class": "URIPathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "URIPathDescriptor_166" + } +} diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_166/npe.json b/Java/maven-doxia-sitetools-URIPathDescriptor_166/npe.json new file mode 100644 index 000000000..ef09d5e34 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_166/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 182, + "npe_method": "rebaseLink", + "deref_field": "newBase", + "npe_class": "URIPathDescriptor" +} \ No newline at end of file diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_222/Dockerfile b/Java/maven-doxia-sitetools-URIPathDescriptor_222/Dockerfile new file mode 100644 index 000000000..36567fd64 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_222/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-doxia-sitetools + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_222/buggy.java b/Java/maven-doxia-sitetools-URIPathDescriptor_222/buggy.java new file mode 100644 index 000000000..63e69fb2c --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_222/buggy.java @@ -0,0 +1,261 @@ +package org.apache.maven.doxia.site.decoration.inheritance; + +/* + * 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. + */ + +import java.net.URI; +import java.net.URISyntaxException; + +import org.codehaus.plexus.util.PathTool; + +/** + * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. + * + * @author ltheussl + * + * @since 1.2 + */ +public class URIPathDescriptor +{ + private final URI baseURI; + private final URI link; + + /** + * A URIPathDescriptor consists of a base URI and a link. + * Both arguments to this constructor have to be parsable to URIs. + * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. + * + * Before being parsed to {@link URI}s, the arguments are modified to catch + * some common bad practices: first all Windows-style backslashes '\' are replaced by + * forward slashes '/'. + * If the baseURI does not end with '/', a slash is appended. + * If the link starts with a '/', the first character is stripped. + * + * @param baseURI The base URI. Has to be a valid absolute URI. + * In addition, the path of the URI should not have any file part, + * ie http://maven.apache.org/ is valid, + * http://maven.apache.org/index.html is not. + * Even though the latter form is accepted without warning, + * the methods in this class will not return what is probably expected, + * because a slash is appended during construction, as noted above. + * @param link the link. This may be a relative link or an absolute link. + * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. + * + * @throws IllegalArgumentException if either argument is not parsable as a URI, + * or if baseURI is not absolute. + */ + public URIPathDescriptor( final String baseURI, final String link ) + { + final String llink = sanitizeLink( link ); + final String bbase = sanitizeBase( baseURI ); + + this.baseURI = URI.create( bbase ).normalize(); + this.link = URI.create( llink ).normalize(); + + if ( !this.baseURI.isAbsolute() ) + { + throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); + } + } + + /** + * Return the base of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized base URI. + */ + public URI getBaseURI() + { + return baseURI; + } + + /** + * Return the link of this URIPathDescriptor as a URI. + * This is always {@link URI#normalize() normalized}. + * + * @return the normalized link URI. + */ + public URI getLink() + { + return link; + } + + /** + * Resolve the link to the base. + * This always returns an absolute URI. If link is absolute, link is returned. + * + * @return the resolved link. This is equivalent to calling + * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). + */ + public URI resolveLink() + { + return baseURI.resolve( link ); + } + + /** + * Calculate the relative link with respect to the base. + * The original link is returned if either + * link is relative; + * or link and base do not share the {@link #sameSite(java.net.URI) same site}. + * + * @return the link as a relative URI. + */ + public URI relativizeLink() + { + return relativizeLink( baseURI.toString(), link ); + } + + // NOTE: URI.relativize does not work as expected, see + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 + private static URI relativizeLink( final String base, final URI link ) + { + if ( !link.isAbsolute() ) + { + return link; + } + + final URI newBaseURI = URI.create( base ); + + if ( !sameSite( link, newBaseURI ) ) + { + return link; + } + + final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); + + return URI.create( correctRelativePath( relativePath ) ); + } + + /** + * Calculate the link as viewed from a different base. + * This returns the original link if link is absolute. + * This returns {@link #resolveLink()} if either + * newBase == null, + * or newBase is not parsable as a URI, + * or newBase and this {@link #getBaseURI()} do not share the + * {@link #sameSite(java.net.URI) same site}. + * + * @param newBase the new base URI. Has to be parsable as a URI. + *. + * @return a new relative link or the original link {@link #resolveLink() resolved}, + * i.e. as an absolute link, if the link cannot be re-based. + */ + public URI rebaseLink( final String newBase ) + { + if ( link.isAbsolute() ) + { + return link; + } + + if ( newBase == null ) + { + return resolveLink(); + } + + final URI newBaseURI; + + try + { + newBaseURI = new URI( newBase ); + } + catch ( URISyntaxException ex ) + { + return resolveLink(); + } + + if ( !sameSite( newBaseURI ) ) + { + return resolveLink(); + } + + final String relativeBasePath = PathTool.getRelativeWebPath( newBaseURI.getPath(), baseURI.getPath() ); + + return URI.create( correctRelativePath( relativeBasePath ) ).resolve( link ); + } + + private static String correctRelativePath( final String relativePath ) + { + if ( "".equals( relativePath ) || "/".equals( relativePath ) ) + { + return "./"; + } + else + { + return relativePath; + } + } + + /** + * Check if this URIPathDescriptor lives on the same site as the given URI. + * + * @param uri a URI to compare with. + * May be null, in which case false is returned. + * + * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI + * where null values are allowed. + */ + public boolean sameSite( final URI uri ) + { + return ( uri != null ) && sameSite( this.baseURI, uri ); + } + +private static boolean sameSite(final java.net.URI baseURI, final java.net.URI newBaseURI) { + final boolean sameScheme = (newBaseURI.getScheme() == null) ? false : baseURI.getScheme().equalsIgnoreCase(newBaseURI.getScheme()); + final boolean sameHost = /* NPEX_NULL_EXP */ + baseURI.getHost().equalsIgnoreCase(newBaseURI.getHost()); + final boolean samePort = baseURI.getPort() == newBaseURI.getPort(); + return (sameScheme && samePort) && sameHost; +} + + /** + * Construct a string representation of this URIPathDescriptor. + * This is equivalent to calling {@link #resolveLink()}.toString(). + * + * @return this URIPathDescriptor as a String. + */ + @Override + public String toString() + { + return resolveLink().toString(); + } + + private static String sanitizeBase( final String base ) + { + String sane = base.replace( '\\', '/' ); + + if ( !sane.endsWith( "/" ) ) + { + sane += "/"; + } + + return sane; + } + + private static String sanitizeLink( final String link ) + { + String sane = link.replace( '\\', '/' ); + + if ( sane.startsWith( "/" ) ) + { + sane = sane.substring( 1 ); + } + + return sane; + } +} diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_222/metadata.json b/Java/maven-doxia-sitetools-URIPathDescriptor_222/metadata.json new file mode 100644 index 000000000..3a69adf94 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_222/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-doxia-sitetools-URIPathDescriptor_222", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 221, + "npe_method": "sameSite", + "deref_field": "getHost", + "npe_class": "URIPathDescriptor", + "repo": "maven-doxia-sitetools", + "bug_id": "URIPathDescriptor_222" + } +} diff --git a/Java/maven-doxia-sitetools-URIPathDescriptor_222/npe.json b/Java/maven-doxia-sitetools-URIPathDescriptor_222/npe.json new file mode 100644 index 000000000..8db35eb63 --- /dev/null +++ b/Java/maven-doxia-sitetools-URIPathDescriptor_222/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "doxia-decoration-model/src/main/java/org/apache/maven/doxia/site/decoration/inheritance/URIPathDescriptor.java", + "line": 221, + "npe_method": "sameSite", + "deref_field": "getHost", + "npe_class": "URIPathDescriptor" +} \ No newline at end of file