KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyDir


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
17  * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
18  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
19  *
20  * Alternatively, the contents of this file may be used under the terms of
21  * either of the GNU General Public License Version 2 or later (the "GPL"),
22  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23  * in which case the provisions of the GPL or the LGPL are applicable instead
24  * of those above. If you wish to allow use of your version of this file only
25  * under the terms of either the GPL or the LGPL, and not to allow others to
26  * use your version of this file under the terms of the CPL, indicate your
27  * decision by deleting the provisions above and replace them with the notice
28  * and other provisions required by the GPL or the LGPL. If you do not delete
29  * the provisions above, a recipient may use your version of this file under
30  * the terms of any one of the CPL, the GPL or the LGPL.
31  ***** END LICENSE BLOCK *****/

32 package org.jruby;
33
34 import java.io.File JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.List JavaDoc;
38
39 import org.jruby.javasupport.JavaUtil;
40 import org.jruby.runtime.Block;
41 import org.jruby.runtime.CallbackFactory;
42 import org.jruby.runtime.ObjectAllocator;
43 import org.jruby.runtime.ThreadContext;
44 import org.jruby.runtime.builtin.IRubyObject;
45 import org.jruby.util.Glob;
46 import org.jruby.util.JRubyFile;
47
48 /**
49  * .The Ruby built-in class Dir.
50  *
51  * @author jvoegele
52  */

53 public class RubyDir extends RubyObject {
54     // What we passed to the constructor for method 'path'
55
private RubyString path;
56     protected JRubyFile dir;
57     private String JavaDoc[] snapshot; // snapshot of contents of directory
58
private int pos; // current position in directory
59
private boolean isOpen = true;
60
61     public RubyDir(Ruby runtime, RubyClass type) {
62         super(runtime, type);
63     }
64     
65     private static ObjectAllocator DIR_ALLOCATOR = new ObjectAllocator() {
66         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
67             return new RubyDir(runtime, klass);
68         }
69     };
70
71     public static RubyClass createDirClass(Ruby runtime) {
72         RubyClass dirClass = runtime.defineClass("Dir", runtime.getObject(), DIR_ALLOCATOR);
73
74         dirClass.includeModule(runtime.getModule("Enumerable"));
75
76         CallbackFactory callbackFactory = runtime.callbackFactory(RubyDir.class);
77
78         dirClass.getMetaClass().defineMethod("glob", callbackFactory.getSingletonMethod("glob", RubyKernel.IRUBY_OBJECT));
79         dirClass.getMetaClass().defineFastMethod("entries", callbackFactory.getFastSingletonMethod("entries", RubyKernel.IRUBY_OBJECT));
80         dirClass.getMetaClass().defineMethod("[]", callbackFactory.getSingletonMethod("glob", RubyKernel.IRUBY_OBJECT));
81         // dirClass.defineAlias("[]", "glob");
82
dirClass.getMetaClass().defineMethod("chdir", callbackFactory.getOptSingletonMethod("chdir"));
83         dirClass.getMetaClass().defineFastMethod("chroot", callbackFactory.getFastSingletonMethod("chroot", RubyKernel.IRUBY_OBJECT));
84         //dirClass.defineSingletonMethod("delete", callbackFactory.getSingletonMethod(RubyDir.class, "delete", RubyString.class));
85
dirClass.getMetaClass().defineMethod("foreach", callbackFactory.getSingletonMethod("foreach", RubyKernel.IRUBY_OBJECT));
86         dirClass.getMetaClass().defineFastMethod("getwd", callbackFactory.getFastSingletonMethod("getwd"));
87         dirClass.getMetaClass().defineFastMethod("pwd", callbackFactory.getFastSingletonMethod("getwd"));
88         // dirClass.defineAlias("pwd", "getwd");
89
dirClass.getMetaClass().defineFastMethod("mkdir", callbackFactory.getFastOptSingletonMethod("mkdir"));
90         dirClass.getMetaClass().defineMethod("open", callbackFactory.getSingletonMethod("open", RubyKernel.IRUBY_OBJECT));
91         dirClass.getMetaClass().defineFastMethod("rmdir", callbackFactory.getFastSingletonMethod("rmdir", RubyKernel.IRUBY_OBJECT));
92         dirClass.getMetaClass().defineFastMethod("unlink", callbackFactory.getFastSingletonMethod("rmdir", RubyKernel.IRUBY_OBJECT));
93         dirClass.getMetaClass().defineFastMethod("delete", callbackFactory.getFastSingletonMethod("rmdir", RubyKernel.IRUBY_OBJECT));
94         // dirClass.defineAlias("unlink", "rmdir");
95
// dirClass.defineAlias("delete", "rmdir");
96

97         dirClass.defineFastMethod("close", callbackFactory.getFastMethod("close"));
98         dirClass.defineMethod("each", callbackFactory.getMethod("each"));
99         dirClass.defineFastMethod("entries", callbackFactory.getFastMethod("entries"));
100         dirClass.defineFastMethod("path", callbackFactory.getFastMethod("path"));
101         dirClass.defineFastMethod("tell", callbackFactory.getFastMethod("tell"));
102         dirClass.defineAlias("pos", "tell");
103         dirClass.defineFastMethod("seek", callbackFactory.getFastMethod("seek", RubyKernel.IRUBY_OBJECT));
104         dirClass.defineFastMethod("pos=", callbackFactory.getFastMethod("setPos", RubyKernel.IRUBY_OBJECT));
105         dirClass.defineFastMethod("read", callbackFactory.getFastMethod("read"));
106         dirClass.defineFastMethod("rewind", callbackFactory.getFastMethod("rewind"));
107         dirClass.defineMethod("initialize", callbackFactory.getMethod("initialize", RubyKernel.IRUBY_OBJECT));
108
109         return dirClass;
110     }
111
112     /**
113      * Creates a new <code>Dir</code>. This method takes a snapshot of the
114      * contents of the directory at creation time, so changes to the contents
115      * of the directory will not be reflected during the lifetime of the
116      * <code>Dir</code> object returned, so a new <code>Dir</code> instance
117      * must be created to reflect changes to the underlying file system.
118      */

