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