Package org.jnode.command.common

Source Code of org.jnode.command.common.UnixTestCommand$Operator

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.command.common;

import java.io.File;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Stack;
import org.jnode.shell.AbstractCommand;
import org.jnode.shell.CommandLine;
import org.jnode.shell.syntax.CommandSyntaxException;


/**
* JNode implementation of the UNIX 'test' command
*
* @author crawley@jnode.org
*/
public class UnixTestCommand extends AbstractCommand {
    // FIXME convert to use the new commandline syntax mechanism so that
    // we get command completion.
    private static class Operator {
        public final int opNo;
        public final int priority;
        public final int kind;
        public Operator(final int opNo, final int priority, int kind) {
            this.opNo = opNo;
            this.priority = priority;
            this.kind = kind;
        }
    }
   
    private boolean bracketted;
    private int pos;
    private String[] args;
    private int lastArg;
    private Stack<Object> operandStack = new Stack<Object>();
    private Stack<Operator> operatorStack = new Stack<Operator>();

    private static final int OP_DIRECTORY = 1;
    private static final int OP_EXISTS = 2;
    private static final int OP_FILE = 3;
    private static final int OP_STRING_LENGTH = 4;
    private static final int OP_STRING_NONEMPTY = 5;
    private static final int OP_STRING_EMPTY = 6;
    private static final int OP_READABLE = 7;
    private static final int OP_WRITEABLE = 8;
    private static final int OP_NONEMPTY = 9;
    private static final int OP_EQ = 10;
    private static final int OP_NE = 11;
    private static final int OP_GT = 12;
    private static final int OP_GE = 13;
    private static final int OP_LT = 14;
    private static final int OP_LE = 15;
    private static final int OP_AND = 16;
    private static final int OP_OR = 17;
    private static final int OP_NOT = 18;
    private static final int OP_STRING_EQUALS = 19;
    private static final int OP_STRING_NONEQUAL = 20;
    private static final int OP_LPAREN = 21;
    private static final int OP_RPAREN = 22;
    private static final int OP_OLDER = 23;
    private static final int OP_NEWER = 24;

    private static final int OP_UNARY = 1;
    private static final int OP_BINARY = 2;
    private static final int OP_SPECIAL = 3;

    private static final HashMap<String, Operator> OPERATOR_MAP;

    static {
        OPERATOR_MAP = new HashMap<String, Operator>();
        OPERATOR_MAP.put("-d", new Operator(OP_DIRECTORY, 3, OP_UNARY));
        OPERATOR_MAP.put("-e", new Operator(OP_EXISTS, 3, OP_UNARY));
        OPERATOR_MAP.put("-f", new Operator(OP_FILE, 3, OP_UNARY));
        OPERATOR_MAP.put("-l", new Operator(OP_STRING_LENGTH, 3, OP_UNARY));
        OPERATOR_MAP.put("-n", new Operator(OP_STRING_NONEMPTY, 3, OP_UNARY));
        OPERATOR_MAP.put("-z", new Operator(OP_STRING_EMPTY, 3, OP_UNARY));
        OPERATOR_MAP.put("-r", new Operator(OP_READABLE, 3, OP_UNARY));
        OPERATOR_MAP.put("-w", new Operator(OP_WRITEABLE, 3, OP_UNARY));
        OPERATOR_MAP.put("-eq", new Operator(OP_EQ, 3, OP_BINARY));
        OPERATOR_MAP.put("-ne", new Operator(OP_NE, 3, OP_BINARY));
        OPERATOR_MAP.put("-lt", new Operator(OP_LT, 3, OP_BINARY));
        OPERATOR_MAP.put("-le", new Operator(OP_LE, 3, OP_BINARY));
        OPERATOR_MAP.put("-gt", new Operator(OP_GT, 3, OP_BINARY));
        OPERATOR_MAP.put("-ge", new Operator(OP_GE, 3, OP_BINARY));
        OPERATOR_MAP.put("-ot", new Operator(OP_OLDER, 3, OP_BINARY));
        OPERATOR_MAP.put("-nt", new Operator(OP_NEWER, 3, OP_BINARY));
        OPERATOR_MAP.put("-a", new Operator(OP_AND, 1, OP_BINARY));
        OPERATOR_MAP.put("-o", new Operator(OP_OR, 0, OP_BINARY));
        OPERATOR_MAP.put("!", new Operator(OP_NOT, 2, OP_UNARY));
        OPERATOR_MAP.put("=", new Operator(OP_STRING_EQUALS, 5, OP_BINARY));
        OPERATOR_MAP.put("!=", new Operator(OP_STRING_NONEQUAL, 5, OP_BINARY));
        OPERATOR_MAP.put("(", new Operator(OP_LPAREN, -1, OP_SPECIAL));
        OPERATOR_MAP.put(")", new Operator(OP_RPAREN, 6, OP_SPECIAL));
    }

