@@ -22,8 +22,23 @@ object Caches:
2222 * the type of cached values
2323 */
2424 trait Cache [K , V ]:
25+
26+ /** Get the value associated with `key` from the cache, or compute it using
27+ * the by-name parameter `value`.
28+ *
29+ * The value is cached iff `mightContain(key) == true`.
30+ */
2531 def apply (key : K , value : => V ): V
32+
33+ /** Check whether the cache might contain a value for `key`.
34+ *
35+ * `true` means that the cache will cache the value for `key` if requested
36+ * via [[apply ]], not that it has already cached it.
37+ */
38+ def mightContain (key : K ): Boolean
39+
2640 def stats (): CacheStats
41+
2742 override def toString : String =
2843 s " ${this .getClass.getSimpleName}(stats() = ${stats()}) "
2944
@@ -42,6 +57,9 @@ object Caches:
4257 total += 1
4358 value
4459
60+ def mightContain (key : K ): Boolean =
61+ false
62+
4563 def stats (): CacheStats =
4664 CacheStats (total, misses = 0 , uncached = total)
4765
@@ -73,6 +91,9 @@ object Caches:
7391 map.put(key, (stamp, v))
7492 v
7593
94+ def mightContain (key : K ): Boolean =
95+ getStamp(key).isDefined
96+
7697 def stats (): CacheStats =
7798 CacheStats (total, misses, uncached)
7899
@@ -103,6 +124,9 @@ object Caches:
103124 (stamp, value)
104125 )._2
105126
127+ def mightContain (key : K ): Boolean =
128+ getStamp(key).isDefined
129+
106130 def stats (): CacheStats =
107131 CacheStats (total.longValue(), misses.longValue(), uncached.longValue())
108132
@@ -123,27 +147,36 @@ object Caches:
123147 final class FileBasedCache [V ]() extends Cache [AbstractFile , V ]:
124148 private case class FileStamp (lastModified : FileTime , fileKey : Object )
125149
126- private def getFileStamp (abstractFile : AbstractFile ): Option [FileStamp ] =
150+ private def getPath (abstractFile : AbstractFile ): Option [Path ] =
127151 abstractFile.underlyingSource match
128152 case Some (underlyingSource) if underlyingSource ne abstractFile =>
129- getFileStamp (underlyingSource)
153+ getPath (underlyingSource)
130154 case _ =>
131- val javaFile = abstractFile.file
132- if javaFile == null then
133- None
134- else
135- val attrs = Files .readAttributes(javaFile.toPath, classOf [BasicFileAttributes ])
136- val lastModified = attrs.lastModifiedTime()
137- // This can be `null` on some platforms, but that's okay, we just use
138- // the last modified timestamp as our stamp in that case.
139- val fileKey = attrs.fileKey()
140- Some (FileStamp (lastModified, fileKey))
155+ val javaPath = abstractFile.jpath
156+ if javaPath != null then Some (javaPath) else None
157+
158+ private def getFileStamp (abstractFile : AbstractFile ): Option [FileStamp ] =
159+ getPath(abstractFile) match
160+ case Some (path) =>
161+ val attrs = Files .readAttributes(path, classOf [BasicFileAttributes ])
162+ val lastModified = attrs.lastModifiedTime()
163+ // This can be `null` on some platforms, but that's okay, we just use
164+ // the last modified timestamp as our stamp in that case.
165+ val fileKey = attrs.fileKey()
166+ Some (FileStamp (lastModified, fileKey))
167+ case None =>
168+ None
141169
142170 private val underlying = SynchronizedMapCache [AbstractFile , FileStamp , V ](getFileStamp)
143171
144172 def apply (key : AbstractFile , value : => V ): V =
145173 underlying(key, value)
146174
175+ def mightContain (key : AbstractFile ): Boolean =
176+ // We just check that a path exists here to avoi IO. `getFileStamp` will
177+ // return `None` iff `getPath` returns `None`.
178+ getPath(key).isDefined
179+
147180 def stats (): CacheStats =
148181 underlying.stats()
149182
@@ -176,5 +209,8 @@ object Caches:
176209 uncached = baseStats.uncached + uncached.longValue()
177210 )
178211
212+ def mightContain (key : K ): Boolean =
213+ shouldCache(key) && underlying.mightContain(key)
214+
179215 override def toString : String =
180216 s " FilteringCache( ${underlying.toString}, uncached = ${uncached.longValue()}) "
0 commit comments