activerecord-jdbc-adapter 1.3.7 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +33 -3
  3. data/Appraisals +11 -5
  4. data/Gemfile +21 -15
  5. data/History.md +31 -1
  6. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  7. data/lib/arel/visitors/firebird.rb +7 -10
  8. data/lib/arel/visitors/h2.rb +9 -0
  9. data/lib/arel/visitors/sql_server.rb +21 -2
  10. data/lib/arjdbc/h2/adapter.rb +31 -2
  11. data/lib/arjdbc/h2/connection_methods.rb +1 -1
  12. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  13. data/lib/arjdbc/jdbc/column.rb +2 -1
  14. data/lib/arjdbc/mssql/adapter.rb +40 -23
  15. data/lib/arjdbc/mssql/column.rb +4 -4
  16. data/lib/arjdbc/mysql/adapter.rb +36 -10
  17. data/lib/arjdbc/mysql/column.rb +12 -7
  18. data/lib/arjdbc/mysql/connection_methods.rb +53 -21
  19. data/lib/arjdbc/oracle/adapter.rb +22 -5
  20. data/lib/arjdbc/postgresql/adapter.rb +54 -18
  21. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  22. data/lib/arjdbc/postgresql/base/oid.rb +460 -0
  23. data/lib/arjdbc/postgresql/column.rb +50 -15
  24. data/lib/arjdbc/postgresql/oid_types.rb +126 -0
  25. data/lib/arjdbc/tasks/h2_database_tasks.rb +4 -2
  26. data/lib/arjdbc/version.rb +1 -1
  27. data/rakelib/02-test.rake +3 -30
  28. data/src/java/arjdbc/derby/DerbyModule.java +0 -8
  29. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +1 -0
  30. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +2 -0
  31. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +8 -8
  32. data/src/java/arjdbc/mssql/MSSQLModule.java +50 -19
  33. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +1 -0
  34. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +6 -6
  35. data/src/java/arjdbc/oracle/OracleModule.java +1 -1
  36. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +66 -2
  37. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +23 -10
  38. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +1 -0
  39. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  40. data/src/java/arjdbc/util/QuotingUtils.java +14 -7
  41. metadata +8 -3
  42. data/lib/arjdbc/postgresql/array_parser.rb +0 -89
@@ -24,8 +24,8 @@ module ArJdbc
24
24
  require 'pg_array_parser'
25
25
  include PgArrayParser
26
26
  rescue LoadError
27
- require 'arjdbc/postgresql/array_parser'
28
- include ArrayParser
27
+ require 'arjdbc/postgresql/base/array_parser'
28
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::ArrayParser
29
29
  end if AR4_COMPAT
30
30
 
31
31
  include Cast
@@ -33,8 +33,21 @@ module ArJdbc
33
33
  end
34
34
  end
35
35
 
36
+ # @private
37
+ def oid_type
38
+ @oid_type ||= begin
39
+ raise "oid not defined" unless oid = (@oid ||= nil)
40
+ @adapter.get_oid_type(oid.to_i, @fmod.to_i, name)
41
+ end
42
+ end if AR4_COMPAT
43
+
44
+ def accessor; oid_type.accessor end if AR4_COMPAT
45
+
36
46
  ( attr_accessor :array; def array?; array; end ) if AR4_COMPAT
37
47
 
48
+ def number?; !array && super end if AR4_COMPAT
49
+ def text?; !array && super end if AR4_COMPAT
50
+
38
51
  # Extracts the value from a PostgreSQL column default definition.
39
52
  #
40
53
  # @override JdbcColumn#default_value
@@ -354,7 +367,7 @@ module ArJdbc
354
367
  when 'infinity' then 1.0 / 0.0
355
368
  when '-infinity' then -1.0 / 0.0
356
369
  when / BC$/
357
- super("-" + string.sub(/ BC$/, ""))
370
+ super("-#{string.sub(/ BC$/, "")}")
358
371
  else
359
372
  super
360
373
  end
