[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/MSNexploder/jruby/parallel_boot/src/org/jruby/RubyDir.java [Back]  [Original]

/***** BEGIN LICENSE BLOCK *****
 * Version: EPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Eclipse Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.eclipse.org/legal/epl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2002-2004 Anders Bengtsson 
 * Copyright (C) 2002-2004 Jan Arne Petersen 
 * Copyright (C) 2004 Thomas E Enebo 
 * Copyright (C) 2004-2005 Charles O Nutter 
 * Copyright (C) 2004 Stefan Matthias Aust 
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the EPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the EPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
package org.jruby;

import static org.jruby.RubyEnumerator.enumeratorize;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
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.exceptions.RaiseException;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.Dir;
import org.jruby.util.JRubyFile;
import org.jruby.util.ByteList;
import static org.jruby.CompatVersion.*;

/**
 * .The Ruby built-in class Dir.
 *
 * @author  jvoegele
 */
@JRubyClass(name = "Dir", include = "Enumerable")
public class RubyDir extends RubyObject {
    private RubyString path;       // What we passed to the constructor for method 'path'
    protected JRubyFile 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;

    public RubyDir(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }

    private static final ObjectAllocator DIR_ALLOCATOR = new ObjectAllocator() {
        public IRubyObject allocate(Ruby runtime, RubyClass klass) {
            return new RubyDir(runtime, klass);
        }
    };

    public static RubyClass createDirClass(Ruby runtime) {
        RubyClass dirClass = runtime.defineClass("Dir", runtime.getObject(), DIR_ALLOCATOR);
        runtime.setDir(dirClass);

        dirClass.index = ClassIndex.DIR;
        dirClass.setReifiedClass(RubyDir.class);

        dirClass.includeModule(runtime.getEnumerable());
        dirClass.defineAnnotatedMethods(RubyDir.class);

        return dirClass;
    }

    private final void checkDir() {
        testFrozen("Dir");
        update();

        if (!isOpen) throw getRuntime().newIOError("closed directory");
    }

    private void update() {
        if (snapshot == null || dir.exists() && dir.lastModified() > lastModified) {
            lastModified = dir.lastModified();
            List snapshotList = new ArrayList();
            snapshotList.add(".");
            snapshotList.add("..");
            snapshotList.addAll(getContents(dir));
            snapshot = (String[]) snapshotList.toArray(new String[snapshotList.size()]);
        }
    }

    /**
     * Creates a new Dir.  This method takes a snapshot of the
     * contents of the directory at creation time, so changes to the contents
     * of the directory will not be reflected during the lifetime of the
     * Dir object returned, so a new Dir instance
     * must be created to reflect changes to the underlying file system.
     */
    @JRubyMethod(compat = RUBY1_8)
    public IRubyObject initialize(IRubyObject arg) {
        RubyString newPath = arg.convertToString();
        path = newPath;
        pos = 0;

        String adjustedPath = RubyFile.adjustRootPathOnWindows(getRuntime(), newPath.toString(), null);
        checkDirIsTwoSlashesOnWindows(getRuntime(), adjustedPath);

        dir = JRubyFile.create(getRuntime().getCurrentDirectory(), adjustedPath);
        List snapshotList = RubyDir.getEntries(getRuntime(), adjustedPath);
        snapshot = (String[]) snapshotList.toArray(new String[snapshotList.size()]);

        return this;
    }

    @JRubyMethod(name = "initialize", compat = RUBY1_9)
    public IRubyObject initialize19(IRubyObject arg) {
        return initialize(RubyFile.get_path(getRuntime().getCurrentContext(), arg));
    }

// ----- Ruby Class Methods ----------------------------------------------------

    private static List dirGlobs(ThreadContext context, String cwd, IRubyObject[] args, int flags) {
        List dirs = new ArrayList();

        for (int i = 0; i < args.length; i++) {
            dirs.addAll(Dir.push_glob(cwd, globArgumentAsByteList(context, args[i]), flags));
        }

        return dirs;
    }

    private static IRubyObject asRubyStringList(Ruby runtime, List dirs) {
        List allFiles = new ArrayList();
        Encoding enc = runtime.getDefaultExternalEncoding();
        if (enc == null) {
            enc = UTF8;
        }

        for (ByteList dir : dirs) {
            allFiles.add(RubyString.newString(runtime, dir, enc));
        }

        IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
        allFiles.toArray(tempFileList);

        return runtime.newArrayNoCopy(tempFileList);
    }

    private static String getCWD(Ruby runtime) {
        try {
            return new org.jruby.util.NormalizedFile(runtime.getCurrentDirectory()).getCanonicalPath();
        } catch (Exception e) {
            return runtime.getCurrentDirectory();
        }
    }

    @JRubyMethod(name = "[]", required = 1, rest = true, meta = true)
    public static IRubyObject aref(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
        Ruby runtime = context.runtime;
        List dirs;
        if (args.length == 1) {
            Pattern pattern = Pattern.compile("file:(.*)!/(.*)");
            String glob = args[0].toString();
            Matcher matcher = pattern.matcher(glob);
            if (matcher.find()) {
                String jarFileName = matcher.group(1);
                String jarUri = "file:" + jarFileName + "!/";
                String fileGlobString = matcher.group(2);
                String filePatternString = convertGlobToRegEx(fileGlobString);
                Pattern filePattern = Pattern.compile(filePatternString);
                try {
                    JarFile jarFile = new JarFile(jarFileName);
                    List allFiles = new ArrayList();
                    Enumeration entries = jarFile.entries();
                    while (entries.hasMoreElements()) {
                        String entry = entries.nextElement().getName();
                        String chomped_entry = entry.endsWith("/") ? entry.substring(0, entry.length() - 1) : entry;
                        if (filePattern.matcher(chomped_entry).find()) {
                            allFiles.add(RubyString.newString(runtime, jarUri + chomped_entry.toString()));
                        }
                    }
                    IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
                    allFiles.toArray(tempFileList);
                    return runtime.newArrayNoCopy(tempFileList);
                } catch (IOException e) {
                    return runtime.newArrayNoCopy(new IRubyObject[0]);
                }
            }

            dirs = Dir.push_glob(getCWD(runtime), globArgumentAsByteList(context, args[0]), 0);
        } else {
            dirs = dirGlobs(context, getCWD(runtime), args, 0);
        }

        return asRubyStringList(runtime, dirs);
    }

    private static ByteList globArgumentAsByteList(ThreadContext context, IRubyObject arg) {
        if (context.runtime.is1_9()) return RubyFile.get_path(context, arg).getByteList();

        return arg.convertToString().getByteList();
    }

    private static String convertGlobToRegEx(String line) {
        line = line.trim();
        StringBuilder sb = new StringBuilder(line.length());
        sb.append("^");
        boolean escaping = false;
        int inCurlies = 0;
        for (char currentChar : line.toCharArray()) {
            switch (currentChar) {
            case '*':
                if (escaping)
                    sb.append("\\*");
                else
                    sb.append("[^/]*");
                escaping = false;
                break;
            case '?':
                if (escaping)
                    sb.append("\\?");
                else
                    sb.append('.');
                escaping = false;
                break;
            case '.':
            case '(':
            case ')':
            case '+':
            case '|':
            case '^':
            case '$':
            case '@':
            case '%':
                sb.append('\\');
                sb.append(currentChar);
                escaping = false;
                break;
            case '\\':
                if (escaping) {
                    sb.append("\\\\");
                    escaping = false;
                } else
                    escaping = true;
                break;
            case '{':
                if (escaping) {
                    sb.append("\\{");
                } else {
                    sb.append('(');
                    inCurlies++;
                }
                escaping = false;
                break;
            case '}':
                if (inCurlies > 0 && !escaping) {
                    sb.append(')');
                    inCurlies--;
                } else if (escaping)
                    sb.append("\\}");
                else
                    sb.append("}");
                escaping = false;
                break;
            case ',':
                if (inCurlies > 0 && !escaping) {
                    sb.append('|');
                } else if (escaping)
                    sb.append("\\,");
                else
                    sb.append(",");
                break;
            default:
                escaping = false;
                sb.append(currentChar);
            }
        }
        sb.append("$");
        return sb.toString().replace("[^/]*[^/]*/", ".*").replace("[^/]*[^/]*", ".*");
    }

    /**
     * Returns an array of filenames matching the specified wildcard pattern
     * pat. If a block is given, the array is iterated internally
     * with each filename is passed to the block in turn. In this case, Nil is
     * returned.
     */
    @JRubyMethod(required = 1, optional = 1, meta = true)
    public static IRubyObject glob(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
        Ruby runtime = context.runtime;
        int flags = args.length == 2 ? RubyNumeric.num2int(args[1]) : 0;

        List dirs;
        IRubyObject tmp = args[0].checkArrayType();
        if (tmp.isNil()) {
            dirs = Dir.push_glob(runtime.getCurrentDirectory(), globArgumentAsByteList(context, args[0]), flags);
        } else {
            dirs = dirGlobs(context, getCWD(runtime), ((RubyArray) tmp).toJavaArray(), flags);
        }

        if (block.isGiven()) {
            for (int i = 0; i < dirs.size(); i++) {
                Encoding enc = runtime.getDefaultExternalEncoding();
                if (enc == null) {
                    enc = UTF8;
                }
                block.yield(context, RubyString.newString(runtime, dirs.get(i), enc));
            }

            return runtime.getNil();
        }

        return asRubyStringList(runtime, dirs);
    }

    /**
     * @return all entries for this Dir
     */
    @JRubyMethod(name = "entries")
    public RubyArray entries() {
        return getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(getRuntime(), snapshot));
    }

    /**
     * Returns an array containing all of the filenames in the given directory.
     */
    @JRubyMethod(name = "entries", meta = true, compat = RUBY1_8)
    public static RubyArray entries(IRubyObject recv, IRubyObject path) {
        return entriesCommon(recv.getRuntime(), path.convertToString().getUnicodeValue());
    }

    @JRubyMethod(name = "entries", meta = true, compat = RUBY1_9)
    public static RubyArray entries19(ThreadContext context, IRubyObject recv, IRubyObject arg) {
        return entriesCommon(context.runtime, RubyFile.get_path(context, arg).asJavaString());
    }

    @JRubyMethod(name = "entries", meta = true, compat = RUBY1_9)
    public static RubyArray entries19(ThreadContext context, IRubyObject recv, IRubyObject arg, IRubyObject opts) {
        // FIXME: do something with opts
        return entriesCommon(context.runtime, RubyFile.get_path(context, arg).asJavaString());
    }

    private static RubyArray entriesCommon(Ruby runtime, String path) {
        String adjustedPath = RubyFile.adjustRootPathOnWindows(runtime, path, null);
        checkDirIsTwoSlashesOnWindows(runtime, adjustedPath);

        Object[] files = getEntries(runtime, adjustedPath).toArray();
        return runtime.newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(runtime, files));
    }

    private static List getEntries(Ruby runtime, String path) {
        if (!RubyFileTest.directory_p(runtime, RubyString.newString(runtime, path)).isTrue()) {
            throw runtime.newErrnoENOENTError("No such directory: " + path);
        }

        if (path.startsWith("jar:")) path = path.substring(4);
        if (path.startsWith("file:")) return entriesIntoAJarFile(runtime, path);

        return entriesIntoADirectory(runtime, path);
    }

    private static List entriesIntoADirectory(Ruby runtime, String path) {
        final JRubyFile directory = JRubyFile.create(runtime.getCurrentDirectory(), path);

        List fileList = getContents(directory);
        fileList.add(0, ".");
        fileList.add(1, "..");
        return fileList;
    }

    private static List entriesIntoAJarFile(Ruby runtime, String path) {
        String file = path.substring(5);
        int bang = file.indexOf('!');
        if (bang == -1) {
          return entriesIntoADirectory(runtime, path.substring(5));
        }
        if (bang == file.length() - 1) {
            file = file + "/";
        }
        String jar = file.substring(0, bang);
        String after = file.substring(bang + 2);
        if (after.length() > 0 && after.charAt(after.length() - 1) != '/') {
            after = after + "/";
        }
        JarFile jf;
        try {
            jf = new JarFile(jar);
        } catch (IOException e) {
            throw new RuntimeException("Valid JAR file expected", e);
        }

        List fileList = new ArrayList();
        Enumeration

Web Proxy Viewer  |  New URL  |  Original Page