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