@@ -376,9 +389,11 @@ module ArJdbc
376
389
  end
377
390
  end if AR4_COMPAT
378
391
 
379
- def hstore_to_string(object)
392
+ def hstore_to_string(object, array_member = false)
380
393
  if Hash === object
381
- object.map { |k,v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
394
+ string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
395
+ string = escape_hstore(string) if array_member
396
+ string
382
397
  else
383
398
  object
384
399
  end
@@ -388,10 +403,10 @@ module ArJdbc
388
403
  if string.nil?
389
404
  nil
390
405
  elsif String === string
391
- Hash[string.scan(HstorePair).map { |k,v|
392
- v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
393
- k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
394
- [k,v]
406
+ Hash[string.scan(HstorePair).map { |k, v|
407
+ v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
408
+ k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
409
+ [k, v]
395
410
  }]
396
411
  else
397
412
  string
@@ -406,7 +421,7 @@ module ArJdbc
406
421
  end
407
422
  end
408
423
 
409
- def array_to_string(value, column, adapter, should_be_quoted = false)
424
+ def array_to_string(value, column, adapter)
410
425
  casted_values = value.map do |val|
411
426
  if String === val
412
427
  if val == "NULL"
@@ -439,7 +454,11 @@ module ArJdbc
439
454
  if string.nil?
440
455
  nil
441
456
  elsif String === string
442
- IPAddr.new(string)
457
+ begin
458
+ IPAddr.new(string)
459
+ rescue ArgumentError
460
+ nil
461
+ end
443
462
  else
444
463
  string
445
464
  end
@@ -454,9 +473,9 @@ module ArJdbc
454
473
  end
455
474
 
456
475
  # @note Only used for default values - we get a "parsed" array from JDBC.
457
- def string_to_array(string, column)
476
+ def string_to_array(string, column_or_oid)
458
477
  return string unless String === string
459
- parse_pg_array(string).map { |val| column.type_cast(val, column.type) }
478
+ parse_pg_array(string).map { |val| type_cast_array(column_or_oid, val) }
460
479
  end
461
480
 
462
481
  private
@@ -480,12 +499,28 @@ module ArJdbc
480
499
  end
481
500
  end
482
501
 
502
+ ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
503
+
483
504
  def quote_and_escape(value)
484
505
  case value
485
- when "NULL"
506
+ when "NULL", Numeric
486
507
  value
487
508
  else
488
- "\"#{value.gsub(/(["\\])/, '\\\\\1')}\""
509
+ value = value.gsub(/\\/, ARRAY_ESCAPE)
510
+ value.gsub!(/"/,"\\\"")
511
+ "\"#{value}\""
512
+ end
513
+ end
514
+
515
+ def type_cast_array(oid, value)
516
+ if ::Array === value
517
+ value.map { |item| type_cast_array(oid, item) }
518
+ else
519
+ if oid.is_a?(Column)
520
+ oid.type_cast value, oid.type # column.type
521
+ else
522
+ oid.type_cast value
523
+ end
489
524
  end
490
525
  end
491
526
 
