Date: 11 February 2018
Version: 2.8.2
1 Introduction hara.reflect
contains methods for class reflection and method invocation.
1.1 Installation Add to project.clj
dependencies:
[zcaudate/hara.reflect "2.8.2"]
All functionality is found contained in the hara.reflect
namespace
2 API apply-element ^ apply the class element to arguments
(->> (apply-element "123" "value" [])
(map char))
=> [1 2 3]
class-hierarchy ^ lists the class and interface hierarchy for the class
(class-hierarchy String)
=> [java.lang.String
[java.lang.Object
#{java.io.Serializable
java.lang.Comparable
java.lang.CharSequence}]]
class-info ^ lists class information
(class-info String)
=> (contains {:name "java.lang.String"
:hash anything
:modifiers #{:instance :class :public :final}})
context-class ^ if x is a class, return x otherwise return the class of x
(context-class String)
=> String
(context-class "")
=> String
delegate ^ allow transparent field access and manipulation to the underlying object.
(def a "hello")
(def >a (delegate a))
(mapv char (>a :value)) => [h e l l o]
(>a :value world-array)
a => "world"
extract-to-ns ^ extracts all class methods into its own namespace.
(defn extract-to-ns
([class]
(extract-to-ns (symbol (.getName *ns*)) class []))
([nssym class]
(extract-to-ns nssym class []))
([nssym class selectors]
(let [eles (q/list-class-elements class selectors)
methods (distinct (map :name eles))]
(create-ns nssym)
(doall (for [method methods]
(extract-to-var nssym (symbol method) class method selectors))))))
(map #(.sym %)
(extract-to-ns 'test.string String [:private #"serial"]))
=> '[serialPersistentFields serialVersionUID]
extract-to-var ^ extracts a class method into a namespace.
(defn extract-to-var
([varsym class method]
(extract-to-var varsym class method []))
([varsym class method selectors]
(let [[nssym varsym] (if-let [nsstr (.getNamespace ^clojure.lang.Symbol varsym)]
[(let [nssym (symbol nsstr)
_ (create-ns nssym)]
nssym)
(symbol (name varsym))]
[(.getName *ns*) varsym])]
(extract-to-var nssym varsym class method selectors)))
([nssym varsym class method selectors]
(let [v (intern nssym varsym (q/query-class class (cons (str method) (cons :# selectors))))]
(alter-meta! v (fn [m] (merge m (element-meta @v))))
v)))
(extract-to-var 'hash-without clojure.lang.IPersistentMap 'without [])
(with-out-str (eval '(clojure.repl/doc hash-without)))
=> (str "-------------------------n"
"hara.reflect.core.extract-test/hash-withoutn"
"[[clojure.lang.IPersistentMap java.lang.Object]]n"
" n"
"member: clojure.lang.IPersistentMap/withoutn"
"type: clojure.lang.IPersistentMapn"
"modifiers: instance, method, public, abstractn")
(eval '(hash-without {:a 1 :b 2} :a))
=> {:b 2}
query-class ^ queries the java view of the class declaration
(query-class String [#"^c" :name])
;;=> ["charAt" "checkBounds" "codePointAt" "codePointBefore"
;; "codePointCount" "compareTo" "compareToIgnoreCase"
;; "concat" "contains" "contentEquals" "copyValueOf"]
query-hierarchy ^ lists what methods could be applied to a particular instance
(query-hierarchy String [:name #"^to"])
=> ["toCharArray" "toLowerCase" "toString" "toUpperCase"]
query-instance ^ lists what methods could be applied to a particular instance
(query-instance "abc" [:name #"^to"])
=> ["toCharArray" "toLowerCase" "toString" "toUpperCase"]
(query-instance String [:name #"^to"])
=> (contains ["toString"])
3 Selectors The option array takes selectors and filters can be used to customise the results returned by the two query calls.
attribute selection name filtering parameter filtering modifier filtering return type filtering For example
(query-class Long [:name "MIN_VALUE" :#])
=> "MIN_VALUE"
(query-class Long [:params "MIN_VALUE" :#])
=> [java.lang.Class]
(query-class Long [:params :name "MIN_VALUE" :#])
=> {:name "MIN_VALUE", :params [java.lang.Class]}
Additional selector keywords include :container
, :hash
, :delegate
, :origins
, :name
, :modifiers
, :tag
and :type
and used as follows:
(query-class Long [:params :name :modifiers "MIN_VALUE" :#])
=> {:modifiers #{:public :static :field :final},
:name "MIN_VALUE",
:params [java.lang.Class]}
3.1 Name Filtering We can filter on the name of the class member using two methods - exact matches using strings and regex matchesusing regexs:
(query-class Long [:name "value"])
=> '("value")
(query-class Long [:name #"value"])
=> '("value" "valueOf")
(query-class Long [:name #"VALUE"])
=> '("MAX_VALUE" "MIN_VALUE")
3.2 Parameter Filtering
3.2.1 Number of Inputs Input parameters can be filtered through specifying the number of inputs:
(query-class Long [:name :params 2])
=> [{:name "compare", :params [Long/TYPE Long/TYPE]}
{:name "compareTo", :params [Long Long]}
{:name "compareTo", :params [Long Object]}
{:name "equals", :params [Long Object]}
{:name "getLong", :params [String Long]}
{:name "getLong", :params [String Long/TYPE]}
{:name "parseLong", :params [String Integer/TYPE]}
{:name "rotateLeft", :params [Long/TYPE Integer/TYPE]}
{:name "rotateRight", :params [Long/TYPE Integer/TYPE]}
{:name "toString", :params [Long/TYPE Integer/TYPE]}
{:name "toUnsignedString", :params [Long/TYPE Integer/TYPE]}
{:name "valueOf", :params [String Integer/TYPE]}]
3.2.2 Exact Inputs Exact inputs can be specified by using a vector with input types:
(query-class Long [:name :params [Long/TYPE]])
=> [{:name "bitCount", :params [Long/TYPE]}
{:name "highestOneBit", :params [Long/TYPE]}
{:name "lowestOneBit", :params [Long/TYPE]}
{:name "new", :params [Long/TYPE]}
{:name "numberOfLeadingZeros", :params [Long/TYPE]}
{:name "numberOfTrailingZeros", :params [Long/TYPE]}
{:name "reverse", :params [Long/TYPE]}
{:name "reverseBytes", :params [Long/TYPE]}
{:name "signum", :params [Long/TYPE]}
{:name "stringSize", :params [Long/TYPE]}
{:name "toBinaryString", :params [Long/TYPE]}
{:name "toHexString", :params [Long/TYPE]}
{:name "toOctalString", :params [Long/TYPE]}
{:name "toString", :params [Long/TYPE]}
{:name "valueOf", :params [Long/TYPE]}]
3.2.3 Partial Inputs Using a vector with :any
as the first input will output all functions with any of the types as input arguments
(query-class Long [:name [:any String Long]])
=> ["bitCount" "compare" "decode" "getChars" "getLong" "highestOneBit" "lowestOneBit" "new" "numberOfLeadingZeros" "numberOfTrailingZeros" "parseLong" "reverse" "reverseBytes" "rotateLeft" "rotateRight" "signum" "stringSize" "toBinaryString" "toHexString" "toOctalString" "toString" "toUnsignedString" "valueOf"]
Using a vector with :all
as the first input will output all functions having all of the types as input arguments
(query-class Long [:name :params [:all String Long]])
=> [{:name "getLong", :params [String Long/TYPE]}]
3.3 Modifier Filtering The following are all the modifier keywords that can be used for filtering, most are directly related to flags, four have been defined for completeness of filtering:
:public 1 ;; java.lang.reflect.Modifier/PUBLIC
:private 2 ;; java.lang.reflect.Modifier/PRIVATE
:protected 4 ;; java.lang.reflect.Modifier/PROTECTED
:static 8 ;; java.lang.reflect.Modifier/STATIC
:final 16 ;; java.lang.reflect.Modifier/FINAL
:synchronized 32 ;; java.lang.reflect.Modifier/SYNCHRONIZE
:native 256 ;; java.lang.reflect.Modifier/NATIVE
:interface 512 ;; java.lang.reflect.Modifier/INTERFACE
:abstract 1024 ;; java.lang.reflect.Modifier/ABSTRACT
:strict 2048 ;; java.lang.reflect.Modifier/STRICT
:synthetic 4096 ;; java.lang.Class/SYNTHETIC
:annotation 8192 ;; java.lang.Class/ANNOTATION
:enum 16384 ;; java.lang.Class/ENUM
:volatile 64 ;; java.lang.reflect.Modifier/VOLATILE
:transient 128 ;; java.lang.reflect.Modifier/TRANSIENT
:bridge 64 ;; java.lang.reflect.Modifier/BRIDGE
:varargs 128 ;; java.lang.reflect.Modifier/VARARGS
:plain 0 ;; not :public, :private or :protected
:instance 0 ;; not :static
:field 0 ;; is field
:method 0 ;; is method
3.3.1 Modifier Examples Find all the fields in java.lang.Long
:
(query-class Long [:name :field])
=> ["MAX_VALUE" "MIN_VALUE" "SIZE" "TYPE" "serialVersionUID" "value"]
Find all the static fields in java.lang.Long
:
(query-class Long [:name :static :field])
=> ["MAX_VALUE" "MIN_VALUE" "SIZE" "TYPE" "serialVersionUID"]
Find all the non-static fields in java.lang.Long
:
(query-class Long [:name :instance :field])
=> ["value"]
Find all public fields in java.lang.Long
:
(query-class Long [:name :public :field])
=> ["MAX_VALUE" "MIN_VALUE" "SIZE" "TYPE"]
Find all private members in java.lang.Long
:
(query-class Long [:name :private])
=> ["serialVersionUID" "toUnsignedString" "value"]
Find all private fields in java.lang.Long
:
(query-class Long [:name :private :field])
=> ["serialVersionUID" "value"]
Find all private methods in java.lang.Long
:
(query-class Long [:name :private :method])
=> ["toUnsignedString"]
Find all protected members in java.lang.Long
:
(query-class Long [:name :protected])
=> []
Find all members in java.lang.Long
with no security attribute:
(query-class Long [:name :plain])
=> ["getChars" "stringSize"]
3.4 Return Type Filtering Return types signatures can be filtered by giving a class in the options, again all filters can be mixed and matched as needed. In the following example, we query for the name of all methods having a return type of Long/TYPE
:
(query-class Long [:name :type :method Long/TYPE])
=> [{:name "highestOneBit", :type Long/TYPE}
{:name "longValue", :type Long/TYPE}
{:name "lowestOneBit", :type Long/TYPE}
{:name "parseLong", :type Long/TYPE}
{:name "parseLong", :type Long/TYPE}
{:name "reverse", :type Long/TYPE}
{:name "reverseBytes", :type Long/TYPE}
{:name "rotateLeft", :type Long/TYPE}
{:name "rotateRight", :type Long/TYPE}]