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
|
;;; find_namespaces.clj: search for ns declarations in dirs, JARs, or CLASSPATH
;; by Stuart Sierra, http://stuartsierra.com/
;; April 19, 2009
;; Copyright (c) Stuart Sierra, 2009. All rights reserved. The use
;; and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this
;; distribution. By using this software in any fashion, you are
;; agreeing to be bound by the terms of this license. You must not
;; remove this notice, or any other, from this software.
(ns
#^{:author "Stuart Sierra",
:doc "Search for ns declarations in dirs, JARs, or CLASSPATH"}
clojure.contrib.find-namespaces
(:require [clojure.contrib.classpath :as cp]
[clojure.contrib.jar :as jar])
(import (java.io File FileReader BufferedReader PushbackReader
InputStreamReader)
(java.util.jar JarFile)))
;;; Finding namespaces in a directory tree
(defn clojure-source-file?
"Returns true if file is a normal file with a .clj extension."
[#^File file]
(and (.isFile file)
(.endsWith (.getName file) ".clj")))
(defn find-clojure-sources-in-dir
"Searches recursively under dir for Clojure source files (.clj).
Returns a sequence of File objects, in breadth-first sort order."
[#^File dir]
;; Use sort by absolute path to get breadth-first search.
(sort-by #(.getAbsolutePath %)
(filter clojure-source-file? (file-seq dir))))
(defn comment?
"Returns true if form is a (comment ...)"
[form]
(and (list? form) (= 'comment (first form))))
(defn ns-decl?
"Returns true if form is a (ns ...) declaration."
[form]
(and (list? form) (= 'ns (first form))))
(defn read-ns-decl
"Attempts to read a (ns ...) declaration from rdr, and returns the
unevaluated form. Returns nil if read fails or if a ns declaration
cannot be found. The ns declaration must be the first Clojure form
in the file, except for (comment ...) forms."
[#^PushbackReader rdr]
(try (let [form (read rdr)]
(cond
(ns-decl? form) form
(comment? form) (recur rdr)
:else nil))
(catch Exception e nil)))
(defn read-file-ns-decl
"Attempts to read a (ns ...) declaration from file, and returns the
unevaluated form. Returns nil if read fails, or if the first form
is not a ns declaration."
[#^File file]
(with-open [rdr (PushbackReader. (BufferedReader. (FileReader. file)))]
(read-ns-decl rdr)))
(defn find-ns-decls-in-dir
"Searches dir recursively for (ns ...) declarations in Clojure
source files; returns the unevaluated ns declarations."
[#^File dir]
(filter identity (map read-file-ns-decl (find-clojure-sources-in-dir dir))))
(defn find-namespaces-in-dir
"Searches dir recursively for (ns ...) declarations in Clojure
source files; returns the symbol names of the declared namespaces."
[#^File dir]
(map second (find-ns-decls-in-dir dir)))
;;; Finding namespaces in JAR files
(defn clojure-sources-in-jar
"Returns a sequence of filenames ending in .clj found in the JAR file."
[#^JarFile jar-file]
(filter #(.endsWith % ".clj") (jar/filenames-in-jar jar-file)))
(defn read-ns-decl-from-jarfile-entry
"Attempts to read a (ns ...) declaration from the named entry in the
JAR file, and returns the unevaluated form. Returns nil if the read
fails, or if the first form is not a ns declaration."
[#^JarFile jarfile #^String entry-name]
(with-open [rdr (PushbackReader.
(BufferedReader.
(InputStreamReader.
(.getInputStream jarfile (.getEntry jarfile entry-name)))))]
(read-ns-decl rdr)))
(defn find-ns-decls-in-jarfile
"Searches the JAR file for Clojure source files containing (ns ...)
declarations; returns the unevaluated ns declarations."
[#^JarFile jarfile]
(filter identity
(map #(read-ns-decl-from-jarfile-entry jarfile %)
(clojure-sources-in-jar jarfile))))
(defn find-namespaces-in-jarfile
"Searches the JAR file for Clojure source files containing (ns ...)
declarations. Returns a sequence of the symbol names of the
declared namespaces."
[#^JarFile jarfile]
(map second (find-ns-decls-in-jarfile jarfile)))
;;; Finding namespaces anywhere on CLASSPATH
(defn find-ns-decls-on-classpath
"Searches CLASSPATH (both directories and JAR files) for Clojure
source files containing (ns ...) declarations. Returns a sequence
of the unevaluated ns declaration forms."
[]
(concat
(mapcat find-ns-decls-in-dir (cp/classpath-directories))
(mapcat find-ns-decls-in-jarfile (cp/classpath-jarfiles))))
(defn find-namespaces-on-classpath
"Searches CLASSPATH (both directories and JAR files) for Clojure
source files containing (ns ...) declarations. Returns a sequence
of the symbol names of the declared namespaces."
[]
(map second (find-ns-decls-on-classpath)))
|