    public void execute()
        throws Exception {
        boolean res = false;
        CommandLine commandLine = getCommandLine();
        String commandName = commandLine.getCommandName();
        bracketted = (commandName != null && commandName.equals("["));
        args = commandLine.getArguments();
        try {
            if (bracketted && args.length == 0) {
                throw new CommandSyntaxException("missing ']'");
            } else if (bracketted && !args[args.length - 1].equals("]")) {
                processAsOptions(args);
                res = true;
            } else {
                lastArg = bracketted ? args.length - 2 : args.length - 1;
                if (lastArg == -1) {
                    res = false;
                } else {
                    Object obj = evaluate();
                    if (pos <= lastArg) {
                        if (args[pos].equals(")")) {
                            throw new CommandSyntaxException("unmatched ')'");
                        } else {
                            throw new AssertionError("I'm confused!  pos = " + pos +
                                    ", lastArg = " + lastArg + ", next arg is " + args[pos]);
                        }
                    }
                    if (obj instanceof Boolean) {
                        res = obj == Boolean.TRUE;
                    } else if (obj instanceof Long) {
                        res = (Long) obj != 0;
                    } else {
                        res = obj.toString().length() > 0;
                    }
                }
            }
            if (!res) {
                exit(1);
            }
        } catch (CommandSyntaxException ex) {
            getError().getPrintWriter().println(ex.getMessage());
            exit(2);
        }
    }

    private Object evaluate() throws CommandSyntaxException {
        evaluateExpression(false);
        while (!operatorStack.isEmpty()) {
            reduce();
        }
        if (operandStack.size() != 1) {
            throw new AssertionError("wrong nos operands left");
        }
        return operandStack.pop();
    }

    private void evaluateExpression(boolean nested) throws CommandSyntaxException {
        evaluatePrimary(nested);
        while (pos <= lastArg) {
            String tok = next();
            Operator op = OPERATOR_MAP.get(tok);
            if (op == null) {
                throw new CommandSyntaxException("expected an operator");
            }
            switch(op.kind) {
                case OP_UNARY:
                    throw new CommandSyntaxException("misplaced unary operator");
                case OP_BINARY:
                    evaluatePrimary(nested);
                    reduceAndPush(op, popOperand());
                    break;
                case OP_SPECIAL:
                    if (op.opNo == OP_LPAREN) {
                        throw new CommandSyntaxException("misplaced '('");
                    } else if (op.opNo == OP_RPAREN) {
                        if (!nested) {
                            throw new CommandSyntaxException("misplaced ')'");
                        }
                        back();
                        return;
                    }
            }
        }
    }

    private void evaluatePrimary(boolean nested) throws CommandSyntaxException {
        String tok = next();
        Operator op = OPERATOR_MAP.get(tok);
        if (op == null) {
            pushOperand(tok);
        } else {
            switch (op.kind) {
                case OP_UNARY:
                    operatorStack.push(op);
                    operandStack.push(next());
                    break;
                case OP_BINARY:
                    throw new CommandSyntaxException("misplaced binary operator");
                case OP_SPECIAL:
                    if (op.opNo == OP_LPAREN) {
                        operatorStack.push(op); // ... as a marker.
                        evaluateExpression(true);
                        if (!next().equals(")")) {
                            throw new CommandSyntaxException("missing ')'");
                        }
                        while (operatorStack.peek() != op) {
                            reduce();
                        }
                        if (operatorStack.pop() != op) {
                            throw new AssertionError("cannot find my marker!");
                        }
                    } else {
                        throw new CommandSyntaxException("unmatched ')'");
                    }
            }
        }
    }

    private void reduceAndPush(Operator operator, Object operand) throws CommandSyntaxException {
        while (!operatorStack.isEmpty() && operator.priority <= operatorStack.peek().priority) {
            reduce();
        }
        operatorStack.push(operator);
        operandStack.push(operand);
    }

