001: /*
002: * Bossa Workflow System
003: *
004: * $Id: Expression.java,v 1.8 2004/01/29 21:24:37 gdvieira Exp $
005: *
006: * Copyright (C) 2003,2004 OpenBR Sistemas S/C Ltda.
007: *
008: * This file is part of Bossa.
009: *
010: * Bossa is free software; you can redistribute it and/or modify it
011: * under the terms of version 2 of the GNU General Public License as
012: * published by the Free Software Foundation.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public
020: * License along with this program; if not, write to the
021: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
022: * Boston, MA 02111-1307, USA.
023: */
024:
025: package com.bigbross.bossa.resource;
026:
027: import java.io.Serializable;
028: import java.util.StringTokenizer;
029:
030: /**
031: * A node of one binary tree representing a compiled resource expression. <p>
032: * The set arithmetic operations are:
033: * <ul>
034: * <li> Union: <code>+</code>
035: * <li> Intersection: <code>^</code>
036: * <li> Exclusion: <code>-</code>
037: * </ul>
038: *
039: * The operands are resource names. <br>
040: * Whitespace characters are ignored. <br>
041: * The expression is evaluated from left to right. <br>
042: * Parenthesis may be used to group subexpressions. <p>
043: *
044: * The resource is linked from the <code>ResourceRegistry</code> at the
045: * compilation time, and is created if needed. A <code>$</code> may be used to
046: * delay the resource resolution to evaluation time, using a new
047: * <code>ResourceRegistry</code>. <p>
048: *
049: * A resource expression example is: <code>sales - $request</code> than means
050: * that only the users of the "sales" group minus the users that are in the
051: * "request" group provided in the evaluation moment are in this role.
052: *
053: * @author <a href="http://www.bigbross.com">BigBross Team</a>
054: * @see ResourceRegistry
055: */
056: public abstract class Expression implements Container, Serializable {
057:
058: protected final static char OR = '+';
059: protected final static char SUB = '-';
060: protected final static char AND = '^';
061: protected final static char VAR = '$';
062: protected final static char SPC = ' ';
063: protected final static char LP = '(';
064: protected final static char RP = ')';
065:
066: protected final static String DELIM = "" + OR + SUB + AND + VAR
067: + SPC + LP + RP;
068:
069: /**
070: * Compiles a resource expression. <p>
071: *
072: * @param registry a <code>ResourceRegistry</code> to link the resources
073: * in the expression.
074: * @param expression the resource expression to be compiled.
075: * @return a <code>Expression</code> representing the compiled resource
076: * expression.
077: */
078: public static Expression compile(ResourceRegistry registry,
079: String expression) {
080: StringTokenizer expr = new StringTokenizer(expression, DELIM,
081: true);
082: return compile(registry, expr, compile(registry, expr, null));
083: }
084:
085: /**
086: * Parses a resource expression building a binary tree. <p>
087: *
088: * @param registry a <code>ResourceRegistry</code> to link the resources
089: * in the expression.
090: * @param expression the remaining resource expression to be compiled.
091: * @param node a <code>Expression</code> value of the left node.
092: * @return a <code>Expression</code> node of the compiled resource
093: * expression.
094: */
095: protected static Expression compile(ResourceRegistry registry,
096: StringTokenizer expression, Expression node) {
097: if (!expression.hasMoreTokens()) {
098: return node;
099: }
100:
101: String tok = expression.nextToken();
102:
103: while (tok.charAt(0) == SPC) {
104: tok = expression.nextToken();
105: }
106:
107: switch (tok.charAt(0)) {
108:
109: case OR: // Union node
110: return compile(registry, expression, new Node(node,
111: compile(registry, expression, node)) {
112:
113: public boolean contains(ResourceRegistry registry,
114: Resource resource) {
115: return left.contains(registry, resource)
116: || right.contains(registry, resource);
117: }
118:
119: public String toString() {
120: return toString(OR);
121: }
122: });
123:
124: case AND: // Intersection node
125: return compile(registry, expression, new Node(node,
126: compile(registry, expression, node)) {
127:
128: public boolean contains(ResourceRegistry registry,
129: Resource resource) {
130: return left.contains(registry, resource)
131: && right.contains(registry, resource);
132: }
133:
134: public String toString() {
135: return toString(AND);
136: }
137: });
138:
139: case SUB: // Subtraction node
140: return compile(registry, expression, new Node(node,
141: compile(registry, expression, node)) {
142:
143: public boolean contains(ResourceRegistry registry,
144: Resource resource) {
145: return !right.contains(registry, resource)
146: && left.contains(registry, resource);
147: }
148:
149: public String toString() {
150: return toString(SUB);
151: }
152: });
153:
154: case LP: // Parenthesis node
155: return compile(registry, expression, compile(registry,
156: expression, node));
157:
158: case RP: // Parenthesis end
159: return node;
160:
161: case VAR: // Resource weak reference
162: return new LazyReference(registry, expression.nextToken());
163:
164: default: // Resource reference
165: return new Reference(registry, tok);
166:
167: }
168:
169: }
170:
171: /**
172: * Determines if a resource is contained in this. <p>
173: *
174: * @param resource the resource to be looked for.
175: * @return <code>true</code> if the resource is found, <code>false</code> otherwise.
176: */
177: public boolean contains(Resource resource) {
178: return contains(null, resource);
179: }
180:
181: /**
182: * Determines if a resource is contained in this. <p>
183: *
184: * @param registry a <code>ResourceRegistry</code> to resolve lazy referenced resource.
185: * @param resource the resource to be looked for.
186: * @return <code>true</code> if the resource is found, <code>false</code> otherwise.
187: */
188: public abstract boolean contains(ResourceRegistry registry,
189: Resource resource);
190:
191: }
192:
193: abstract class Node extends Expression {
194:
195: protected Expression left;
196: protected Expression right;
197:
198: protected Node(Expression left, Expression right) {
199: this .left = left;
200: this .right = right;
201: }
202:
203: /**
204: * Returns a string with the resource expression. <p>
205: *
206: * @return a string representation of this resource.
207: */
208: protected String toString(char op) {
209: StringBuffer sb = new StringBuffer();
210:
211: if (left instanceof Node) {
212: sb.append("(").append(left).append(")");
213: } else {
214: sb.append(left);
215: }
216:
217: sb.append(op);
218:
219: if (right instanceof Node) {
220: sb.append("(").append(right).append(")");
221: } else {
222: sb.append(right);
223: }
224:
225: return sb.toString();
226: }
227:
228: }
229:
230: class Reference extends Expression {
231:
232: Resource group;
233:
234: Reference(ResourceRegistry registry, String resource) {
235: this .group = registry.getResource(resource);
236: if (group == null) {
237: this .group = registry.createResourceImpl(resource, false);
238: }
239: }
240:
241: public boolean contains(ResourceRegistry registry, Resource resource) {
242: return group != null && group.contains(resource);
243: }
244:
245: public String toString() {
246: return String.valueOf(group);
247: }
248:
249: }
250:
251: class LazyReference extends Expression {
252:
253: ResourceRegistry registry;
254: String resourceId;
255:
256: LazyReference(ResourceRegistry registry, String resourceId) {
257: this .registry = registry;
258: this .resourceId = resourceId;
259: }
260:
261: public boolean contains(ResourceRegistry context, Resource resource) {
262: Resource group = (context == null) ? registry
263: .getResource(resourceId) : context
264: .getResource(resourceId);
265: return group != null && group.contains(resource);
266: }
267:
268: public String toString() {
269: return VAR + resourceId;
270: }
271:
272: }
|