Ignore charsets when checking for HTML index file
[jSite.git] / src / main / java / de / todesbaum / jsite / application / validation / ProjectValidator.java
1 package de.todesbaum.jsite.application.validation;
2
3 import java.io.File;
4 import java.util.Arrays;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Set;
10 import java.util.concurrent.CountDownLatch;
11 import java.util.logging.Level;
12 import java.util.logging.Logger;
13 import java.util.stream.Stream;
14
15 import de.todesbaum.jsite.application.FileOption;
16 import de.todesbaum.jsite.application.Project;
17 import de.todesbaum.jsite.gui.FileScanner;
18 import de.todesbaum.jsite.gui.ScannedFile;
19
20 /**
21  * Validates a project and returns a number of {@link Issue}s in a {@link CheckReport}.
22  *
23  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
24  */
25 public class ProjectValidator {
26
27         private static final Logger logger = Logger.getLogger(ProjectValidator.class.getName());
28
29         public static CheckReport validateProject(Project project) {
30                 CheckReport checkReport = new CheckReport();
31                 if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) {
32                         checkReport.addIssue("error.no-local-path", true);
33                         return checkReport;
34                 }
35                 if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
36                         checkReport.addIssue("error.no-path", true);
37                 }
38                 if ((project.getIndexFile() == null) || (project.getIndexFile().length() == 0)) {
39                         checkReport.addIssue("warning.empty-index", false);
40                 } else {
41                         File indexFile = new File(project.getLocalPath(), project.getIndexFile());
42                         if (!indexFile.exists()) {
43                                 checkReport.addIssue("error.index-missing", true);
44                         }
45                 }
46                 String indexFile = project.getIndexFile();
47                 boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0);
48                 if (hasIndexFile && indexFileIsNotHtml(project, indexFile)) {
49                         checkReport.addIssue("warning.index-not-html", false);
50                 }
51                 Map<String, FileOption> fileOptions = project.getFileOptions();
52                 Set<Entry<String, FileOption>> fileOptionEntries = fileOptions.entrySet();
53                 boolean insert = fileOptionEntries.isEmpty();
54                 for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
55                         String fileName = fileOptionEntry.getKey();
56                         FileOption fileOption = fileOptionEntry.getValue();
57                         insert |= fileOption.isInsert() || fileOption.isInsertRedirect();
58                         if (fileName.equals(project.getIndexFile()) && !fileOption.isInsert() && !fileOption.isInsertRedirect()) {
59                                 checkReport.addIssue("error.index-not-inserted", true);
60                         }
61                         if (!fileOption.isInsert() && fileOption.isInsertRedirect() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(
62                                         fileOption.getCustomKey()))) {
63                                 checkReport.addIssue("error.no-custom-key", true, fileName);
64                         }
65                 }
66                 if (!insert) {
67                         checkReport.addIssue("error.no-files-to-insert", true);
68                 }
69                 Set<String> fileNames = new HashSet<>();
70                 for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
71                         FileOption fileOption = fileOptionEntry.getValue();
72                         if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) {
73                                 logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey());
74                                 continue;
75                         }
76                         String fileName = fileOption.getChangedName().orElse(fileOptionEntry.getKey());
77                         logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() });
78                         if (!fileNames.add(fileName)) {
79                                 checkReport.addIssue("error.duplicate-file", true, fileName);
80                         }
81                 }
82                 long totalSize = 0;
83                 final CountDownLatch completionLatch = new CountDownLatch(1);
84                 FileScanner fileScanner = new FileScanner(project, (error, files) -> completionLatch.countDown());
85                 fileScanner.startInBackground();
86                 while (completionLatch.getCount() > 0) {
87                         try {
88                                 completionLatch.await();
89                         } catch (InterruptedException ie1) {
90                                 /* TODO: logging */
91                         }
92                 }
93                 for (ScannedFile scannedFile : fileScanner.getFiles()) {
94                         String fileName = scannedFile.getFilename();
95                         FileOption fileOption = project.getFileOption(fileName);
96                         if ((fileOption != null) && !fileOption.isInsert()) {
97                                 continue;
98                         }
99                         totalSize += new File(project.getLocalPath(), fileName).length();
100                 }
101                 if (totalSize > 2 * 1024 * 1024) {
102                         checkReport.addIssue("warning.site-larger-than-2-mib", false);
103                 }
104                 return checkReport;
105         }
106
107         private static boolean indexFileIsNotHtml(Project project, String indexFile) {
108                 return Stream.of("text/html", "application/xhtml+xml")
109                                 .noneMatch(mimeType -> project.getFileOption(indexFile).getMimeType().startsWith(mimeType));
110         }
111
112 }