1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.doxia.siterenderer;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.BufferedReader;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.Reader;
32 import java.io.StringReader;
33 import java.io.StringWriter;
34 import java.io.UnsupportedEncodingException;
35 import java.io.Writer;
36 import java.net.URL;
37 import java.net.URLClassLoader;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Enumeration;
42 import java.util.Iterator;
43 import java.util.LinkedHashMap;
44 import java.util.LinkedList;
45 import java.util.List;
46 import java.util.Locale;
47 import java.util.Map;
48 import java.util.Properties;
49 import java.util.TimeZone;
50 import java.util.zip.ZipEntry;
51 import java.util.zip.ZipException;
52 import java.util.zip.ZipFile;
53
54 import org.apache.commons.lang3.ArrayUtils;
55 import org.apache.commons.lang3.SystemUtils;
56 import org.apache.maven.artifact.Artifact;
57 import org.apache.maven.artifact.versioning.ArtifactVersion;
58 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
59 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
60 import org.apache.maven.artifact.versioning.Restriction;
61 import org.apache.maven.artifact.versioning.VersionRange;
62 import org.apache.maven.doxia.Doxia;
63 import org.apache.maven.doxia.parser.ParseException;
64 import org.apache.maven.doxia.parser.Parser;
65 import org.apache.maven.doxia.parser.manager.ParserNotFoundException;
66 import org.apache.maven.doxia.parser.module.ParserModule;
67 import org.apache.maven.doxia.parser.module.ParserModuleManager;
68 import org.apache.maven.doxia.site.decoration.DecorationModel;
69 import org.apache.maven.doxia.site.skin.SkinModel;
70 import org.apache.maven.doxia.site.skin.io.xpp3.SkinXpp3Reader;
71 import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink;
72 import org.apache.maven.doxia.util.XmlValidator;
73 import org.apache.velocity.Template;
74 import org.apache.velocity.context.Context;
75 import org.apache.velocity.exception.ParseErrorException;
76 import org.apache.velocity.exception.ResourceNotFoundException;
77 import org.apache.velocity.exception.VelocityException;
78 import org.apache.velocity.tools.Scope;
79 import org.apache.velocity.tools.ToolManager;
80 import org.apache.velocity.tools.config.ConfigurationUtils;
81 import org.apache.velocity.tools.config.EasyFactoryConfiguration;
82 import org.apache.velocity.tools.config.FactoryConfiguration;
83 import org.apache.velocity.tools.generic.AlternatorTool;
84 import org.apache.velocity.tools.generic.ClassTool;
85 import org.apache.velocity.tools.generic.ComparisonDateTool;
86 import org.apache.velocity.tools.generic.ContextTool;
87 import org.apache.velocity.tools.generic.ConversionTool;
88 import org.apache.velocity.tools.generic.DisplayTool;
89 import org.apache.velocity.tools.generic.EscapeTool;
90 import org.apache.velocity.tools.generic.FieldTool;
91 import org.apache.velocity.tools.generic.LinkTool;
92 import org.apache.velocity.tools.generic.LoopTool;
93 import org.apache.velocity.tools.generic.MathTool;
94 import org.apache.velocity.tools.generic.NumberTool;
95 import org.apache.velocity.tools.generic.RenderTool;
96 import org.apache.velocity.tools.generic.ResourceTool;
97 import org.apache.velocity.tools.generic.SortTool;
98 import org.apache.velocity.tools.generic.XmlTool;
99 import org.codehaus.plexus.PlexusContainer;
100 import org.codehaus.plexus.util.DirectoryScanner;
101 import org.codehaus.plexus.util.FileUtils;
102 import org.codehaus.plexus.util.IOUtil;
103 import org.codehaus.plexus.util.Os;
104 import org.codehaus.plexus.util.PathTool;
105 import org.codehaus.plexus.util.ReaderFactory;
106 import org.codehaus.plexus.util.StringUtils;
107 import org.codehaus.plexus.util.WriterFactory;
108 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
109 import org.codehaus.plexus.velocity.VelocityComponent;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
112
113
114
115
116
117
118
119
120 @Singleton
121 @Named
122 public class DefaultSiteRenderer implements Renderer {
123 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSiteRenderer.class);
124
125
126
127
128
129 @Inject
130 private VelocityComponent velocity;
131
132 @Inject
133 private ParserModuleManager parserModuleManager;
134
135 @Inject
136 private Doxia doxia;
137
138 @Inject
139 private PlexusContainer plexus;
140
141 private static final String SKIN_TEMPLATE_LOCATION = "META-INF/maven/site.vm";
142
143 private static final String TOOLS_LOCATION = "META-INF/maven/site-tools.xml";
144
145 private static final String DOXIA_SITE_RENDERER_VERSION = getSiteRendererVersion();
146
147
148
149
150
151
152 public Map<String, DocumentRenderer> locateDocumentFiles(SiteRenderingContext siteRenderingContext)
153 throws IOException, RendererException {
154 return locateDocumentFiles(siteRenderingContext, false);
155 }
156
157
158 public Map<String, DocumentRenderer> locateDocumentFiles(
159 SiteRenderingContext siteRenderingContext, boolean editable) throws IOException, RendererException {
160 Map<String, DocumentRenderer> files = new LinkedHashMap<String, DocumentRenderer>();
161 Map<String, String> moduleExcludes = siteRenderingContext.getModuleExcludes();
162
163
164 for (File siteDirectory : siteRenderingContext.getSiteDirectories()) {
165 if (siteDirectory.exists()) {
166 Collection<ParserModule> modules = parserModuleManager.getParserModules();
167
168 for (ParserModule module : modules) {
169 File moduleBasedir = new File(siteDirectory, module.getSourceDirectory());
170
171 String excludes = (moduleExcludes == null) ? null : moduleExcludes.get(module.getParserId());
172
173 addModuleFiles(
174 siteRenderingContext.getRootDirectory(), moduleBasedir, module, excludes, files, editable);
175 }
176 }
177 }
178
179 return files;
180 }
181
182 private List<String> filterExtensionIgnoreCase(List<String> fileNames, String extension) {
183 List<String> filtered = new LinkedList<String>(fileNames);
184 for (Iterator<String> it = filtered.iterator(); it.hasNext(); ) {
185 String name = it.next();
186
187
188 if (!endsWithIgnoreCase(name, extension)) {
189 it.remove();
190 }
191 }
192 return filtered;
193 }
194
195 private void addModuleFiles(
196 File rootDir,
197 File moduleBasedir,
198 ParserModule module,
199 String excludes,
200 Map<String, DocumentRenderer> files,
201 boolean editable)
202 throws IOException, RendererException {
203 if (!moduleBasedir.exists() || ArrayUtils.isEmpty(module.getExtensions())) {
204 return;
205 }
206
207 String moduleRelativePath =
208 PathTool.getRelativeFilePath(rootDir.getAbsolutePath(), moduleBasedir.getAbsolutePath());
209
210 List<String> allFiles = FileUtils.getFileNames(moduleBasedir, "**/*", excludes, false);
211
212 for (String extension : module.getExtensions()) {
213 String fullExtension = "." + extension;
214
215 List<String> docs = filterExtensionIgnoreCase(allFiles, fullExtension);
216
217
218 List<String> velocityFiles = filterExtensionIgnoreCase(allFiles, fullExtension + ".vm");
219
220 docs.addAll(velocityFiles);
221
222 for (String doc : docs) {
223 RenderingContext context = new RenderingContext(
224 moduleBasedir, moduleRelativePath, doc, module.getParserId(), extension, editable);
225
226
227 if (endsWithIgnoreCase(doc, ".vm")) {
228 context.setAttribute("velocity", "true");
229 }
230
231 String key = context.getOutputName();
232 key = StringUtils.replace(key, "\\", "/");
233
234 if (files.containsKey(key)) {
235 DocumentRenderer renderer = files.get(key);
236
237 RenderingContext originalContext = renderer.getRenderingContext();
238
239 File originalDoc = new File(originalContext.getBasedir(), originalContext.getInputName());
240
241 throw new RendererException("File '" + module.getSourceDirectory() + File.separator + doc
242 + "' clashes with existing '" + originalDoc + "'.");
243 }
244
245
246
247 for (Map.Entry<String, DocumentRenderer> entry : files.entrySet()) {
248 if (entry.getKey().equalsIgnoreCase(key)) {
249 RenderingContext originalContext = entry.getValue().getRenderingContext();
250
251 File originalDoc = new File(originalContext.getBasedir(), originalContext.getInputName());
252
253 if (Os.isFamily(Os.FAMILY_WINDOWS)) {
254 throw new RendererException("File '" + module.getSourceDirectory() + File.separator + doc
255 + "' clashes with existing '" + originalDoc + "'.");
256 }
257
258 if (LOGGER.isWarnEnabled()) {
259 LOGGER.warn("File '" + module.getSourceDirectory() + File.separator + doc
260 + "' could clash with existing '" + originalDoc + "'.");
261 }
262 }
263 }
264
265 files.put(key, new DoxiaDocumentRenderer(context));
266 }
267 }
268 }
269
270
271 public void render(
272 Collection<DocumentRenderer> documents, SiteRenderingContext siteRenderingContext, File outputDirectory)
273 throws RendererException, IOException {
274 for (DocumentRenderer docRenderer : documents) {
275 RenderingContext renderingContext = docRenderer.getRenderingContext();
276
277 File outputFile = new File(outputDirectory, docRenderer.getOutputName());
278
279 File inputFile = new File(renderingContext.getBasedir(), renderingContext.getInputName());
280
281 boolean modified = !outputFile.exists()
282 || (inputFile.lastModified() > outputFile.lastModified())
283 || (siteRenderingContext.getDecoration().getLastModified() > outputFile.lastModified());
284
285 if (modified || docRenderer.isOverwrite()) {
286 if (!outputFile.getParentFile().exists()) {
287 outputFile.getParentFile().mkdirs();
288 }
289
290 if (LOGGER.isDebugEnabled()) {
291 LOGGER.debug("Generating " + outputFile);
292 }
293
294 Writer writer = null;
295 try {
296 if (!docRenderer.isExternalReport()) {
297 writer = WriterFactory.newWriter(outputFile, siteRenderingContext.getOutputEncoding());
298 }
299 docRenderer.renderDocument(writer, this, siteRenderingContext);
300 } finally {
301 IOUtil.close(writer);
302 }
303 } else {
304 if (LOGGER.isDebugEnabled()) {
305 LOGGER.debug(inputFile + " unchanged, not regenerating...");
306 }
307 }
308 }
309 }
310
311
312 public void renderDocument(Writer writer, RenderingContext docRenderingContext, SiteRenderingContext siteContext)
313 throws RendererException, FileNotFoundException, UnsupportedEncodingException {
314 SiteRendererSink sink = new SiteRendererSink(docRenderingContext);
315
316 File doc = new File(docRenderingContext.getBasedir(), docRenderingContext.getInputName());
317
318 Reader reader = null;
319 try {
320 String resource = doc.getAbsolutePath();
321
322 Parser parser = doxia.getParser(docRenderingContext.getParserId());
323
324 parser.setEmitComments(false);
325
326
327 if (docRenderingContext.getAttribute("velocity") != null) {
328 LOGGER.debug("Processing Velocity for " + docRenderingContext.getDoxiaSourcePath());
329 try {
330 Context vc = createDocumentVelocityContext(docRenderingContext, siteContext);
331
332 StringWriter sw = new StringWriter();
333
334 velocity.getEngine().mergeTemplate(resource, siteContext.getInputEncoding(), vc, sw);
335
336 String doxiaContent = sw.toString();
337
338 if (siteContext.getProcessedContentOutput() != null) {
339
340 saveVelocityProcessedContent(docRenderingContext, siteContext, doxiaContent);
341 }
342
343 reader = new StringReader(doxiaContent);
344 } catch (VelocityException e) {
345 throw new RendererException(
346 "Error parsing " + docRenderingContext.getDoxiaSourcePath() + " as a Velocity template", e);
347 }
348
349 if (parser.getType() == Parser.XML_TYPE && siteContext.isValidate()) {
350 reader = validate(reader, resource);
351 }
352 } else {
353 switch (parser.getType()) {
354 case Parser.XML_TYPE:
355 reader = ReaderFactory.newXmlReader(doc);
356 if (siteContext.isValidate()) {
357 reader = validate(reader, resource);
358 }
359 break;
360
361 case Parser.TXT_TYPE:
362 case Parser.UNKNOWN_TYPE:
363 default:
364 reader = ReaderFactory.newReader(doc, siteContext.getInputEncoding());
365 }
366 }
367
368 doxia.parse(reader, docRenderingContext.getParserId(), sink, docRenderingContext.getInputName());
369 } catch (ParserNotFoundException e) {
370 throw new RendererException("Error getting a parser for '" + doc + "'", e);
371 } catch (ParseException e) {
372 StringBuilder errorMsgBuilder = new StringBuilder();
373 errorMsgBuilder.append("Error parsing '").append(doc).append("'");
374 if (e.getLineNumber() > 0) {
375 errorMsgBuilder.append(", line ").append(e.getLineNumber());
376 }
377 throw new RendererException(errorMsgBuilder.toString(), e);
378 } catch (IOException e) {
379 throw new RendererException("Error while processing '" + doc + "'", e);
380 } finally {
381 sink.flush();
382
383 sink.close();
384
385 IOUtil.close(reader);
386 }
387
388 mergeDocumentIntoSite(writer, (DocumentContent) sink, siteContext);
389 }
390
391 private void saveVelocityProcessedContent(
392 RenderingContext docRenderingContext, SiteRenderingContext siteContext, String doxiaContent)
393 throws IOException {
394 if (!siteContext.getProcessedContentOutput().exists()) {
395 siteContext.getProcessedContentOutput().mkdirs();
396 }
397
398 String input = docRenderingContext.getInputName();
399 File outputFile = new File(siteContext.getProcessedContentOutput(), input.substring(0, input.length() - 3));
400
401 File outputParent = outputFile.getParentFile();
402 if (!outputParent.exists()) {
403 outputParent.mkdirs();
404 }
405
406 FileUtils.fileWrite(outputFile, siteContext.getInputEncoding(), doxiaContent);
407 }
408
409
410
411
412
413
414
415 protected Context createToolManagedVelocityContext(SiteRenderingContext siteRenderingContext) {
416 Locale locale = siteRenderingContext.getLocale();
417 String dateFormat =
418 siteRenderingContext.getDecoration().getPublishDate().getFormat();
419 String timeZoneId =
420 siteRenderingContext.getDecoration().getPublishDate().getTimezone();
421 TimeZone timeZone =
422 "system".equalsIgnoreCase(timeZoneId) ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZoneId);
423
424 EasyFactoryConfiguration config = new EasyFactoryConfiguration(false);
425 config.property("safeMode", Boolean.FALSE);
426 config.toolbox(Scope.REQUEST)
427 .tool(ContextTool.class)
428 .tool(LinkTool.class)
429 .tool(LoopTool.class)
430 .tool(RenderTool.class);
431 config.toolbox(Scope.APPLICATION)
432 .property("locale", locale)
433 .tool(AlternatorTool.class)
434 .tool(ClassTool.class)
435 .tool(ComparisonDateTool.class)
436 .property("format", dateFormat)
437 .property("timezone", timeZone)
438 .tool(ConversionTool.class)
439 .property("dateFormat", dateFormat)
440 .tool(DisplayTool.class)
441 .tool(EscapeTool.class)
442 .tool(FieldTool.class)
443 .tool(MathTool.class)
444 .tool(NumberTool.class)
445 .tool(ResourceTool.class)
446 .property("bundles", new String[] {"site-renderer"})
447 .tool(SortTool.class)
448 .tool(XmlTool.class);
449
450 FactoryConfiguration customConfig = ConfigurationUtils.findInClasspath(TOOLS_LOCATION);
451
452 if (customConfig != null) {
453 config.addConfiguration(customConfig);
454 }
455
456 ToolManager manager = new ToolManager(false, false);
457 manager.configure(config);
458
459 return manager.createContext();
460 }
461
462
463
464
465
466
467
468
469 protected Context createDocumentVelocityContext(
470 RenderingContext renderingContext, SiteRenderingContext siteRenderingContext) {
471 Context context = createToolManagedVelocityContext(siteRenderingContext);
472
473
474
475
476 context.put("relativePath", renderingContext.getRelativePath());
477
478 String currentFileName = renderingContext.getOutputName().replace('\\', '/');
479 context.put("currentFileName", currentFileName);
480
481 context.put("alignedFileName", PathTool.calculateLink(currentFileName, renderingContext.getRelativePath()));
482
483 context.put("decoration", siteRenderingContext.getDecoration());
484
485 context.put("locale", siteRenderingContext.getLocale());
486 context.put("supportedLocales", Collections.unmodifiableList(siteRenderingContext.getSiteLocales()));
487
488 context.put("publishDate", siteRenderingContext.getPublishDate());
489
490 if (DOXIA_SITE_RENDERER_VERSION != null) {
491 context.put("doxiaSiteRendererVersion", DOXIA_SITE_RENDERER_VERSION);
492 }
493
494
495 Map<String, ?> templateProperties = siteRenderingContext.getTemplateProperties();
496
497 if (templateProperties != null) {
498 for (Map.Entry<String, ?> entry : templateProperties.entrySet()) {
499 context.put(entry.getKey(), entry.getValue());
500 }
501 }
502
503
504
505
506
507 context.put("PathTool", new PathTool());
508
509 context.put("StringUtils", new StringUtils());
510
511 context.put("plexus", plexus);
512 return context;
513 }
514
515
516
517
518
519
520
521
522
523 protected Context createSiteTemplateVelocityContext(
524 DocumentContent content, SiteRenderingContext siteRenderingContext) {
525
526 Context context = createDocumentVelocityContext(content.getRenderingContext(), siteRenderingContext);
527
528
529
530
531 context.put("authors", content.getAuthors());
532
533 context.put("shortTitle", content.getTitle());
534
535
536 StringBuilder title = new StringBuilder();
537 if (siteRenderingContext.getDecoration() != null
538 && StringUtils.isNotEmpty(siteRenderingContext.getDecoration().getName())) {
539 title.append(siteRenderingContext.getDecoration().getName());
540 } else if (StringUtils.isNotEmpty(siteRenderingContext.getDefaultTitle())) {
541 title.append(siteRenderingContext.getDefaultTitle());
542 }
543
544 if (title.length() > 0 && StringUtils.isNotEmpty(content.getTitle())) {
545 title.append(" – ");
546 }
547 if (StringUtils.isNotEmpty(content.getTitle())) {
548 title.append(content.getTitle());
549 }
550
551 context.put("title", title.length() > 0 ? title.toString() : null);
552
553 context.put("headContent", content.getHead());
554
555 context.put("bodyContent", content.getBody());
556
557
558 context.put("documentDate", content.getDate());
559
560
561 context.put("docRenderingContext", content.getRenderingContext());
562
563 return context;
564 }
565
566
567 public void generateDocument(Writer writer, SiteRendererSink sink, SiteRenderingContext siteRenderingContext)
568 throws RendererException {
569 mergeDocumentIntoSite(writer, sink, siteRenderingContext);
570 }
571
572
573 public void mergeDocumentIntoSite(Writer writer, DocumentContent content, SiteRenderingContext siteRenderingContext)
574 throws RendererException {
575 String templateName = siteRenderingContext.getTemplateName();
576
577 LOGGER.debug("Processing Velocity for template " + templateName + " on "
578 + content.getRenderingContext().getInputName());
579
580 Context context = createSiteTemplateVelocityContext(content, siteRenderingContext);
581
582 ClassLoader old = null;
583
584 if (siteRenderingContext.getTemplateClassLoader() != null) {
585
586
587
588
589 old = Thread.currentThread().getContextClassLoader();
590
591 Thread.currentThread().setContextClassLoader(siteRenderingContext.getTemplateClassLoader());
592 }
593
594 try {
595 Template template;
596 Artifact skin = siteRenderingContext.getSkin();
597
598 try {
599 SkinModel skinModel = siteRenderingContext.getSkinModel();
600 String encoding = (skinModel == null) ? null : skinModel.getEncoding();
601
602 template = (encoding == null)
603 ? velocity.getEngine().getTemplate(templateName)
604 : velocity.getEngine().getTemplate(templateName, encoding);
605 } catch (ParseErrorException pee) {
606 throw new RendererException(
607 "Velocity parsing error while reading the site decoration template " + "from " + skin.getId()
608 + " skin",
609 pee);
610 } catch (ResourceNotFoundException rnfe) {
611 throw new RendererException(
612 "Could not find the site decoration template " + "from " + skin.getId() + " skin", rnfe);
613 }
614
615 try {
616 StringWriter sw = new StringWriter();
617 template.merge(context, sw);
618 writer.write(sw.toString().replaceAll("\r?\n", SystemUtils.LINE_SEPARATOR));
619 } catch (VelocityException ve) {
620 throw new RendererException("Velocity error while merging site decoration template.", ve);
621 } catch (IOException ioe) {
622 throw new RendererException("IO exception while merging site decoration template.", ioe);
623 }
624 } finally {
625 IOUtil.close(writer);
626
627 if (old != null) {
628 Thread.currentThread().setContextClassLoader(old);
629 }
630 }
631 }
632
633 private SiteRenderingContext createSiteRenderingContext(
634 Map<String, ?> attributes, DecorationModel decoration, String defaultTitle, Locale locale) {
635 SiteRenderingContext context = new SiteRenderingContext();
636
637 context.setTemplateProperties(attributes);
638 context.setLocale(locale);
639 context.setDecoration(decoration);
640 context.setDefaultTitle(defaultTitle);
641
642 return context;
643 }
644
645
646 public SiteRenderingContext createContextForSkin(
647 Artifact skin, Map<String, ?> attributes, DecorationModel decoration, String defaultTitle, Locale locale)
648 throws IOException, RendererException {
649 SiteRenderingContext context = createSiteRenderingContext(attributes, decoration, defaultTitle, locale);
650
651 context.setSkin(skin);
652
653 ZipFile zipFile = getZipFile(skin.getFile());
654 InputStream in = null;
655
656 try {
657 if (zipFile.getEntry(SKIN_TEMPLATE_LOCATION) == null) {
658 throw new RendererException("Skin does not contain template at " + SKIN_TEMPLATE_LOCATION);
659 }
660 context.setTemplateName(SKIN_TEMPLATE_LOCATION);
661 context.setTemplateClassLoader(
662 new URLClassLoader(new URL[] {skin.getFile().toURI().toURL()}));
663
664 ZipEntry skinDescriptorEntry = zipFile.getEntry(SkinModel.SKIN_DESCRIPTOR_LOCATION);
665 if (skinDescriptorEntry != null) {
666 in = zipFile.getInputStream(skinDescriptorEntry);
667
668 SkinModel skinModel = new SkinXpp3Reader().read(in);
669 context.setSkinModel(skinModel);
670
671 String toolsPrerequisite = skinModel.getPrerequisites() == null
672 ? null
673 : skinModel.getPrerequisites().getDoxiaSitetools();
674
675 Package p = DefaultSiteRenderer.class.getPackage();
676 String current = (p == null) ? null : p.getImplementationVersion();
677
678 if (StringUtils.isNotBlank(toolsPrerequisite)
679 && (current != null)
680 && !matchVersion(current, toolsPrerequisite)) {
681 throw new RendererException("Cannot use skin: has " + toolsPrerequisite
682 + " Doxia Sitetools prerequisite, but current is " + current);
683 }
684 }
685 } catch (XmlPullParserException e) {
686 throw new RendererException(
687 "Failed to parse " + SkinModel.SKIN_DESCRIPTOR_LOCATION + " skin descriptor from " + skin.getId()
688 + " skin",
689 e);
690 } finally {
691 IOUtil.close(in);
692 closeZipFile(zipFile);
693 }
694
695 return context;
696 }
697
698 boolean matchVersion(String current, String prerequisite) throws RendererException {
699 try {
700 ArtifactVersion v = new DefaultArtifactVersion(current);
701 VersionRange vr = VersionRange.createFromVersionSpec(prerequisite);
702
703 boolean matched = false;
704 ArtifactVersion recommendedVersion = vr.getRecommendedVersion();
705 if (recommendedVersion == null) {
706 List<Restriction> restrictions = vr.getRestrictions();
707 for (Restriction restriction : restrictions) {
708 if (restriction.containsVersion(v)) {
709 matched = true;
710 break;
711 }
712 }
713 } else {
714
715 @SuppressWarnings("unchecked")
716 int compareTo = recommendedVersion.compareTo(v);
717 matched = (compareTo <= 0);
718 }
719
720 if (LOGGER.isDebugEnabled()) {
721 LOGGER.debug("Skin doxia-sitetools prerequisite: " + prerequisite + ", current: " + current
722 + ", matched = " + matched);
723 }
724
725 return matched;
726 } catch (InvalidVersionSpecificationException e) {
727 throw new RendererException("Invalid skin doxia-sitetools prerequisite: " + prerequisite, e);
728 }
729 }
730
731
732 public void copyResources(SiteRenderingContext siteRenderingContext, File resourcesDirectory, File outputDirectory)
733 throws IOException {
734 throw new AssertionError("copyResources( SiteRenderingContext, File, File ) is deprecated.");
735 }
736
737
738 public void copyResources(SiteRenderingContext siteRenderingContext, File outputDirectory) throws IOException {
739 ZipFile file = getZipFile(siteRenderingContext.getSkin().getFile());
740
741 try {
742 for (Enumeration<? extends ZipEntry> e = file.entries(); e.hasMoreElements(); ) {
743 ZipEntry entry = e.nextElement();
744
745 if (!entry.getName().startsWith("META-INF/")) {
746 File destFile = new File(outputDirectory, entry.getName());
747 if (!entry.isDirectory()) {
748 if (destFile.exists()) {
749
750
751 continue;
752 }
753
754 destFile.getParentFile().mkdirs();
755
756 copyFileFromZip(file, entry, destFile);
757 } else {
758 destFile.mkdirs();
759 }
760 }
761 }
762 } finally {
763 closeZipFile(file);
764 }
765
766
767 for (File siteDirectory : siteRenderingContext.getSiteDirectories()) {
768 File resourcesDirectory = new File(siteDirectory, "resources");
769
770 if (resourcesDirectory != null && resourcesDirectory.exists()) {
771 copyDirectory(resourcesDirectory, outputDirectory);
772 }
773 }
774
775
776 File siteCssFile = new File(outputDirectory, "/css/site.css");
777 if (!siteCssFile.exists()) {
778
779 File cssDirectory = new File(outputDirectory, "/css/");
780 boolean created = cssDirectory.mkdirs();
781 if (created && LOGGER.isDebugEnabled()) {
782 LOGGER.debug("The directory '" + cssDirectory.getAbsolutePath() + "' did not exist. It was created.");
783 }
784
785
786 if (LOGGER.isDebugEnabled()) {
787 LOGGER.debug(
788 "The file '" + siteCssFile.getAbsolutePath() + "' does not exist. Creating an empty file.");
789 }
790 Writer writer = null;
791 try {
792 writer = WriterFactory.newWriter(siteCssFile, siteRenderingContext.getOutputEncoding());
793
794 writer.write("/* You can override this file with your own styles */");
795 } finally {
796 IOUtil.close(writer);
797 }
798 }
799 }
800
801 private static void copyFileFromZip(ZipFile file, ZipEntry entry, File destFile) throws IOException {
802 FileOutputStream fos = new FileOutputStream(destFile);
803
804 try {
805 IOUtil.copy(file.getInputStream(entry), fos);
806 } finally {
807 IOUtil.close(fos);
808 }
809 }
810
811
812
813
814
815
816
817
818 protected void copyDirectory(File source, File destination) throws IOException {
819 if (source.exists()) {
820 DirectoryScanner scanner = new DirectoryScanner();
821
822 String[] includedResources = {"**/*"};
823
824 scanner.setIncludes(includedResources);
825
826 scanner.addDefaultExcludes();
827
828 scanner.setBasedir(source);
829
830 scanner.scan();
831
832 List<String> includedFiles = Arrays.asList(scanner.getIncludedFiles());
833
834 for (String name : includedFiles) {
835 File sourceFile = new File(source, name);
836
837 File destinationFile = new File(destination, name);
838
839 FileUtils.copyFile(sourceFile, destinationFile);
840 }
841 }
842 }
843
844 private Reader validate(Reader source, String resource) throws ParseException, IOException {
845 LOGGER.debug("Validating: " + resource);
846
847 try {
848 String content = IOUtil.toString(new BufferedReader(source));
849
850 new XmlValidator().validate(content);
851
852 return new StringReader(content);
853 } finally {
854 IOUtil.close(source);
855 }
856 }
857
858
859 static boolean endsWithIgnoreCase(String str, String searchStr) {
860 if (str.length() < searchStr.length()) {
861 return false;
862 }
863
864 return str.regionMatches(true, str.length() - searchStr.length(), searchStr, 0, searchStr.length());
865 }
866
867 private static ZipFile getZipFile(File file) throws IOException {
868 if (file == null) {
869 throw new IOException("Error opening ZipFile: null");
870 }
871
872 try {
873
874 return new ZipFile(file);
875 } catch (ZipException ex) {
876 IOException ioe = new IOException("Error opening ZipFile: " + file.getAbsolutePath());
877 ioe.initCause(ex);
878 throw ioe;
879 }
880 }
881
882 private static void closeZipFile(ZipFile zipFile) {
883
884 try {
885 zipFile.close();
886 } catch (IOException e) {
887
888 }
889 }
890
891 private static String getSiteRendererVersion() {
892 InputStream inputStream = DefaultSiteRenderer.class.getResourceAsStream(
893 "/META-INF/" + "maven/org.apache.maven.doxia/doxia-site-renderer/pom.properties");
894 if (inputStream == null) {
895 LOGGER.debug("pom.properties for doxia-site-renderer not found");
896 } else {
897 Properties properties = new Properties();
898 try (InputStream in = inputStream) {
899 properties.load(in);
900 return properties.getProperty("version");
901 } catch (IOException e) {
902 LOGGER.debug("Failed to load pom.properties, so Doxia SiteRenderer version will not be available", e);
903 }
904 }
905
906 return null;
907 }
908 }