forked from objectionary/eo
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCache.java
More file actions
116 lines (106 loc) · 3.66 KB
/
Copy pathCache.java
File metadata and controls
116 lines (106 loc) · 3.66 KB
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
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.maven;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.cactoos.Func;
import org.cactoos.func.UncheckedFunc;
/**
* Simple cache mechanism.
* This class isn't thread-safe, use {@link ConcurrentCache} for concurrent scenarios.
* @since 0.60
*/
final class Cache {
/**
* Base cache directory.
*/
private final Path base;
/**
* Compilation function.
*/
private final Func<Path, String> compilation;
/**
* Constructor.
* @param path Cache path
* @param compilation Compilation function
*/
Cache(final CachePath path, final Func<Path, String> compilation) {
this(path.get(), compilation);
}
/**
* Ctor.
* @param base Base cache directory
* @param compilation Compilation function
*/
Cache(final Path base, final Func<Path, String> compilation) {
this.base = base;
this.compilation = compilation;
}
/**
* Check cache and apply compilation if needed.
* @param source From file
* @param target To file
* @param tail Tail path in cache
*/
public void apply(final Path source, final Path target, final Path tail) {
try {
final String sha = Cache.sha(source);
final Path hash = this.hash(tail);
final Path cache = this.base.resolve(tail);
if (
Files.notExists(hash)
|| Files.notExists(cache)
|| !Files.readString(hash).equals(sha)
) {
final String content = new UncheckedFunc<>(this.compilation).apply(source);
new Saved(sha, this.hash(tail)).value();
new Saved(content, cache).value();
new Saved(content, target).value();
} else {
new Saved(Files.readString(cache), target).value();
}
} catch (final IOException ioexception) {
throw new IllegalStateException(
"Failed to perform an IO operation with cache",
ioexception
);
} catch (final NoSuchAlgorithmException exception) {
throw new IllegalStateException("SHA-256 hashing algorithm isn't found", exception);
}
}
/**
* Get hash file path for the given tail.
* @param tail Tail path
* @return Hash file path
*/
private Path hash(final Path tail) {
final Path full = this.base.resolve(tail.normalize());
return full.getParent().resolve(String.format("%s.sha256", full.getFileName().toString()));
}
/**
* Calculate SHA-256 hash of a file and return it as Base64 string.
* @param file File path
* @return Base64-encoded SHA-256 hash
* @throws NoSuchAlgorithmException If SHA-256 algorithm is not available
* @throws IOException If an I/O error occurs reading the file
*/
private static String sha(final Path file) throws NoSuchAlgorithmException, IOException {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (InputStream stream = Files.newInputStream(file)) {
final byte[] buffer = new byte[8192];
int read = stream.read(buffer);
while (read != -1) {
digest.update(buffer, 0, read);
read = stream.read(buffer);
}
}
return Base64.getEncoder().encodeToString(digest.digest());
}
}