Clojure gen-class this keyword - java

Is it possible to refer to Java's 'this' keyword from within a gen-class method?
I am trying to implement daredesm's answer here, in Clojure. However, when I try to use 'this' in the run function, I get "java.lang.RuntimeException: Unable to resolve symbol: this in this context."
(gen-class
:name ClipboardListener
:extends java.lang.Thread
:implements [java.awt.datatransfer.ClipboardOwner]
:prefix ClipboardListener-
:methods [[takeOwnership [Transferable] void]])
(def systemClipboard (.getSystemClipboard (java.awt.Toolkit/getDefaultToolkit)))
(defn ClipboardListener-run []
(let [transferable (.getContents systemClipboard this)]
(.takeOwnership transferable)))
(defn ClipboardListener-lostOwnership [clipboard trasferable] (prn "hit lost"))
(defn ClipboardListener-takeOwnership [transferable] (prn "hit take"))
(defn processClipboard [transferable clipboard] (prn "hit process"))
Note: This is my first time generating Java classes in Clojure, so any general feedback/resources is greatly appreciated.

Instance methods can take an implicit 'self' arg- as the first argument. So to take your example:
(defn ClipboardListener-run [this]
(let [transferable (.getContents systemClipboard this)]
(.takeOwnership transferable)))
Note the this argument :)
Same goes for any instance method, e.g:
(defn ClipboardListener-toString [this]
"override Object#toString with something cool")
Have a look at this (no pun intended) for more info on gen-class.
Also consider reify for cases like Runnable, Callable, e.t.c where you just need to implement a small-ish interface.

Related

Link vector element in Clojure

