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