/
util.clj
144 lines (125 loc) · 4.67 KB
/
util.clj
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(ns cljam.util
"General utilities."
(:require [clojure.java.io :as cio]
[clojure.tools.logging :as logging]
[cljam.io.util.bgzf :as bgzf])
(:import [java.net MalformedURLException URL]
[java.nio.file Files FileVisitor FileVisitResult]
[java.nio.file.attribute FileAttribute]
[org.apache.commons.compress.compressors
CompressorStreamFactory CompressorException]))
;; Disk cache
;; ----------
(defn create-temp-dir
"Returns a created temporary directory with the given `prefix`."
[prefix]
(->> 0
(make-array FileAttribute)
(Files/createTempDirectory prefix)
.toFile))
(defn delete-temp-dir!
"Takes the temporary directory created by `create-temp-dir` and deletes the
`dir` and the files in `dir`."
[^java.io.File dir]
(Files/walkFileTree
(.toPath dir)
(reify FileVisitor
(visitFile [_ file _attrs]
(when-not (Files/deleteIfExists file)
(logging/warnf
"The file could not be deleted because it did not exist: %s"
(str file)))
FileVisitResult/CONTINUE)
(visitFileFailed [_ _file _exc]
FileVisitResult/CONTINUE)
(preVisitDirectory [_ _dir _attrs]
FileVisitResult/CONTINUE)
(postVisitDirectory [_ dir _exc]
(when-not (Files/deleteIfExists dir)
(logging/warnf
"The directory could not be deleted because it did not exist: %s"
(str dir)))
FileVisitResult/CONTINUE))))
(defmacro with-temp-dir
"bindings => [dir prefix ...]
Creates a temporary directory with the given `prefix` and binds the given
`dir` to it. Finally, deletes `dir` and the files in `dir`."
[bindings & body]
(assert (vector? bindings) "bindings must be a vector")
(assert (even? (count bindings)) "bindings must have an even number of forms")
(cond
(zero? (count bindings)) `(do ~@body)
(symbol? (bindings 0)) (let [[dir prefix] bindings]
`(let [~dir (create-temp-dir ~prefix)]
(try
(with-temp-dir ~(subvec bindings 2) ~@body)
(finally
(delete-temp-dir! ~dir)))))
:else (throw (IllegalArgumentException. "binding must be a symbol"))))
;; byte array
;; ----------
(defn ubyte
"Casts to byte avoiding an error about out of range for byte."
[n]
{:pre [(<= 0 n 255)]}
(byte (if (< n 0x80) n (- n 0x100))))
;; string utils
;; ------------
(defn ^"[B" string->bytes [^String s]
(.getBytes s))
(defn ^String bytes->string [^bytes b]
(String. b 0 (alength b)))
(defn graph?
"Returns true if c is a visible character, false if not."
[c]
(<= 0x20 (byte c) 0x7E))
(defn space?
"Returns true if c is a character that creates \"white space\" in displayed
text."
[c]
(not (nil? (#{\space \tab \newline \formfeed \return (char 0x0b)} c))))
;; file utils
;; ---------
(defn ^URL as-url
[x]
(try
(cio/as-url x)
(catch MalformedURLException _
(cio/as-url (cio/file x)))))
(defn basename
[x]
(when-let [url (as-url x)]
(second (re-find #"([^/]+)\.(?=[^\./]+$)" (.getPath url)))))
(def ^:private compressor-map
{:gzip CompressorStreamFactory/GZIP
:bzip2 CompressorStreamFactory/BZIP2})
(defn ^java.io.InputStream compressor-input-stream
"Returns a compressor input stream from f, autodetecting the compressor type
from the first few bytes of f. Returns java.io.BufferedInputStream if the
compressor type is not known. Should be used inside with-open to ensure the
InputStream is properly closed."
[f]
(let [is (cio/input-stream f)]
(try
(.createCompressorInputStream (CompressorStreamFactory. true) is)
(catch CompressorException _
is))))
(defn ^java.io.OutputStream compressor-output-stream
"Returns a compressor output stream from `f` and a compressor type `k`. `k`
must be selected from `:bgzip`, `:gzip` or `:bzip2`. Autodetects the
compressor type from the extension of `f` if `k` is not passed. Returns
`java.io.BufferedOutputStream` if the compressor type is not known. Should be
used inside with-open to ensure the OutputStream is properly closed."
([f]
(compressor-output-stream f (condp re-find (.getPath (as-url f))
#"(?i)\.(bgz|bgzip|gz)$" :bgzip
#"(?i)\.gzip$" :gzip
#"(?i)\.(bz2|bzip2)$" :bzip2
nil)))
([f k]
(if (= :bgzip k)
(bgzf/make-bgzf-output-stream f)
(let [os (cio/output-stream f)]
(if-let [s (get compressor-map k)]
(.createCompressorOutputStream (CompressorStreamFactory.) s os)
os)))))