-
Notifications
You must be signed in to change notification settings - Fork 27
/
storage.scala
120 lines (114 loc) · 4.24 KB
/
storage.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package pamflet
import java.io.File
import java.nio.charset.Charset
import com.tristanhunt.knockoff._
import collection.immutable.Map
import collection.concurrent.TrieMap
trait Storage {
def globalized: Globalized
}
/** Cache FileStorage based on the last modified time.
* This should make previewing much faster on large pamflets.
*/
case class CachedFileStorage(base: File) extends Storage {
def allFiles(f0: File): Seq[File] =
f0.listFiles.toVector flatMap {
case dir if dir.isDirectory => allFiles(dir)
case f => Vector(f)
}
def maxLastModified(f0: File): Long =
(allFiles(f0) map {_.lastModified}).max
def globalized = {
val lm = maxLastModified(base)
CachedFileStorage.cache.get(base) match {
case Some((lm0, gl0)) if lm == lm0 => gl0
case _ =>
val st = FileStorage(base)
val gl = st.globalized
CachedFileStorage.cache(base) = (lm, gl)
gl
}
}
}
object CachedFileStorage {
val cache: TrieMap[File, (Long, Globalized)] = TrieMap()
}
case class FileStorage(base: File) extends Storage {
def propFile(dir: File): Option[File] =
new File(dir, "template.properties") match {
case file if file.exists => Some(file)
case _ => None
}
def globalized = {
val contents = Map(defaultTemplate.languages map { lang =>
val isDefaultLang = lang == defaultTemplate.defaultLanguage
val dir = if (isDefaultLang) base
else new File(base, lang)
val css = dir.listFiles.filter {
_.getName.endsWith(".css")
}.map { f => (f.getName, read(f)) }
val files = dir.listFiles.filter(_.getName=="files").
flatMap(_.listFiles.map { f => (f.getName, f.toURI) })
val favicon = dir.listFiles.find(_.getName == "favicon.ico").
map { _.toURI }
val propFiles = if (isDefaultLang) propFile(base).toSeq
else propFile(base).toSeq ++ propFile(dir).toSeq
val layouts = dir.listFiles.filter(_.getName == "layouts").
flatMap(_.listFiles.map { f => (f.getName, read(f))})
lang -> Contents(lang, isDefaultLang, rootSection(dir, propFiles), css, files,
favicon, defaultTemplate, layouts)
}: _*)
Globalized(contents, defaultTemplate)
}
def isSpecialDir(dir: File): Boolean =
dir.isDirectory && ((dir.getName == "layouts") || (dir.getName == "files"))
def rootSection(dir: File, propFiles: Seq[File]): Section = {
def emptySection = Section("", "", Seq.empty, Nil, defaultTemplate)
if (dir.exists) section("", dir, propFiles).headOption getOrElse emptySection
else emptySection
}
def section(localPath: String, dir: File, propFiles: Seq[File]): Seq[Section] = {
val files: List[File] = (Option(dir.listFiles) match {
case None => Nil
case Some(fs) => fs.toList
}).sortWith {
_.getName < _.getName
}
files.find(isMarkdown).map { head =>
val (raw, blocks, template) = knock(head, propFiles)
val childFiles = files.filterNot { _ == head } filterNot { f =>
f.isDirectory && defaultTemplate.languages.contains(f.getName)
}
val children = childFiles.flatMap { f =>
if (isMarkdown(f))
Seq(Leaf(localPath + "/" + f.getName, knock(f, propFiles)))
else if (f.isDirectory && !isSpecialDir(f)) section(localPath + "/" + f.getName, f, propFiles)
else Seq()
}
Section(localPath, raw, blocks, children, template)
}.toSeq
}
def read(file: File, encoding: String = Charset.defaultCharset.name) = doWith(scala.io.Source.fromFile(file, encoding)) { source =>
source.mkString("")
}
def knock(file: File, propFiles: Seq[File]): (String, Seq[Block], Template) =
Knock.knockEither(read(file, defaultTemplate.defaultEncoding), propFiles) match {
case Right(x) => x
case Left(x) =>
Console.err.println("Error while processing " + file.toString)
throw x
}
def isMarkdown(f: File) = {
!f.isDirectory &&
!f.getName.startsWith(".") &&
(f.getName.endsWith(".markdown") || f.getName.endsWith(".md"))
}
def defaultTemplate = StringTemplate(propFile(base).toSeq, None, Map())
def doWith[T <: { def close() }, R](toClose: T)(f: T => R): R = {
try {
f(toClose)
} finally {
toClose.close()
}
}
}