119     public IRubyObject initialize(IRubyObject _newPath, Block unusedBlock) {
120         RubyString newPath = _newPath.convertToString();
121         newPath.checkSafeString();
122         dir = JRubyFile.create(getRuntime().getCurrentDirectory(),newPath.toString());
123         if (!dir.isDirectory()) {
124             dir = null;
125             throw getRuntime().newErrnoENOENTError(newPath.toString() + " is not a directory");
126         }
127         path = newPath;
128         List JavaDoc snapshotList = new ArrayList JavaDoc();
129         snapshotList.add(".");
130         snapshotList.add("..");
131         snapshotList.addAll(getContents(dir));
132         snapshot = (String JavaDoc[]) snapshotList.toArray(new String JavaDoc[snapshotList.size()]);
133         pos = 0;
134
135         return this;
136     }
137
138 // ----- Ruby Class Methods ----------------------------------------------------
139

140     /**
141      * Returns an array of filenames matching the specified wildcard pattern
142      * <code>pat</code>. If a block is given, the array is iterated internally
143      * with each filename is passed to the block in turn. In this case, Nil is
144      * returned.
145      */

146     public static IRubyObject glob(IRubyObject recv, IRubyObject pat, Block block) {
147         String JavaDoc pattern = pat.convertToString().toString();
148         String JavaDoc[] files = new Glob(recv.getRuntime().getCurrentDirectory(), pattern).getNames();
149         if (block.isGiven()) {
150             ThreadContext context = recv.getRuntime().getCurrentContext();
151             
152             for (int i = 0; i < files.length; i++) {
153                 context.yield(JavaUtil.convertJavaToRuby(recv.getRuntime(), files[i]), block);
154             }
155             return recv.getRuntime().getNil();
156         }
157         return recv.getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(recv.getRuntime(), files));
158     }
159
160     /**
161      * @return all entries for this Dir
162      */

163     public RubyArray entries() {
164         return getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(getRuntime(), snapshot));
165     }
166     
167     /**
168      * Returns an array containing all of the filenames in the given directory.
169      */

170     public static RubyArray entries(IRubyObject recv, IRubyObject path) {
171         final JRubyFile directory = JRubyFile.create(recv.getRuntime().getCurrentDirectory(),path.convertToString().toString());
172         
173         if (!directory.isDirectory()) {
174             throw recv.getRuntime().newErrnoENOENTError("No such directory");
175         }
176         List JavaDoc fileList = getContents(directory);
177         fileList.add(0,".");
178         fileList.add(1,"..");
179         Object JavaDoc[] files = fileList.toArray();
180         return recv.getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(recv.getRuntime(), files));
181     }
182
183     /** Changes the current directory to <code>path</code> */
184     public static IRubyObject chdir(IRubyObject recv, IRubyObject[] args, Block block) {
185         recv.checkArgumentCount(args, 0, 1);
186         RubyString path = args.length == 1 ?
187             (RubyString) args[0].convertToString() : getHomeDirectoryPath(recv);
188         JRubyFile dir = getDir(recv.getRuntime(), path.toString(), true);
189         String JavaDoc realPath = null;
190         String JavaDoc oldCwd = recv.getRuntime().getCurrentDirectory();
191         
192         // We get canonical path to try and flatten the path out.
193
// a dir '/subdir/..' should return as '/'
194
// cnutter: Do we want to flatten path out?
195
try {
196             realPath = dir.getCanonicalPath();
197         } catch (IOException JavaDoc e) {
198             realPath = dir.getAbsolutePath();
199         }
200         
201         IRubyObject result = null;
202         if (block.isGiven()) {
203             // FIXME: Don't allow multiple threads to do this at once
204
recv.getRuntime().setCurrentDirectory(realPath);
205             try {
206                 result = recv.getRuntime().getCurrentContext().yield(path, block);
207             } finally {
208                 recv.getRuntime().setCurrentDirectory(oldCwd);
209             }
210         } else {
211             recv.getRuntime().setCurrentDirectory(realPath);
212             result = recv.getRuntime().newFixnum(0);
213         }
214         
215         return result;
216     }
217
218     /**
219      * Changes the root directory (only allowed by super user). Not available
220      * on all platforms.
221      */

