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