Update css file name in conf.py
[policy/engine.git] / POLICY-SDK-APP / src / main / java / org / onap / policy / components / HumanPolicyComponent.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP Policy Engine
4  * ================================================================================
5  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019 Bell Canada
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.components;
23
24 import com.att.research.xacml.api.AttributeValue;
25 import com.att.research.xacml.std.IdentifierImpl;
26 import com.att.research.xacml.std.StdAttribute;
27 import com.att.research.xacml.std.StdAttributeValue;
28 import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult;
29 import com.att.research.xacml.util.XACMLPolicyScanner.SimpleCallback;
30
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.PrintWriter;
34 import java.io.StringWriter;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42
43 import javax.xml.bind.JAXBElement;
44
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType;
62
63 import org.apache.commons.io.FilenameUtils;
64 import org.json.JSONObject;
65 import org.onap.policy.common.logging.flexlogger.FlexLogger;
66 import org.onap.policy.common.logging.flexlogger.Logger;
67 import org.onap.policy.controller.PolicyController;
68 import org.onap.policy.rest.jpa.FunctionDefinition;
69 import org.onap.policy.xacml.api.XACMLErrorConstants;
70 import org.onap.policy.xacml.util.XACMLPolicyScanner;
71
72 public class HumanPolicyComponent {
73
74     private static final Logger LOGGER = FlexLogger.getLogger(HumanPolicyComponent.class);
75
76     // Constants Used in XML Creation
77     public static final String CATEGORY_RECIPIENT_SUBJECT =
78             "urn:oasis:names:tc:xacml:1.0:subject-category:recipient-subject";
79     public static final String CATEGORY_RESOURCE = "urn:oasis:names:tc:xacml:3.0:attribute-category:resource";
80     public static final String CATEGORY_ACTION = "urn:oasis:names:tc:xacml:3.0:attribute-category:action";
81     public static final String CATEGORY_ACCESS_SUBJECT = "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject";
82     public static final String ACTION_ID = "urn:oasis:names:tc:xacml:1.0:action:action-id";
83     public static final String SUBJECT_ID = "urn:oasis:names:tc:xacml:1.0:subject:subject-id";
84     public static final String RESOURCE_ID = "urn:oasis:names:tc:xacml:1.0:resource:resource-id";
85     public static final String FUNTION_INTEGER_ONE_AND_ONLY =
86             "urn:oasis:names:tc:xacml:1.0:function:integer-one-and-only";
87     public static final String FUNCTION_STRING_ONE_AND_ONLY =
88             "urn:oasis:names:tc:xacml:1.0:function:string-one-and-only";
89     public static final String FUNCTION_STRING_EQUAL = "urn:oasis:names:tc:xacml:1.0:function:string-equal";
90     public static final String FUNCTION_STRING_REGEX_MATCH = "org.onap.function.regex-match";
91     public static final String FUNCTION_STRING_EQUAL_IGNORE =
92             "urn:oasis:names:tc:xacml:3.0:function:string-equal-ignore-case";
93     public static final String INTEGER_DATATYPE = "http://www.w3.org/2001/XMLSchema#integer";
94     public static final String BOOLEAN_DATATYPE = "http://www.w3.org/2001/XMLSchema#boolean";
95     public static final String STRING_DATATYPE = "http://www.w3.org/2001/XMLSchema#string";
96     public static final String URI_DATATYPE = "http://www.w3.org/2001/XMLSchema#anyURI";
97     public static final String RULE_VARIABLE = "var:";
98     public static final String EMPTY_STRING = "";
99     private static final String ENTER = "ENTER";
100
101     private static HtmlProcessor htmlProcessor;
102
103     private static File policyFile;
104
105     private HumanPolicyComponent() {
106         // Default Constructor
107     }
108
109     /**
110      * DescribePolicy.
111      *
112      * @param policyFile File
113      * @return JSONObject
114      */
115     public static JSONObject DescribePolicy(final File policyFile) {
116         if (LOGGER.isTraceEnabled()) {
117             LOGGER.trace(ENTER);
118         }
119
120         HumanPolicyComponent.policyFile = policyFile;
121         return humanPolicyLayout();
122
123     }
124
125     private static JSONObject humanPolicyLayout() {
126         if (LOGGER.isTraceEnabled()) {
127             LOGGER.trace(ENTER);
128         }
129
130         try {
131             String html = processPolicy();
132             JSONObject result = new JSONObject();
133             result.put("html", html);
134             return result;
135
136         } catch (IllegalArgumentException e) {
137             LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "cannot build html area por policy", e);
138         }
139         return null;
140     }
141
142     private static String processPolicy() {
143         if (LOGGER.isTraceEnabled()) {
144             LOGGER.trace(ENTER);
145         }
146         try (FileInputStream pIS = new FileInputStream(policyFile)) {
147             Object policy = XACMLPolicyScanner.readPolicy(pIS);
148             if (policy == null) {
149                 throw new IllegalArgumentException("Policy File " + policyFile.getName() + " cannot be unmarshalled");
150             }
151
152             HumanPolicyComponent.htmlProcessor = new HtmlProcessor(HumanPolicyComponent.policyFile, policy);
153
154             Path policyPath = Paths.get(policyFile.getAbsolutePath());
155             XACMLPolicyScanner xacmlScanner = new XACMLPolicyScanner(policyPath, htmlProcessor);
156             xacmlScanner.scan();
157             String html = htmlProcessor.html();
158             if (LOGGER.isDebugEnabled()) {
159                 LOGGER.debug(policyPath + System.lineSeparator() + html);
160             }
161
162             return html;
163
164         } catch (Exception e) {
165             String msg = "Exception reading policy: " + policyFile.getAbsolutePath() + ": " + e.getMessage();
166             LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + msg, e);
167             throw new IllegalArgumentException(msg);
168         }
169     }
170
171 }
172
173
174 class HtmlProcessor extends SimpleCallback {
175
176     private static final Logger LOGGER = FlexLogger.getLogger(HtmlProcessor.class);
177
178     private static final String ENTER = "ENTER";
179     private static Map<String, String> function2human;
180
181     static {
182         function2human = new HashMap<>();
183         function2human.put(HumanPolicyComponent.FUNCTION_STRING_EQUAL, "equal");
184         function2human.put(HumanPolicyComponent.FUNCTION_STRING_EQUAL_IGNORE, "equal");
185         function2human.put(HumanPolicyComponent.FUNCTION_STRING_ONE_AND_ONLY, "one-and-only");
186         function2human.put(HumanPolicyComponent.FUNCTION_STRING_REGEX_MATCH, "matching regular expression");
187         function2human.put(HumanPolicyComponent.FUNTION_INTEGER_ONE_AND_ONLY, "one-and-only");
188     }
189
190     private static Map<String, String> combiningAlgo2human;
191
192     static {
193         combiningAlgo2human = new HashMap<>();
194         combiningAlgo2human.put("deny-overrides", "to deny if any $placeholder$ below evaluates to <i>deny</i>");
195         combiningAlgo2human.put("permit-overrides", "to permit if any $placeholder$ below evaluates to <i>permit</i>");
196
197         combiningAlgo2human.put("ordered-deny-overrides",
198                 "to deny if any $placeholder$ below evaluates to <i>deny</i>");
199         combiningAlgo2human.put("ordered-permit-overrides",
200                 "to permit if any $placeholder$ below evaluates to <i>permit</i>");
201         combiningAlgo2human.put("deny-unless-permit",
202                 "to permit if any $placeholder$ below evaluates to <i>deny</i> and not <i>indeterminate</i>");
203
204         combiningAlgo2human.put("permit-unless-deny",
205                 "to deny if any $placeholder$ below evaluates to is <i>permit</i> and not <i>indeterminate</i>");
206         combiningAlgo2human.put("first-applicable",
207                 "to honour the result of the first successfully evaluated $placeholder$ in order");
208         combiningAlgo2human.put("only-one-applicable",
209                 "to honour the result of the first successfully evaluated $placeholder$ in order");
210     }
211
212     private Map<String, AttributeIdentifiers> attributeIdentifiersMap = new HashMap<>();
213
214     private final StringWriter stringWriter = new StringWriter();
215     private final PrintWriter htmlOut = new PrintWriter(stringWriter);
216     private final String policyName;
217     private final Object rootPolicyObject;
218
219     public HtmlProcessor(File policyFile, Object policyObject) {
220         if (LOGGER.isTraceEnabled()) {
221             LOGGER.trace(ENTER);
222         }
223
224         if (policyFile == null) {
225             LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Null Policy File");
226             throw new IllegalArgumentException("Null Policy File");
227         }
228
229         if (!policyFile.exists() || !policyFile.canRead()) {
230             String msg = "Can't access " + policyFile.getAbsolutePath();
231             LOGGER.error(XACMLErrorConstants.ERROR_PERMISSIONS + msg);
232             throw new IllegalArgumentException(msg);
233         }
234
235         if ((!(policyObject instanceof PolicySetType) && !(policyObject instanceof PolicyType))) {
236             String msg = "Invalid unmarshalled object: " + policyObject;
237             LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID + msg);
238             throw new IllegalArgumentException(msg);
239         }
240
241         this.policyName = FilenameUtils.removeExtension(policyFile.getName());
242         this.rootPolicyObject = policyObject;
243
244         String version = "-";
245         if (policyObject instanceof PolicyType) {
246             PolicyType policy = (PolicyType) policyObject;
247             version = policy.getVersion();
248             htmlOut.println("<h1>Policy:   " + policyName + "  (version " + version + ") </h1>");
249
250         } else {
251             PolicySetType policySet = (PolicySetType) policyObject;
252             version = policySet.getVersion();
253             htmlOut.println("<h1>Policy Set:   " + policyName + "  (v" + version + ") </h1>");
254         }
255
256         htmlOut.println("<h3><b>Location: </b>" + policyFile.getPath() + "</h3>");
257         htmlOut.println("<hr>");
258
259         if (rootPolicyObject instanceof PolicySetType) {
260             if (policyName.startsWith("Config_")) {
261                 htmlOut.println("<p>This is a <b>config</b> policy set.</p>");
262             } else if (policyName.startsWith("Action_")) {
263                 htmlOut.println("<p>This is an <b>action</b> policy set.</p>");
264             }
265             htmlOut.println("<dl>");
266         } else {
267             if (policyName.startsWith("Config_")) {
268                 htmlOut.println("<p>This is a <b>config</b> policy.</p>");
269             } else if (policyName.startsWith("Action_")) {
270                 htmlOut.println("<p>This is an <b>action</b> policy.</p>");
271             }
272             htmlOut.println("<ol>");
273         }
274     }
275
276     /**
277      * getAttributeIdentifiersMap.
278      *
279      * @return the attributeIdentifiersMap
280      */
281     public Map<String, AttributeIdentifiers> getAttributeIdentifiersMap() {
282         return attributeIdentifiersMap;
283     }
284
285     @Override
286     public void onFinishScan(Object root) {
287         if (LOGGER.isTraceEnabled()) {
288             LOGGER.trace(ENTER);
289         }
290
291         if (rootPolicyObject instanceof PolicySetType) {
292             htmlOut.println("</dl>");
293         } else {
294             htmlOut.println("</ol>");
295         }
296
297         htmlOut.println("<hr>");
298
299         htmlOut.println("<h3>Attribute Table:</h3>");
300
301         htmlOut.println("<table border=\"3\" style=\"width:100%\">");
302         htmlOut.println("<tr>");
303         htmlOut.print("<th>Category</th>");
304         htmlOut.print("<th>Type</th>");
305         htmlOut.print("<th>Identifier</th>");
306         htmlOut.println("</tr>");
307         for (Map.Entry<String, AttributeIdentifiers> entry : this.attributeIdentifiersMap.entrySet()) {
308             AttributeIdentifiers value = entry.getValue();
309             htmlOut.println("<tr>");
310             htmlOut.print("<td><a name=\"" + entry.getKey() + "\"></a>" + value.category + "</td>");
311             htmlOut.print("<td>" + value.getType() + "</td>");
312             htmlOut.print("<td>" + value.id + "</td>");
313             htmlOut.println("</tr>");
314         }
315         htmlOut.println("</table>");
316
317         htmlOut.println("<p></p>");
318
319         // Not necessary for the user, uncomment if desired at some point
320         // writeRawXACML()
321
322         super.onFinishScan(root);
323     }
324
325     @Override
326     public CallbackResult onPreVisitPolicySet(PolicySetType parent, PolicySetType policySet) {
327         if (LOGGER.isTraceEnabled()) {
328             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Version: " + policySet.getVersion());
329         }
330
331         if (parent != null && LOGGER.isTraceEnabled()) {
332             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + "Parent PolicySet: " + parent.getPolicySetId()
333                     + " Version: " + parent.getVersion());
334         }
335
336         String description = policySet.getDescription();
337         if (description != null && LOGGER.isTraceEnabled()) {
338             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Description: " + policySet.getDescription());
339         }
340
341         if (parent == null) { // root
342             policySet(policySet, "dl");
343         } else {
344             policySet(policySet, "li");
345         }
346
347         if (!policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty()) {
348             htmlOut.println("<ol>");
349         }
350
351         return super.onPreVisitPolicySet(parent, policySet);
352     }
353
354     @Override
355     public CallbackResult onPostVisitPolicySet(PolicySetType parent, PolicySetType policySet) {
356         if (LOGGER.isTraceEnabled()) {
357             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Version: " + policySet.getVersion());
358         }
359
360         if (parent != null && LOGGER.isTraceEnabled()) {
361             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + "Parent PolicySet: " + parent.getPolicySetId()
362                     + " Version: " + parent.getVersion());
363         }
364
365         String description = policySet.getDescription();
366         if (description != null && LOGGER.isTraceEnabled()) {
367             LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Description: " + policySet.getDescription());
368         }
369
370         if (!policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty()) {
371             htmlOut.println("</ol>");
372         }
373
374         htmlOut.println("<p></p>");
375
376         return super.onPostVisitPolicySet(parent, policySet);
377     }
378
379     public void policySet(PolicySetType policySet, String htmlListElement) {
380         if (LOGGER.isTraceEnabled()) {
381             LOGGER.trace("PolicySet: " + policySet.getPolicySetId());
382         }
383
384         String combiningAlgorithm = "-";
385         String id = "-";
386         String version = "-";
387
388         if (policySet.getPolicyCombiningAlgId() != null) {
389             combiningAlgorithm = extractLastIdentifier(policySet.getPolicyCombiningAlgId(), ":");
390         }
391
392         if (policySet.getPolicySetId() != null) {
393             id = extractLastIdentifier(policySet.getPolicySetId(), ":");
394         }
395
396         if (policySet.getVersion() != null) {
397             version = policySet.getVersion();
398         }
399
400         htmlOut.println("<" + htmlListElement + "><b>Policy Set ID</b>: <i>" + id + "</i>  (v" + version + ") " + "</"
401                 + htmlListElement + ">");
402
403         if (policySet.getTarget() == null || policySet.getTarget().getAnyOf() == null
404                 || policySet.getTarget().getAnyOf().isEmpty()) {
405             htmlOut.println("<p>This policy set applies to all requests.</p>");
406         } else {
407             htmlOut.print("<p>");
408             htmlOut.print("This policy set applies to requests with attributes ");
409
410             target(policySet.getTarget().getAnyOf());
411             htmlOut.println(".</p>");
412         }
413
414         if (policySet.getPolicySetOrPolicyOrPolicySetIdReference() != null
415                 && !policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty()) {
416             String algoDesc = combiningAlgo2human.get(combiningAlgorithm);
417             if (algoDesc != null) {
418                 algoDesc = algoDesc.replace("$placeholder$", "policy") + " (" + "<i>" + combiningAlgorithm + "</i>)";
419             } else {
420                 algoDesc = combiningAlgorithm;
421             }
422
423             htmlOut.println("<p>The result is " + algoDesc + ": </p>");
424         }
425     }
426
427     @Override
428     public CallbackResult onPreVisitPolicy(PolicySetType parent, PolicyType policy) {
429         if (LOGGER.isTraceEnabled()) {
430             LOGGER.trace("PolicySet: " + policy.getPolicyId() + " Version: " + policy.getVersion());
431         }
432
433         if (parent != null && LOGGER.isTraceEnabled()) {
434             LOGGER.trace("PolicySet: " + policy.getPolicyId() + "Parent PolicySet: " + parent.getPolicySetId()
435                     + " Version: " + parent.getVersion());
436         }
437
438         String description = policy.getDescription();
439         if (description != null && LOGGER.isTraceEnabled()) {
440             LOGGER.trace("PolicySet: " + policy.getPolicyId() + " Description: " + policy.getDescription());
441         }
442
443         policy(policy);
444
445         if (!policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty()) {
446             htmlOut.println("<ol type=\"i\">");
447         }
448
449         return super.onPreVisitPolicy(parent, policy);
450     }
451
452     @Override
453     public CallbackResult onPostVisitPolicy(PolicySetType parent, PolicyType policy) {
454         if (LOGGER.isTraceEnabled()) {
455             LOGGER.trace("PolicySet: " + policy.getPolicyId() + " Version: " + policy.getVersion());
456         }
457
458         if (parent != null && LOGGER.isTraceEnabled()) {
459             LOGGER.trace("PolicySet: " + policy.getPolicyId() + "Parent PolicySet: " + parent.getPolicySetId()
460                     + " Version: " + parent.getVersion());
461         }
462
463         if (!policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty()) {
464             htmlOut.println("</ol>");
465         }
466
467         htmlOut.println("<p></p>");
468         return super.onPostVisitPolicy(parent, policy);
469     }
470
471     public void policy(PolicyType policy) {
472         if (LOGGER.isTraceEnabled()) {
473             LOGGER.trace("Policy: " + policy.getPolicyId());
474         }
475
476         String combiningAlgorithm = "-";
477         String id = "-";
478         String version = "-";
479
480         if (policy.getRuleCombiningAlgId() != null) {
481             combiningAlgorithm = extractLastIdentifier(policy.getRuleCombiningAlgId(), ":");
482         }
483
484         if (policy.getPolicyId() != null) {
485             id = extractLastIdentifier(policy.getPolicyId(), ":");
486         }
487
488         if (policy.getVersion() != null) {
489             version = policy.getVersion();
490         }
491
492         htmlOut.println("<li><b>Policy ID</b>: <i>" + id + "</i>  (v" + version + ") " + "</li>");
493
494         if (policy.getTarget() == null || policy.getTarget().getAnyOf() == null
495                 || policy.getTarget().getAnyOf().isEmpty()) {
496             htmlOut.println("<p>This policy applies to all requests.</p>");
497         } else {
498             htmlOut.print("<p>");
499             htmlOut.print("This policy applies to requests with attributes ");
500
501             target(policy.getTarget().getAnyOf());
502             htmlOut.println(".</p>");
503         }
504
505         if (policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition() != null
506                 && !policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty()) {
507             String algoDesc = combiningAlgo2human.get(combiningAlgorithm);
508             if (algoDesc != null) {
509                 algoDesc = algoDesc.replace("$placeholder$", "rule") + " (<i>" + combiningAlgorithm + "</i>)";
510             } else {
511                 algoDesc = combiningAlgorithm;
512             }
513             htmlOut.println("<p>The result is " + algoDesc + ": </p>");
514         }
515     }
516
517     @Override
518     public CallbackResult onPreVisitRule(PolicyType parent, RuleType rule) {
519         if (LOGGER.isTraceEnabled()) {
520             LOGGER.trace("Rule: " + rule.getRuleId());
521         }
522
523         if (parent != null && LOGGER.isTraceEnabled()) {
524             LOGGER.trace("Parent Policy: " + parent.getPolicyId() + " Version: " + parent.getVersion());
525         }
526
527         String description = rule.getDescription();
528         if (description != null && LOGGER.isTraceEnabled()) {
529             LOGGER.trace("Rule: " + rule.getRuleId() + " Description: " + rule.getDescription());
530         }
531
532         rule(rule);
533
534         return super.onPreVisitRule(parent, rule);
535     }
536
537     @Override
538     public CallbackResult onPostVisitRule(PolicyType parent, RuleType rule) {
539
540         if (parent != null && LOGGER.isTraceEnabled()) {
541             LOGGER.trace("Parent Policy: " + parent.getPolicyId() + " Version: " + parent.getVersion());
542         }
543
544         return super.onPostVisitRule(parent, rule);
545     }
546
547     public void rule(RuleType rule) {
548
549         String id = "-";
550
551         if (rule.getRuleId() != null) {
552             id = extractLastIdentifier(rule.getRuleId(), ":");
553         }
554
555         htmlOut.println("<li><b>Rule ID</b>: <i>" + id + "</i></li>");
556
557         htmlOut.println("<dl>");
558
559         htmlOut.print("<p>");
560         htmlOut.print(rule.getEffect().value());
561
562         if (rule.getTarget() == null || rule.getTarget().getAnyOf() == null || rule.getTarget().getAnyOf().isEmpty()) {
563             htmlOut.print(" for all requests");
564         } else {
565             htmlOut.print(" for requests with attributes ");
566             target(rule.getTarget().getAnyOf());
567         }
568
569         if (rule.getCondition() != null) {
570             htmlOut.print(" when ");
571             htmlOut.println(this.stringifyCondition(rule.getCondition()) + " ");
572         } else {
573             htmlOut.print(" with no conditions ");
574         }
575
576         if (rule.getAdviceExpressions() != null) {
577             advice(rule.getAdviceExpressions());
578             if (rule.getObligationExpressions() != null) {
579                 htmlOut.println(" and ");
580             }
581         }
582
583         if (rule.getObligationExpressions() != null) {
584             obligation(rule.getObligationExpressions());
585         }
586
587         htmlOut.println("</p>");
588     }
589
590     private void advice(AdviceExpressionsType adviceExpressions) {
591         if (LOGGER.isTraceEnabled()) {
592             LOGGER.trace(ENTER);
593         }
594
595         List<AdviceExpressionType> ae = adviceExpressions.getAdviceExpression();
596         for (AdviceExpressionType expression : ae) {
597             htmlOut.println(" with <b>advice</b> (<i>" + expression.getAdviceId() + "</i>) on <i>"
598                     + expression.getAppliesTo().value() + "</i>:");
599             htmlOut.println("<ol type=\"a\">");
600             List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
601             if (assignments != null) {
602                 processAttributeAssignments(assignments);
603             }
604             htmlOut.println("</ol>");
605         }
606     }
607
608     private void obligation(ObligationExpressionsType obligationExpressions) {
609         if (LOGGER.isTraceEnabled()) {
610             LOGGER.trace(ENTER);
611         }
612
613         List<ObligationExpressionType> oe = obligationExpressions.getObligationExpression();
614         for (ObligationExpressionType expression : oe) {
615             htmlOut.println(" with <b>obligations</b> (<i>" + expression.getObligationId()
616                     + "</i>) to be fullfilled on <i>" + expression.getFulfillOn().value() + "</i>:");
617             htmlOut.println("<ol type=\"a\">");
618             List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
619             if (assignments != null) {
620                 processAttributeAssignments(assignments);
621             }
622             htmlOut.println("</ol>");
623         }
624     }
625
626     /**
627      * processAttributeAssignments.
628      *
629      * @param assignments List of AttributeAssignmentExpressionType
630      */
631     private void processAttributeAssignments(List<AttributeAssignmentExpressionType> assignments) {
632         if (LOGGER.isTraceEnabled()) {
633             LOGGER.trace(ENTER);
634         }
635
636         for (AttributeAssignmentExpressionType assignment : assignments) {
637             String succintIdentifier = extractLastIdentifier(assignment.getCategory(), ":") + ":"
638                     + extractLastIdentifier(assignment.getAttributeId(), ":");
639             AttributeIdentifiers attributeIdentifiers = null;
640             if (!this.attributeIdentifiersMap.containsKey(succintIdentifier)) {
641                 // Note Attribute Assignments do not have an Attribute Type, assume string
642                 // but note this case is unlikely since attributeMap should have been populated
643                 // during parsing of target and conditions, and not in this case for Advice and
644                 // Obligations.
645                 attributeIdentifiers =
646                         new AttributeIdentifiers(assignment.getCategory(), "NA", assignment.getAttributeId());
647                 this.attributeIdentifiersMap.put(succintIdentifier, attributeIdentifiers);
648             }
649
650             htmlOut.print("<li><i><a href=\"#" + succintIdentifier + "\">" + succintIdentifier + "</a></i> is ");
651             // AttributeValueType
652             JAXBElement<?> jaxbExp = assignment.getExpression();
653             Object assignmentObject = jaxbExp.getValue();
654             if (assignmentObject instanceof AttributeValueType) {
655                 AttributeValueType avt = (AttributeValueType) assignmentObject;
656                 if (attributeIdentifiers != null) {
657                     attributeIdentifiers.setType(avt.getDataType());
658                 }
659                 int numContent = avt.getContent().size();
660                 int countContent = 0;
661                 for (Object c : avt.getContent()) {
662                     countContent++;
663                     htmlOut.print("<i>" + c + "</i>");
664                     if (countContent < numContent) {
665                         htmlOut.print(" or ");
666                     }
667                 }
668                 htmlOut.println("</li>");
669             } else if (assignmentObject instanceof AttributeDesignatorType
670                     || assignmentObject instanceof AttributeSelectorType || assignmentObject instanceof ApplyType) {
671                 htmlOut.println("NA");
672             } else {
673                 htmlOut.println("Unexpected");
674             }
675         }
676     }
677
678     /**
679      * target.
680      *
681      * @param anyOfList List of AnyOfType's
682      */
683     public void target(List<AnyOfType> anyOfList) {
684         if (LOGGER.isTraceEnabled()) {
685             LOGGER.trace(ENTER);
686         }
687
688         if (anyOfList == null) {
689             return;
690         }
691         Iterator<AnyOfType> iterAnyOf = anyOfList.iterator();
692         StringBuilder targetInHuman = new StringBuilder();
693         while (iterAnyOf.hasNext()) {
694             AnyOfType anyOf = iterAnyOf.next();
695             List<AllOfType> allOfList = anyOf.getAllOf();
696             Iterator<AllOfType> iterAllOf = allOfList.iterator();
697             while (iterAllOf.hasNext()) {
698                 AllOfType allOf = iterAllOf.next();
699                 List<MatchType> matchList = allOf.getMatch();
700                 Iterator<MatchType> iterMatch = matchList.iterator();
701                 if (matchList.size() > 1) {
702                     targetInHuman.append("(");
703                 }
704                 while (iterMatch.hasNext()) {
705                     MatchType match = iterMatch.next();
706                     //
707                     // Finally down to the actual attribute
708                     //
709                     StdAttribute attribute = null;
710                     AttributeValueType value = match.getAttributeValue();
711                     String attributeDataType;
712                     if (match.getAttributeDesignator() != null && value != null) {
713                         AttributeDesignatorType designator = match.getAttributeDesignator();
714                         attribute = new StdAttribute(new IdentifierImpl(designator.getCategory()),
715                                 new IdentifierImpl(designator.getAttributeId()),
716                                 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()),
717                                         value.getContent()),
718                                 designator.getIssuer(), false);
719                         attributeDataType = designator.getDataType();
720                     } else if (match.getAttributeSelector() != null && value != null) {
721                         AttributeSelectorType selector = match.getAttributeSelector();
722                         attribute = new StdAttribute(new IdentifierImpl(selector.getCategory()),
723                                 new IdentifierImpl(selector.getContextSelectorId()),
724                                 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()),
725                                         value.getContent()),
726                                 null, false);
727                         attributeDataType = selector.getDataType();
728                     } else {
729                         LOGGER.warn("NULL designator/selector or value for match.");
730                         attributeDataType = "NA";
731                     }
732
733                     String functionName = getHumanFunction(match.getMatchId());
734                     if (attribute != null) {
735                         String succintIdentifier = extractLastIdentifier(
736                                 attribute.getCategory().stringValue(), ":") + ":"
737                                 + extractLastIdentifier(attribute.getAttributeId().stringValue(), ":");
738                         AttributeIdentifiers ai =
739                                 new AttributeIdentifiers(attribute.getCategory().stringValue(),
740                                         attributeDataType, attribute.getAttributeId().stringValue());
741                         this.attributeIdentifiersMap.put(succintIdentifier, ai);
742
743                         targetInHuman.append("<i><a href=\"#" + succintIdentifier + "\">"
744                                 + succintIdentifier + "</a></i> " + functionName + " ");
745
746                         int numAttributes = attribute.getValues().size();
747                         int count = 0;
748                         for (AttributeValue<?> v : attribute.getValues()) {
749                             count++;
750                             if (v.getValue() instanceof Collection<?>) {
751                                 Collection<?> collectionValues = (Collection<?>) v.getValue();
752                                 int numValues = collectionValues.size();
753                                 int countValues = 0;
754                                 for (Object o : collectionValues) {
755                                     countValues++;
756                                     targetInHuman.append(" <I>" + o + "</I>");
757                                     if (countValues < numValues) {
758                                         targetInHuman.append(", or");
759                                     }
760                                 }
761                             } else {
762                                 targetInHuman.append(" <I>" + v.getValue() + "</I>");
763                                 if (count < numAttributes) {
764                                     targetInHuman.append(", or ");
765                                 }
766                             }
767                         }
768                     }
769
770                     if (iterMatch.hasNext()) {
771                         targetInHuman.append(" and ");
772                     }
773                 } // end iterMatch
774                 if (matchList.size() > 1) {
775                     targetInHuman.append(")");
776                 }
777             }
778             if (iterAllOf.hasNext()) {
779                 targetInHuman.append(" or ");
780             }
781         } // end iterAllOf
782         if (iterAnyOf.hasNext()) {
783             targetInHuman = new StringBuilder();
784             targetInHuman.append("(" + targetInHuman + ")" + " or ");
785         } else {
786             if (anyOfList.size() > 1) {
787                 targetInHuman.append(")");
788             }
789         }
790         htmlOut.println(targetInHuman);
791     }
792
793     private String getHumanFunction(String matchId) {
794         if (HtmlProcessor.function2human.containsKey(matchId)) {
795             return HtmlProcessor.function2human.get(matchId);
796         }
797
798         FunctionDefinition function = PolicyController.getFunctionIdMap().get(matchId);
799         String functionName = function.getShortname();
800
801         if (LOGGER.isDebugEnabled()) {
802             LOGGER.debug(functionName + ": #args[" + function.getArgLb() + "," + function.getArgUb() + "]");
803         }
804
805         return extractLastIdentifier(removePrimitives(functionName), ":");
806     }
807
808     public String html() {
809         this.htmlOut.flush();
810         return this.stringWriter.toString();
811     }
812
813     private String extractLastIdentifier(String in, String separator) {
814         int lastIndex = in.lastIndexOf(separator);
815         if (lastIndex < 0) {
816             return in;
817         } else {
818             return in.substring(lastIndex + 1);
819         }
820     }
821
822     private String removePrimitives(String in) {
823         String newIn = in;
824         newIn = newIn.replace("string-", "");
825         newIn = newIn.replace("integer-", "");
826         newIn = newIn.replace("double-", "");
827         newIn = newIn.replace("boolean-", "");
828         return newIn;
829     }
830
831     private String stringifyCondition(ConditionType condition) {
832         if (condition.getExpression() == null) {
833             return "";
834         }
835
836         return stringifyExpression(condition.getExpression().getValue());
837     }
838
839     private String stringifyExpression(Object expression) {
840         if (expression instanceof ApplyType) {
841             ApplyType apply = (ApplyType) expression;
842             FunctionDefinition function = PolicyController.getFunctionIdMap().get(apply.getFunctionId());
843             String functionName = function.getShortname();
844
845             if (LOGGER.isDebugEnabled()) {
846                 LOGGER.debug(functionName + ": #args[" + function.getArgLb() + "," + function.getArgUb() + "]");
847             }
848
849             if (functionName.contains("one-and-only")) {
850                 if (LOGGER.isDebugEnabled()) {
851                     LOGGER.debug("one-and-only found: " + functionName);
852                 }
853
854                 List<JAXBElement<?>> exps = apply.getExpression();
855                 if (exps == null || exps.isEmpty()) {
856                     return "";
857                 } else {
858                     StringBuilder forResult = new StringBuilder();
859                     for (JAXBElement<?> e : exps) {
860                         Object theValue = e.getValue();
861                         if (LOGGER.isDebugEnabled()) {
862                             LOGGER.debug("one-and-only children: " + theValue);
863                         }
864                         if (theValue != null) {
865                             forResult.append(stringifyExpression(theValue));
866                         }
867                     }
868                     return forResult.toString();
869                 }
870             }
871
872             final int numExpr = (apply.getExpression() == null) ? -1 : apply.getExpression().size();
873             if (numExpr <= 0) {
874                 if (LOGGER.isDebugEnabled()) {
875                     LOGGER.debug(functionName + " 0 expressions: " + numExpr);
876                 }
877                 return "";
878             } else if (numExpr == 1) {
879                 // eg: not
880                 if (LOGGER.isDebugEnabled()) {
881                     LOGGER.debug(functionName + " 1 expression: " + numExpr);
882                 }
883                 StringBuilder applySubresult = new StringBuilder();
884                 for (JAXBElement<?> e : apply.getExpression()) {
885                     Object theValue = e.getValue();
886                     if (theValue != null) {
887                         applySubresult.append(this.stringifyExpression(e.getValue()));
888                     }
889                 }
890                 return " " + removePrimitives(functionName) + " (" + applySubresult.toString() + ")";
891             } else {
892                 // > 1 arguments
893                 if (LOGGER.isDebugEnabled()) {
894                     LOGGER.debug(functionName + " > 1 expressions: " + numExpr);
895                 }
896                 StringBuilder applySubresult = new StringBuilder();
897                 int exprCount = 0;
898                 for (JAXBElement<?> e : apply.getExpression()) {
899                     exprCount++;
900                     Object ev = e.getValue();
901                     if (ev != null) {
902                         if (ev instanceof ApplyType) {
903                             if (((ApplyType) ev).getFunctionId().contains("one-and-only")) {
904                                 applySubresult.append(this.stringifyExpression(e.getValue()));
905                             } else {
906                                 applySubresult.append("(" + this.stringifyExpression(e.getValue()) + ")");
907                             }
908                         } else {
909                             applySubresult.append(this.stringifyExpression(e.getValue()));
910                         }
911
912                         if (exprCount < numExpr) {
913                             applySubresult.append(" " + removePrimitives(functionName) + " ");
914                         }
915                     }
916                 }
917                 return applySubresult.toString();
918             }
919         }
920         if (expression instanceof AttributeDesignatorType) {
921             AttributeDesignatorType adt = (AttributeDesignatorType) expression;
922
923             String succintIdentifier = extractLastIdentifier(adt.getCategory(), ":") + ":"
924                     + extractLastIdentifier(adt.getAttributeId(), ":");
925             AttributeIdentifiers ai =
926                     new AttributeIdentifiers(adt.getCategory(), adt.getDataType(), adt.getAttributeId());
927             this.attributeIdentifiersMap.put(succintIdentifier, ai);
928
929             return "<a href=\"#" + succintIdentifier + "\">" + succintIdentifier + "</a>";
930         }
931         if (expression instanceof AttributeSelectorType) {
932             AttributeSelectorType ast = (AttributeSelectorType) expression;
933
934             String attrName = ast.getPath();
935             if (attrName == null || (attrName.length() == 0)) {
936                 return "";
937             }
938
939             String textSelector = "/text()";
940             if (attrName.endsWith(textSelector)) {
941                 attrName = attrName.substring(0, attrName.length() - textSelector.length());
942             }
943
944             attrName = extractLastIdentifier(attrName, "/");
945             attrName = extractLastIdentifier(attrName, ":");
946             return " " + attrName;
947         }
948         if (expression instanceof AttributeValueType) {
949             AttributeValueType avt = (AttributeValueType) expression;
950             List<Object> content = avt.getContent();
951             StringBuilder stringValue = new StringBuilder(" ");
952             for (Object o : content) {
953                 stringValue.append(" ");
954                 stringValue.append(o.toString());
955             }
956             return stringValue.toString();
957         }
958         if (expression instanceof VariableReferenceType) {
959             //
960             // Really unknown - the variable may or may not have been defined
961             //
962             return " VARIABLEREF-NOT-HANDLED";
963         } else {
964             throw new IllegalArgumentException("Unexpected input expression");
965         }
966     }
967 }
968
969
970 class AttributeIdentifiers {
971     public final String category;
972     private String type;
973     public final String id;
974
975     public AttributeIdentifiers(String category, String type, String id) {
976         this.category = category;
977         this.setType(type);
978         this.id = id;
979     }
980
981     public String getType() {
982         return type;
983     }
984
985     public void setType(String type) {
986         this.type = type;
987     }
988 }