Merge: SharedPreferences: Nit API wrapping android SharedPreferences class
authorJean Privat <jean@pryen.org>
Wed, 18 Jun 2014 00:42:51 +0000 (20:42 -0400)
committerJean Privat <jean@pryen.org>
Wed, 18 Jun 2014 00:42:51 +0000 (20:42 -0400)
A module wrapping 'android.content.SharedPreferences' allowing to load/save data to internal storage for the android platform. Some tests have been added to the mnit_simple example.

The compiler has been slightly modified to ignore '
' in java extern types.

As asked by @privat and @xymus to store persistant game data.
Signed-off-by: Frédéric Vachon <fredvac@gmail.com>

Pull-Request: #495
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>

examples/mnit_simple/src/simple_android.nit
lib/android/shared_preferences/shared_preferences.nit [new file with mode: 0644]
lib/android/shared_preferences/shared_preferences_api10.nit [new file with mode: 0644]
lib/android/shared_preferences/shared_preferences_api11.nit [new file with mode: 0644]
src/common_ffi/java.nit

index 157bc7e..1f17806 100644 (file)
@@ -21,6 +21,7 @@ end
 
 import simple
 import mnit_android
+import android::shared_preferences
 
 in "Java" `{
        import android.content.Context;
@@ -30,11 +31,54 @@ in "Java" `{
 redef class App
        redef fun input( ie )
        do
-               if ie isa PointerEvent and ie.depressed then do_java_stuff
-
+               if ie isa PointerEvent and ie.depressed then
+                       do_java_stuff
+                       test_shared_preferences
+               end     
                return super
        end
 
+       fun test_shared_preferences
+       do
+               # Private mode tests
+               var sp = new SharedPreferences.privately(self, "test")
+               sp.add_bool("a_boolean",  true)
+               sp.add_float("a_float", 66.6)
+               sp.add_int("an_int", 666)
+               sp.add_int("a_second_int", 666777)
+               sp.add_long("a_long", 6666666666)
+               sp.add_string("a_string", "A string")
+               sp["another_int"] = 85
+               sp["yet_another_string"] = "Another string"
+               sp.remove("a_second_int")
+
+               # Serialized object test
+               var my_point = new Point(10, 10)
+               sp["a_point"] = my_point
+               var my_deserialized_point = sp.deserialize("a_point")
+               assert my_point.to_s == my_deserialized_point.to_s
+               
+               assert sp.bool("a_boolean", false) == true
+               assert sp.bool("wrong_boolean", false) == false
+               assert sp.float("a_float", 0.0) != 0.0
+               assert sp.float("wrong_float", 0.0) == 0.0
+               assert sp.int("an_int", 0) == 666
+               assert sp.int("a_second_int", 0) == 0
+               assert sp.long("a_long", 0) == 6666666666
+               assert sp.long("wrong_long", 0) == 0
+               assert sp.string("a_string", "ERROR!") == "A string"
+               assert sp.string("wrong_string", "ERROR!") == "ERROR!"
+               assert sp.long("another_int", 0) == 85
+               assert sp.string("yet_another_string", "ERROR!") == "Another string"
+               assert sp.has("an_int") == true
+               assert sp.has("a_second_int") == false
+
+               sp.clear
+               assert sp.all == null
+
+               sp.destroy
+       end
+
        fun do_java_stuff import native_activity in "Java" `{
                // + Log (no context needed)
                android.util.Log.d("mnit_simple", "Java within NIT!!!");
@@ -61,3 +105,20 @@ redef class App
                });
        `}
 end
