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