/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.functions.postscript; import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; /** * Operator factory takes a operand char offset and quickly returns a Operator * object which contains the respective operator evaluation logic. The calling * method can defer the execution of operator as needed. * * @author ICEsoft Technologies Inc. * @since 4.2 */ public class OperatorFactory { private static ConcurrentHashMap<Integer, Operator> operatorCache = new ConcurrentHashMap<Integer, Operator>(); @SuppressWarnings(value = "unchecked") public static Operator getOperator(char ch[], int offset, int length) { // get the operator int value. final int operatorType = OperatorNames.getType(ch, offset, length); // check operator cache Operator operator = operatorCache.get(operatorType); if (operator != null) { return operator; } // build the operation, consider added a few if range checks to limit // the number of compares. switch (operatorType) { /** * num1 abs = num2 * 4.5 abs -> 4.5 * 3 abs -> 3 * 0 abs -> 0 */ case OperatorNames.OP_ABS: operator = new Operator(OperatorNames.OP_ABS) { public void eval(Stack stack) { Float num = (Float) stack.pop(); stack.push(Math.abs(num)); } }; break; /** * num1 num2 add = sum * 3 4 add -> 7 * 9.9 1.1 add -> 11.0 */ case OperatorNames.OP_ADD: operator = new Operator(OperatorNames.OP_ADD) { public void eval(Stack stack) { Float num2 = (Float) stack.pop(); Float num1 = (Float) stack.pop(); stack.push(num1 + num2); } }; break; /** * bool1 bool2 and = bool3 * int1 int2 and = int3 * true true and -> true % A complete truth table * true false and -> false * false true and -> false * false false and -> false * 99 1 and -> 1 * 52 7 and -> 4 */ case OperatorNames.OP_AND: operator = new Operator(OperatorNames.OP_AND) { public void eval(Stack stack) { Object value = stack.pop(); if (value instanceof Boolean) { boolean bool2 = (Boolean) value; boolean bool1 = (Boolean) stack.pop(); stack.push(bool1 && bool2); } else { int val1 = ((Float) value).intValue(); int val2 = ((Float) stack.pop()).intValue(); stack.push(val1 & val2); } } }; break; /*** * num den atan = angle * 0 1 atan -> 0.0 * 1 0 atan -> 90.0 * -100 0 atan -> 270.0 * 4 4 atan -> 45.0 */ case OperatorNames.OP_ATAN: operator = new Operator(OperatorNames.OP_ATAN) { public void eval(Stack stack) { float den = (Float) stack.pop(); float num = (Float) stack.pop(); stack.push(((Number) Math.toDegrees(Math.atan(num / den))).floatValue()); } }; break; /*** * int1 shift bitshift int2 * 07 3 bitshift -> 56 * 142 3 bitshift -> 17 */ case OperatorNames.OP_BITSHIFT: operator = new Operator(OperatorNames.OP_BITSHIFT) { public void eval(Stack stack) { long shift = (Long) stack.pop(); long int1 = (Long) stack.pop(); stack.push(int1 << shift); } }; break; /** * num1 ceiling = num2 * 3.2 ceiling -> 4.0 * 4.8 ceiling -> 4.0 * 99 ceiling -> 99 */ case OperatorNames.OP_CEILING: operator = new Operator(OperatorNames.OP_CEILING) { public void eval(Stack stack) { float num1 = (Float) stack.pop(); stack.push(((Number) Math.ceil(num1)).floatValue()); } }; break; /** * aAngle cos = real * 0 cos -> 1.0 * 90 cos -> 0.0 */ case OperatorNames.OP_COS: operator = new Operator(OperatorNames.OP_COS) { public void eval(Stack stack) { float aAngle = (Float) stack.pop(); stack.push(((Number) Math.cos(aAngle)).floatValue()); } }; break; /** * any1 ... anyn n copy any1 ... anyn any1 ... anyn * * array1 array2 copy subarray2 * dict1 dict2 copy dict2 * string1 string2 copy substring2 * packedarray1 array2 copy subarray2 * gstate1 gstate2 copy gstate2 * * (a) (b) (c) 2 copy -> (a) (b) (c) (b) (c) * (a) (b) (c) 0 copy -> (a) (b) (c) */ case OperatorNames.OP_COPY: operator = new Operator(OperatorNames.OP_COPY) { public void eval(Stack stack) { int n = ((Float) stack.pop()).intValue(); int top = stack.size(); for (int i = top - n; i < top; i++) { stack.push(stack.get(i)); } } }; break; /** * num cvi = int * string cvi = int * * (3.3E1) cvi -> 33 * 47.8 cvi -> 47 * 520.9 cvi -> 520 */ case OperatorNames.OP_CVI: operator = new Operator(OperatorNames.OP_CVI) { public void eval(Stack stack) { // doesn't really convert to int but not a bit deal for // java in general. int number = ((Float) stack.pop()).intValue(); stack.push(number); } }; break; /** * num cvr real * string cvr real */ case OperatorNames.OP_CVR: operator = new Operator(OperatorNames.OP_CVR) { public void eval(Stack stack) { // doesn't really convert to int but not a bit deal for // java in general. float number = (Float) stack.pop(); stack.push(number); } }; break; /** * num1 num2 div quotient * 3 2 div -> 1.5 * 4 2 div -> 2.0 */ case OperatorNames.OP_DIV: operator = new Operator(OperatorNames.OP_DIV) { public void eval(Stack stack) { // doesn't really convert to int but not a bit deal for // java in general. float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 / num2); } }; break; /** * any dup = any any * duplicates the top element on the operand stack */ case OperatorNames.OP_DUP: operator = new Operator(OperatorNames.OP_DUP) { public void eval(Stack stack) { // peek and push should give us the duplication. stack.push(stack.peek()); } }; break; /** * any1 any2 eq bool * pops two objects from the operand stack and pushes true if they * are equal, or false if not. */ case OperatorNames.OP_EQ: operator = new Operator(OperatorNames.OP_EQ) { public void eval(Stack stack) { Object any2 = stack.pop(); Object any1 = stack.pop(); stack.push(any1.equals(any2)); } }; break; /** * any1 any2 exch any2 any1 * exchanges the top two elements on the operand stack. * 1 2 exch -> 2 1 */ case OperatorNames.OP_EXCH: operator = new Operator(OperatorNames.OP_EXCH) { public void eval(Stack stack) { Object any2 = stack.pop(); Object any1 = stack.pop(); stack.push(any2); stack.push(any1); } }; break; /** * base exponent exp = real * 9 0.5 exp -> 3.0 * -9 -1 exp -> -0.111111 */ case OperatorNames.OP_EXP: operator = new Operator(OperatorNames.OP_EXP) { public void eval(Stack stack) { float exponent = (Float) stack.pop(); float base = (Float) stack.pop(); stack.push(((Number) Math.pow(base, exponent)).floatValue()); } }; break; /** * num1 floor num2 * 3.2 floor -> 3.0 * -4.8 floor -> -5.0 * 99 floor -> 99 */ case OperatorNames.OP_FLOOR: operator = new Operator(OperatorNames.OP_FLOOR) { public void eval(Stack stack) { float num1 = (Float) stack.pop(); stack.push(((Number) Math.floor(num1)).floatValue()); } }; break; /** * num1 num2 ge bool * string1 string2 ge bool * * pops two objects from the operand stack and pushes true if the * first operand iS greater than or equal to the second, or false * otherwise. * 4.2 4 ge -> true * (abc) (d) ge -> false * (aba) (ab) ge -> true * (aba) (aba) ge -> true */ case OperatorNames.OP_GE: operator = new Operator(OperatorNames.OP_GE) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 >= num2); } }; break; /** * num1 num2 gt bool * string1 string2 gt bool (not implemented) */ case OperatorNames.OP_GT: operator = new Operator(OperatorNames.OP_GT) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 > num2); } }; break; /** * int1 int2 idiv quotient * * divides int1 by int2 and returns the integer part of the quotient, * with any fractional part discarded. Both operands of idiv must * be integers and the result is an integer. * Examples * 3 2 idiv -> 1 * 4 2 idiv -> 2 * -5 2 idiv -> -2 */ case OperatorNames.OP_IDIV: operator = new Operator(OperatorNames.OP_IDIV) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push((int) (num1 / num2)); } }; break; /** * bool expression if * removes both operands from the stack, then executes proc if bool is true. * * 3 4 lt {(3 is less than 4)} if -> (3 is less than 4) */ case OperatorNames.OP_IF: operator = new Operator(OperatorNames.OP_IF) { public void eval(Stack stack) { // pop off the express so we can get at the bool // if we don't have an Expression we can't continue. Procedure proc1 = null; if (stack.peek() instanceof Procedure) { proc1 = (Procedure) stack.pop(); } boolean bool = (Boolean) stack.pop(); // process expression 'if' expression is true if (bool) { proc1.eval(stack); } } }; break; /** * bool proc1 proc2 ifelse - * removes all three operands from the stack, then executes proc1 * if bool is true or proc2 if bool is false. * * 3 4 lt {(3 is less than 4)} if -> (3 is less than 4) */ case OperatorNames.OP_IFELSE: operator = new Operator(OperatorNames.OP_IFELSE) { public void eval(Stack stack) { // if we don't have an Expression we can't continue. Procedure proc2 = null, proc1 = null; if (stack.peek() instanceof Procedure) { proc2 = (Procedure) stack.pop(); } if (stack.peek() instanceof Procedure) { proc1 = (Procedure) stack.pop(); } boolean bool = (Boolean) stack.pop(); // process ifelse clause if (bool) { proc1.eval(stack); } else { proc2.eval(stack); } } }; break; /** * anyn ... any0 n index anyn ... any0 anyn * * removes the nonnegative integer n from the operand stack, counts * down to the nth element from the top of the stack, and pushes a * copy of that element on the stack. * (a) (b) (c) (d) 0 index -> (a) (b) (c) (d) (d) * (a) (b) (c) (d) 3 index -> (a) (b) (c) (d) (a) */ case OperatorNames.OP_INDEX: operator = new Operator(OperatorNames.OP_INDEX) { public void eval(Stack stack) { float n = (Float) stack.pop(); stack.push(stack.get((int) ((stack.size() - 1) - n))); } }; break; /** * num1 num2 le bool * string1 string2 le bool * * pops two objects from the operand stack and pushes true if the * first operand is less than or equal to the second, or false * otherwise. */ case OperatorNames.OP_LE: operator = new Operator(OperatorNames.OP_LE) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 <= num2); } }; break; /** * num ln real * returns the natural logarithm (base e) of num. */ case OperatorNames.OP_LN: operator = new Operator(OperatorNames.OP_LN) { public void eval(Stack stack) { float num = (Float) stack.pop(); stack.push(((Number) Math.log(num)).floatValue()); } }; break; /** * num log real * returns the common logarithm (base 10) of num. */ case OperatorNames.OP_LOG: operator = new Operator(OperatorNames.OP_LOG) { public void eval(Stack stack) { float num = (Float) stack.pop(); stack.push(((Number) Math.log10(num)).floatValue()); } }; break; /** * num1 num2 lt bool */ case OperatorNames.OP_LT: operator = new Operator(OperatorNames.OP_LT) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 < num2); } }; break; /** * int1 int2 mod remainder * returns the remainder that results from dividing int1 by int2. */ case OperatorNames.OP_MOD: operator = new Operator(OperatorNames.OP_MOD) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 % num2); } }; break; /** * num1 num2 mul product * returns the product of num1 and num2. */ case OperatorNames.OP_MUL: operator = new Operator(OperatorNames.OP_MUL) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 * num2); } }; break; /** * any1 any2 ne bool * pops two objects from the operand stack and pushes false if they * are equal, or true if not. */ case OperatorNames.OP_NE: operator = new Operator(OperatorNames.OP_NE) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 != num2); } }; break; /** * num1 neg num2 * returns the negative of num1. */ case OperatorNames.OP_NEG: operator = new Operator(OperatorNames.OP_NEG) { public void eval(Stack stack) { float num1 = (Float) stack.pop(); stack.push(-num1); } }; break; /** * bool1 not bool2 * returns the logical negation of the operand if it is boolean */ case OperatorNames.OP_NOT: operator = new Operator(OperatorNames.OP_NOT) { public void eval(Stack stack) { boolean num1 = (Boolean) stack.pop(); stack.push(!num1); } }; break; /** * bool1 bool2 or bool3 * returns the logical disjunction of the operands if they are boolean. */ case OperatorNames.OP_OR: operator = new Operator(OperatorNames.OP_OR) { public void eval(Stack stack) { boolean bool2 = (Boolean) stack.pop(); boolean bool1 = (Boolean) stack.pop(); stack.push(bool1 || bool2); } }; break; /** * any pop * removes the top element from the operand stack and discards it. */ case OperatorNames.OP_POP: operator = new Operator(OperatorNames.OP_POP) { public void eval(Stack stack) { stack.pop(); } }; break; /** * anyn-1 ... any0 n j roll any (j-1) mod n ... any0 anyn-1 ... anyj mod n * * performs a circular shift of the objects anyn-1 through any0 on * the operand stack by the amount j. Positive j indicates upward * motion on the stack, whereas negative j indicates downward motion. * n must be a nonnegative integer and j must be an integer. roll * first removes these operands from the stack; there must be at * least n additional elements. It then performs a circular shift * of these n elements by j positions. If j is positive, each shift * consists of removing an element from the top of the stack and * inserting it between element n - 1 and element n of the stack, * moving all in tervening elements one level higher on the stack. * If j is negative, each shift consists of removing element n - 1 * of the stack and pushing it on the top of the stack, moving all * intervening elements one level lower on the stack. * * (a) (b) (c) 3 -1 roll -> (b) (c) (a) * (a) (b) (c) 3 1 roll -> (c) (a) (b) * (a) (b) (c) 3 0 roll -> (a) (b) (c) */ case OperatorNames.OP_ROLL: operator = new Operator(OperatorNames.OP_ROLL) { public void eval(Stack stack) { float j = (Float) stack.pop(); float n = (Float) stack.pop(); // each sift consists of removing an element from the top of the // stack and inserting it between element n-1 and element n of the stack if (j > 0) { for (int i = 0; i < j; i++) { stack.insertElementAt(stack.lastElement(), (int) (stack.size() - (n))); // finish the move by poping the top; stack.pop(); } } // each shift consists of removing an element n-1 off the stack // and pushing it on top of the stack else if (j < 0) { for (int i = 0, max = (int) -j; i < max; i++) { stack.push(stack.remove((int) (stack.size() - (n)))); } } } }; break; /** * num1 round num2 * returns the integer value nearest to num1 */ case OperatorNames.OP_ROUND: operator = new Operator(OperatorNames.OP_ROUND) { public void eval(Stack stack) { float num1 = (Float) stack.pop(); stack.push(((Number) Math.round(num1)).floatValue()); } }; break; /** * angle sin real * returns the sine of angle, which is interpreted as an angle in degrees. */ case OperatorNames.OP_SIN: operator = new Operator(OperatorNames.OP_SIN) { public void eval(Stack stack) { float aAngle = (Float) stack.pop(); stack.push(((Number) Math.sin(aAngle)).floatValue()); } }; break; /** * num sqrt real * returns the sine of angle, which is interpreted as an angle in degrees. */ case OperatorNames.OP_SQRT: operator = new Operator(OperatorNames.OP_SQRT) { public void eval(Stack stack) { float num = (Float) stack.pop(); stack.push(((Number) Math.sqrt(num)).floatValue()); } }; break; /** * num1 num2 sub difference * returns the result of subtracting num2 from num1. */ case OperatorNames.OP_SUB: operator = new Operator(OperatorNames.OP_SUB) { public void eval(Stack stack) { float num2 = (Float) stack.pop(); float num1 = (Float) stack.pop(); stack.push(num1 - num2); } }; break; /** * num1 truncate num2 * truncates num1 toward 0 by removing its fractional part. */ case OperatorNames.OP_TRUNCATE: operator = new Operator(OperatorNames.OP_TRUNCATE) { public void eval(Stack stack) { float num1 = (Float) stack.pop(); stack.push(((Number) Math.floor(num1)).floatValue()); } }; break; /** * bool1 bool2 xor bool3 * int1 int2 xor int3 * returns the logical "exclusive or" of the operands if they are * boolean. If the operands are integers, xor returns the bitwise * "exclusive or" of their binary representations. */ case OperatorNames.OP_XOR: operator = new Operator(OperatorNames.OP_XOR) { public void eval(Stack stack) { Object obj2 = stack.pop(); if (obj2 instanceof Number) { float num2 = (Float) obj2; float num1 = (Float) stack.pop(); stack.push((int) num1 ^ (int) num2); } else if (obj2 instanceof Boolean) { boolean bool2 = (Boolean) obj2; boolean bool1 = (Boolean) stack.pop(); stack.push(bool1 ^ bool2); } } }; break; case OperatorNames.OP_EXP_START: operator = new Expression(OperatorNames.OP_EXP_START); break; case OperatorNames.OP_EXP_END: operator = new Expression(OperatorNames.OP_EXP_END); break; default: operator = new Operator(OperatorNames.NO_OP) { public void eval(Stack stack) { // throw something? System.out.println(operatorType + " not implemented "); } }; break; } // add the new operator to the cache if (operator != null) { operatorCache.put(operator.getType(), operator); } return operator; } }