Mercurial Hosting > luan
diff src/luan/modules/IoLuan.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/modules/IoLuan.java@ef0fc9ad30c1 |
children | c49980cdece6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/IoLuan.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,904 @@ +package luan.modules; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.io.Writer; +import java.io.StringReader; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.StringWriter; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.URL; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.NetworkInterface; +import java.net.MalformedURLException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanJavaFunction; +import luan.LuanException; +import luan.modules.url.LuanUrl; + + +public final class IoLuan { + + private static void add(LuanTable t,String method,Class... parameterTypes) throws NoSuchMethodException { + t.rawPut( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) ); + } + + public static String read_console_line(String prompt) throws IOException { + if( prompt==null ) + prompt = "> "; + return System.console().readLine(prompt); + } + + + public interface LuanWriter { + public void write(LuanState luan,Object... args) throws LuanException, IOException; + public void close() throws IOException; + } + + public static LuanTable textWriter(final PrintStream out) { + LuanWriter luanWriter = new LuanWriter() { + + public void write(LuanState luan,Object... args) throws LuanException { + for( Object obj : args ) { + out.print( luan.toString(obj) ); + } + } + + public void close() { + out.close(); + } + }; + return writer(luanWriter); + } + + public static LuanTable textWriter(final Writer out) { + LuanWriter luanWriter = new LuanWriter() { + + public void write(LuanState luan,Object... args) throws LuanException, IOException { + for( Object obj : args ) { + out.write( luan.toString(obj) ); + } + } + + public void close() throws IOException { + out.close(); + } + }; + return writer(luanWriter); + } + + private static LuanTable writer(LuanWriter luanWriter) { + LuanTable writer = new LuanTable(); + try { + writer.rawPut( "write", new LuanJavaFunction( + LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter + ) ); + writer.rawPut( "close", new LuanJavaFunction( + LuanWriter.class.getMethod( "close" ), luanWriter + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return writer; + } + + + public static LuanTable binaryWriter(final OutputStream out) { + LuanTable writer = new LuanTable(); + try { + writer.rawPut( "write", new LuanJavaFunction( + OutputStream.class.getMethod( "write", new byte[0].getClass() ), out + ) ); + writer.rawPut( "close", new LuanJavaFunction( + OutputStream.class.getMethod( "close" ), out + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return writer; + } + + static LuanFunction lines(final BufferedReader in) { + return new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + in.close(); + return null; + } + String rtn = in.readLine(); + if( rtn==null ) + in.close(); + return rtn; + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + static LuanFunction blocks(final InputStream in,final int blockSize) { + return new LuanFunction() { + final byte[] a = new byte[blockSize]; + + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + in.close(); + return null; + } + if( in.read(a) == -1 ) { + in.close(); + return null; + } + return a; + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + + private static File objToFile(Object obj) { + if( obj instanceof String ) { + return new File((String)obj); + } + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + Object java = t.rawGet("java"); + if( java instanceof LuanFile ) { + LuanFile luanFile = (LuanFile)java; + return luanFile.file; + } + } + return null; + } + + + public static abstract class LuanIn { + public abstract InputStream inputStream() throws IOException, LuanException; + public abstract String to_string(); + public abstract String to_uri_string(); + + public Reader reader() throws IOException, LuanException { + return new InputStreamReader(inputStream()); + } + + public String read_text() throws IOException, LuanException { + Reader in = reader(); + String s = Utils.readAll(in); + in.close(); + return s; + } + + public byte[] read_binary() throws IOException, LuanException { + InputStream in = inputStream(); + byte[] a = Utils.readAll(in); + in.close(); + return a; + } + + public LuanFunction read_lines() throws IOException, LuanException { + return lines(new BufferedReader(reader())); + } + + public LuanFunction read_blocks(Integer blockSize) throws IOException, LuanException { + int n = blockSize!=null ? blockSize : Utils.bufSize; + return blocks(inputStream(),n); + } + + public boolean exists() throws IOException, LuanException { + try { + inputStream().close(); + return true; + } catch(FileNotFoundException e) { + return false; + } + } + + public LuanTable table() { + LuanTable tbl = new LuanTable(); + try { + tbl.rawPut( "java", this ); + tbl.rawPut( "to_string", new LuanJavaFunction( + LuanIn.class.getMethod( "to_string" ), this + ) ); + tbl.rawPut( "to_uri_string", new LuanJavaFunction( + LuanIn.class.getMethod( "to_uri_string" ), this + ) ); + tbl.rawPut( "read_text", new LuanJavaFunction( + LuanIn.class.getMethod( "read_text" ), this + ) ); + tbl.rawPut( "read_binary", new LuanJavaFunction( + LuanIn.class.getMethod( "read_binary" ), this + ) ); + tbl.rawPut( "read_lines", new LuanJavaFunction( + LuanIn.class.getMethod( "read_lines" ), this + ) ); + tbl.rawPut( "read_blocks", new LuanJavaFunction( + LuanIn.class.getMethod( "read_blocks", Integer.class ), this + ) ); + tbl.rawPut( "exists", new LuanJavaFunction( + LuanIn.class.getMethod( "exists" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static final LuanIn defaultStdin = new LuanIn() { + + @Override public InputStream inputStream() { + return System.in; + } + + @Override public String to_string() { + return "<stdin>"; + } + + @Override public String to_uri_string() { + return "stdin:"; + } + + @Override public String read_text() throws IOException { + return Utils.readAll(new InputStreamReader(System.in)); + } + + @Override public byte[] read_binary() throws IOException { + return Utils.readAll(System.in); + } + + @Override public boolean exists() { + return true; + } + }; + + public static abstract class LuanIO extends LuanIn { + abstract OutputStream outputStream() throws IOException; + + public void write(Object obj) throws LuanException, IOException { + if( obj instanceof String ) { + String s = (String)obj; + Writer out = new OutputStreamWriter(outputStream()); + out.write(s); + out.close(); + return; + } + if( obj instanceof byte[] ) { + byte[] a = (byte[])obj; + OutputStream out = outputStream(); + Utils.copyAll(new ByteArrayInputStream(a),out); + out.close(); + return; + } + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + Object java = t.rawGet("java"); + if( java instanceof LuanIn ) { + LuanIn luanIn = (LuanIn)java; + InputStream in = luanIn.inputStream(); + OutputStream out = outputStream(); + Utils.copyAll(in,out); + out.close(); + in.close(); + return; + } + } + throw new LuanException( "bad argument #1 to 'write' (string or binary or Io.uri expected)" ); + } + + public LuanTable text_writer() throws IOException { + return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream()))); + } + + public LuanTable binary_writer() throws IOException { + return binaryWriter(new BufferedOutputStream(outputStream())); + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "write", new LuanJavaFunction( + LuanIO.class.getMethod( "write", Object.class ), this + ) ); + tbl.rawPut( "text_writer", new LuanJavaFunction( + LuanIO.class.getMethod( "text_writer" ), this + ) ); + tbl.rawPut( "binary_writer", new LuanJavaFunction( + LuanIO.class.getMethod( "binary_writer" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + private static final LuanIO nullIO = new LuanIO() { + private final InputStream in = new InputStream() { + @Override public int read() { + return -1; + } + }; + private final OutputStream out = new OutputStream() { + @Override public void write(int b) {} + }; + + @Override public InputStream inputStream() { + return in; + } + + @Override OutputStream outputStream() { + return out; + } + + @Override public String to_string() { + return "<null>"; + } + + @Override public String to_uri_string() { + return "null:"; + } + + }; + + public static final class LuanString extends LuanIO { + private String s; + + private LuanString(String s) { + this.s = s; + } + + @Override public InputStream inputStream() { + throw new UnsupportedOperationException(); + } + + @Override OutputStream outputStream() { + throw new UnsupportedOperationException(); + } + + @Override public String to_string() { + return "<string>"; + } + + @Override public String to_uri_string() { + return "string:" + s; + } + + @Override public Reader reader() { + return new StringReader(s); + } + + @Override public String read_text() { + return s; + } + + @Override public boolean exists() { + return true; + } + + @Override public LuanTable text_writer() throws IOException { + LuanWriter luanWriter = new LuanWriter() { + private final Writer out = new StringWriter(); + + public void write(LuanState luan,Object... args) throws LuanException, IOException { + for( Object obj : args ) { + out.write( luan.toString(obj) ); + } + } + + public void close() throws IOException { + s = out.toString(); + } + }; + return writer(luanWriter); + } + } + + public static final class LuanFile extends LuanIO { + public final File file; + + private LuanFile(LuanState luan,File file) throws LuanException { + this(file); + check(luan,"file:"+file.toString()); + } + + private LuanFile(File file) { + this.file = file; + } + + @Override public InputStream inputStream() throws IOException { + return new FileInputStream(file); + } + + @Override OutputStream outputStream() throws IOException { + return new FileOutputStream(file); + } + + @Override public String to_string() { + return file.toString(); + } + + @Override public String to_uri_string() { + return "file:" + file.toString(); + } + + public LuanTable child(LuanState luan,String name) throws LuanException { + return new LuanFile(luan,new File(file,name)).table(); + } + + public LuanTable children(LuanState luan) throws LuanException { + File[] files = file.listFiles(); + if( files==null ) + return null; + LuanTable list = new LuanTable(); + for( File f : files ) { + list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table()); + } + return list; + } + + public LuanTable parent(LuanState luan) throws LuanException, IOException { + File parent = file.getParentFile(); + if( parent==null ) + parent = file.getCanonicalFile().getParentFile(); + return new LuanFile(luan,parent).table(); + } + + @Override public boolean exists() { + return file.exists(); + } + + public void rename_to(Object destObj) throws LuanException { + File dest = objToFile(destObj); + if( dest==null ) + throw new LuanException( "bad argument #1 to 'objToFile' (string or file table expected)" ); + if( !file.renameTo(dest) ) + throw new LuanException("couldn't rename file "+file+" to "+dest); + } + + public LuanTable canonical(LuanState luan) throws LuanException, IOException { + return new LuanFile(luan,file.getCanonicalFile()).table(); + } + + public LuanTable create_temp_file(LuanState luan,String prefix,String suffix) throws LuanException, IOException { + File tmp = File.createTempFile(prefix,suffix,file); + return new LuanFile(luan,tmp).table(); + } + + public void delete() throws LuanException { + if( file.exists() ) + delete(file); + } + + private static void delete(File file) throws LuanException { + File[] children = file.listFiles(); + if( children != null ) { + for( File child : children ) { + delete(child); + } + } + if( !file.delete() ) + throw new LuanException("couldn't delete file "+file); + } + + public void mkdir() throws LuanException { + if( !file.isDirectory() ) { + if( !file.mkdirs() ) + throw new LuanException("couldn't make directory "+file); + } + } + + public void set_last_modified(long time) throws LuanException { + if( !file.setLastModified(time) ) + throw new LuanException("couldn't set_last_modified on "+file); + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "name", new LuanJavaFunction( + File.class.getMethod( "getName" ), file + ) ); + tbl.rawPut( "is_directory", new LuanJavaFunction( + File.class.getMethod( "isDirectory" ), file + ) ); + tbl.rawPut( "is_file", new LuanJavaFunction( + File.class.getMethod( "isFile" ), file + ) ); + tbl.rawPut( "delete", new LuanJavaFunction( + LuanFile.class.getMethod( "delete" ), this + ) ); + tbl.rawPut( "delete_on_exit", new LuanJavaFunction( + File.class.getMethod( "deleteOnExit" ), file + ) ); + tbl.rawPut( "mkdir", new LuanJavaFunction( + LuanFile.class.getMethod( "mkdir" ), this + ) ); + tbl.rawPut( "last_modified", new LuanJavaFunction( + File.class.getMethod( "lastModified" ), file + ) ); + tbl.rawPut( "set_last_modified", new LuanJavaFunction( + LuanFile.class.getMethod( "set_last_modified", Long.TYPE ), this + ) ); + tbl.rawPut( "length", new LuanJavaFunction( + File.class.getMethod( "length" ), file + ) ); + tbl.rawPut( "child", new LuanJavaFunction( + LuanFile.class.getMethod( "child", LuanState.class, String.class ), this + ) ); + tbl.rawPut( "children", new LuanJavaFunction( + LuanFile.class.getMethod( "children", LuanState.class ), this + ) ); + tbl.rawPut( "parent", new LuanJavaFunction( + LuanFile.class.getMethod( "parent", LuanState.class ), this + ) ); + tbl.rawPut( "rename_to", new LuanJavaFunction( + LuanFile.class.getMethod( "rename_to", Object.class ), this + ) ); + tbl.rawPut( "canonical", new LuanJavaFunction( + LuanFile.class.getMethod( "canonical", LuanState.class ), this + ) ); + tbl.rawPut( "create_temp_file", new LuanJavaFunction( + LuanFile.class.getMethod( "create_temp_file", LuanState.class, String.class, String.class ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static LuanTable null_() { + return nullIO.table(); + } + + public static LuanTable string(String s) throws LuanException { + Utils.checkNotNull(s); + return new LuanString(s).table(); + } + + public static LuanTable file(LuanState luan,String name) throws LuanException { + File file = new File(name); + return new LuanFile(file).table(); + } + + public static LuanTable classpath(LuanState luan,String name) throws LuanException { + if( name.contains("//") ) + return null; + String path = name; + check(luan,"classpath:"+path); + URL url; + if( !path.contains("#") ) { + url = ClassLoader.getSystemResource(path); + } else { + String[] a = path.split("#"); + url = ClassLoader.getSystemResource(a[0]); + if( url==null ) { + for( int i=1; i<a.length; i++ ) { + url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); + if( url != null ) { + try { + url = new URL(url,"."); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + break; + } + } + } + } + if( url != null ) + return new LuanUrl(luan,url,null).table(); + + return null; + } + + private static LuanTable url(LuanState luan,String url,LuanTable options) throws IOException, LuanException { + return new LuanUrl(luan,new URL(url),options).table(); + } + + public static LuanTable http(LuanState luan,String path,LuanTable options) throws IOException, LuanException { + return url(luan,"http:"+path,options); + } + + public static LuanTable https(LuanState luan,String path,LuanTable options) throws IOException, LuanException { + return url(luan,"https:"+path,options); + } + + public static LuanTable luan(LuanState luan,String path) throws LuanException { + return classpath( luan, "luan/modules/" + path ); + } + + public static LuanTable stdin(LuanState luan) throws LuanException { + LuanTable io = (LuanTable)PackageLuan.require(luan,"luan:Io.luan"); + return (LuanTable)io.get(luan,"stdin"); + } + + public static LuanTable newSchemes() { + LuanTable schemes = new LuanTable(); + try { + schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_"),null) ); + add( schemes, "string", String.class ); + add( schemes, "file", LuanState.class, String.class ); + add( schemes, "classpath", LuanState.class, String.class ); + add( schemes, "socket", String.class ); + add( schemes, "http", LuanState.class, String.class, LuanTable.class ); + add( schemes, "https", LuanState.class, String.class, LuanTable.class ); + add( schemes, "luan", LuanState.class, String.class ); + add( schemes, "stdin", LuanState.class ); + add( schemes, "os", LuanState.class, String.class, LuanTable.class ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return schemes; + } + + private static LuanTable schemes(LuanState luan) throws LuanException { + LuanTable t = (LuanTable)PackageLuan.loaded(luan).rawGet("luan:Io.luan"); + if( t == null ) + return newSchemes(); + t = (LuanTable)t.get(luan,"schemes"); + if( t == null ) + return newSchemes(); + return t; + } + + public static LuanTable uri(LuanState luan,String name,LuanTable options) throws LuanException { + int i = name.indexOf(':'); + if( i == -1 ) + throw new LuanException( "invalid Io.uri name '"+name+"', missing scheme" ); + String scheme = name.substring(0,i); + String location = name.substring(i+1); + LuanTable schemes = schemes(luan); + LuanFunction opener = (LuanFunction)schemes.get(luan,scheme); + if( opener == null ) + throw new LuanException( "invalid scheme '"+scheme+"' in '"+name+"'" ); + return (LuanTable)Luan.first(opener.call(luan,new Object[]{location,options})); + } + + public static final class LuanSocket extends LuanIO { + public final Socket socket; + + private LuanSocket(String host,int port) throws LuanException { + try { + this.socket = new Socket(host,port); + } catch(IOException e) { + throw new LuanException(e.toString()); + } + } + + private LuanSocket(Socket socket) { + this.socket = socket; + } + + @Override public InputStream inputStream() throws IOException { + return socket.getInputStream(); + } + + @Override OutputStream outputStream() throws IOException { + return socket.getOutputStream(); + } + + @Override public String to_string() { + return socket.toString(); + } + + @Override public String to_uri_string() { + throw new UnsupportedOperationException(); + } + } + + public static LuanTable socket(String name) throws LuanException, IOException { + int i = name.indexOf(':'); + if( i == -1 ) + throw new LuanException( "invalid socket '"+name+"', format is: <host>:<port>" ); + String host = name.substring(0,i); + String portStr = name.substring(i+1); + int port = Integer.parseInt(portStr); + return new LuanSocket(host,port).table(); + } + + public static LuanFunction socket_server(int port) throws IOException { + final ServerSocket ss = new ServerSocket(port); + return new LuanFunction() { + @Override public Object call(LuanState luan,Object[] args) throws LuanException { + try { + if( args.length > 0 ) { + if( args.length > 1 || !"close".equals(args[0]) ) + throw new LuanException( "the only argument allowed is 'close'" ); + ss.close(); + return null; + } + return new LuanSocket(ss.accept()).table(); + } catch(IOException e) { + throw new LuanException(e); + } + } + }; + } + + + public static final class LuanOs extends LuanIO { + private final String cmd; + private final Process proc; + + private LuanOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { + this.cmd = cmd; + File dir = null; + if( options != null ) { + Map map = options.asMap(luan); + Object obj = map.remove("dir"); + dir = objToFile(obj); + if( dir==null ) + throw new LuanException( "bad option 'dir' (string or file table expected)" ); + if( !map.isEmpty() ) + throw new LuanException( "unrecognized options: "+map ); + } + this.proc = Runtime.getRuntime().exec(cmd,null,dir); + } + + @Override public InputStream inputStream() throws IOException { + return proc.getInputStream(); + } + + @Override OutputStream outputStream() throws IOException { + return proc.getOutputStream(); + } + + @Override public String to_string() { + return proc.toString(); + } + + @Override public String to_uri_string() { + throw new UnsupportedOperationException(); + } + + @Override public boolean exists() { + return true; + } + + public void wait_for() + throws IOException, LuanException + { + try { + proc.waitFor(); + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + int exitVal = proc.exitValue(); + if( exitVal != 0 ) { + Reader err = new InputStreamReader(proc.getErrorStream()); + String error = "error in: "+cmd+"\n"+Utils.readAll(err); + err.close(); + throw new LuanException(error); + } + } + + @Override public String read_text() throws IOException, LuanException { + String s = super.read_text(); + wait_for(); + return s; + } + + @Override public LuanTable table() { + LuanTable tbl = super.table(); + try { + tbl.rawPut( "wait_for", new LuanJavaFunction( + LuanOs.class.getMethod( "wait_for" ), this + ) ); + } catch(NoSuchMethodException e) { + throw new RuntimeException(e); + } + return tbl; + } + } + + public static LuanTable os(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { + return new LuanOs(luan,cmd,options).table(); + } + + + public static String ip(String domain) { + try { + return InetAddress.getByName(domain).getHostAddress(); + } catch(UnknownHostException e) { + return null; + } + } + + public static LuanTable my_ips() throws IOException { + LuanTable tbl = new LuanTable(); + for( Enumeration<NetworkInterface> e1 = NetworkInterface.getNetworkInterfaces(); e1.hasMoreElements(); ) { + NetworkInterface ni = e1.nextElement(); + for( Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements(); ) { + InetAddress ia = e2.nextElement(); + if( ia instanceof Inet4Address ) + tbl.rawPut(ia.getHostAddress(),true); + } + } + return tbl; + } + +/* + // files maps zip name to uri + public static void zip(LuanState luan,String zipUri,LuanTable files) throws LuanException, IOException { + Object obj = uri(luan,zipUri,null).rawGet("java"); + if( !(obj instanceof LuanIO) ) + throw new LuanException("invalid uri for zip"); + LuanIO zipIo = (LuanIO)obj; + ZipOutputStream out = new ZipOutputStream(zipIo.outputStream()); + for( Map.Entry<Object,Object> entry : files.iterable(luan) ) { + obj = entry.getKey(); + if( !(obj instanceof String) ) + throw new LuanException("zip file table keys must be strings"); + String fileName = (String)obj; + obj = entry.getValue(); + if( !(obj instanceof String) ) + throw new LuanException("zip file table values must be strings"); + String uriStr = (String)obj; + out.putNextEntry(new ZipEntry(fileName)); + obj = uri(luan,uriStr,null).rawGet("java"); + if( !(obj instanceof LuanIn) ) + throw new LuanException("invalid uri for zip"); + LuanIn zipIn = (LuanIn)obj; + InputStream in = zipIn.inputStream(); + Utils.copyAll(in,out); + in.close(); + out.closeEntry(); + } + out.close(); + } +*/ + + // security + + public interface Security { + public void check(LuanState luan,String name) throws LuanException; + } + + private static String SECURITY_KEY = "Io.Security"; + + private static void check(LuanState luan,String name) throws LuanException { + Security s = (Security)luan.registry().get(SECURITY_KEY); + if( s!=null ) + s.check(luan,name); + } + + public static void setSecurity(LuanState luan,Security s) { + luan.registry().put(SECURITY_KEY,s); + } + + private void IoLuan() {} // never +}