Source code: com/sun/xacml/cond/Apply.java
1
2 /*
3 * @(#)Apply.java
4 *
5 * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistribution of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistribution in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
18 * be used to endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * This software is provided "AS IS," without a warranty of any kind. ALL
22 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
23 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
24 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
25 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
26 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
27 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
28 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
29 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
30 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
31 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use in
34 * the design, construction, operation or maintenance of any nuclear facility.
35 */
36
37 package com.sun.xacml.cond;
38
39 import com.sun.xacml.EvaluationCtx;
40 import com.sun.xacml.Indenter;
41 import com.sun.xacml.ParsingException;
42 import com.sun.xacml.UnknownIdentifierException;
43
44 import com.sun.xacml.attr.AttributeDesignator;
45 import com.sun.xacml.attr.AttributeFactory;
46 import com.sun.xacml.attr.AttributeSelector;
47 import com.sun.xacml.attr.AttributeValue;
48
49 import java.io.OutputStream;
50 import java.io.PrintStream;
51
52 import java.net.URI;
53
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.Iterator;
57 import java.util.List;
58
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61
62
63 /**
64 * Represents the XACML ApplyType and ConditionType XML types.
65 *
66 * @since 1.0
67 * @author Seth Proctor
68 */
69 public class Apply implements Evaluatable
70 {
71
72 // the function used to evaluate the contents of the apply
73 private Function function;
74
75 // the paramaters to the function...ie, the contents of the apply
76 private List evals;
77
78 // an apply may have an entry that's a function for bag operations
79 private Function bagFunction;
80
81 // whether or not this is a condition
82 private boolean isCondition;
83
84 /**
85 * Constructs an <code>Apply</code> object. Throws an
86 * <code>IllegalArgumentException</code> if the given parameter list
87 * isn't valid for the given function.
88 *
89 * @param function the <code>Function</code> to use in evaluating the
90 * elements in the apply
91 * @param evals the contents of the apply which will be the parameters
92 * to the function, each of which is an
93 * <code>Evaluatable</code>
94 * @param isCondition true if this <code>Apply</code> is a Condition,
95 * false otherwise
96 */
97 public Apply(Function function, List evals, boolean isCondition)
98 throws IllegalArgumentException
99 {
100 this(function, evals, null, isCondition);
101 }
102
103 /**
104 * Constructs an <code>Apply</code> object that contains a higher-order
105 * bag function. Throws an <code>IllegalArgumentException</code> if the
106 * given parameter list isn't valid for the given function.
107 *
108 * @param function the <code>Function</code> to use in evaluating the
109 * elements in the apply
110 * @param evals the contents of the apply which will be the parameters
111 * to the function, each of which is an
112 * <code>Evaluatable</code>
113 * @param bagFunction the higher-order function to use
114 * @param isCondition true if this <code>Apply</code> is a Condition,
115 * false otherwise
116 */
117 public Apply(Function function, List evals, Function bagFunction,
118 boolean isCondition)
119 throws IllegalArgumentException
120 {
121 // check that the given inputs work for the function
122 List inputs = evals;
123 if (bagFunction != null) {
124 inputs = new ArrayList();
125 inputs.add(bagFunction);
126 inputs.addAll(evals);
127 }
128 function.checkInputs(inputs);
129
130 // if everything checks out, then store the inputs
131 this.function = function;
132 this.evals = Collections.unmodifiableList(new ArrayList(evals));
133 this.bagFunction = bagFunction;
134 this.isCondition = isCondition;
135 }
136
137 /**
138 * Returns an instance of an <code>Apply</code> based on the given DOM
139 * root node. This will actually return a special kind of
140 * <code>Apply</code>, namely an XML ConditionType, which is the root
141 * of the condition logic in a RuleType. A ConditionType is the same
142 * as an ApplyType except that it must use a FunctionId that returns
143 * a boolean value.
144 *
145 * @param root the DOM root of a ConditionType XML type
146 * @param xpathVersion the XPath version to use in any selectors or XPath
147 * functions, or null if this is unspecified (ie, not
148 * supplied in the defaults section of the policy)
149 *
150 * @throws ParsingException if this is not a valid ConditionType
151 */
152 public static Apply getConditionInstance(Node root, String xpathVersion)
153 throws ParsingException
154 {
155 return getInstance(root, FunctionFactory.getConditionInstance(), true,
156 xpathVersion);
157 }
158
159 /**
160 * Returns an instance of <code>Apply</code> based on the given DOM root.
161 *
162 * @param root the DOM root of an ApplyType XML type
163 * @param xpathVersion the XPath version to use in any selectors or XPath
164 * functions, or null if this is unspecified (ie, not
165 * supplied in the defaults section of the policy)
166 *
167 * @throws ParsingException if this is not a valid ApplyType
168 */
169 public static Apply getInstance(Node root, String xpathVersion)
170 throws ParsingException
171 {
172 return getInstance(root, FunctionFactory.getGeneralInstance(), false,
173 xpathVersion);
174 }
175
176 /**
177 * This is a helper method that is called by the two getInstance
178 * methods. It takes a factory so we know that we're getting the right
179 * kind of function.
180 */
181 private static Apply getInstance(Node root, FunctionFactory factory,
182 boolean isCondition, String xpathVersion)
183 throws ParsingException
184 {
185 Function function = getFunction(root, xpathVersion, factory);
186 Function bagFunction = null;
187 List evals = new ArrayList();
188
189 AttributeFactory attrFactory = AttributeFactory.getInstance();
190
191 NodeList nodes = root.getChildNodes();
192 for (int i = 0; i < nodes.getLength(); i++) {
193 Node node = nodes.item(i);
194 String name = node.getNodeName();
195
196 if (name.equals("Apply")) {
197 evals.add(Apply.getInstance(node, xpathVersion));
198 } else if (name.equals("AttributeValue")) {
199 try {
200 evals.add(attrFactory.createValue(node));
201 } catch (UnknownIdentifierException uie) {
202 throw new ParsingException("Unknown DataType", uie);
203 }
204 } else if (name.equals("SubjectAttributeDesignator")) {
205 evals.add(AttributeDesignator.
206 getInstance(node,
207 AttributeDesignator.SUBJECT_TARGET));
208 } else if (name.equals("ResourceAttributeDesignator")) {
209 evals.add(AttributeDesignator.
210 getInstance(node,
211 AttributeDesignator.RESOURCE_TARGET));
212 } else if (name.equals("ActionAttributeDesignator")) {
213 evals.add(AttributeDesignator.
214 getInstance(node,
215 AttributeDesignator.ACTION_TARGET));
216 } else if (name.equals("EnvironmentAttributeDesignator")) {
217 evals.add(AttributeDesignator.
218 getInstance(node,
219 AttributeDesignator.ENVIRONMENT_TARGET));
220 } else if (name.equals("AttributeSelector")) {
221 evals.add(AttributeSelector.getInstance(node, xpathVersion));
222 } else if (name.equals("Function")) {
223 // while the schema doesn't enforce this, it's illegal to
224 // have more than one FunctionType in a given ApplyType
225 if (bagFunction != null)
226 throw new ParsingException("Too many FunctionTypes");
227
228 bagFunction =
229 getFunction(node, xpathVersion,
230 FunctionFactory.getGeneralInstance());
231 }
232 }
233
234 return new Apply(function, evals, bagFunction, isCondition);
235 }
236
237 /**
238 * Helper method that tries to get a function instance
239 */
240 private static Function getFunction(Node root, String version,
241 FunctionFactory factory)
242 throws ParsingException
243 {
244 Node functionNode = root.getAttributes().getNamedItem("FunctionId");
245 String functionName = functionNode.getNodeValue();
246
247 try {
248 // try to get an instance of the given function
249 return factory.createFunction(functionName);
250 } catch (UnknownIdentifierException uie) {
251 throw new ParsingException("Unknown FunctionId in Apply", uie);
252 } catch (FunctionTypeException fte) {
253 // try creating as an abstract function, using a general factory
254 try {
255 FunctionFactory ff = FunctionFactory.getGeneralInstance();
256 return ff.createAbstractFunction(functionName, root, version);
257 } catch (Exception e) {
258 // any exception at this point is a failure
259 throw new ParsingException("failed to create abstract function"
260 + " " + functionName, e);
261 }
262 }
263 }
264
265 /**
266 * Returns the <code>Function</code> used by this <code>Apply</code>.
267 *
268 * @return the <code>Function</code>
269 */
270 public Function getFunction() {
271 return function;
272 }
273
274 /**
275 * Returns the <code>List</code> of children for this <code>Apply</code>.
276 * The <code>List</code> contains <code>Evaluatable</code>s. The list is
277 * unmodifiable, and may be empty.
278 *
279 * @return a <code>List</code> of <code>Evaluatable</code>s
280 */
281 public List getChildren() {
282 return evals;
283 }
284
285 /**
286 * Returns the higher order bag function used by this <code>Apply</code>
287 * if it exists, or null if no higher order function is used.
288 *
289 * @return the higher order <code>Function</code> or null
290 */
291 public Function getHigherOrderFunction() {
292 return bagFunction;
293 }
294
295 /**
296 * Returns whether or not this ApplyType is actually a ConditionType.
297 *
298 * @return whether or not this represents a ConditionType
299 */
300 public boolean isCondition() {
301 return isCondition;
302 }
303
304 /**
305 * Evaluates the apply object using the given function. This will in
306 * turn call evaluate on all the given parameters, some of which may be
307 * other <code>Apply</code> objects.
308 *
309 * @param context the representation of the request
310 *
311 * @return the result of trying to evaluate this apply object
312 */
313 public EvaluationResult evaluate(EvaluationCtx context) {
314 List parameters = evals;
315
316 // see if there is a higher-order function in here
317 if (bagFunction != null) {
318 // this is a special case, so we setup the parameters, starting
319 // with the function
320 parameters = new ArrayList();
321 parameters.add(bagFunction);
322
323 // now we evaluate all the parameters, returning INDETERMINATE
324 // if that's what any of them return, and otherwise tracking
325 // all the AttributeValues that get returned
326 Iterator it = evals.iterator();
327 while (it.hasNext()) {
328 Evaluatable eval = (Evaluatable)(it.next());
329 EvaluationResult result = eval.evaluate(context);
330
331 // in a higher-order case, if anything is INDETERMINATE, then
332 // we stop right away
333 if (result.indeterminate())
334 return result;
335
336 parameters.add(result.getAttributeValue());
337 }
338 }
339
340 // now we can call the base function
341 return function.evaluate(parameters, context);
342 }
343
344 /**
345 * Returns the type of attribute that this object will return on a call
346 * to <code>evaluate</code>. In practice, this will always be the same as
347 * the result of calling <code>getReturnType</code> on the function used
348 * by this object.
349 *
350 * @return the type returned by <code>evaluate</code>
351 */
352 public URI getType() {
353 return function.getReturnType();
354 }
355
356 /**
357 * Returns whether or not the <code>Function</code> will return a bag
358 * of values on evaluation.
359 *
360 * @return true if evaluation will return a bag of values, false otherwise
361 */
362 public boolean evaluatesToBag() {
363 return function.returnsBag();
364 }
365
366 /**
367 * Encodes this <code>Apply</code> into its XML representation and
368 * writes this encoding to the given <code>OutputStream</code> with no
369 * indentation.
370 *
371 * @param output a stream into which the XML-encoded data is written
372 */
373 public void encode(OutputStream output) {
374 encode(output, new Indenter(0));
375 }
376
377 /**
378 * Encodes this <code>Apply</code> into its XML representation and
379 * writes this encoding to the given <code>OutputStream</code> with
380 * indentation.
381 *
382 * @param output a stream into which the XML-encoded data is written
383 * @param indenter an object that creates indentation strings
384 */
385 public void encode(OutputStream output, Indenter indenter) {
386 PrintStream out = new PrintStream(output);
387 String indent = indenter.makeString();
388
389 if (isCondition)
390 out.println(indent + "<Condition FunctionId=\"" +
391 function.getIdentifier() + "\">");
392 else
393 out.println(indent + "<Apply FunctionId=\"" +
394 function.getIdentifier() + "\">");
395 indenter.in();
396
397 if (bagFunction != null)
398 out.println("<Function FunctionId=\"" +
399 bagFunction.getIdentifier() + "\"/>");
400
401 Iterator it = evals.iterator();
402 while (it.hasNext()) {
403 Evaluatable eval = (Evaluatable)(it.next());
404 eval.encode(output, indenter);
405 }
406
407 indenter.out();
408 if (isCondition)
409 out.println(indent + "</Condition>");
410 else
411 out.println(indent + "</Apply>");
412 }
413
414 }