0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package com.sun.rave.web.ui.util;
0042:
0043: import com.sun.rave.web.ui.component.util.descriptors.LayoutElement;
0044:
0045: import java.util.ArrayList;
0046: import java.util.EmptyStackException;
0047: import java.util.HashMap;
0048: import java.util.Iterator;
0049: import java.util.List;
0050: import java.util.Map;
0051: import java.util.Stack;
0052:
0053: import javax.faces.component.UIComponent;
0054:
0055: /**
0056: * This class takes a "Permission String" and is cabable of determining if
0057: * a user passes the permission check. Supported "checks" are:
0058: *
0059: * <UL><LI>Boolean -- "true" or "false"</LI>
0060: * </UL>
0061: *
0062: * The format of the "Permission String" must be an equation that results in a
0063: * boolean answer. The supported functions/operators are:
0064: *
0065: * --
0066: * <UL><LI>$<type>{<key>} -- To read a value according to
0067: * <type> using <key> (See:
0068: * {@link com.sun.rave.web.ui.util.VariableResolver}). (null is
0069: * interpretted as false, non boolean values (besides the string
0070: * "false") are interpretted to mean true) </LI>
0071: * <LI>'(' and ')' can be used to define order of operation </LI>
0072: * <LI>'!' may be used to negate a value </LI>
0073: * <LI>'|' may be used as a logical OR </LI>
0074: * <LI>'&' may be used as a logical AND </LI>
0075: * <LI>'=' may be used as a String equals </LI>
0076: * </UL>
0077: *
0078: *
0079: * Operator Precedence (for infix notation) is:
0080: * <UL><LI> () -- Highest </LI>
0081: * <LI> ! </LI>
0082: * <LI> & </LI>
0083: * <LI> | </LI>
0084: * <LI> = </LI>
0085: * </UL>
0086: *
0087: * @see VariableResolver
0088: *
0089: * @author Ken Paulsen (ken.paulsen@sun.com)
0090: */
0091: public class PermissionChecker {
0092:
0093: /**
0094: * This is the constructor method that is required to create this object.
0095: */
0096: public PermissionChecker(LayoutElement desc, UIComponent component,
0097: String infixStr) {
0098: setLayoutElement(desc);
0099: setUIComponent(component);
0100: setInfix(stripWhiteSpace(infixStr));
0101: }
0102:
0103: /**
0104: * <P>This method sets the LayoutElement that is associated with the 'if'
0105: * check being evaluated. This is not normally needed, it is only needed
0106: * if the 'if' check contains an expression which requires a
0107: * LayoutElement to be properly evaluated.</P>
0108: */
0109: protected void setUIComponent(UIComponent component) {
0110: _component = component;
0111: }
0112:
0113: /**
0114: * <P>Retreives the LayoutElement associated with this PermissionChecker
0115: * (only needed in cases where a expression requires a LayoutElement for
0116: * evaluation).</P>
0117: */
0118: public UIComponent getUIComponent() {
0119: return _component;
0120: }
0121:
0122: /**
0123: * <P>This method sets the LayoutElement that is associated with the 'if'
0124: * check being evaluated. This is not normally needed, it is only needed
0125: * if the 'if' check contains an expression which requires a
0126: * LayoutElement to be properly evaluated.</P>
0127: */
0128: protected void setLayoutElement(LayoutElement desc) {
0129: _desc = desc;
0130: }
0131:
0132: /**
0133: * <P>Retreives the LayoutElement associated with this PermissionChecker
0134: * (only needed in cases where a expression requires a LayoutElement for
0135: * evaluation).</P>
0136: */
0137: public LayoutElement getLayoutElement() {
0138: return _desc;
0139: }
0140:
0141: /**
0142: * This method returns the precedence of the given operator. This only
0143: * applies to infix notation (of course) and is needed to correctly order
0144: * the operators when converting to postfix.
0145: *
0146: * <UL><LI> ! (not) has the highest precedence
0147: * <LI> & (and)
0148: * <LI> | (and)
0149: * </UL>
0150: *
0151: * Of course '(' and ')' can be used to override the order of operations
0152: * in infix notation.
0153: *
0154: * @param op The operator to evaluate.
0155: *
0156: * @return A number that can be used to compare its precedence.
0157: */
0158: private static int getPrecedence(char op) {
0159: switch (op) {
0160: case LEFT_PAREN:
0161: return 1;
0162: case RIGHT_PAREN:
0163: return 999;
0164: case EQUALS_OPERATOR:
0165: return 2;
0166: case LESS_THAN_OPERATOR:
0167: case MORE_THAN_OPERATOR:
0168: return 4;
0169: case OR_OPERATOR:
0170: return 8;
0171: case AND_OPERATOR:
0172: return 16;
0173: case MODULUS_OPERATOR:
0174: return 32;
0175: case NOT_OPERATOR:
0176: return 64;
0177: }
0178: return 1;
0179: }
0180:
0181: /**
0182: * This method replaces all "true" / "false" strings w/ 't'/'f'. It
0183: * converts the String into a char[]. It replaces all user defined
0184: * functions w/ 'F' and places a Function in a list per the registered
0185: * user-defined function. All other strings are converted to an 'F' and
0186: * a StringFunction is added to the function list.
0187: */
0188: protected char[] preProcessString(String source) {
0189: char arr[] = source.toCharArray();
0190: int sourceLen = arr.length;
0191: int destLen = 0;
0192: String str = null;
0193:
0194: // Loop through the String, char by char
0195: for (int idx = 0; idx < sourceLen; idx++) {
0196: switch (arr[idx]) {
0197: case POST_TRUE:
0198: case POST_TRUE_CAP:
0199: if (((idx + TRUE.length()) <= sourceLen)
0200: && TRUE.equalsIgnoreCase(new String(arr, idx,
0201: TRUE.length()))) {
0202: arr[destLen++] = POST_TRUE;
0203: idx += TRUE.length() - 1;
0204: } else {
0205: idx = storeFunction(arr, idx);
0206: arr[destLen++] = FUNCTION_MARKER;
0207: }
0208: break;
0209: case POST_FALSE:
0210: case POST_FALSE_CAP:
0211: if (((idx + FALSE.length()) <= sourceLen)
0212: && FALSE.equalsIgnoreCase(new String(arr, idx,
0213: FALSE.length()))) {
0214: arr[destLen++] = POST_FALSE;
0215: idx += FALSE.length() - 1;
0216: } else {
0217: idx = storeFunction(arr, idx);
0218: arr[destLen++] = FUNCTION_MARKER;
0219: }
0220: break;
0221: case OR_OPERATOR:
0222: case EQUALS_OPERATOR:
0223: case LESS_THAN_OPERATOR:
0224: case MORE_THAN_OPERATOR:
0225: case MODULUS_OPERATOR:
0226: case AND_OPERATOR:
0227: case NOT_OPERATOR:
0228: case LEFT_PAREN:
0229: case RIGHT_PAREN:
0230: arr[destLen++] = arr[idx];
0231: break;
0232: default:
0233: idx = storeFunction(arr, idx);
0234: arr[destLen++] = FUNCTION_MARKER;
0235: }
0236: }
0237: char dest[] = new char[destLen];
0238: for (int idx = 0; idx < destLen; idx++) {
0239: dest[idx] = arr[idx];
0240: }
0241: return dest;
0242: }
0243:
0244: /**
0245: * This method searches for "$...{...}" in given string and replaces them
0246: * with FUNCTION_MARKER's. It adds a Function to the _tmpFunctionStack
0247: * for each of these so that it can be inserted in the proper place during
0248: * preProcessing. If the "${}" evaluates to (null), it is replaced with
0249: * false (BooleanFunction); if it evalutes to "false", it also will use a
0250: * false BooleanFunction. In all other cases, it will be replaced with a
0251: * StringFunction which evaluates to true.
0252: protected String substituteVariables(String string) {
0253: // FIXME: Consider reworking this to be an inline substitution... since nested
0254: // FIXME: $()'s aren't supported, a single-pass forward replacement may
0255: // FIXME: simplify this.
0256: int stringLen = string.length();
0257: int startTokenLen = VariableResolver.SUB_START.length();
0258: int delimLen = VariableResolver.SUB_TYPE_DELIM.length();
0259: int endTokenLen = VariableResolver.SUB_END.length();
0260: int endIndex, delimIndex;
0261: int parenSemi;
0262: char firstEndChar = VariableResolver.SUB_END.charAt(0);
0263: char firstDelimChar = VariableResolver.SUB_TYPE_DELIM.charAt(0);
0264: char currChar;
0265: Object obj = null;
0266:
0267: // Temporary Function List (functions here will be inserted correctly
0268: // during preProcessing)
0269: _tmpFunctionStack = new Stack();
0270: Stack tmpStack = new Stack();
0271: _currTmpFunction = 0;
0272:
0273: // Iterate through the String backwards looking for substitutions
0274: for (int startIndex = string.indexOf(VariableResolver.SUB_START);
0275: startIndex != -1;
0276: startIndex = string.indexOf(VariableResolver.SUB_START, startIndex+1)) {
0277:
0278: // Find make sure we have a typeDelim
0279: delimIndex = string.indexOf(VariableResolver.SUB_TYPE_DELIM, startIndex+startTokenLen);
0280: if (delimIndex == -1) {
0281: continue;
0282: }
0283:
0284: // Next find the end token
0285: parenSemi = 0;
0286: endIndex = -1;
0287: for (int curr = delimIndex+delimLen; curr<stringLen; ) {
0288: currChar = string.charAt(curr);
0289: if ((currChar == firstDelimChar) && VariableResolver.SUB_TYPE_DELIM.equals(string.substring(curr, curr+delimLen))) {
0290: parenSemi++;
0291: curr += delimLen;
0292: continue;
0293: }
0294: if ((currChar == firstEndChar) && VariableResolver.SUB_END.equals(string.substring(curr, curr+endTokenLen))) {
0295: parenSemi--;
0296: if (parenSemi < 0) {
0297: endIndex = curr;
0298: break;
0299: }
0300: curr += endTokenLen;
0301: continue;
0302: }
0303: curr++;
0304: }
0305: if (endIndex == -1) {
0306: continue;
0307: }
0308:
0309: // increment the endIndex to point just after it
0310: endIndex += endTokenLen;
0311:
0312: // We found a match! Create a StringFunction
0313: obj = new StringFunction(string.substring(startIndex, endIndex));
0314:
0315: // Add the function
0316: tmpStack.push(obj);
0317:
0318: // Make new string
0319: string = string.substring(0, startIndex) + // Before replacement
0320: FUNCTION_MARKER + // Replacement
0321: string.substring(endIndex); // After
0322: }
0323:
0324: // Flip Stack (b/c we search forward to help eval ${} correctly
0325: while (!tmpStack.empty()) {
0326: _tmpFunctionStack.push(tmpStack.pop());
0327: }
0328:
0329: // Return the (new) string
0330: return string;
0331: }
0332: */
0333:
0334: /**
0335: * <P>This method looks at the given char array starting at index and
0336: * continues until an operator (or end of String) is encountered. It then
0337: * uses this string to lookup a registered function (if any), it stores
0338: * that function (with parameters)... or if the function is not found, it
0339: * registers a "String function" (which always returns true).</P>
0340: */
0341: protected int storeFunction(char arr[], int idx) {
0342: // Find string...
0343: int start = idx;
0344: int len = arr.length;
0345: while ((idx < len) && !isOperator(arr[idx])) {
0346: idx++;
0347: }
0348:
0349: // Create String...
0350: String str = new String(arr, start, idx - start);
0351:
0352: // Check to see if it is a registered function...
0353: Function function = getFunction(str);
0354: if (function != null) {
0355: // Find the left paren...
0356: int left = idx;
0357: if ((left >= len) || (arr[left] != LEFT_PAREN)) {
0358: throw new RuntimeException("Function '" + str
0359: + "' is expected to have a '" + LEFT_PAREN
0360: + "' immediately following it. Equation: '"
0361: + new String(arr) + "'.");
0362: }
0363:
0364: ArrayList arguments = new ArrayList();
0365:
0366: // Find the right Paren...
0367: while ((++idx < len) && (arr[idx] != RIGHT_PAREN)) {
0368: if (arr[idx] == ARGUMENT_SEPARATOR) {
0369: left++;
0370: arguments.add(new String(arr, left, idx - left));
0371: left = idx;
0372: }
0373: }
0374:
0375: // Make sure we don't have ()
0376: left++;
0377: if (idx > left) {
0378: arguments.add(new String(arr, left, idx - left));
0379: }
0380:
0381: // Set the arguments...
0382: function.setArguments(arguments);
0383: } else {
0384: // Not a registered function...
0385: idx--; // In this case, there are no ()'s to consume, backup 1
0386: if ((str.charAt(0) == FUNCTION_MARKER)
0387: && (str.length() == 1)
0388: && !_tmpFunctionStack.empty()) {
0389: // We have a function added during the subtitute() phase
0390: function = (Function) _tmpFunctionStack.pop();
0391: } else {
0392: // Create a StringFunction
0393: function = new StringFunction(str);
0394: }
0395: }
0396:
0397: // Add the function to the function list
0398: _functionList.add(function);
0399:
0400: // Return the number of characters that we consumed...
0401: return idx;
0402: }
0403:
0404: /**
0405: * This method is a factory method for constructing a new function based
0406: * on the function name passed in. The function must be registered prior
0407: * to invoking this method.
0408: */
0409: protected static Function getFunction(String functionName) {
0410: // Get the Function class
0411: Class functionClass = (Class) _functions.get(functionName);
0412: if (functionClass == null) {
0413: return null;
0414: }
0415:
0416: // Create a new instance
0417: Function function = null;
0418: try {
0419: function = (Function) functionClass.newInstance();
0420: } catch (Exception ex) {
0421: throw new RuntimeException("Unable to instantiate '"
0422: + functionClass.getName() + "' for '"
0423: + functionName + "'", ex);
0424: }
0425:
0426: // Return the instance
0427: return function;
0428: }
0429:
0430: /**
0431: * <P>This method allows arbitrary functions to be registered. Function
0432: * names should only contain letters or numbers, other characters or
0433: * whitespace may cause problems. No checking is done to ensure this,
0434: * however.</P>
0435: *
0436: * <P>Functions will be expressed in an equation as follows:</P>
0437: *
0438: * <UL><LI>function_name(param1,param2)</LI></UL>
0439: *
0440: * <P>Function parameters also should only contain alpha-numeric
0441: * characters.</P>
0442: *
0443: * <P>Functions must implement PermissionChecker.Function interface</P>
0444: */
0445: public static void registerFunction(String functionName,
0446: Class function) {
0447: if (function == null) {
0448: _functions.remove(functionName);
0449: }
0450: if (!Function.class.isAssignableFrom(function)) {
0451: throw new RuntimeException("'" + function.getName()
0452: + "' must implement '" + Function.class.getName()
0453: + "'");
0454: }
0455: _functions.put(functionName, function);
0456: }
0457:
0458: /**
0459: * This method returns true if the given character is a valid operator.
0460: */
0461: public static boolean isOperator(char ch) {
0462: switch (ch) {
0463: case LEFT_PAREN:
0464: case RIGHT_PAREN:
0465: case EQUALS_OPERATOR:
0466: case LESS_THAN_OPERATOR:
0467: case MORE_THAN_OPERATOR:
0468: case MODULUS_OPERATOR:
0469: case OR_OPERATOR:
0470: case AND_OPERATOR:
0471: case NOT_OPERATOR:
0472: // case AT_OPERATOR:
0473: // case POUND_OPERATOR:
0474: // case DOLLAR_OPERATOR:
0475: // case UP_OPERATOR:
0476: // case STAR_OPERATOR:
0477: // case TILDA_OPERATOR:
0478: // case ARGUMENT_SEPARATOR:
0479: return true;
0480: }
0481: return false;
0482: }
0483:
0484: /**
0485: * This method calculates the postfix representation of the infix equation
0486: * passed in. It returns the postfix equation as a char[].
0487: *
0488: * @param infixStr The infix representation of the equation.
0489: *
0490: * @return postfix representation of the equation as a char[] (the f()'s
0491: * are removed and stored in _functionList).
0492: */
0493: protected char[] generatePostfix(String infixStr) {
0494: // Reset the _functionList
0495: _functionList = new ArrayList();
0496:
0497: // Convert string to our parsable format
0498: char result[] = preProcessString(infixStr);
0499: //System.out.println("DEBUG: Initial String: '"+infixStr+"'");
0500: //System.out.println("DEBUG: After Pre-process: '"+new String(result)+"'");
0501: int resultLen = result.length;
0502: int postIdx = 0;
0503: int precedence = 0;
0504: Stack opStack = new Stack();
0505:
0506: // Put f()'s directly into result, push operators into right order
0507: for (int idx = 0; idx < resultLen; idx++) {
0508: switch (result[idx]) {
0509: case FUNCTION_MARKER:
0510: case POST_TRUE:
0511: case POST_FALSE:
0512: result[postIdx++] = result[idx];
0513: break;
0514: case LEFT_PAREN:
0515: opStack.push(new Character(LEFT_PAREN));
0516: break;
0517: case RIGHT_PAREN:
0518: while (!opStack.empty()
0519: && (((Character) opStack.peek()).charValue() != LEFT_PAREN)) {
0520: result[postIdx++] = ((Character) opStack.pop())
0521: .charValue();
0522: }
0523: if (!opStack.empty()) {
0524: // Throw away the LEFT_PAREN that should still be there
0525: opStack.pop();
0526: }
0527: break;
0528: default:
0529: // clear stuff
0530: precedence = getPrecedence(result[idx]);
0531: while (!opStack.empty()
0532: && (getPrecedence(((Character) opStack.peek())
0533: .charValue()) >= precedence)) {
0534: result[postIdx++] = ((Character) opStack.pop())
0535: .charValue();
0536: }
0537:
0538: /* Put it on the stack */
0539: opStack.push(new Character(result[idx]));
0540: break;
0541: }
0542: }
0543:
0544: // empty the rest of the stack to the result
0545: while (!opStack.empty()) {
0546: result[postIdx++] = ((Character) opStack.pop()).charValue();
0547: }
0548: // Copy the result to the postfixStr
0549: char postfixStr[] = new char[postIdx];
0550: for (int idx = 0; idx < postIdx; idx++) {
0551: postfixStr[idx] = result[idx];
0552: }
0553: //System.out.println("DEBUG: Postfix String: '"+new String(postfixStr)+"'");
0554: return postfixStr;
0555: }
0556:
0557: /**
0558: * This method is invoked to determine if the equation evaluates to true
0559: * or false.
0560: */
0561: public boolean hasPermission() {
0562: char postfixArr[] = getPostfixArr();
0563: int len = postfixArr.length;
0564: Stack result = new Stack();
0565: result.push(FALSE_BOOLEAN_FUNCTION); // Default to false
0566: boolean val1, val2;
0567: Iterator it = _functionList.iterator();
0568: Function func = null;
0569:
0570: // Iterate through the postfix array
0571: for (int idx = 0; idx < len; idx++) {
0572: switch (postfixArr[idx]) {
0573: case POST_TRUE:
0574: result.push(TRUE_BOOLEAN_FUNCTION);
0575: break;
0576: case POST_FALSE:
0577: result.push(FALSE_BOOLEAN_FUNCTION);
0578: break;
0579: case FUNCTION_MARKER:
0580: if (!it.hasNext()) {
0581: throw new RuntimeException("Unable to evaluate: '"
0582: + toString()
0583: + "' -- found function marker w/o "
0584: + "cooresponding function!");
0585: }
0586: result.push(it.next());
0587: break;
0588: case EQUALS_OPERATOR:
0589: try {
0590: // Allow reg expression matching
0591: String matchStr = result.pop().toString();
0592: val1 = result.pop().toString().matches(matchStr);
0593: } catch (EmptyStackException ex) {
0594: throw new RuntimeException("Unable to evaluate: '"
0595: + toString() + "'.", ex);
0596: }
0597: result.push(val1 ? TRUE_BOOLEAN_FUNCTION
0598: : FALSE_BOOLEAN_FUNCTION);
0599: break;
0600: case LESS_THAN_OPERATOR:
0601: try {
0602: // The stack reverses the order, so check greater than
0603: val1 = Integer.parseInt(result.pop().toString()) > Integer
0604: .parseInt(result.pop().toString());
0605: } catch (EmptyStackException ex) {
0606: throw new RuntimeException("Unable to evaluate: '"
0607: + toString() + "'.", ex);
0608: }
0609: result.push(val1 ? TRUE_BOOLEAN_FUNCTION
0610: : FALSE_BOOLEAN_FUNCTION);
0611: break;
0612: case MORE_THAN_OPERATOR:
0613: try {
0614: // The stack reverses the order, so check greater than
0615: val1 = Integer.parseInt(result.pop().toString()) < Integer
0616: .parseInt(result.pop().toString());
0617: } catch (EmptyStackException ex) {
0618: throw new RuntimeException("Unable to evaluate: '"
0619: + toString() + "'.", ex);
0620: }
0621: result.push(val1 ? TRUE_BOOLEAN_FUNCTION
0622: : FALSE_BOOLEAN_FUNCTION);
0623: break;
0624: case MODULUS_OPERATOR:
0625: try {
0626: // The stack reverses the order...
0627: int modNumber = Integer.parseInt(result.pop()
0628: .toString());
0629: int num = Integer.parseInt(result.pop().toString());
0630: result.push(new StringFunction(""
0631: + (num % modNumber)));
0632: } catch (EmptyStackException ex) {
0633: throw new RuntimeException("Unable to evaluate: '"
0634: + toString() + "'.", ex);
0635: }
0636: break;
0637: case OR_OPERATOR:
0638: try {
0639: val1 = ((Function) result.pop()).evaluate();
0640: val2 = ((Function) result.pop()).evaluate();
0641: } catch (EmptyStackException ex) {
0642: throw new RuntimeException("Unable to evaluate: '"
0643: + toString() + "'.", ex);
0644: }
0645: result.push((val1 || val2) ? TRUE_BOOLEAN_FUNCTION
0646: : FALSE_BOOLEAN_FUNCTION);
0647: break;
0648: case AND_OPERATOR:
0649: try {
0650: val1 = ((Function) result.pop()).evaluate();
0651: val2 = ((Function) result.pop()).evaluate();
0652: } catch (EmptyStackException ex) {
0653: throw new RuntimeException("Unable to evaluate: '"
0654: + toString() + "'.", ex);
0655: }
0656: result.push((val1 && val2) ? TRUE_BOOLEAN_FUNCTION
0657: : FALSE_BOOLEAN_FUNCTION);
0658: break;
0659: case NOT_OPERATOR:
0660: try {
0661: val1 = ((Function) result.pop()).evaluate();
0662: } catch (EmptyStackException ex) {
0663: throw new RuntimeException("Unable to evaluate: '"
0664: + toString() + "'.", ex);
0665: }
0666: result.push((!val1) ? TRUE_BOOLEAN_FUNCTION
0667: : FALSE_BOOLEAN_FUNCTION);
0668: break;
0669: }
0670: }
0671:
0672: // Return the only element on the stack (hopefully)
0673: try {
0674: val1 = ((Function) result.pop()).evaluate();
0675: } catch (EmptyStackException ex) {
0676: throw new RuntimeException("Unable to evaluate: '"
0677: + toString() + "'.", ex);
0678: }
0679: if (!result.empty()) {
0680: result.pop(); // We added a false that wasn't needed
0681: if (!result.empty()) {
0682: throw new RuntimeException("Unable to evaluate: '"
0683: + toString() + "' -- values left on the stack.");
0684: }
0685: }
0686: return val1;
0687: }
0688:
0689: /**
0690: * This method returns the infix representation of the equation, in other
0691: * words: the original String passed in.
0692: */
0693: public String getInfix() {
0694: return _infixStr;
0695: }
0696:
0697: /**
0698: * This method sets the equation and forces a re-evaluation of the
0699: * equation. It returns the postfix representation of the equation.
0700: *
0701: * @param equation The infix equation to use
0702: *
0703: */
0704: public void setInfix(String equation) {
0705: _infixStr = equation;
0706: setPostfixArr(generatePostfix(equation));
0707: }
0708:
0709: /**
0710: *
0711: */
0712: protected char[] getPostfixArr() {
0713: if (_postfixArr == null) {
0714: _postfixArr = new char[] { ' ' };
0715: }
0716: return _postfixArr;
0717: }
0718:
0719: /**
0720: *
0721: */
0722: protected void setPostfixArr(char postfix[]) {
0723: _postfixArr = postfix;
0724: }
0725:
0726: /**
0727: *
0728: */
0729: public String getPostfix() {
0730: if (getPostfixArr() == null) {
0731: return "";
0732: }
0733: return new String(getPostfixArr());
0734: }
0735:
0736: /**
0737: * Displays the infix and postfix version of the equation.
0738: */
0739: public String toString() {
0740: return _infixStr + " = " + toString(getPostfixArr());
0741: }
0742:
0743: /**
0744: * This toString(...) method generates just the postfix representation of
0745: * the equation. The postfix notation is stored as a char[] and it has
0746: * the functions removed from the char[]. This method iterates through
0747: * the char[] and generates a String with the functions put back into the
0748: * equation.
0749: *
0750: * @param post The char[] representation of the postfix equation
0751: */
0752: private String toString(char post[]) {
0753: int len = post.length;
0754: StringBuffer result = new StringBuffer("");
0755: Iterator it = _functionList.iterator();
0756:
0757: for (int idx = 0; idx < len; idx++) {
0758: switch (post[idx]) {
0759: case POST_TRUE:
0760: result.append(TRUE);
0761: break;
0762: case POST_FALSE:
0763: result.append(FALSE);
0764: break;
0765: case FUNCTION_MARKER:
0766: result.append(((Function) it.next()).toString());
0767: break;
0768: default:
0769: result.append(post[idx]);
0770: }
0771: }
0772:
0773: return result.toString();
0774: }
0775:
0776: /**
0777: * This method removes all whitespace from the given String
0778: */
0779: public static String stripWhiteSpace(String input) {
0780: char arr[] = input.toCharArray();
0781: int len = arr.length;
0782: int destLen = 0;
0783:
0784: // Loop through the array skipping whitespace
0785: for (int idx = 0; idx < len; idx++) {
0786: if (Character.isWhitespace(arr[idx])) {
0787: continue;
0788: }
0789: arr[destLen++] = arr[idx];
0790: }
0791:
0792: // Return the result
0793: return new String(arr, 0, destLen);
0794: }
0795:
0796: /**
0797: * This class must be implemented by all user defined Functions.
0798: *
0799: * <P>In addition to these methods, a toString() should be implemented
0800: * that reconstructs the original format of the function (i.e.
0801: * function_name(arg1,arg2...)).
0802: */
0803: public static interface Function {
0804:
0805: /**
0806: * This method returns the List of arguments.
0807: */
0808: public List getArguments();
0809:
0810: /**
0811: * This method is invoked be the PermissionChecker to set the
0812: * arguments.
0813: */
0814: public void setArguments(List args);
0815:
0816: /**
0817: * This method is invoked by the PermissionCheck to evaluate the
0818: * function to true or false.
0819: */
0820: public boolean evaluate();
0821: }
0822:
0823: /**
0824: * StringFunction implements Function and serves as the default function.
0825: * This function is special in that it is NEVER registered and is the
0826: * only function that SHOULD NOT be followed by ()'s. This function will
0827: * process embedded expressions and evaulate to false if the entire string
0828: * evaulates to null. Otherwise it will return true. This Function
0829: * ignores all arguments (arguments only apply if it is registered, which
0830: * shouldn't be the case anyway).
0831: */
0832: protected class StringFunction implements
0833: PermissionChecker.Function {
0834:
0835: /**
0836: * Constructor.
0837: *
0838: * @param value The expression to evaluate.
0839: */
0840: public StringFunction(String value) {
0841: _value = value;
0842: }
0843:
0844: /**
0845: * Not used.
0846: */
0847: public List getArguments() {
0848: return null;
0849: }
0850:
0851: /**
0852: * Not used.
0853: */
0854: public void setArguments(List args) {
0855: }
0856:
0857: public boolean evaluate() {
0858: Object obj = getEvaluatedValue();
0859: if (obj == null) {
0860: return false;
0861: }
0862: if (obj.toString().equalsIgnoreCase("false")) {
0863: return false;
0864: }
0865: return true;
0866: }
0867:
0868: public Object getEvaluatedValue() {
0869: return VariableResolver.resolveVariables(
0870: getLayoutElement(), getUIComponent(), _value);
0871: }
0872:
0873: public String toString() {
0874: Object obj = getEvaluatedValue();
0875: if (obj == null) {
0876: return "";
0877: }
0878: return obj.toString();
0879: }
0880:
0881: private String _value;
0882: }
0883:
0884: /**
0885: * BooleanFunction is either true or false. It is used internally by
0886: * PermissionChecker and is not needed outside PermissionChecker since
0887: * "true" and "false" used in an equation are equivalent.
0888: */
0889: protected static class BooleanFunction implements
0890: PermissionChecker.Function {
0891: public BooleanFunction() {
0892: }
0893:
0894: public BooleanFunction(boolean value) {
0895: _value = value;
0896: }
0897:
0898: public List getArguments() {
0899: return null;
0900: }
0901:
0902: public void setArguments(List args) {
0903: }
0904:
0905: public boolean evaluate() {
0906: return _value;
0907: }
0908:
0909: public String toString() {
0910: return _value ? "true" : "false";
0911: }
0912:
0913: private boolean _value;
0914: }
0915:
0916: /**
0917: * This is here to provide some test cases. It only tests the conversion
0918: * to postfix notation.
0919: */
0920: public static void main(String args[]) {
0921: PermissionChecker checker;
0922: if (args.length > 0) {
0923: for (int count = 0; count < args.length; count++) {
0924: checker = new PermissionChecker(null, null, args[count]);
0925: System.out.println("Output:\n" + checker.toString()
0926: + " (" + checker.hasPermission() + ")");
0927: }
0928: } else {
0929: boolean success = true;
0930: checker = new PermissionChecker(null, null, "true |false");
0931: System.out.println("Output:\n" + checker.toString() + " ("
0932: + checker.hasPermission() + ")");
0933: if (!checker.toString().equals("true|false = truefalse|")) {
0934: System.out.println("\tFAILED!");
0935: System.out.println("Should have been:\n"
0936: + "true|false = truefalse|");
0937: success = false;
0938: }
0939: if (!checker.hasPermission()) {
0940: System.out.println("\tFAILED!");
0941: System.out.println("hasPermission("
0942: + checker.toString(checker.getPostfixArr())
0943: + ") returned the wrong result!");
0944: success = false;
0945: }
0946:
0947: checker = new PermissionChecker(null, null,
0948: "true&(false|true)");
0949: System.out.println("Output:\n" + checker.toString() + " ("
0950: + checker.hasPermission() + ")");
0951: if (!checker.toString().equals(
0952: "true&(false|true) = truefalsetrue|&")) {
0953: System.out.println("\tFAILED!");
0954: System.out.println("Should have been:\n"
0955: + "true&(false|true) = truefalsetrue|&");
0956: success = false;
0957: }
0958: if (!checker.hasPermission()) {
0959: System.out.println("\tFAILED!");
0960: System.out.println("hasPermission("
0961: + checker.toString(checker.getPostfixArr())
0962: + ") returned the wrong result!");
0963: success = false;
0964: }
0965:
0966: checker = new PermissionChecker(null, null,
0967: "true&false|true");
0968: System.out.println("Output:\n" + checker.toString() + " ("
0969: + checker.hasPermission() + ")");
0970: if (!checker.toString().equals(
0971: "true&false|true = truefalse&true|")) {
0972: System.out.println("\tFAILED!");
0973: System.out.println("Should have been:\n"
0974: + "true&false|true = truefalse&true|");
0975: success = false;
0976: }
0977: if (!checker.hasPermission()) {
0978: System.out.println("\tFAILED!");
0979: System.out.println("hasPermission("
0980: + checker.toString(checker.getPostfixArr())
0981: + ") returned the wrong result!");
0982: success = false;
0983: }
0984:
0985: checker = new PermissionChecker(null, null,
0986: "true&true|false&true");
0987: System.out.println("Output:\n" + checker.toString() + " ("
0988: + checker.hasPermission() + ")");
0989: if (!checker.toString().equals(
0990: "true&true|false&true = truetrue&falsetrue&|")) {
0991: System.out.println("\tFAILED!");
0992: System.out
0993: .println("Should have been:\n"
0994: + "true&true|false&true = truetrue&falsetrue&|");
0995: success = false;
0996: }
0997: if (!checker.hasPermission()) {
0998: System.out.println("\tFAILED!");
0999: System.out.println("hasPermission("
1000: + checker.toString(checker.getPostfixArr())
1001: + ") returned the wrong result!");
1002: success = false;
1003: }
1004:
1005: checker = new PermissionChecker(null, null,
1006: "!true|false&!(false|true)");
1007: System.out.println("Output:\n" + checker.toString() + " ("
1008: + checker.hasPermission() + ")");
1009: if (!checker
1010: .toString()
1011: .equals(
1012: "!true|false&!(false|true) = true!falsefalsetrue|!&|")) {
1013: System.out.println("\tFAILED!");
1014: System.out
1015: .println("Should have been:\n"
1016: + "!true|false&!(false|true) = true!falsefalsetrue|!&|");
1017: success = false;
1018: }
1019: if (checker.hasPermission()) {
1020: System.out.println("\tFAILED!");
1021: System.out.println("hasPermission("
1022: + checker.toString(checker.getPostfixArr())
1023: + ") returned the wrong result!");
1024: success = false;
1025: }
1026:
1027: checker = new PermissionChecker(null, null,
1028: "!(!(true&!true)|!(false|false))|(true|false)&true");
1029: System.out.println("Output:\n" + checker.toString() + " ("
1030: + checker.hasPermission() + ")");
1031: if (!checker
1032: .toString()
1033: .equals(
1034: "!(!(true&!true)|!(false|false))|(true|false)&true = truetrue!&!falsefalse|!|!truefalse|true&|")) {
1035:
1036: System.out.println("\tFAILED!");
1037: System.out
1038: .println("Should have been:\n"
1039: + "!(!(true&!true)|!(false|false))|(true|false)&true = truetrue!&!falsefalse|!|!truefalse|true&|");
1040: success = false;
1041: }
1042: if (!checker.hasPermission()) {
1043: System.out.println("\tFAILED!");
1044: System.out.println("hasPermission("
1045: + checker.toString(checker.getPostfixArr())
1046: + ") returned the wrong result!");
1047: success = false;
1048: }
1049:
1050: // Test '='
1051: checker = new PermissionChecker(null, null, "false =false");
1052: System.out.println("Output:\n" + checker.toString() + " ("
1053: + checker.hasPermission() + ")");
1054: if (!checker.toString().equals("false=false = falsefalse=")) {
1055: System.out.println("\tFAILED!");
1056: System.out.println("Should have been:\n"
1057: + "false=false = falsefalse=");
1058: success = false;
1059: }
1060: if (!checker.hasPermission()) {
1061: System.out.println("\tFAILED!");
1062: System.out.println("hasPermission("
1063: + checker.toString(checker.getPostfixArr())
1064: + ") returned the wrong result!");
1065: success = false;
1066: }
1067:
1068: checker = new PermissionChecker(null, null, " test= me ");
1069: System.out.println("Output:\n" + checker.toString() + " ("
1070: + checker.hasPermission() + ")");
1071: if (!checker.toString().equals("test=me = testme=")) {
1072: System.out.println("\tFAILED!");
1073: System.out.println("Should have been:\n"
1074: + "test=me = testme=");
1075: success = false;
1076: }
1077: if (checker.hasPermission()) {
1078: System.out.println("\tFAILED!");
1079: System.out.println("hasPermission("
1080: + checker.toString(checker.getPostfixArr())
1081: + ") returned the wrong result!");
1082: success = false;
1083: }
1084:
1085: checker = new PermissionChecker(null, null,
1086: " this should work=thisshouldwork");
1087: System.out.println("Output:\n" + checker.toString() + " ("
1088: + checker.hasPermission() + ")");
1089: if (!checker
1090: .toString()
1091: .equals(
1092: "thisshouldwork=thisshouldwork = thisshouldworkthisshouldwork=")) {
1093: System.out.println("\tFAILED!");
1094: System.out
1095: .println("Should have been:\n"
1096: + "thisshouldwork=thisshouldwork = thisshouldworkthisshouldwork=");
1097: success = false;
1098: }
1099: if (!checker.hasPermission()) {
1100: System.out.println("\tFAILED!");
1101: System.out.println("hasPermission("
1102: + checker.toString(checker.getPostfixArr())
1103: + ") returned the wrong result!");
1104: success = false;
1105: }
1106:
1107: checker = new PermissionChecker(null, null, "false|ab=true");
1108: System.out.println("Output:\n" + checker.toString() + " ("
1109: + checker.hasPermission() + ")");
1110: if (!checker.toString().equals(
1111: "false|ab=true = falseab|true=")) {
1112: System.out.println("\tFAILED!");
1113: System.out.println("Should have been:\n"
1114: + "false|ab=true = falseab|true=");
1115: success = false;
1116: }
1117: if (!checker.hasPermission()) {
1118: System.out.println("\tFAILED!");
1119: System.out.println("hasPermission("
1120: + checker.toString(checker.getPostfixArr())
1121: + ") returned the wrong result!");
1122: success = false;
1123: }
1124:
1125: checker = new PermissionChecker(null, null,
1126: "false|(ab=true)");
1127: System.out.println("Output:\n" + checker.toString() + " ("
1128: + checker.hasPermission() + ")");
1129: if (!checker.toString().equals(
1130: "false|(ab=true) = falseabtrue=|")) {
1131: System.out.println("\tFAILED!");
1132: System.out.println("Should have been:\n"
1133: + "false|ab=true = falseab|true=");
1134: success = false;
1135: }
1136: if (checker.hasPermission()) {
1137: System.out.println("\tFAILED!");
1138: System.out.println("hasPermission("
1139: + checker.toString(checker.getPostfixArr())
1140: + ") returned the wrong result!");
1141: success = false;
1142: }
1143:
1144: checker = new PermissionChecker(null, null, "false|(ab=ab)");
1145: System.out.println("Output:\n" + checker.toString() + " ("
1146: + checker.hasPermission() + ")");
1147: if (!checker.toString().equals(
1148: "false|(ab=ab) = falseabab=|")) {
1149: System.out.println("\tFAILED!");
1150: System.out.println("Should have been:\n"
1151: + "false|ab=true = falseab|true=");
1152: success = false;
1153: }
1154: if (!checker.hasPermission()) {
1155: System.out.println("\tFAILED!");
1156: System.out.println("hasPermission("
1157: + checker.toString(checker.getPostfixArr())
1158: + ") returned the wrong result!");
1159: success = false;
1160: }
1161:
1162: if (success) {
1163: System.out.println("\n\tALL TESTS PASSED!");
1164: } else {
1165: System.out.println("\n\tNOT ALL TESTS PASSED!");
1166: }
1167: }
1168: }
1169:
1170: /**
1171: * This variable represents a "false" BooleanFunction.
1172: */
1173: public static final BooleanFunction FALSE_BOOLEAN_FUNCTION = new BooleanFunction(
1174: false);
1175:
1176: /**
1177: * This variable represents a "true" BooleanFunction.
1178: */
1179: public static final BooleanFunction TRUE_BOOLEAN_FUNCTION = new BooleanFunction(
1180: true);
1181:
1182: protected static final char POST_TRUE = 't';
1183: protected static final char POST_FALSE = 'f';
1184: protected static final char POST_TRUE_CAP = 'T';
1185: protected static final char POST_FALSE_CAP = 'F';
1186:
1187: public static final String TRUE = "true";
1188: public static final String FALSE = "false";
1189:
1190: // Function representation in postfix String
1191: public static final char FUNCTION_MARKER = 'F';
1192:
1193: // Operator constants
1194: public static final char LEFT_PAREN = '(';
1195: public static final char RIGHT_PAREN = ')';
1196: public static final char EQUALS_OPERATOR = '=';
1197: public static final char OR_OPERATOR = '|';
1198: public static final char AND_OPERATOR = '&';
1199: public static final char NOT_OPERATOR = '!';
1200: public static final char LESS_THAN_OPERATOR = '<';
1201: public static final char MORE_THAN_OPERATOR = '>';
1202: public static final char MODULUS_OPERATOR = '%';
1203:
1204: // The COMMA separates function arguments... not really an operator
1205: public static final char ARGUMENT_SEPARATOR = ',';
1206:
1207: // Reserved operators, although not currently used...
1208: /*
1209: * These will be added eventually, but currently they are commented out to
1210: * enable the AppServer AdminGUI to function correctly
1211: *
1212: * public static final char AT_OPERATOR = '@';
1213: * public static final char POUND_OPERATOR = '#';
1214: * public static final char DOLLAR_OPERATOR = '$';
1215: * public static final char UP_OPERATOR = '^';
1216: * public static final char STAR_OPERATOR = '*';
1217: * public static final char TILDA_OPERATOR = '~';
1218: */
1219:
1220: /**
1221: * This holds the infix equation
1222: */
1223: private String _infixStr = null;
1224:
1225: /**
1226: * This holds the postfix equation
1227: */
1228: private char _postfixArr[] = null;
1229:
1230: /**
1231: * This is a Map of Class objects which are user-registered functions.
1232: */
1233: private static Map _functions = new HashMap();
1234:
1235: /**
1236: * This List holds the actual Function objects that correspond to the 'F'
1237: * markers in the postfix string.
1238: */
1239: private List _functionList = null;
1240:
1241: /**
1242: * This List of functions maintains variableSubstitution Functions which
1243: * happen out-of-order. They will be pulled from this list as placed into
1244: * the actual _functionList when the are encountered during the
1245: * preProcessing.
1246: */
1247: private Stack _tmpFunctionStack = null;
1248: private LayoutElement _desc = null;
1249: private UIComponent _component = null;
1250: }
|