Refactor the filesystem and HTTP code

- Move the encoding/decoding from the Filesystem implementation to the
   individual handles.
 - Move each handle into an core.apis.handles package from the main fs
   API.
 - Move the HTTP response to inherit from these handles.
 - Allow binary handles' read function to accept a number, specifying
   how many characters to read - these will be returned as a Lua string.
 - Add readAll to binary handles
 - Allow binary handles' write function to accept a string which is
   decoded into the individual bytes.
 - Add "binary" argument to http.request and friends in order to return
   a binary handle.
 - Ensure file handles are open when reading from/writing to them.
 - Return the error message when opening a file fails.
This commit is contained in:
SquidDev 2017-05-12 22:49:44 +01:00
parent 2fd01b2adf
commit 6ccffe9742
15 changed files with 593 additions and 642 deletions

View File

@ -7,15 +7,17 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.BinaryOutputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
import dan200.computercraft.core.apis.handles.EncodedOutputHandle;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.filesystem.IMountedFileBinary;
import dan200.computercraft.core.filesystem.IMountedFileNormal;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
@ -259,33 +261,33 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
try {
if( mode.equals( "r" ) ) {
// Open the file for reading, then create a wrapper around the reader
IMountedFileNormal reader = m_fileSystem.openForRead( path );
return wrapBufferedReader( reader );
InputStream reader = m_fileSystem.openForRead( path );
return new Object[] { new EncodedInputHandle( reader ) };
} else if( mode.equals( "w" ) ) {
// Open the file for writing, then create a wrapper around the writer
IMountedFileNormal writer = m_fileSystem.openForWrite( path, false );
return wrapBufferedWriter( writer );
OutputStream writer = m_fileSystem.openForWrite( path, false );
return new Object[] { new EncodedOutputHandle( writer ) };
} else if( mode.equals( "a" ) ) {
// Open the file for appending, then create a wrapper around the writer
IMountedFileNormal writer = m_fileSystem.openForWrite( path, true );
return wrapBufferedWriter( writer );
OutputStream writer = m_fileSystem.openForWrite( path, true );
return new Object[] { new EncodedOutputHandle( writer ) };
} else if( mode.equals( "rb" ) ) {
// Open the file for binary reading, then create a wrapper around the reader
IMountedFileBinary reader = m_fileSystem.openForBinaryRead( path );
return wrapInputStream( reader );
InputStream reader = m_fileSystem.openForRead( path );
return new Object[] { new BinaryInputHandle( reader ) };
} else if( mode.equals( "wb" ) ) {
// Open the file for binary writing, then create a wrapper around the writer
IMountedFileBinary writer = m_fileSystem.openForBinaryWrite( path, false );
return wrapOutputStream( writer );
OutputStream writer = m_fileSystem.openForWrite( path, false );
return new Object[] { new BinaryOutputHandle( writer ) };
} else if( mode.equals( "ab" ) ) {
// Open the file for binary appending, then create a wrapper around the reader
IMountedFileBinary writer = m_fileSystem.openForBinaryWrite( path, true );
return wrapOutputStream( writer );
OutputStream writer = m_fileSystem.openForWrite( path, true );
return new Object[] { new BinaryOutputHandle( writer ) };
} else {
throw new LuaException( "Unsupported mode" );
@ -368,288 +370,4 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
}
}
}
private static Object[] wrapBufferedReader( final IMountedFileNormal reader )
{
return new Object[] { new ILuaObject() {
private boolean open = true;
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"readLine",
"readAll",
"close"
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
{
// readLine
if( !open ) throw new LuaException( "attempt to use a closed file" );
try {
String line = reader.readLine();
if( line != null ) {
return new Object[] { line };
} else {
return null;
}
} catch( IOException e ) {
return null;
}
}
case 1:
{
// readAll
if( !open ) throw new LuaException( "attempt to use a closed file" );
try {
StringBuilder result = new StringBuilder( "" );
String line = reader.readLine();
while( line != null ) {
result.append( line );
line = reader.readLine();
if( line != null ) {
result.append( "\n" );
}
}
return new Object[] { result.toString() };
} catch( IOException e ) {
return null;
}
}
case 2:
{
// close
try {
reader.close();
open = false;
return null;
} catch( IOException e ) {
return null;
}
}
default:
{
return null;
}
}
}
} };
}
private static Object[] wrapBufferedWriter( final IMountedFileNormal writer )
{
return new Object[] { new ILuaObject() {
private boolean open = true;
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"write",
"writeLine",
"close",
"flush"
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
{
// write
if( !open ) throw new LuaException( "attempt to use a closed file" );
String text;
if( args.length > 0 && args[0] != null ) {
text = args[0].toString();
} else {
text = "";
}
try {
writer.write( text, 0, text.length(), false );
return null;
} catch( IOException e ) {
throw new LuaException( e.getMessage() );
}
}
case 1:
{
// writeLine
if( !open ) throw new LuaException( "attempt to use a closed file" );
String text;
if( args.length > 0 && args[0] != null ) {
text = args[0].toString();
} else {
text = "";
}
try {
writer.write( text, 0, text.length(), true );
return null;
} catch( IOException e ) {
throw new LuaException( e.getMessage() );
}
}
case 2:
{
// close
try {
writer.close();
open = false;
return null;
} catch( IOException e ) {
return null;
}
}
case 3:
{
try {
if( !open ) throw new LuaException( "attempt to use a closed file" );
writer.flush();
return null;
} catch ( IOException e ) {
return null;
}
}
default:
{
assert( false );
return null;
}
}
}
} };
}
private static Object[] wrapInputStream( final IMountedFileBinary reader )
{
return new Object[] { new ILuaObject() {
private boolean open = true;
@Nonnull
@Override
public String[] getMethodNames() {
return new String[] {
"read",
"close"
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args) throws LuaException {
switch( method )
{
case 0:
{
// read
if( !open ) throw new LuaException( "attempt to use a closed file" );
try {
int b = reader.read();
if( b != -1 ) {
return new Object[] { b };
} else {
return null;
}
} catch( IOException e ) {
return null;
}
}
case 1:
{
//close
try {
reader.close();
open = false;
return null;
} catch( IOException e ) {
return null;
}
}
default:
{
assert( false );
return null;
}
}
}
}};
}
private static Object[] wrapOutputStream( final IMountedFileBinary writer )
{
return new Object[] { new ILuaObject() {
private boolean open = true;
@Nonnull
@Override
public String[] getMethodNames() {
return new String[] {
"write",
"close",
"flush"
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args) throws LuaException {
switch( method )
{
case 0:
{
// write
if( !open ) throw new LuaException( "attempt to use a closed file" );
try {
if( args.length > 0 && args[0] instanceof Number )
{
int number = ((Number)args[0]).intValue();
writer.write( number );
}
return null;
} catch( IOException e ) {
throw new LuaException(e.getMessage());
}
}
case 1:
{
//close
try {
writer.close();
open = false;
return null;
} catch( IOException e ) {
return null;
}
}
case 2:
{
if( !open ) throw new LuaException( "attempt to use a closed file" );
try {
writer.flush();
return null;
} catch ( IOException e ) {
return null;
}
}
default:
{
assert( false );
return null;
}
}
}
}};
}
}

View File

@ -9,10 +9,11 @@
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
import javax.annotation.Nonnull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class HTTPAPI implements ILuaAPI
@ -52,15 +53,21 @@ public void advance( double _dt )
final String url = h.getURL();
if( h.wasSuccessful() ) {
// Queue the "http_success" event
final BufferedReader contents = h.getContents();
final Object result = wrapBufferedReader( contents, h.getResponseCode(), h.getResponseHeaders() );
InputStream contents = h.getContents();
Object result = wrapStream(
h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ),
h.getResponseCode(), h.getResponseHeaders()
);
m_apiEnvironment.queueEvent( "http_success", new Object[] { url, result } );
} else {
// Queue the "http_failure" event
BufferedReader contents = h.getContents();
InputStream contents = h.getContents();
Object result = null;
if( contents != null ) {
result = wrapBufferedReader( contents, h.getResponseCode(), h.getResponseHeaders() );
result = wrapStream(
h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ),
h.getResponseCode(), h.getResponseHeaders()
);
}
m_apiEnvironment.queueEvent( "http_failure", new Object[]{ url, "Could not connect", result } );
}
@ -69,80 +76,40 @@ public void advance( double _dt )
}
}
}
private static ILuaObject wrapBufferedReader( final BufferedReader reader, final int responseCode, final Map<String, String> responseHeaders )
private static ILuaObject wrapStream( final ILuaObject reader, final int responseCode, final Map<String, String> responseHeaders )
{
return new ILuaObject() {
private boolean open = true;
String[] oldMethods = reader.getMethodNames();
final int methodOffset = oldMethods.length;
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
newMethods[ methodOffset + 0 ] = "getResponseCode";
newMethods[ methodOffset + 1 ] = "getResponseHeaders";
return new ILuaObject()
{
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"readLine",
"readAll",
"close",
"getResponseCode",
"getResponseHeaders",
};
return newMethods;
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
{
switch( method )
if( method < methodOffset )
{
return reader.callMethod( context, method, args );
}
switch( method - methodOffset )
{
case 0:
{
// readLine
if( !open ) throw new LuaException( "attempt to use a closed response" );
try {
String line = reader.readLine();
if( line != null ) {
return new Object[] { line };
} else {
return null;
}
} catch( IOException e ) {
return null;
}
}
case 1:
{
// readAll
if( !open ) throw new LuaException( "attempt to use a closed response" );
try {
StringBuilder result = new StringBuilder( "" );
String line = reader.readLine();
while( line != null ) {
result.append( line );
line = reader.readLine();
if( line != null ) {
result.append( "\n" );
}
}
return new Object[] { result.toString() };
} catch( IOException e ) {
return null;
}
}
case 2:
{
// close
try {
reader.close();
open = false;
return null;
} catch( IOException e ) {
return null;
}
}
case 3:
{
// getResponseCode
return new Object[] { responseCode };
}
case 4:
case 1:
{
// getResponseHeaders
return new Object[] { responseHeaders };
@ -216,11 +183,18 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
}
}
}
// Get binary
boolean binary = false;
if( args.length >= 4 )
{
binary = args[ 3 ] != null && !args[ 3 ].equals( Boolean.FALSE );
}
// Make the request
try
{
HTTPRequest request = new HTTPRequest( urlString, postString, headers );
HTTPRequest request = new HTTPRequest( urlString, postString, headers, binary );
synchronized( m_httpRequests )
{
m_httpRequests.add( request );

View File

@ -7,6 +7,7 @@
package dan200.computercraft.core.apis;
import com.google.common.base.Joiner;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import java.io.*;
@ -60,11 +61,12 @@ public static URL checkURL( String urlString ) throws HTTPRequestException
return url;
}
public HTTPRequest( String url, final String postText, final Map<String, String> headers ) throws HTTPRequestException
public HTTPRequest( String url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_urlString = url;
m_url = checkURL( m_urlString );
m_binary = binary;
// Start the thread
m_cancelled = false;
@ -136,54 +138,10 @@ public void run()
is = connection.getErrorStream();
responseSuccess = false;
}
InputStreamReader isr;
try
{
String contentEncoding = connection.getContentEncoding();
if( contentEncoding != null )
{
try
{
isr = new InputStreamReader( is, contentEncoding );
}
catch( UnsupportedEncodingException e )
{
isr = new InputStreamReader( is, "UTF-8" );
}
}
else
{
isr = new InputStreamReader( is, "UTF-8" );
}
}
catch( UnsupportedEncodingException e )
{
isr = new InputStreamReader( is );
}
// Download the contents
BufferedReader reader = new BufferedReader( isr );
StringBuilder result = new StringBuilder();
while( true )
{
synchronized( m_lock )
{
if( m_cancelled )
{
break;
}
}
String line = reader.readLine();
if( line == null )
{
break;
}
result.append( line );
result.append( '\n' );
}
reader.close();
byte[] result = ByteStreams.toByteArray( is );
is.close();
synchronized( m_lock )
{
if( m_cancelled )
@ -198,8 +156,9 @@ public void run()
// We completed
m_complete = true;
m_success = responseSuccess;
m_result = result.toString();
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
@ -264,20 +223,29 @@ public boolean wasSuccessful()
return m_success;
}
}
public BufferedReader getContents()
public boolean isBinary()
{
String result;
return m_binary;
}
public InputStream getContents()
{
byte[] result;
synchronized(m_lock) {
result = m_result;
}
if( result != null ) {
return new BufferedReader( new StringReader( result ) );
return new ByteArrayInputStream( result );
}
return null;
}
public String getEncoding() {
return m_encoding;
}
private final Object m_lock = new Object();
private final URL m_url;
private final String m_urlString;
@ -285,7 +253,9 @@ public BufferedReader getContents()
private boolean m_complete;
private boolean m_cancelled;
private boolean m_success;
private String m_result;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode;
private Map<String, String> m_responseHeaders;
}

View File

@ -0,0 +1,93 @@
package dan200.computercraft.core.apis.handles;
import com.google.common.io.ByteStreams;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class BinaryInputHandle extends HandleGeneric
{
private final InputStream m_stream;
public BinaryInputHandle( InputStream reader )
{
super( reader );
this.m_stream = reader;
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"read",
"readAll",
"close",
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
// read
checkOpen();
try
{
if( args.length > 0 && args[ 0 ] != null )
{
if( !(args[ 0 ] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
int count = ((Number) args[ 0 ]).intValue();
if( count <= 0 || count >= 1024 * 16 )
{
throw new LuaException( "Count out of range" );
}
byte[] bytes = new byte[ count ];
count = m_stream.read( bytes );
if( count < 0 ) return null;
if( count < bytes.length ) bytes = Arrays.copyOf( bytes, count );
return new Object[] { bytes };
}
else
{
int b = m_stream.read();
return b == -1 ? null : new Object[] { b };
}
}
catch( IOException e )
{
return null;
}
case 1:
// readAll
checkOpen();
try
{
byte[] out = ByteStreams.toByteArray( m_stream );
return out == null ? null : new Object[] { out };
}
catch( IOException e )
{
return null;
}
case 2:
//close
close();
return null;
default:
return null;
}
}
}

View File

@ -0,0 +1,82 @@
package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.OutputStream;
public class BinaryOutputHandle extends HandleGeneric
{
private final OutputStream m_writer;
public BinaryOutputHandle( OutputStream writer )
{
super( writer );
this.m_writer = writer;
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"write",
"flush",
"close",
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
// write
checkOpen();
try
{
if( args.length > 0 && args[ 0 ] instanceof Number )
{
int number = ((Number) args[ 0 ]).intValue();
m_writer.write( number );
}
else if( args.length > 0 && args[ 0 ] instanceof String )
{
String value = (String) args[ 0 ];
m_writer.write( StringUtil.encodeString( value ) );
}
else
{
throw new LuaException( "Expected number" );
}
return null;
}
catch( IOException e )
{
throw new LuaException( e.getMessage() );
}
case 1:
// flush
checkOpen();
try
{
m_writer.flush();
return null;
}
catch( IOException e )
{
return null;
}
case 2:
//close
close();
return null;
default:
return null;
}
}
}

View File

@ -0,0 +1,105 @@
package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import java.io.*;
public class EncodedInputHandle extends HandleGeneric
{
private final BufferedReader m_reader;
public EncodedInputHandle( BufferedReader reader )
{
super( reader );
this.m_reader = reader;
}
public EncodedInputHandle( InputStream stream )
{
this( stream, "UTF-8" );
}
public EncodedInputHandle( InputStream stream, String encoding )
{
super( stream );
if( encoding == null ) encoding = "UTF-8";
InputStreamReader streamReader;
try
{
streamReader = new InputStreamReader( stream, encoding );
}
catch( UnsupportedEncodingException e )
{
streamReader = new InputStreamReader( stream );
}
this.m_reader = new BufferedReader( streamReader );
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"readLine",
"readAll",
"close",
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
// readLine
checkOpen();
try
{
String line = m_reader.readLine();
if( line != null )
{
return new Object[] { line };
}
else
{
return null;
}
}
catch( IOException e )
{
return null;
}
case 1:
// readAll
checkOpen();
try
{
StringBuilder result = new StringBuilder( "" );
String line = m_reader.readLine();
while( line != null )
{
result.append( line );
line = m_reader.readLine();
if( line != null )
{
result.append( "\n" );
}
}
return new Object[] { result.toString() };
}
catch( IOException e )
{
return null;
}
case 2:
// close
close();
return null;
default:
return null;
}
}
}

