Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 124 lines (100 sloc) 4.833 kb
479bb230 »
2010-10-14 #453 reflection
1 ; Copyright (c) Rich Hickey. All rights reserved.
2 ; The use and distribution terms for this software are covered by the
3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 ; which can be found in the file epl-v10.html at the root of this distribution.
5 ; By using this software in any fashion, you are agreeing to be bound by
6 ; the terms of this license.
7 ; You must not remove this notice, or any other, from this software.
8
9 (ns ^{:author "Stuart Halloway"
10 :added "1.3"
11 :doc "Reflection on Host Types
12 Alpha - subject to change.
13
14 Two main entry points:
15
16 * type-reflect reflects on something that implements TypeReference.
17 * reflect (for REPL use) reflects on the class of an instance, or
18 on a class if passed a class
19
20 Key features:
21
22 * Exposes the read side of reflection as pure data. Reflecting
23 on a type returns a map with keys :bases, :flags, and :members.
24
25 * Canonicalizes class names as Clojure symbols. Types can extend
26 to the TypeReference protocol to indicate that they can be
27 unambiguously resolved as a type name. The canonical format
28 requires one non-Java-ish convention: array brackets are <>
29 instead of [] so they can be part of a Clojure symbol.
30
31 * Pluggable Reflectors for different implementations. The default
32 JavaReflector is good when you have a class in hand, or use
33 the AsmReflector for \"hands off\" reflection without forcing
34 classes to load.
35
36 Platform implementers must:
37
38 * Create an implementation of Reflector.
39 * Create one or more implementations of TypeReference.
40 * def default-reflector to be an instance that satisfies Reflector."}
41 clojure.reflect
42 (:require [clojure.set :as set]))
43
44 (defprotocol Reflector
45 "Protocol for reflection implementers."
46 (do-reflect [reflector typeref]))
47
48 (defprotocol TypeReference
49 "A TypeReference can be unambiguously converted to a type name on
50 the host platform.
51
52 All typerefs are normalized into symbols. If you need to
53 normalize a typeref yourself, call typesym."
54 (typename [o] "Returns Java name as returned by ASM getClassName, e.g. byte[], java.lang.String[]"))
55
56 (declare default-reflector)
57
58 (defn type-reflect
59 "Alpha - subject to change.
60 Reflect on a typeref, returning a map with :bases, :flags, and
61 :members. In the discussion below, names are always Clojure symbols.
62
63 :bases a set of names of the type's bases
64 :flags a set of keywords naming the boolean attributes
65 of the type.
66 :members a set of the type's members. Each membrer is a map
67 and can be a constructor, method, or field.
68
69 Keys common to all members:
70 :name name of the type
71 :declaring-class name of the declarer
72 :flags keyword naming boolean attributes of the member
73
74 Keys specific to constructors:
75 :parameter-types vector of parameter type names
76 :exception-types vector of exception type names
77
78 Key specific to methods:
79 :parameter-types vector of parameter type names
80 :exception-types vector of exception type names
81 :return-type return type name
82
83 Keys specific to fields:
84 :type type name
85
86 Options:
87
88 :ancestors in addition to the keys described above, also
89 include an :ancestors key with the entire set of
90 ancestors, and add all ancestor members to
91 :members.
92 :reflector implementation to use. Defaults to JavaReflector,
93 AsmReflector is also an option."
94 {:added "1.3"}
95 [typeref & options]
96 (let [{:keys [ancestors reflector]}
97 (merge {:reflector default-reflector}
98 (apply hash-map options))
99 refl (partial do-reflect reflector)
100 result (refl typeref)]
101 ;; could make simpler loop of two args: names an
102 (if ancestors
103 (let [make-ancestor-map (fn [names]
104 (zipmap names (map refl names)))]
105 (loop [reflections (make-ancestor-map (:bases result))]
106 (let [ancestors-visited (set (keys reflections))
107 ancestors-to-visit (set/difference (set (mapcat :bases (vals reflections)))
108 ancestors-visited)]
109 (if (seq ancestors-to-visit)
110 (recur (merge reflections (make-ancestor-map ancestors-to-visit)))
111 (apply merge-with into result {:ancestors ancestors-visited}
112 (map #(select-keys % [:members]) (vals reflections)))))))
113 result)))
114
115 (defn reflect
116 "Alpha - subject to change.
117 Reflect on the type of obj (or obj itself if obj is a class).
118 Return value and options are the same as for type-reflect. "
119 {:added "1.3"}
120 [obj & options]
121 (apply type-reflect (if (class? obj) obj (class obj)) options))
122
123 (load "reflect/java")
Something went wrong with that request. Please try again.