@@ -0,0 +1,126 @@
1
+ require 'arjdbc/postgresql/base/oid' # 'active_record/connection_adapters/postgresql/oid'
2
+ require 'thread'
3
+
4
+ module ArJdbc
5
+ module PostgreSQL
6
+ module OIDTypes
7
+
8
+ OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
9
+
10
+ def get_oid_type(oid, fmod, column_name)
11
+ type_map.fetch(oid, fmod) {
12
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
13
+ type_map[oid] = OID::Identity.new
14
+ }
15
+ end
16
+
17
+ # @override
18
+ def enable_extension(name)
19
+ result = super(name)
20
+ @extensions = nil
21
+ reload_type_map
22
+ result
23
+ end
24
+
25
+ # @override
26
+ def disable_extension(name)
27
+ result = super(name)
28
+ @extensions = nil
29
+ reload_type_map
30
+ result
31
+ end
32
+
33
+ # @override
34
+ def extensions
35
+ @extensions ||= super
36
+ end
37
+
38
+ private
39
+
40
+ @@type_map_cache = {}
41
+ @@type_map_cache_lock = Mutex.new
42
+
43
+ # @private
44
+ class OID::TypeMap
45
+ def dup
46
+ dup = super # make sure @mapping is not shared
47
+ dup.instance_variable_set(:@mapping, @mapping.dup)
48
+ dup
49
+ end
50
+ end
51
+
52
+ def type_map
53
+ # NOTE: our type_map is lazy since it's only used for `adapter.accessor`
54
+ @type_map ||= begin
55
+ if type_map = @@type_map_cache[ type_cache_key ]
56
+ type_map.dup
57
+ else
58
+ type_map = OID::TypeMap.new
59
+ initialize_type_map(type_map)
60
+ cache_type_map(type_map)
61
+ type_map
62
+ end
63
+ end
64
+ end
65
+
66
+ def reload_type_map
67
+ if ( @type_map ||= nil )
68
+ @type_map.clear
69
+ initialize_type_map(@type_map)
70
+ end
71
+ end
72
+
73
+ def cache_type_map(type_map)
74
+ @@type_map_cache_lock.synchronize do
75
+ @@type_map_cache[ type_cache_key ] = type_map
76
+ end
77
+ end
78
+
79
+ def type_cache_key
80
+ config.hash + ( 7 * extensions.hash )
81
+ end
82
+
83
+ def add_oid(row, records_by_oid, type_map)
84
+ return type_map if type_map.key? row['type_elem'].to_i
85
+
86
+ if OID.registered_type? typname = row['typname']
87
+ # this composite type is explicitly registered
88
+ vector = OID::NAMES[ typname ]
89
+ else
90
+ # use the default for composite types
91
+ unless type_map.key? typelem = row['typelem'].to_i
92
+ add_oid records_by_oid[ row['typelem'] ], records_by_oid, type_map
93
+ end
94
+
95
+ vector = OID::Vector.new row['typdelim'], type_map[typelem]
96
+ end
97
+
98
+ type_map[ row['oid'].to_i ] = vector
99
+ type_map
100
+ end
101
+
102
+ def initialize_type_map(type_map)
103
+ result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
104
+ leaves, nodes = result.partition { |row| row['typelem'].to_s == '0' }
105
+ # populate the leaf nodes
106
+ leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
107
+ type_map[ row['oid'].to_i ] = OID::NAMES[ row['typname'] ]
108
+ end
109
+
110
+ records_by_oid = result.group_by { |row| row['oid'] }
111
+
112
+ arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
113
+
114
+ # populate composite types
115
+ nodes.each { |row| add_oid row, records_by_oid, type_map }
116
+
117
+ # populate array types
118
+ arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
119
+ array = OID::Array.new type_map[ row['typelem'].to_i ]
120
+ type_map[ row['oid'].to_i ] = array
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+ end
@@ -20,8 +20,10 @@ module ArJdbc
20
20
  # @override
21
21
  def delete_database_files(config)
22
22
  return unless db_base = database_base_name(config)
23
- db_files = [ "#{db_base}.h2.db", "#{db_base}.lock.db", "#{db_base}.trace.db" ]
24
- db_files.each { |file| File.delete(file) if File.exist?(file) }
23
+ for suffix in [ '.h2,db', '.mv.db', '.lock.db', '.trace.db' ]
24
+ db_file = "#{db_base}#{suffix}"
25
+ File.delete(db_file) if File.exist?(db_file)
26
+ end
25
27
  end
26
28
 
27
29
  end
@@ -1,5 +1,5 @@
1
1
  module ArJdbc
2
- VERSION = "1.3.7"
2
+ VERSION = "1.3.8"
3
3
  # @deprecated
4
4
  module Version
5
5
  # @private 1.2.x compatibility
@@ -47,16 +47,6 @@ task 'test_appraisal_hint' do
47
47
  end
48
48
  end
49
49
 
