Skip to content

Commit

Permalink
Merge pull request #5211 from jruby/ji-path
Browse files Browse the repository at this point in the history
[ji] coercing Ruby File/Dir to Java File/Path
  • Loading branch information
kares committed Jun 9, 2018
2 parents 6baf780 + 6562738 commit 0e17f04
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 45 deletions.
57 changes: 39 additions & 18 deletions core/src/main/java/org/jruby/RubyDir.java
Expand Up @@ -32,26 +32,25 @@

package org.jruby;

import static org.jruby.RubyEnumerator.enumeratorize;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Watchable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

import jnr.posix.FileStat;

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;

import jnr.posix.util.Platform;

import org.jcodings.Encoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.RaiseException;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Block;
Expand All @@ -62,22 +61,23 @@
import org.jruby.util.*;
import org.jruby.ast.util.ArgsUtil;

import static org.jruby.RubyEnumerator.enumeratorize;
import static org.jruby.RubyString.UTF8;

/**
* .The Ruby built-in class Dir.
* The Ruby built-in class Dir.
*
* @author jvoegele
*/
@JRubyClass(name = "Dir", include = "Enumerable")
public class RubyDir extends RubyObject {
public class RubyDir extends RubyObject implements Closeable {
private RubyString path; // What we passed to the constructor for method 'path'
protected FileResource dir;
private long lastModified = Long.MIN_VALUE;
private String[] snapshot; // snapshot of contents of directory
private int pos; // current position in directory
private boolean isOpen = true;

private final static Encoding UTF8 = UTF8Encoding.INSTANCE;

private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^(uri|jar|file|classpath):([^:]*:)?//?.*");

public RubyDir(Ruby runtime, RubyClass type) {
Expand Down Expand Up @@ -650,13 +650,15 @@ public static IRubyObject open19(ThreadContext context, IRubyObject recv, IRubyO
* Closes the directory stream.
*/
@JRubyMethod(name = "close")
public IRubyObject close() {
public IRubyObject close(ThreadContext context) {
close();
return context.nil;
}

public final void close() {
// Make sure any read()s after close fail.
checkDirIgnoreClosed();

isOpen = false;

return getRuntime().getNil();
}

/**
Expand Down Expand Up @@ -769,6 +771,11 @@ public IRubyObject to_path(ThreadContext context) {
return path(context);
}

public String getPath() {
if (path == null) return null;
return path.asJavaString();
}

/** Returns the next entry from this directory. */
@JRubyMethod(name = "read")
public IRubyObject read() {
Expand Down Expand Up @@ -992,17 +999,18 @@ public static RubyString getHomeDirectoryPath(ThreadContext context) {
return getHomeDirectoryPath(context, context.runtime.getENV().op_aref(context, homeKey));
}

private static final ByteList user_home = new ByteList(new byte[] {'u','s','e','r','.','h','o','m','e'}, false);

static RubyString getHomeDirectoryPath(ThreadContext context, IRubyObject home) {
final Ruby runtime = context.runtime;

if (home == null || home == context.nil) {
IRubyObject ENV_JAVA = runtime.getObject().getConstant("ENV_JAVA");
home = ENV_JAVA.callMethod(context, "[]", runtime.newString("user.home"));
home = ENV_JAVA.callMethod(context, "[]", RubyString.newString(runtime, user_home, UTF8));
}

if (home == null || home == context.nil) {
RubyHash ENV = (RubyHash) runtime.getObject().getConstant("ENV");
home = ENV.op_aref(context, runtime.newString("LOGDIR"));
home = context.runtime.getENV().op_aref(context, runtime.newString("LOGDIR"));
}

if (home == null || home == context.nil) {
Expand All @@ -1012,6 +1020,19 @@ static RubyString getHomeDirectoryPath(ThreadContext context, IRubyObject home)
return (RubyString) home;
}

@Override
public <T> T toJava(Class<T> target) {
if (target == File.class) {
final String path = getPath();
return path == null ? null : (T) new File(path);
}
if (target == Path.class || target == Watchable.class) {
final String path = getPath();
return path == null ? null : (T) FileSystems.getDefault().getPath(path);
}
return super.toJava(target);
}

@Deprecated
public static RubyArray entries19(ThreadContext context, IRubyObject recv, IRubyObject arg) {
return entries(context, recv, arg);
Expand Down
26 changes: 10 additions & 16 deletions core/src/main/java/org/jruby/RubyFile.java
Expand Up @@ -44,7 +44,6 @@
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.NotImplementedError;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.*;
import org.jruby.runtime.JavaSites.FileSites;
import org.jruby.runtime.builtin.IRubyObject;
Expand All @@ -65,11 +64,7 @@
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.*;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFileAttributeView;
import java.util.ArrayList;
Expand All @@ -88,8 +83,8 @@
import static org.jruby.util.io.EncodingUtils.vperm;

/**
* Ruby File class equivalent in java.
**/
* The Ruby File class.
*/
@JRubyClass(name="File", parent="IO", include="FileTest")
public class RubyFile extends RubyIO implements EncodingCapable {

Expand Down Expand Up @@ -298,7 +293,7 @@ protected IRubyObject rbIoClose(ThreadContext context) {
public IRubyObject flock(ThreadContext context, IRubyObject operation) {

// Solaris uses a ruby-ffi version defined in jruby/kernel/file.rb, so re-dispatch
if (org.jruby.platform.Platform.IS_SOLARIS) {
if (Platform.IS_SOLARIS) {
return callMethod(context, "flock", operation);
}

Expand Down Expand Up @@ -423,7 +418,6 @@ public IRubyObject birthtime(ThreadContext context) {

public static final FileTime getBirthtimeWithNIO(String pathString) {
// FIXME: birthtime is in stat, so we should use that if platform supports it (#2152)
// TODO: This requires Java 7 APIs and may not work on Android
Path path = Paths.get(pathString);
PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
try {
Expand Down Expand Up @@ -943,10 +937,6 @@ public static IRubyObject ftype(ThreadContext context, IRubyObject recv, IRubyOb
return runtime.newFileStat(path.getUnicodeValue(), true).ftype();
}

/*
* Fixme: This does not have exact same semantics as RubyArray.join, but they
* probably could be consolidated (perhaps as join(args[], sep, doChomp)).
*/
@JRubyMethod(rest = true, meta = true)
public static RubyString join(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return doJoin(context, recv, args);
Expand Down Expand Up @@ -2116,14 +2106,18 @@ private static RubyString checkHome(ThreadContext context) {
public <T> T toJava(Class<T> target) {
if (target == File.class) {
final String path = getPath();
return path == null ? null : target.cast(new File(path));
return path == null ? null : (T) new File(path);
}
if (target == Path.class || target == Watchable.class) {
final String path = getPath();
return path == null ? null : (T) FileSystems.getDefault().getPath(path);
}
return super.toJava(target);
}

private static RubyString doJoin(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
final Ruby runtime = context.runtime;
final String separator = runtime.getClass("File").getConstant("SEPARATOR").toString();
final String separator = runtime.getFile().getConstant("SEPARATOR").toString();

final RubyArray argsAry = RubyArray.newArrayMayCopy(runtime, args);

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyString.java
Expand Up @@ -120,8 +120,8 @@
public class RubyString extends RubyObject implements CharSequence, EncodingCapable, MarshalEncoding, CodeRangeable {
public static final String DEBUG_INFO_FIELD = "@debug_created_info";

private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
private static final UTF8Encoding UTF8 = UTF8Encoding.INSTANCE;
static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
static final UTF8Encoding UTF8 = UTF8Encoding.INSTANCE;

// string doesn't share any resources
private static final int SHARE_LEVEL_NONE = 0;
Expand Down
20 changes: 20 additions & 0 deletions spec/java_integration/addons/dir_spec.rb
@@ -0,0 +1,20 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe "Ruby Dir" do

it "is coercible to java.io.File" do
dir = Dir.new('..')
expect(java.io.File).to be === dir.to_java('java.io.File')
file = dir.to_java(java.io.File)
expect(file.getPath).to eql '..'
dir.close
end

it "is coercible to (java) Path" do
Dir.open('.') do |dir|
java_file= dir.to_java java.nio.file.Path
expect(java_file).to eql java.io.File.new('.').toPath
end
end

end
22 changes: 22 additions & 0 deletions spec/java_integration/addons/file_spec.rb
@@ -0,0 +1,22 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe "Ruby File" do

before(:all) { require 'tempfile' }

it "is coercible to java.io.File" do
file = Tempfile.new("io_spec").to_java 'java.io.File'
expect(java.io.File).to be === file
file = File.open(__FILE__).to_java java.io.File
expect(java.io.File).to be === file
expect(file.getPath).to eql __FILE__
end

it "is coercible to (java) Path" do
File.open('.') do |file|
java_file= file.to_java java.nio.file.Path
expect(java_file).to eql java.io.File.new('.').toPath
end
end

end
11 changes: 2 additions & 9 deletions spec/java_integration/addons/io_spec.rb
@@ -1,8 +1,9 @@
require File.dirname(__FILE__) + "/../spec_helper"
require 'tempfile'

describe "Ruby IO" do

before(:all) { require 'tempfile' }

let(:input_number) { "1234567890" }

it "gets an IO from a java.io.InputStream" do
Expand Down Expand Up @@ -126,12 +127,4 @@
expect(str).to eq(String.from_java_bytes(bytes.array))
end

it "is coercible to java.io.Files" do
file = Tempfile.new("io_spec").to_java 'java.io.File'
expect(java.io.File).to be === file
file = File.open(__FILE__).to_java java.io.File
expect(java.io.File).to be === file
expect(file.getPath).to eql __FILE__
end

end

0 comments on commit 0e17f04

Please sign in to comment.