+
+class Point
+       auto_serializable
+       super Serializable
+
+       var x: Int
+       var y: Int
+
+       init(x, y: Int)
+       do
+               self.x = x
+               self.y = y
+       end
+
+       redef fun to_s do return "({x}, {y})"
+end
+
diff --git a/lib/android/shared_preferences/shared_preferences.nit b/lib/android/shared_preferences/shared_preferences.nit
new file mode 100644 (file)
index 0000000..382b364
--- /dev/null
@@ -0,0 +1,24 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services allowing to save and load datas to internal android device
+# memory using `android.content.SharedPreferences` class.
+#
+# By default, the API 10 is imported. You can import API 11 to have
+# access to this platform new features.
+module shared_preferences
+
+import shared_preferences_api10
diff --git a/lib/android/shared_preferences/shared_preferences_api10.nit b/lib/android/shared_preferences/shared_preferences_api10.nit
new file mode 100644 (file)
index 0000000..c78a09d
--- /dev/null
@@ -0,0 +1,439 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to save/load data using `android.content.SharedPreferences` for the android platform
+module shared_preferences_api10
+
+import native_app_glue
+import serialization
+import json_serialization
+
+in "Java" `{
+       import android.content.SharedPreferences;
+       import android.content.Context; 
+       import android.app.Activity;
+       import java.util.Map;
+       import java.util.Iterator;
+       import java.lang.ClassCastException;
+       import java.lang.NullPointerException;
+`}
+
+extern class NativeSharedPreferences in "Java" `{ android.content.SharedPreferences `}
+       super JavaObject
+       redef type SELF: NativeSharedPreferences
+       
+       fun contains(key: JavaString): Bool in "Java" `{ return recv.contains(key); `}
+       fun get_all: HashMap[JavaString, JavaObject] import HashMap[JavaString, JavaObject],
+               HashMap[JavaString, JavaObject].[]= in "Java" `{ 
+               Map<String, ?> java_map = null;
+               int nit_hashmap = new_HashMap_of_JavaString_JavaObject();
+               try {
+                       java_map = recv.getAll();
+               } catch (NullPointerException e) {
+                       return nit_hashmap;
+               }
+
+               for (Map.Entry<String, ?> entry: java_map.entrySet())
+                       HashMap_of_JavaString_JavaObject__index_assign(nit_hashmap, 
+                               entry.getKey(), entry.getValue());
+
+               return nit_hashmap;
+       `}
+       fun get_boolean(key: JavaString, def_value: Bool): Bool in "Java" `{ 
+               boolean return_value;
+               try {
+                       return_value = recv.getBoolean(key, def_value); 
+               } catch (ClassCastException e) {
+                       return def_value;
+               }
+
+               return return_value;
+       `}
+       fun get_float(key: JavaString, def_value: Float): Float in "Java" `{ 
+               float return_value;
+               try {
+                       return_value = recv.getFloat(key, (float) def_value); 
+               } catch (ClassCastException e) {
+                       return def_value;
+               }
+
+               return return_value;
+       `}
+       fun get_int(key: JavaString, def_value: Int): Int in "Java" `{
+               int return_value;
+               try {
+                       return_value = recv.getInt(key, def_value); 
+               } catch (ClassCastException e) {
+                       return def_value;
+               }
+
+               return return_value;
+       `}
+       #FIXME: Get rid of the `int` cast when the ffi is fixed
+       fun get_long(key: JavaString, def_value: Int): Int in "Java" `{
+               long return_value;
+               try {
+                       return_value = recv.getLong(key, def_value); 
+               } catch (ClassCastException e) {
+                       return def_value;
+               }
+
+               return (int) return_value;
+       `}
+       fun get_string(key: JavaString, def_value: JavaString): JavaString in "Java" `{
+               String return_value = null;
+               try {
+                       return_value = recv.getString(key, def_value); 
+               } catch (ClassCastException e) {
+                       return def_value;
+               }
+
+               return return_value;
+       `}
+end
+
+extern class NativeSharedPreferencesEditor in "Java" `{ android.content.SharedPreferences$Editor `}
+       super JavaObject
+       redef type SELF: NativeSharedPreferencesEditor
+
+       fun clear: NativeSharedPreferencesEditor in "Java" `{ return recv.clear(); `}
+       fun commit: Bool in "Java" `{ return recv.commit(); `}
+       fun put_boolean(key: JavaString, value: Bool ): NativeSharedPreferencesEditor in "Java" `{ 
+               return recv.putBoolean (key, value); 
+       `}
+       fun put_float(key: JavaString, value: Float): NativeSharedPreferencesEditor in "Java" `{
+               return recv.putFloat(key, (float) value); 
+       `}
+       fun put_int(key: JavaString, value: Int): NativeSharedPreferencesEditor in "Java" `{
+               return recv.putInt(key, value); 
+       `}
+       fun put_long(key: JavaString, value: Int): NativeSharedPreferencesEditor in "Java" `{
+               return recv.putLong(key, value); 
+       `}
+       fun put_string(key: JavaString, value: JavaString): NativeSharedPreferencesEditor in "Java" `{
+               return recv.putString(key, value); 
+       `}
+       fun remove(key: JavaString): NativeSharedPreferencesEditor in "Java" `{ 
+               return recv.remove(key); 
+       `}
+end
+
+# Provides services to save and load data for the android platform
+class SharedPreferences
+       protected var context: NativeActivity
+       protected var shared_preferences: NativeSharedPreferences
+       protected var editor: NativeSharedPreferencesEditor
+
+       # Automatically commits every saving/removing instructions (`true` by default)
+       var auto_commit = true
+       
+       protected init(app: App, file_name: String, mode: Int) 
+       do
+               self.context = app.native_activity
+               sys.jni_env.push_local_frame(1)
+               setup(file_name.to_java_string, mode)
+               sys.jni_env.pop_local_frame
+       end
+
+       # Restricts file access to the current application
+       init privately(app: App, file_name: String) 
+       do
+               self.init(app, file_name, private_mode)
+       end
+
+       # File access mode
+       private fun private_mode: Int in "Java" `{ return Context.MODE_PRIVATE; `}
+
+       private fun set_vars(shared_pref: NativeSharedPreferences, editor: NativeSharedPreferencesEditor)
+       do
+               self.shared_preferences = shared_pref.new_global_ref
+               self.editor = editor.new_global_ref
+       end
+
+       private fun setup(file_name: JavaString, mode: Int) import context, set_vars in "Java" `{
+               Activity context = (Activity) SharedPreferences_context(recv);
+               SharedPreferences sp;
+
+               // Uses default SharedPreferences if file_name is an empty String
+               if (file_name.equals("")) {
+                       sp = context.getPreferences( mode);
+               } else { 
+                       sp = context.getSharedPreferences(file_name, mode);
+               }
+
+               SharedPreferences.Editor editor = sp.edit();
+               
+               SharedPreferences_set_vars(recv, sp, editor);
+       `}
+
+       private fun commit_if_auto do if auto_commit then self.commit
+
+       # Returns true if there's an entry corresponding the given key
+       fun has(key: String): Bool 
+       do
+               sys.jni_env.push_local_frame(2)
+               var return_value = shared_preferences.contains(key.to_java_string) 
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Returns a `HashMap` containing all entries or `null` if there's no entries
+       #
+       # User has to manage local stack deallocation himself
+       #
+       # Example :
+       # ~~~
+       # var foo = new HashMap[JavaString, JavaObject]
+       # # ...
+       # for key, value in foo do
+       #      key.delete_local_ref
+       #      value.delete_local_ref
+       # end
+       # ~~~
+       # *You should use Nit getters instead and get each value one by one* 
+       fun all: nullable HashMap[JavaString, JavaObject]
+       do 
+               var hashmap = shared_preferences.get_all
+               if hashmap.is_empty then return null
+               return hashmap
+       end
+
+       # Returns the `Bool` value corresponding the given key or `def_value` if none
+       # or if the value isn't of correct type
+       fun bool(key: String, def_value: Bool): Bool 
+       do 
+               sys.jni_env.push_local_frame(2)
+               var return_value = shared_preferences.get_boolean(key.to_java_string, def_value)
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Returns the `Float` value corresponding the given key or `def_value` if none
+       # or if the value isn't of correct type
+       fun float(key: String, def_value: Float): Float 
+       do 
+               sys.jni_env.push_local_frame(2)
+               var return_value = shared_preferences.get_float(key.to_java_string, def_value)
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Returns the `Int` value corresponding the given key or `def_value` if none
+       # or if the value isn't of correct type
+       # Be aware of possible `def_value` integer overflow as the Nit `Int` corresponds 
+       # to Java `long`
+       fun int(key: String, def_value: Int): Int 
+       do 
+               sys.jni_env.push_local_frame(2)
+               var return_value = shared_preferences.get_int(key.to_java_string, def_value)
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Returns the `Int` value corresponding the given key or `def_value` if none
+       # or if the value isn't of correct type
+       # Calls `getLong(key, value)` java method
+       # Nit `Int` is equivalent to Java `long` so that no integer overflow will occur
+       fun long(key: String, def_value: Int): Int 
+       do 
+               sys.jni_env.push_local_frame(2)
+               var return_value = shared_preferences.get_long(key.to_java_string, def_value)
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Returns the `String` value corresponding the given key or `def_value` if none
+       # or if the value isn't of correct type
+       fun string(key: String, def_value: String): String 
+       do 
+               sys.jni_env.push_local_frame(3)
+               var java_return_value = shared_preferences.get_string(key.to_java_string, 
+                       def_value.to_java_string)
+               var nit_return_value = java_return_value.to_s
+               sys.jni_env.pop_local_frame
+               return nit_return_value
+       end
+
+       # Clears all the dictionnary entries in the specified file or the default file 
+       # if none specified at instanciation
+       # Returns `self` allowing fluent programming
+       fun clear: SharedPreferences 
+       do 
+               editor.clear
+               commit_if_auto
+               return self
+       end
+       
+       # If auto_commit is `false`, has to be called to save the data to persistant memory
+       fun commit: Bool 
+       do 
+               sys.jni_env.push_local_frame(1)
+               var return_value = editor.commit
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Set a key-value pair using a `Bool` value
+       # Returns `self` allowing fluent programming
+       fun add_bool(key: String, value: Bool ): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(1)
+               editor.put_boolean(key.to_java_string, value)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Set a key-value pair using a `Float` value
+       # Returns `self` allowing fluent programming
+       #
+       # Be aware of possible loss of precision as Nit `Float` corresponds to Java `double`
+       # and the methods stores a Java `float`
+       fun add_float(key: String, value: Float): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(1)
+               editor.put_float(key.to_java_string, value)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Set a key-value pair using a `Int` type value
+       # Returns `self` allowing fluent programming
+       #
+       # Be aware of possible integer overflow as the Nit `Int` corresponds to Java `long`
+       # and the methods stores a Java `int`
+       # *You might want to use add_long instead*
+       fun add_int(key: String, value: Int): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(1)
+               editor.put_int(key.to_java_string, value)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Set a key-value pair using a `Int` type value
+       # Returns `self` allowing fluent programming
+       fun add_long(key: String, value: Int): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(1)
+               editor.put_long(key.to_java_string, value)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Set a key-value pair using a `String` type value
+       # Returns `self` allowing fluent programming
+       fun add_string(key: String, value: String): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(2)
+               editor.put_string(key.to_java_string, value.to_java_string)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Removes the corresponding entry in the file
+       # Returns `self` allowing fluent programming
+       fun remove(key: String): SharedPreferences 
+       do 
+               sys.jni_env.push_local_frame(1)
+               editor.remove(key.to_java_string)
+               sys.jni_env.pop_local_frame
+               commit_if_auto
+               return self
+       end
+
+       # Deallocate global references allocated by the SharedPreferences instance
+       fun destroy 
+       do
+               self.shared_preferences.delete_global_ref
+               self.editor.delete_global_ref
+       end
+
+       # Save data to file dynamically calling the appropriate method according to value type
+       # Non-primitive Object (`String` excluded) will be stored as a serialized json `String`
+       # Nit `Int` are stored as Java `long`, therefore you'll have to retrieve it with `long` method
+       fun []=(key: String, value: Serializable)
+       do
+               value.add_to_preferences(self, key)
+               commit_if_auto
+       end
+
+       # Retrieve an `Object` serialized via `[]=` function
+       # Returns `null` if there's no serialized object corresponding to the given key
+       # Make sure that the serialized object is `auto_serializable` or that it redefines
+       # the appropriate methods. Refer to `Serializable` documentation for further details
+       fun deserialize(key: String): nullable Object
+       do
+               var serialized_string = self.string(key, "")
+
+               if serialized_string == "" then return null
+
+               var deserializer = new JsonDeserializer(serialized_string)
+               return deserializer.deserialize
+       end
+end
+
+redef class App
+       fun shared_preferences: SharedPreferences is cached do 
+               return new SharedPreferences.privately(self, "")
+       end
+end
+
+redef class Serializable
+       # Called by []= operator to dynamically call the appropriate add method 
+       # Non-primitive Object (`String` excluded) will be stored as a serialized json `String`
+       # Refine your class to customize this method behaviour
+       protected fun add_to_preferences(shared_preferences: SharedPreferences, key: String) 
+       do 
+               var serialized_string = new StringOStream
+               var serializer = new JsonSerializer(serialized_string)
+               serializer.serialize(self)
+
+               shared_preferences.add_string(key, serialized_string.to_s)
+       end
+end
+
+redef class Bool
+       redef fun add_to_preferences(shared_preferences, key)
+       do
+               shared_preferences.add_bool(key, self)
+       end
+end
+
+redef class Int
+       redef fun add_to_preferences(shared_preferences, key)
+       do
+               shared_preferences.add_long(key, self)
+       end
+end
+
+redef class Float
+       redef fun add_to_preferences(shared_preferences, key)
+       do
+               shared_preferences.add_float(key, self)
+       end
+end
+
+redef class String
+       redef fun add_to_preferences(shared_preferences, key)
+       do
+               shared_preferences.add_string(key, self)
+       end
+end
diff --git a/lib/android/shared_preferences/shared_preferences_api11.nit b/lib/android/shared_preferences/shared_preferences_api11.nit
new file mode 100644 (file)
index 0000000..4a87478
--- /dev/null
@@ -0,0 +1,105 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Refines shared_preferences module to add API 11 services
+module shared_preferences_api11
+
+import shared_preferences
+
+in "Java" `{ 
+       import java.util.HashSet;
+       import java.util.Set;
+`}
+
+redef extern class NativeSharedPreferences
+       
+       # Default value to null instead of Set<String>
+       fun get_string_set(key: JavaString): HashSet[JavaString] import HashSet[JavaString], 
+               HashSet[JavaString].add in "Java" `{ 
+               Set<String> def_value = new HashSet<String>();
+               Set<String> java_set = recv.getStringSet(key, def_value);
+               int nit_hashset = new_HashSet_of_JavaString();
+
+               for (String element: java_set)
+                       HashSet_of_JavaString_add(nit_hashset, element);
+
+               return nit_hashset;
+       `}
+end
+
+redef extern class NativeSharedPreferencesEditor 
+       
+       fun put_string_set(key: JavaString, value: HashSet[JavaString]): NativeSharedPreferencesEditor 
+               import HashSet[JavaString], HashSet[JavaString].iterator, Iterator[JavaString].is_ok, 
+               Iterator[JavaString].item, Iterator[JavaString].next in "Java" `{ 
+               Set<String> java_set = new HashSet<String>();
+               int itr = HashSet_of_JavaString_iterator(value);
+               
+               while (Iterator_of_JavaString_is_ok(itr)) {
+                       java_set.add(Iterator_of_JavaString_item(itr));
+                       Iterator_of_JavaString_next(itr);
+               }
+
+               return recv.putStringSet(key, java_set); 
+       `}
+end
+       
+redef class SharedPreferences
+
+       # Allows multiple processes to write into the same `SharedPreferences` file
+       init multi_process(app: App, file_name: String)
+       do
+               self.init(app, file_name, multi_process_mode)
+       end
+
+       # File access mode
+       private fun multi_process_mode: Int in "Java" `{ return Content.MODE_MULTI_PROCESS; `}
+
+       # Returns the `HashSet[JavaString]` value corresponding the given key or `null` if none
+       #
+       # User has to manage local stack deallocation himself
+       #
+       # Example :
+       # ~~~
+       # var a_hash_set = shared_preferences.string_set("A key")
+       # ...
+       # for element in a_hash_set do element.delete_local_ref
+       # ~~~
+       fun string_set(key: String): HashSet[JavaString] 
+       do 
+               sys.jni_env.push_local_frame(3)
+               var return_value = shared_preferences.get_string_set(key.to_java_string)
+               sys.jni_env.pop_local_frame
+               return return_value
+       end
+
+       # Set a key-value pair using a `HashSet[JavaString]` value
+       # Returns self allowing fluent programming
+       #
+       # User has to manage local stack deallocation himself
+       #
+       # Example :
+       # ~~~
+       # var foo = new HashSet[JavaString]
+       # shared_preferences.add_string_set("A key", foo)
+       # for element in foo do element.delete_local_ref
+       # ~~~
+       fun add_string_set(key: String, value: HashSet[JavaString]): SharedPreferences
+       do 
+               editor.put_string_set(key.to_java_string, value)
+               return self
+       end
+end
index 68ba1e9..f6e44f7 100644 (file)
@@ -465,7 +465,7 @@ redef class MClassType
        do
                var ftype = mclass.ftype
                if ftype isa ForeignJavaType then return ftype.java_type.
-                       replace('/', ".").replace('$', ".").replace(' ', "")
+                       replace('/', ".").replace('$', ".").replace(' ', "").replace('\n',"")
                if mclass.name == "Bool" then return "boolean"
                if mclass.name == "Char" then return "char"
                if mclass.name == "Int" then return "int"
@@ -487,7 +487,7 @@ redef class MClassType
        redef fun jni_format
        do
                var ftype = mclass.ftype
-               if ftype isa ForeignJavaType then return "L{ftype.java_type.replace('.', "/").replace(' ', "")};"
+               if ftype isa ForeignJavaType then return "L{ftype.java_type.replace('.', "/").replace(' ', "").replace('\n', "")};"
                if mclass.name == "Bool" then return "Z"
                if mclass.name == "Char" then return "C"
                if mclass.name == "Int" then return "I"