I'm trying to read a file in a macro in Clojure.
I'm launching my macro with that line :
(def result (rd [s (FileReader. (File. "myFile.txt"))] (.read s)))
where "rd" is the name of my macro.
The prototype of my macro is like that :
(defmacro rd
([] nil)
([arg] arg)
([[variable val] expr]
)
)
The thing is that I can "execute" the FileReader, but when I'm trying to "execute" expr (.read s), it's not working because s is not known.
So I'm trying to link my elements of a vector to made s known, so I want "variable" pointed by val.
I'm not sure I'm in what I want to do, so if you see other ways, I'm up to it.
Thanks in advance guys.
if you need to read the file at runtime, as you said, you need to introduce the var.. something like this:
(defmacro rd [[variable val] expr]
`(let [~variable ~val]
~expr))
and then your macro call would expand to this:
(let [s (FileReader. (File. "myFile.txt"))] (.read s))

Log all method calls to a Java object from Clojure

I'm working on a Clojure wrapper for some Java library.
In order to help me debug, I would like to log all calls to specific Java objects.
After searching how I might do this from a raw Java perspective, I discovered the java.lang.reflect.Proxy class and java.lang.reflect.InvocationHandler interfaces.
This led me to find a small snipped posted by R.H. a few years ago :
(defn debug-proxy [obj]
(java.lang.reflect.Proxy/newProxyInstance
(.. obj getClass getClassLoader)
(.. obj getClass getInterfaces)
(proxy [java.lang.reflect.InvocationHandler] []
(invoke [proxy m args]
(apply println m args)
(.invoke m obj args)))))
Small note: I had to change java.lang.reflect.Proxy.newProxyInstance into java.lang.reflect/newProxyInstance to make it work in my REPL, since newProxyInstance is a static method. Corrected version is given so that you may cut & paste it.
With this version, calling methods on the proxy appear work at first :
youpi.core=> (.charAt (debug-proxy "foo") 2)
#object[java.lang.reflect.Method 0x53ce1eb0 public abstract char java.lang.CharSequence.charAt(int)] 2
=> \o
But sadly, calling a method that accepts no argument will not work:
youpi.core=> (.toUpperCase (debug-proxy "foo"))
IllegalArgumentException No matching field found: toUpperCase for class com.sun.proxy.$Proxy1 clojure.lang.Reflector.getInstanceField (Reflector.java:271)
java.lang.IllegalArgumentException: No matching field found: toUpperCase for class com.sun.proxy.$Proxy1
at clojure.lang.Reflector.getInstanceField(Reflector.java:271)
at clojure.lang.Reflector.invokeNoArgInstanceMember(Reflector.java:315)
at youpi.core$eval5981.invokeStatic(form-init3685547971046661105.clj:1)
at youpi.core$eval5981.invoke(form-init3685547971046661105.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6890)
at clojure.core$eval.invokeStatic(core.clj:3105)
at clojure.core$eval.invoke(core.clj:3101)
at clojure.main$repl$read_eval_print__7408$fn__7411.invoke(main.clj:240)
at clojure.main$repl$read_eval_print__7408.invoke(main.clj:240)
<...>
at java.lang.Thread.run(Thread.java:745)
I tried to update the invoke implementation following advice given in this clojuredocs comment, but it didn't fix.
Here is my (still broken) attempt at implementing the comment's suggestion:
(defn- debug-print-invoke [obj proxy m args]
(apply println m args)
(.invoke m obj args))
(defn debug-proxy [obj]
(java.lang.reflect.Proxy/newProxyInstance
(.. obj getClass getClassLoader)
(.. obj getClass getInterfaces)
(proxy [java.lang.reflect.InvocationHandler] []
(invoke
([proxy m] (debug-print-invoke obj proxy m []))
([proxy m args] (debug-print-invoke obj proxy m args))))))
A good answer would either fix the snippet I've been working with, or suggest an alternative way to achieve the same goal from Clojure.

Could not locate apply /clojure/core/vector__init.class or apply/clojure/core/vector.clj on classpath

I am getting the following error when I try to run my program: Exception in thread "main" java.io.FileNotFoundException: Could not locate apply
/clojure/core/vector__init.class or apply/clojure/core/vector.clj on classpath:
, compiling:(erbium/compile.clj:1:1). It seems to be pointing to file below and suggests that I need to put clojure.core/vector in my dependencies. Is it not included by default?
(ns erbium.compile
(require `[clojure.string :as string])
)
(defn produce-out "Convert 'command %1 %2 %3' with stack [5 6 7] to 'command 5 6 7'" [word stack definitions]
(let [
code (definitions word) ; dictionary/hash lookup. eg. "println" -> "echo $1"
replacement (fn [match] (-> match second Long/parseLong dec stack str))
]
; evaluate arguments. eg. "echo %1"
; stack=["blah"]
; -> "echo blah"
(string/replace code #"%(\d)" replacement)
)
)
(defn parse-word "Verifies that word is in defintitions and then returns (produce-out word stack)" [word stack definitions]
(if (some #{word} (keys definitions))
(produce-out word stack)
)
)
(defn compile "Main compile function" [code]
(let [
split-code (string/split code #"\s")
definitions {
"println" "echo %1"
"+" "%1 + %2"
"-" "%1 - %2"
}
stack []
]
(for [word [split-code]]
(if (integer? (read-string word))
(do
(println "Found integer" word)
(def stack (conj stack (read-string word)))
(println "Adding to argument stack:" stack)
)
; else
(do
(parse-word word stack definitions)
(def stack [])
)
)
)
)
)
This file is loaded by the core file via (load "compile") if that makes a difference.
the first error I see is this:
(require `[clojure.string :as string])
It should be like this:
(:require [clojure.string :as string])
in a regular clojure source file. This fixed it for me.
That said, here comes some general advices:
There are a lot of formatting "mistakes". Of course you can format your code as you would like to, however, it's easier for others to follow if you stick to basic formatting principles. Here is a nice collection of them: https://github.com/bbatsov/clojure-style-guide Most editors implement some formatting tool.
split-code (str/split code #"\s") this won't work as you required [clojure.string :as string] So change it to: split-code (string/split code #"\s")
I am not sure about (load ...) and in which context one uses that generally. However, for starting to learn clojure I recommend Lighttable as it has instant feedback built in which is very valuable when learning something new.
To expand on the answer, the "require" appears in the namespace declaration "ns". ns is actually a macro that expands to a series of statements to create the namespace, and do some setup.
This macro treats statements like (:require ...) as calls to a function with the name require, and automatically quotes any following arguments. Since you had specified a quote yourself:
(ns erbium.compile
(require '[clojure.string :as string]))
Then the result ends up being double-quoted, and the call to require ends up being:
... (require (quote (quote [clojure.string :as string])))
So it ends up trying to load a namespace called "quote" followed by a vector that was syntactically in the wrong place. :)
The ns macro is a standard and convenient way to set up namespaces, but it took me a long time to learn it properly. I found the best way was to copy other people's setup code until I learnt how to do it right.
Incidentally, the use of require instead of :require does not matter, though the standard is to use :require so it does not look like a direct call to that function.

Clojure macro for calling Java setters based on a map?

I'm writing a Clojure wrapper for the Braintree Java library to provide a more concise and idiomatic interface. I'd like to provide functions to instantiate the Java objects quickly and concisely, like:
(transaction-request :amount 10.00 :order-id "user42")
I know I can do this explicitly, as shown in this question:
(defn transaction-request [& {:keys [amount order-id]}]
(doto (TransactionRequest.)
(.amount amount)
(.orderId order-id)))
But this is repetitive for many classes and becomes more complex when parameters are optional. Using reflection, it's possible to define these functions much more concisely:
(defn set-obj-from-map [obj m]
(doseq [[k v] m]
(clojure.lang.Reflector/invokeInstanceMethod
obj (name k) (into-array Object [v])))
obj)
(defn transaction-request [& {:as m}]
(set-obj-from-map (TransactionRequest.) m))
(defn transaction-options-request [tr & {:as m}]
(set-obj-from-map (TransactionOptionsRequest. tr) m))
Obviously, I'd like to avoid reflection if at all possible. I tried defining a macro version of set-obj-from-map but my macro-fu isn't strong enough. It probably requires eval as explained here.
Is there a way to call a Java method specified at runtime, without using reflection?
Thanks in advance!
Updated solution:
Following the advice from Joost, I was able to solve the problem using a similar technique. A macro uses reflection at compile-time to identify which setter methods the class has and then spits out forms to check for the param in a map and call the method with it's value.
Here's the macro and an example use:
; Find only setter methods that we care about
(defn find-methods [class-sym]
(let [cls (eval class-sym)
methods (.getMethods cls)
to-sym #(symbol (.getName %))
setter? #(and (= cls (.getReturnType %))
(= 1 (count (.getParameterTypes %))))]
(map to-sym (filter setter? methods))))
; Convert a Java camelCase method name into a Clojure :key-word
(defn meth-to-kw [method-sym]
(-> (str method-sym)
(str/replace #"([A-Z])"
#(str "-" (.toLowerCase (second %))))
(keyword)))
; Returns a function taking an instance of klass and a map of params
(defmacro builder [klass]
(let [obj (gensym "obj-")
m (gensym "map-")
methods (find-methods klass)]
`(fn [~obj ~m]
~#(map (fn [meth]
`(if-let [v# (get ~m ~(meth-to-kw meth))] (. ~obj ~meth v#)))
methods)
~obj)))
; Example usage
(defn transaction-request [& {:as params}]
(-> (TransactionRequest.)
((builder TransactionRequest) params)
; some further use of the object
))
You can use reflection at compile time ~ as long as you know the class you're dealing with by then ~ to figure out the field names, and generate "static" setters from that. I wrote some code that does pretty much this for getters a while ago that you might find interesting. See https://github.com/joodie/clj-java-fields (especially, the def-fields macro in https://github.com/joodie/clj-java-fields/blob/master/src/nl/zeekat/java/fields.clj).
The macro could be as simple as:
(defmacro set-obj-map [a & r] `(doto (~a) ~#(partition 2 r)))
But this would make your code look like:
(set-obj-map TransactionRequest. .amount 10.00 .orderId "user42")
Which I guess is not what you would prefer :)

Calling a very simple clojure function from Java does not work

I'm very new in learning Clojure. This intended to be my first and very simple Clojure tries in which I call a simple Clojure method from inside java code. Unfortunately it does not work. The Compilation is successful and from the Clojure REPL the written function does as it was ordered, but when calling from Java it says the following:
Exception in thread "main" java.lang.IllegalArgumentException: Wrong number of args (2) passed to: ClojNum$-myinc
at clojure.lang.AFn.throwArity(AFn.java:439)
at clojure.lang.AFn.invoke(AFn.java:43)
at com.experimental.clojure.test.ClojNum.myinc(Unknown Source)
at com.experimental.clojure.java.JavaCaller.main(JavaCaller.java:14)
Here is the very simple Clojure code:
(ns com.experimental.clojure.test.ClojNum
(:gen-class
:init init
:name com.experimental.clojure.test.ClojNum
:methods [
[myinc [int] int]
]))
(defn -init [] [[] (atom [])])
(defn myinc "comment" [x] (+ x 1))
(defn -myinc "comment" [x] (myinc x))
And the java part:
package com.experimental.clojure.java;
import com.experimental.clojure.test.ClojNum;
public class JavaCaller {
/**
* #param args
*/
public static void main(String[] args) {
int i = 0;
System.out.println(i);
ClojNum c = new ClojNum();
i = c.myinc(0);
System.out.println(i);
}
}
What did I do wrong?
(Note again: This is primitve test code just to make a first successful function call)
Thanks for the help, I'm clueless.
Jeremy's link in the comments show you one way to call a static method in a clojure class. If you want to call a clojure function on an object instance, you need to add a parameter to your wrapper method definition:
(defn -myinc "comment" [this x] (myinc x))
The 'this' parameter is required for any non-static wrapper function. Clojure threw an exception because it received two parameters for a function only defined with one. Note, you do not change anything in your :gen-class :methods section or the myinc function definition itself.
The documentation is a bit sparse, but examples of this can be found at:
http://clojure.org/compilation (the last example on the page shows instance methods).

Categories

Resources