222     public static IRubyObject chroot(IRubyObject recv, IRubyObject path) {
223         throw recv.getRuntime().newNotImplementedError("chroot not implemented: chroot is non-portable and is not supported.");
224     }
225
226     /**
227      * Deletes the directory specified by <code>path</code>. The directory must
228      * be empty.
229      */

230     public static IRubyObject rmdir(IRubyObject recv, IRubyObject path) {
231         JRubyFile directory = getDir(recv.getRuntime(), path.convertToString().toString(), true);
232         
233         if (!directory.delete()) {
234             throw recv.getRuntime().newSystemCallError("No such directory");
235         }
236         
237         return recv.getRuntime().newFixnum(0);
238     }
239
240     /**
241      * Executes the block once for each file in the directory specified by
242      * <code>path</code>.
243      */

244     public static IRubyObject foreach(IRubyObject recv, IRubyObject _path, Block block) {
245         RubyString path = _path.convertToString();
246         path.checkSafeString();
247
248         RubyClass dirClass = recv.getRuntime().getClass("Dir");
249         RubyDir dir = (RubyDir) dirClass.newInstance(new IRubyObject[] { path }, block);
250         
251         dir.each(block);
252         return recv.getRuntime().getNil();
253     }
254
255     /** Returns the current directory. */
256     public static RubyString getwd(IRubyObject recv) {
257         return recv.getRuntime().newString(recv.getRuntime().getCurrentDirectory());
258     }
259
260     /**
261      * Creates the directory specified by <code>path</code>. Note that the
262      * <code>mode</code> parameter is provided only to support existing Ruby
263      * code, and is ignored.
264      */

265     public static IRubyObject mkdir(IRubyObject recv, IRubyObject[] args) {
266         if (args.length < 1) {
267             throw recv.getRuntime().newArgumentError(args.length, 1);
268         }
269         if (args.length > 2) {
270             throw recv.getRuntime().newArgumentError(args.length, 2);
271         }
272
273         args[0].checkSafeString();
274         String JavaDoc path = args[0].toString();
275
276         File JavaDoc newDir = getDir(recv.getRuntime(), path, false);
277         if (File.separatorChar == '\\') {
278             newDir = new File JavaDoc(newDir.getPath());
279         }
280         
281         return newDir.mkdirs() ? RubyFixnum.zero(recv.getRuntime()) :
282             RubyFixnum.one(recv.getRuntime());
283     }
284
285     /**
286      * Returns a new directory object for <code>path</code>. If a block is
287      * provided, a new directory object is passed to the block, which closes the
288      * directory object before terminating.
289      */

290     public static IRubyObject open(IRubyObject recv, IRubyObject path, Block block) {
291         RubyDir directory =
292             (RubyDir) recv.getRuntime().getClass("Dir").newInstance(
293                     new IRubyObject[] { path }, Block.NULL_BLOCK);
294
295         if (!block.isGiven()) return directory;
296         
297         try {
298             recv.getRuntime().getCurrentContext().yield(directory, block);
299         } finally {
300             directory.close();
301         }
302             
303         return recv.getRuntime().getNil();
304     }
305
306 // ----- Ruby Instance Methods -------------------------------------------------
307

308     /**
309      * Closes the directory stream.
310      */

311     public IRubyObject close() {
312         // Make sure any read()s after close fail.
313
isOpen = false;
314
315         return getRuntime().getNil();
316     }
317
318     /**
319      * Executes the block once for each entry in the directory.
320      */

321     public IRubyObject each(Block block) {
322         String JavaDoc[] contents = snapshot;
323         ThreadContext context = getRuntime().getCurrentContext();
324         for (int i=0; i<contents.length; i++) {
325             context.yield(getRuntime().newString(contents[i]), block);
326         }
327         return this;
328     }
329
330     /**
331      * Returns the current position in the directory.
332      */