View File

@ -0,0 +1,124 @@
package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import java.io.*;
public class EncodedOutputHandle extends HandleGeneric
{
private final BufferedWriter m_writer;
public EncodedOutputHandle( BufferedWriter writer )
{
super( writer );
this.m_writer = writer;
}
public EncodedOutputHandle( OutputStream stream )
{
this( stream, "UTF-8" );
}
public EncodedOutputHandle( OutputStream stream, String encoding )
{
super( stream );
if( encoding == null ) encoding = "UTF-8";
OutputStreamWriter streamWriter;
try
{
streamWriter = new OutputStreamWriter( stream, encoding );
}
catch( UnsupportedEncodingException e )
{
streamWriter = new OutputStreamWriter( stream );
}
this.m_writer = new BufferedWriter( streamWriter );
}
@Nonnull
@Override
public String[] getMethodNames()
{
return new String[] {
"write",
"writeLine",
"flush",
"close",
};
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{
switch( method )
{
case 0:
{
// write
checkOpen();
String text;
if( args.length > 0 && args[ 0 ] != null )
{
text = args[ 0 ].toString();
}
else
{
text = "";
}
try
{
m_writer.write( text, 0, text.length() );
return null;
}
catch( IOException e )
{
throw new LuaException( e.getMessage() );
}
}
case 1:
{
// writeLine
checkOpen();
String text;
if( args.length > 0 && args[ 0 ] != null )
{
text = args[ 0 ].toString();
}
else
{
text = "";
}
try
{
m_writer.write( text, 0, text.length() );
m_writer.newLine();
return null;
}
catch( IOException e )
{
throw new LuaException( e.getMessage() );
}
}
case 2:
// flush
checkOpen();
try
{
m_writer.flush();
return null;
}
catch( IOException e )
{
return null;
}
case 3:
// close
close();
return null;
default:
return null;
}
}
}

View File

@ -0,0 +1,35 @@
package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import java.io.Closeable;
import java.io.IOException;
public abstract class HandleGeneric implements ILuaObject
{
protected final Closeable m_closable;
protected boolean m_open = true;
public HandleGeneric( Closeable m_closable )
{
this.m_closable = m_closable;
}
protected void checkOpen() throws LuaException
{
if( !m_open ) throw new LuaException( "attempt to use a closed file" );
}
protected void close()
{
try
{
m_closable.close();
m_open = false;
}
catch( IOException ignored )
{
}
}
}

View File

@ -9,6 +9,7 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import net.minecraftforge.fml.common.FMLLog;
import java.io.*;
import java.util.*;
@ -291,7 +292,7 @@ private String toLocal( String path )
}
private final Map<String, MountWrapper> m_mounts = new HashMap<String, MountWrapper>();
private final Set<IMountedFile> m_openFiles = Collections.newSetFromMap( new WeakHashMap<IMountedFile, Boolean>() );
private final Set<Closeable> m_openFiles = Collections.newSetFromMap( new WeakHashMap<Closeable, Boolean>() );
public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException
{
@ -308,7 +309,7 @@ public void unload()
// Close all dangling open files
synchronized( m_openFiles )
{
for(IMountedFile file : m_openFiles)
for( Closeable file : m_openFiles )
{
try {
file.close();
@ -647,7 +648,7 @@ private synchronized void copyRecursive( String sourcePath, MountWrapper sourceM
}
}
private synchronized <T extends IMountedFile> T openFile(T file, Closeable handle) throws FileSystemException
private synchronized <T> T openFile( T file, Closeable handle ) throws FileSystemException
{
synchronized( m_openFiles )
{
@ -665,199 +666,44 @@ private synchronized <T extends IMountedFile> T openFile(T file, Closeable handl
throw new FileSystemException("Too many files already open");
}
m_openFiles.add( file );
m_openFiles.add( handle );
return file;
}
}
private synchronized void closeFile( IMountedFile file, Closeable handle ) throws IOException
private synchronized void closeFile( Closeable handle ) throws IOException
{
synchronized( m_openFiles )
{
m_openFiles.remove( file );
if( handle != null )
{
handle.close();
}
m_openFiles.remove( handle );
handle.close();
}
}
public synchronized IMountedFileNormal openForRead( String path ) throws FileSystemException
public synchronized InputStream openForRead( String path ) throws FileSystemException
{
path = sanitizePath ( path );
MountWrapper mount = getMount( path );
InputStream stream = mount.openForRead( path );
if( stream != null )
{
InputStreamReader isr;
try
{
isr = new InputStreamReader( stream, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
isr = new InputStreamReader( stream );
}
final BufferedReader reader = new BufferedReader( isr );
IMountedFileNormal file = new IMountedFileNormal()
{
@Override
public String readLine() throws IOException
{
return reader.readLine();
}
@Override
public void write(String s, int off, int len, boolean newLine) throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void close() throws IOException
{
closeFile( this, reader );
}
@Override
public void flush() throws IOException
{
throw new UnsupportedOperationException();
}
};
return openFile( file, reader );
return openFile( new ClosingInputStream( stream ), stream );
}
return null;
}
public synchronized IMountedFileNormal openForWrite( String path, boolean append ) throws FileSystemException
public synchronized OutputStream openForWrite( String path, boolean append ) throws FileSystemException
{
path = sanitizePath ( path );
MountWrapper mount = getMount( path );
OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
if( stream != null )
{
OutputStreamWriter osw;
try
{
osw = new OutputStreamWriter( stream, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
osw = new OutputStreamWriter( stream );
}
final BufferedWriter writer = new BufferedWriter( osw );
IMountedFileNormal file = new IMountedFileNormal()
{
@Override
public String readLine() throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void write( String s, int off, int len, boolean newLine ) throws IOException
{
writer.write( s, off, len );
if( newLine )
{
writer.newLine();
}
}
@Override
public void close() throws IOException
{
closeFile( this, writer );
}
@Override
public void flush() throws IOException
{
writer.flush();
}
};
return openFile( file, writer );
return openFile( new ClosingOutputStream( stream ), stream );
}
return null;
}
public synchronized IMountedFileBinary openForBinaryRead( String path ) throws FileSystemException
{
path = sanitizePath ( path );
MountWrapper mount = getMount( path );
final InputStream stream = mount.openForRead( path );
if( stream != null )
{
IMountedFileBinary file = new IMountedFileBinary()
{
@Override
public int read() throws IOException
{
return stream.read();
}
@Override
public void write(int i) throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void close() throws IOException
{
closeFile( this, stream );
}
@Override
public void flush() throws IOException
{
throw new UnsupportedOperationException();
}
};
return openFile( file, stream );
}
return null;
}
public synchronized IMountedFileBinary openForBinaryWrite( String path, boolean append ) throws FileSystemException
{
path = sanitizePath ( path );
MountWrapper mount = getMount( path );
final OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path );
if( stream != null )
{
IMountedFileBinary file = new IMountedFileBinary()
{
@Override
public int read() throws IOException
{
throw new UnsupportedOperationException();
}
@Override
public void write(int i) throws IOException
{
stream.write(i);
}
@Override
public void close() throws IOException
{
closeFile( this, stream );
}
@Override
public void flush() throws IOException
{
stream.flush();
}
};
return openFile( file, stream );
}
return null;
}
public long getFreeSpace( String path ) throws FileSystemException
{
path = sanitizePath( path );
@ -1010,4 +856,34 @@ public static String toLocal( String path, String location )
return local;
}
}
private class ClosingInputStream extends FilterInputStream
{
protected ClosingInputStream( InputStream in )
{
super( in );
}
@Override
public void close() throws IOException
{
super.close();
closeFile( in );
}
}
private class ClosingOutputStream extends FilterOutputStream
{
protected ClosingOutputStream( OutputStream out )
{
super( out );
}
@Override
public void close() throws IOException
{
super.close();
closeFile( out );
}
}
}

View File

@ -1,13 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import java.io.IOException;
public interface IMountedFile {
void close() throws IOException;
}

View File

@ -1,16 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import java.io.IOException;
public interface IMountedFileBinary extends IMountedFile {
int read() throws IOException;
void write( int i ) throws IOException;
void close() throws IOException;
void flush() throws IOException;
}

View File

@ -1,16 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import java.io.IOException;
public interface IMountedFileNormal extends IMountedFile {
String readLine() throws IOException;
void write( String s, int off, int len, boolean newLine ) throws IOException;
void close() throws IOException;
void flush() throws IOException;
}

View File

@ -23,6 +23,7 @@
import javax.annotation.Nonnull;
import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
@ -509,6 +510,11 @@ else if( object instanceof String )
String s = object.toString();
return LuaValue.valueOf( s );
}
else if( object instanceof byte[] )
{
byte[] b = (byte[]) object;
return LuaValue.valueOf( Arrays.copyOf( b, b.length ) );
}
else if( object instanceof Map )
{
// Table:

View File

@ -41,4 +41,17 @@ public static String translateToLocalFormatted( String key, Object... format )
{
return net.minecraft.util.text.translation.I18n.translateToLocalFormatted( key, format );
}
public static byte[] encodeString( String string )
{
byte[] chars = new byte[ string.length() ];
for( int i = 0; i < chars.length; ++i )
{
char c = string.charAt( i );
chars[ i ] = c < 256 ? (byte) c : 63;
}
return chars;
}
}

View File

@ -639,8 +639,8 @@ end
if http then
local nativeHTTPRequest = http.request
local function wrapRequest( _url, _post, _headers )
local ok, err = nativeHTTPRequest( _url, _post, _headers )
local function wrapRequest( _url, _post, _headers, _binary )
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
if ok then
while true do
local event, param1, param2, param3 = os.pullEvent()
@ -654,16 +654,16 @@ if http then
return nil, err
end
http.get = function( _url, _headers )
return wrapRequest( _url, nil, _headers )
http.get = function( _url, _headers, _binary)
return wrapRequest( _url, nil, _headers, _binary)
end
http.post = function( _url, _post, _headers )
return wrapRequest( _url, _post or "", _headers )
http.post = function( _url, _post, _headers, _binary)
return wrapRequest( _url, _post or "", _headers, _binary)
end
http.request = function( _url, _post, _headers )
local ok, err = nativeHTTPRequest( _url, _post, _headers )
http.request = function( _url, _post, _headers, _binary )
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
if not ok then
os.queueEvent( "http_failure", _url, err )
end