50
- desc "Run unit tests (not connecting to a DB)."
51
- Rake::TestTask.new(:test_unit) do |test_task|
52
- test_task.test_files = FileList["test/unit*_test.rb"] + FileList["test/unit/*_test.rb"]
53
- test_task.libs << 'lib' if defined?(JRUBY_VERSION)
54
- test_task.libs << 'test'
55
- test_task.verbose = true if $VERBOSE
56
- set_test_task_compat_version test_task
57
- end
58
- task :test_units => :test_unit # alias
59
-
60
50
  Rake::TestTask.class_eval { attr_reader :test_files }
61
51
 
62
52
  def test_task_for(adapter, options = {})
@@ -95,12 +85,14 @@ test_task_for :H2, :desc => 'Run tests against H2 database engine'
95
85
  test_task_for :HSQLDB, :desc => 'Run tests against HyperSQL (Java) database'
96
86
  test_task_for :MSSQL, :driver => :jtds, :database_name => 'MS-SQL (SQLServer)'
97
87
  test_task_for :MySQL, :prereqs => 'db:mysql'
98
- test_task_for :PostgreSQL, :prereqs => 'db:postgresql', :driver => 'postgres'
88
+ test_task_for :PostgreSQL, :driver => 'postgres', :prereqs => 'db:postgresql'
99
89
  task :test_postgres => :test_postgresql # alias
100
90
  test_task_for :SQLite3
101
91
  task :test_sqlite => :test_sqlite3 # alias
102
92
  test_task_for :Firebird
103
93
 
94
+ test_task_for :MariaDB, :prereqs => 'db:mysql', :files => FileList["test/db/mysql/*_test.rb"]
95
+
104
96
  # ensure driver for these DBs is on your class-path
105
97
  [ :Oracle, :DB2, :Informix, :CacheDB ].each do |adapter|
106
98
  test_task_for adapter, :desc => "Run tests against #{adapter} (ensure driver is on class-path)"
@@ -136,22 +128,3 @@ end
136
128
  #task :test_sybase_jtds => :test_sybase # alias
137
129
  #test_task_for :Sybase, :name => 'sybase_jconnect',
138
130
  # :desc => "Run tests against Sybase (ensure jConnect driver is on class-path)"
139
-
140
- Rake::TraceOutput.module_eval do
141
-
142
- # NOTE: avoid TypeError: String can't be coerced into Fixnum
143
- # due this method getting some strings == [ 1 ] argument ...
144
- def trace_on(out, *strings)
145
- sep = $\ || "\n"
146
- if strings.empty?
147
- output = sep
148
- else
149
- output = strings.map { |s|
150
- next if s.nil?; s = s.to_s
151
- s =~ /#{sep}$/ ? s : s + sep
152
- }.join
153
- end
154
- out.print(output)
155
- end
156
-
157
- end
@@ -171,14 +171,6 @@ public class DerbyModule {
171
171
  return RubyString.newString(context.getRuntime(), BYTES_0);
172
172
  }
173
173
 