333     public RubyInteger tell() {
334         return getRuntime().newFixnum(pos);
335     }
336
337     /**
338      * Moves to a position <code>d</code>. <code>pos</code> must be a value
339      * returned by <code>tell</code> or 0.
340      */

341     public IRubyObject seek(IRubyObject newPos) {
342         setPos(newPos);
343         return this;
344     }
345     
346     public IRubyObject setPos(IRubyObject newPos) {
347         this.pos = RubyNumeric.fix2int(newPos);
348         return newPos;
349     }
350
351     public IRubyObject path() {
352         if (!isOpen) {
353             throw getRuntime().newIOError("closed directory");
354         }
355         
356         return path;
357     }
358
359     /** Returns the next entry from this directory. */
360     public IRubyObject read() {
361     if (!isOpen) {
362         throw getRuntime().newIOError("Directory already closed");
363     }
364
365         if (pos >= snapshot.length) {
366             return getRuntime().getNil();
367         }
368         RubyString result = getRuntime().newString(snapshot[pos]);
369         pos++;
370         return result;
371     }
372
373     /** Moves position in this directory to the first entry. */
374     public IRubyObject rewind() {
375         pos = 0;
376         return getRuntime().newFixnum(pos);
377     }
378
379 // ----- Helper Methods --------------------------------------------------------
380

381     /** Returns a Java <code>File</code> object for the specified path. If
382      * <code>path</code> is not a directory, throws <code>IOError</code>.
383      *
384      * @param path path for which to return the <code>File</code> object.
385      * @param mustExist is true the directory must exist. If false it must not.
386      * @throws IOError if <code>path</code> is not a directory.
387      */

388     protected static JRubyFile getDir(final Ruby runtime, final String JavaDoc path, final boolean mustExist) {
389         JRubyFile result = JRubyFile.create(runtime.getCurrentDirectory(),path);
390         boolean isDirectory = result.isDirectory();
391         
392         if (mustExist && !isDirectory) {
393             throw runtime.newErrnoENOENTError(path + " is not a directory");
394         } else if (!mustExist && isDirectory) {
395             throw runtime.newErrnoEEXISTError("File exists - " + path);
396         }
397
398         return result;
399     }
400
401     /**
402      * Returns the contents of the specified <code>directory</code> as an
403      * <code>ArrayList</code> containing the names of the files as Java Strings.
404      */

405     protected static List JavaDoc getContents(File JavaDoc directory) {
406         String JavaDoc[] contents = directory.list();
407         List JavaDoc result = new ArrayList JavaDoc();
408
409         // If an IO exception occurs (something odd, but possible)
410
// A directory may return null.
411
if (contents != null) {
412             for (int i=0; i<contents.length; i++) {
413                 result.add(contents[i]);
414             }
415         }
416         return result;
417     }
418
419     /**
420      * Returns the contents of the specified <code>directory</code> as an
421      * <code>ArrayList</code> containing the names of the files as Ruby Strings.
422      */

423     protected static List JavaDoc getContents(File JavaDoc directory, Ruby runtime) {
424         List JavaDoc result = new ArrayList JavaDoc();
425         String JavaDoc[] contents = directory.list();
426         
427         for (int i = 0; i < contents.length; i++) {
428             result.add(runtime.newString(contents[i]));
429         }
430         return result;
431     }
432     
433     /*
434      * Poor mans find home directory. I am not sure how windows ruby behaves with '~foo', but
435      * this mostly will work on any unix/linux/cygwin system. When someone wants to extend this
436      * to include the windows way, we should consider moving this to an external ruby file.
437      */

438     public static IRubyObject getHomeDirectoryPath(IRubyObject recv, String JavaDoc user) {
439         // TODO: Having a return where I set user inside readlines created a JumpException. It seems that
440
// evalScript should catch that and return?
441
return recv.getRuntime().evalScript("File.open('/etc/passwd') do |f| f.readlines.each do" +
442                 "|l| f = l.split(':'); return f[5] if f[0] == '" + user + "'; end; end; nil");
443     }
444     
445     public static RubyString getHomeDirectoryPath(IRubyObject recv) {
446         RubyHash hash = (RubyHash) recv.getRuntime().getObject().getConstant("ENV_JAVA");
447         IRubyObject home = hash.aref(recv.getRuntime().newString("user.home"));
448         
449         if (home == null || home.isNil()) {
450             home = hash.aref(recv.getRuntime().newString("LOGDIR"));
451         }
452         
453         if (home == null || home.isNil()) {
454             throw recv.getRuntime().newArgumentError("user.home/LOGDIR not set");
455         }
456         
457         return (RubyString) home;
458     }
459 }
460
Popular Tags