Module: TreeHaver::Backends::Java

Defined in:
lib/tree_haver/backends/java.rb

Overview

Java backend for JRuby using jtreesitter (java-tree-sitter)

This backend integrates with jtreesitter JARs on JRuby, leveraging JRuby’s native Java integration for optimal performance.

Features

jtreesitter (java-tree-sitter) provides Java bindings to tree-sitter and supports:

  • Parsing source code into syntax trees

  • Incremental parsing via Parser.parse(Tree, String)

  • The Query API for pattern matching

  • Tree editing for incremental re-parsing

Tree/Node Architecture

This backend defines Ruby wrapper classes (Java::Language, Java::Parser, Java::Tree, Java::Node) that wrap the raw jtreesitter Java objects via JRuby’s Java interop. These are **raw backend wrappers** not intended for direct use by application code.

The wrapping hierarchy is:

Java::Tree/Node (this backend) → TreeHaver::Tree/Node → Base::Tree/Node

When you use ‘TreeHaver::Parser#parse`:

  1. ‘Java::Parser#parse` returns a Java::Tree (wrapper around jtreesitter Tree)

  2. TreeHaver::Parser wraps it in TreeHaver::Tree (adds source storage)

  3. ‘TreeHaver::Tree#root_node` wraps Java::Node in TreeHaver::Node

The TreeHaver::Tree and TreeHaver::Node wrappers provide the full unified API including #children, #text, #source, #source_position, etc.

This differs from pure-Ruby backends (Citrus, Parslet, Prism, Psych) which define Tree/Node classes that directly inherit from Base::Tree/Base::Node.

Version Requirements

  • jtreesitter >= 0.26.0 (required)

  • tree-sitter runtime library >= 0.26.0 (must match jtreesitter version)

Older versions of jtreesitter are NOT supported due to API changes.

Platform Compatibility

  • MRI Ruby: ✗ Not available (no JVM)

  • JRuby: ✓ Full support (native Java integration)

  • TruffleRuby: ✗ Not available (jtreesitter requires JRuby’s Java interop)

Installation

  1. Download jtreesitter 0.26.0+ JAR from Maven Central: central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter

  2. Set the environment variable to point to the JAR directory: export TREE_SITTER_JAVA_JARS_DIR=/path/to/jars

  3. Use JRuby to run your code: jruby -e “require ‘tree_haver’; puts TreeHaver::Backends::Java.available?”

Defined Under Namespace

Classes: Language, Node, Parser, Tree

Constant Summary collapse

JAVA_PACKAGE =

The Java package for java-tree-sitter

"io.github.treesitter.jtreesitter"

Class Method Summary collapse

Class Method Details

.add_jars_from_env!void

This method returns an undefined value.

Attempt to append JARs from TREE_SITTER_JAVA_JARS_DIR to JRuby classpath and configure native library path from TREE_SITTER_RUNTIME_LIB

If the environment variable is set and points to a directory, all .jar files in that directory (recursively) are added to the JRuby classpath.

Examples:

ENV["TREE_SITTER_JAVA_JARS_DIR"] = "/path/to/java-tree-sitter/jars"
TreeHaver::Backends::Java.add_jars_from_env!


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/tree_haver/backends/java.rb', line 106

def add_jars_from_env!
  # :nocov:
  # This method requires JRuby and cannot be tested on MRI/CRuby.
  # JRuby-specific CI jobs would test this code.
  require "java"

  # Add JARs to classpath
  dir = ENV["TREE_SITTER_JAVA_JARS_DIR"]
  if dir && Dir.exist?(dir)
    Dir[File.join(dir, "**", "*.jar")].each do |jar|
      next if $CLASSPATH.include?(jar)
      $CLASSPATH << jar
    end
  end

  # Configure native library path for libtree-sitter
  # java-tree-sitter uses JNI and needs to find the native library
  configure_native_library_path!
  # :nocov:
rescue LoadError
  # ignore; not JRuby or Java bridge not available
end

.available?Boolean

Returns:

  • (Boolean)


167
168
169
170
171
# File 'lib/tree_haver/backends/java.rb', line 167

def available?
  return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
  @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
  @loaded = check_availability # rubocop:disable ThreadSafety/ClassInstanceVariable
end

.capabilitiesHash{Symbol => Object}

Get capabilities supported by this backend

Examples:

TreeHaver::Backends::Java.capabilities
# => { backend: :java, parse: true, query: true, bytes_field: true, incremental: true }

Returns:

  • (Hash{Symbol => Object})

    capability map



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/tree_haver/backends/java.rb', line 226

def capabilities
  # :nocov:
  # This method returns meaningful data only on JRuby when java-tree-sitter is available.
  return {} unless available?
  {
    backend: :java,
    parse: true,
    query: true, # java-tree-sitter supports the Query API
    bytes_field: true,
    incremental: true, # java-tree-sitter supports Parser.parse(Tree, String)
  }
  # :nocov:
end

.configure_native_library_path!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Configure java.library.path to include the directory containing libtree-sitter



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/tree_haver/backends/java.rb', line 133

def configure_native_library_path!
  # :nocov:
  # This method requires JRuby and cannot be tested on MRI/CRuby.
  lib_path = ENV["TREE_SITTER_RUNTIME_LIB"]
  return unless lib_path && File.exist?(lib_path)

  lib_dir = File.dirname(lib_path)
  current_path = java.lang.System.getProperty("java.library.path") || ""

  unless current_path.include?(lib_dir)
    new_path = current_path.empty? ? lib_dir : "#{lib_dir}:#{current_path}"
    java.lang.System.setProperty("java.library.path", new_path)

    # Also set jna.library.path in case it uses JNA
    java.lang.System.setProperty("jna.library.path", new_path)
  end
  # :nocov:
rescue => _error
  # Ignore errors setting library path
end

.java_classesHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the loaded Java classes

Returns:

  • (Hash)

    the Java class references



216
217
218
# File 'lib/tree_haver/backends/java.rb', line 216

def java_classes
  @java_classes
end

.load_errorString?

Get the last load error message (for debugging)

Returns:

  • (String, nil)

    the error message or nil if no error



208
209
210
# File 'lib/tree_haver/backends/java.rb', line 208

def load_error
  @load_error
end

.reset!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Reset the load state (primarily for testing)



177
178
179
180
181
182
183
# File 'lib/tree_haver/backends/java.rb', line 177

def reset!
  @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
  @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
  @load_error = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
  @loader = nil # rubocop:disable ThreadSafety/ClassInstanceVariable
  @java_classes = {} # rubocop:disable ThreadSafety/ClassInstanceVariable
end

.runtime_lookupObject?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the cached runtime library SymbolLookup

Returns:

  • (Object, nil)

    the SymbolLookup for libtree-sitter.so



85
86
87
# File 'lib/tree_haver/backends/java.rb', line 85

def runtime_lookup
  @runtime_lookup
end

.runtime_lookup=(lookup) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set the cached runtime library SymbolLookup

Parameters:

  • lookup (Object)

    the SymbolLookup



92
93
94
# File 'lib/tree_haver/backends/java.rb', line 92

def runtime_lookup=(lookup)
  @runtime_lookup = lookup
end