174
- private static RubyString quoteBoolean(final Ruby runtime, final IRubyObject value) {
175
- return value.isTrue() ? runtime.newString(BYTES_1) : runtime.newString(BYTES_0);
176
- }
177
-
178
- private static boolean isMultibyteChars(final Ruby runtime, final IRubyObject value) {
179
- return getMultibyteChars(runtime).isInstance(value);
180
- }
181
-
182
174
  private static RubyModule getMultibyteChars(final Ruby runtime) {
183
175
  return (RubyModule) ((RubyModule) runtime.getModule("ActiveSupport").
184
176
  getConstant("Multibyte")).getConstantAt("Chars");
@@ -46,6 +46,7 @@ import org.jruby.util.ByteList;
46
46
  * @author kares
47
47
  */
48
48
  public class DerbyRubyJdbcConnection extends RubyJdbcConnection {
49
+ private static final long serialVersionUID = 4809475910953623325L;
49
50
 
50
51
  protected DerbyRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
51
52
  super(runtime, metaClass);
@@ -37,6 +37,8 @@ import org.jruby.runtime.builtin.IRubyObject;
37
37
  * @author nicksieger
38
38
  */
39
39
  public class H2RubyJdbcConnection extends RubyJdbcConnection {
40
+ private static final long serialVersionUID = -2652911264521657428L;
41
+
40
42
  protected H2RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
41
43
  super(runtime, metaClass);
42
44
  }
@@ -1300,7 +1300,7 @@ public class RubyJdbcConnection extends RubyObject {
1300
1300
  final boolean binary = // column.type == :binary
1301
1301
  column.callMethod(context, "type").toString() == (Object) "binary";
1302
1302
 
1303
- final RubyClass recordClass = record.getMetaClass(); // record.class
1303
+ final IRubyObject recordClass = record.callMethod(context, "class");
1304
1304
  final IRubyObject adapter = recordClass.callMethod(context, "connection");
1305
1305
 
1306
1306
  IRubyObject columnName = column.callMethod(context, "name");
@@ -1314,7 +1314,7 @@ public class RubyJdbcConnection extends RubyObject {
1314
1314
 
1315
1315
  final IRubyObject id = record.callMethod(context, "id"); // record.id
1316
1316
 
1317
- int count = updateLobValue(context,
1317
+ final int count = updateLobValue(context,
1318
1318
  tableName.toString(), columnName.toString(), column,
1319
1319
  idKey.toString(), id, idColumn, value, binary
1320
1320
  );
@@ -2860,12 +2860,12 @@ public class RubyJdbcConnection extends RubyObject {
2860
2860
  final Ruby runtime = context.getRuntime();
2861
2861
  final IRubyObject jdbcColumn = getJdbcColumnClass(context);
2862
2862
 
2863
- final List<String> primarykeyNames = new ArrayList<String>();
2863
+ final List<String> primaryKeyNames = new ArrayList<String>(4);
2864
2864
  while ( primaryKeys.next() ) {
2865
- primarykeyNames.add( primaryKeys.getString(COLUMN_NAME) );
2865
+ primaryKeyNames.add( primaryKeys.getString(COLUMN_NAME) );
2866
2866
  }
2867
2867
 
2868
- final List<IRubyObject> columns = new ArrayList<IRubyObject>();
2868
+ final RubyArray columns = runtime.newArray();
2869
2869
  final IRubyObject config = getInstanceVariable("@config");
2870
2870
  while ( results.next() ) {
2871
2871
  final String colName = results.getString(COLUMN_NAME);
@@ -2877,13 +2877,13 @@ public class RubyJdbcConnection extends RubyObject {
2877
2877
  RubyString.newUnicodeString( runtime, typeFromResultSet(results) ),
2878
2878
  runtime.newBoolean( ! results.getString(IS_NULLABLE).trim().equals("NO") )
2879
2879
  });
2880
- columns.add(column);
2880
+ columns.append(column);
2881
2881
 
2882
- if ( primarykeyNames.contains(colName) ) {
2882
+ if ( primaryKeyNames.contains(colName) ) {
2883
2883
  column.callMethod(context, "primary=", runtime.getTrue());
2884
2884
  }
2885
2885
  }
2886
- return runtime.newArray(columns);
2886
+ return columns;
2887
2887
  }
2888
2888
 
2889
2889
  protected IRubyObject mapGeneratedKeys(
@@ -25,47 +25,78 @@ package arjdbc.mssql;
25
25
 
26
26
  import static arjdbc.util.QuotingUtils.BYTES_0;
27
27
  import static arjdbc.util.QuotingUtils.BYTES_1;
28
+ import static arjdbc.util.QuotingUtils.quoteCharWith;
28
29
  import static arjdbc.util.QuotingUtils.quoteSingleQuotesWithFallback;
29
30
 
30
31
  import org.jruby.RubyModule;
31
32
  import org.jruby.RubyString;
32
33
  import org.jruby.anno.JRubyMethod;
33
34
  import org.jruby.runtime.ThreadContext;
35
+ import org.jruby.runtime.Visibility;
34
36
  import org.jruby.runtime.builtin.IRubyObject;
37
+ import org.jruby.util.ByteList;
35
38
 
36
39
  /**
37
40
  * ArJdbc::MSSQL
38
- *
41
+ *
39
42
  * @author kares
40
43
  */
41
44
  public class MSSQLModule {
42
-
45
+
43
46
  public static RubyModule load(final RubyModule arJdbc) {
44
47
  RubyModule mssql = arJdbc.defineModuleUnder("MSSQL");
45
48
  mssql.defineAnnotatedMethods( MSSQLModule.class );
46
49
  return mssql;
47
50
  }
48
-
49
- @JRubyMethod(name = "quote_string", required = 1, frame = false)
50
- public static IRubyObject quote_string(
51
- final ThreadContext context,
52
- final IRubyObject self,
53
- final IRubyObject string) {
51
+
52
+ @JRubyMethod(name = "quote_string", required = 1)
53
+ public static IRubyObject quote_string(final ThreadContext context,
54
+ final IRubyObject self, final IRubyObject string) {
54
55
  return quoteSingleQuotesWithFallback(context, string);
55
56
  }
56
-
57
- @JRubyMethod(name = "quoted_true", required = 0, frame = false)
58
- public static IRubyObject quoted_true(
59
- final ThreadContext context,
60
- final IRubyObject self) {
57
+
58
+ @JRubyMethod(name = "quoted_true", required = 0)
59
+ public static IRubyObject quoted_true(final ThreadContext context,
60
+ final IRubyObject self) {
61
61
  return RubyString.newString(context.getRuntime(), BYTES_1);
62
62
  }
63
-
64
- @JRubyMethod(name = "quoted_false", required = 0, frame = false)
65
- public static IRubyObject quoted_false(
66
- final ThreadContext context,
67
- final IRubyObject self) {
63
+
64
+ @JRubyMethod(name = "quoted_false", required = 0)
65
+ public static IRubyObject quoted_false(final ThreadContext context,
66
+ final IRubyObject self) {
68
67
  return RubyString.newString(context.getRuntime(), BYTES_0);
69
68
  }
70
-
69
+
70
+ // part =~ /^\[.*\]$/ ? part : "[#{part.gsub(']', ']]')}]"
71
+ @SuppressWarnings("deprecation")
72
+ @JRubyMethod(required = 1, visibility = Visibility.PRIVATE)
73
+ public static IRubyObject quote_name_part(final ThreadContext context,
74
+ final IRubyObject self, final IRubyObject part) {
75
+
76
+ final RubyString partString = ((RubyString) part);
77
+ final ByteList str = partString.getByteList();
78
+ if ( str.charAt(0) == '[' && str.charAt(str.length() - 1) == ']' ) {
79
+ return part; // part =~ /^\[.*\]$/ ? part
80
+ }
81
+ final RubyString quotedString = // part.gsub(']', ']]')
82
+ quoteCharWith(context, partString, ']', ']', 1, 4 + 1);
83
+ if ( quotedString == partString ) {
84
+ final int realSize = str.getRealSize();
85
+ final ByteList quoted = new ByteList(
86
+ new byte[realSize + 2], partString.getEncoding(), false
87
+ );
88
+ quoted.begin = 0; quoted.realSize = 0;
89
+ quoted.append('[');
90
+ quoted.append(str.unsafeBytes(), str.getBegin(), realSize);
91
+ quoted.append(']');
92
+ return context.getRuntime().newString(quoted);
93
+ }
94
+ // we got a new string with a reserve of 1 byte front and back :
95
+ final ByteList quoted = quotedString.getByteList();
96
+ quoted.begin = 0; // setBegin invalidates
97
+ quoted.bytes[0] = '['; quoted.realSize++;
98
+ quoted.append(']');
99
+ return quotedString;
100
+ }
101
+
71
102
  }