    private void reduce() throws CommandSyntaxException {
        Operator operator = operatorStack.pop();
        Object operand = null, operand2 = null;
        if (operator.kind == OP_UNARY) {
            operand = popOperand();
        } else if (operator.kind == OP_BINARY) {
            operand2 = popOperand();
            operand = popOperand();
        }
        switch (operator.opNo) {
            case OP_EXISTS:
                pushOperand(toFile(operand).exists());
                break;
            case OP_DIRECTORY:
                pushOperand(toFile(operand).isDirectory());
                break;
            case OP_FILE:
                pushOperand(toFile(operand).isFile());
                break;
            case OP_NONEMPTY:
                pushOperand(toFile(operand).length() > 0);
                break;
            case OP_READABLE:
                pushOperand(toFile(operand).canRead());
                break;
            case OP_WRITEABLE:
                pushOperand(toFile(popOperand()).canWrite());
                break;
            case OP_OLDER:
                pushOperand(toFile(operand).lastModified() < toFile(operand2).lastModified());
                break;
            case OP_NEWER:
                pushOperand(toFile(operand).lastModified() > toFile(operand2).lastModified());
                break;
            case OP_STRING_EMPTY:
                pushOperand(toString().length() == 0);
                break;
            case OP_STRING_NONEMPTY:
                pushOperand(toString(operand).length() > 0);
                break;
            case OP_STRING_LENGTH:
                pushOperand(toString(operand).length());
                break;
            case OP_EQ:
                pushOperand(toNumber(operand) == toNumber(operand2));
                break;
            case OP_NE:
                pushOperand(toNumber(operand) != toNumber(operand2));
                break;
            case OP_LT:
                pushOperand(toNumber(operand) < toNumber(operand2));
                break;
            case OP_LE:
                pushOperand(toNumber(operand) <= toNumber(operand2));
                break;
            case OP_GT:
                pushOperand(toNumber(operand) > toNumber(operand2));
                break;
            case OP_GE:
                pushOperand(toNumber(operand) >= toNumber(operand2));
                break;
            case OP_AND:
                pushOperand(toBoolean(operand) & toBoolean(operand2));
                break;
            case OP_OR:
                pushOperand(toBoolean(operand) | toBoolean(operand2));
                break;
            case OP_NOT:
                pushOperand(!toBoolean(operand));
                break;
            case OP_STRING_EQUALS:
                pushOperand(toString(operand).equals(toString(operand2)));
                break;
            case OP_STRING_NONEQUAL:
                pushOperand(!toString(operand).equals(toString(operand2)));
                break;
            default:
                throw new AssertionError("bad operator");
        }
    }

    private void processAsOptions(String[] args) throws CommandSyntaxException {
        PrintWriter err = getError().getPrintWriter();
        for (String option : args) {
            if (option.equals("--help")) {
                err.println("Don't panic!");
            } else if (option.equals("--version")) {
                err.println("JNode test 0.0");
            } else {
                throw new CommandSyntaxException("unknown option '" + option + '\'');
            }
        }
    }

    private void pushOperand(Object value) {
        operandStack.push(value);
    }

    private void pushOperand(boolean value) {
        operandStack.push(value ? Boolean.TRUE : Boolean.FALSE);
    }

    private void pushOperand(long value) {
        operandStack.push(value);
    }

    private Object popOperand() throws CommandSyntaxException {
        if (operandStack.isEmpty()) {
            throw new CommandSyntaxException("missing LHS for operator");
        }
        return operandStack.pop();
    }

    private long toNumber(Object obj) throws CommandSyntaxException {
        if (obj instanceof Long) {
            return (Long) obj;
        } else if (obj instanceof String) {
            try {
                return Long.parseLong((String) obj);
            } catch (NumberFormatException ex) {
                throw new CommandSyntaxException(
                        "operand is not a number: '" + obj.toString() + '\'');
            }
        } else {
            throw new CommandSyntaxException("subexpression is not an INTEGER");
        }
    }

    private boolean toBoolean(Object obj) throws CommandSyntaxException {
        if (obj instanceof Boolean) {
            return obj == Boolean.TRUE;
        } else if (obj instanceof String) {
            return ((String) obj).length() > 0;
        } else {
            throw new CommandSyntaxException("operand is an INTEGER");
        }
    }

    private String toString(Object obj) throws CommandSyntaxException {
        if (obj instanceof String) {
            return (String) obj;
        } else {
            throw new CommandSyntaxException("operand is not a STRING");
        }
    }

    private File toFile(Object obj) throws CommandSyntaxException {
        if (obj instanceof String) {
            return new File((String) obj);
        } else {
            throw new CommandSyntaxException("operand is not a FILENAME");
        }
    }

    private String next() {
        if (pos > lastArg) {
            return null;
        }
        return args[pos++];
    }

    private void back() throws CommandSyntaxException {
        pos--;
    }

    public static void main(String[] args) throws Exception {
        // If you invoke it this way you cannot distinguish 'test' from '['
        new UnixTestCommand().execute(args);
    }
}
TOP

Related Classes of org.jnode.command.common.UnixTestCommand$Operator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.