Merge "Redundant code removal and hadrcoded strings"
[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() throws IllegalArgumentException { 
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                    throws IllegalArgumentException {
214                 if (LOGGER.isTraceEnabled())
215                         LOGGER.trace("ENTER");
216                 
217                 if (policyFile == null) {
218                         LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Null Policy File");
219                         throw new IllegalArgumentException("Null Policy File");
220                 }
221                 
222                 if (!policyFile.exists() || !policyFile.canRead()) {
223                         String msg = "Can't access " + policyFile.getAbsolutePath();
224                         LOGGER.error(XACMLErrorConstants.ERROR_PERMISSIONS + msg);
225                         throw new IllegalArgumentException(msg);
226                 }
227                 
228                 if (policyObject == null || 
229                         (!(policyObject instanceof PolicySetType) && !(policyObject instanceof PolicyType))) {
230                         String msg = "Invalid unmarshalled object: " + policyObject;
231                         LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID + msg);
232                         throw new IllegalArgumentException(msg);                        
233                 }
234                 
235                 this.policyName = FilenameUtils.removeExtension(policyFile.getName());
236                 this.rootPolicyObject = policyObject;
237                 
238                 String version = "-";           
239                 if (policyObject instanceof PolicyType) {
240                         PolicyType policy = (PolicyType) policyObject;
241                         version = policy.getVersion();
242                         htmlOut.println("<h1>Policy:   " + policyName + 
243                                                 "  (version " + version + ") </h1>");
244                         
245                 } else {
246                         PolicySetType policySet = (PolicySetType) policyObject;
247                         version = policySet.getVersion();
248                         htmlOut.println("<h1>Policy Set:   " + policyName + 
249                                                 "  (v" + version + ") </h1>");
250                 }
251                 
252                 htmlOut.println("<h3><b>Location: </b>" + policyFile.getPath() + "</h3>");
253                 htmlOut.println("<hr>");
254                 
255                 if (rootPolicyObject instanceof PolicySetType) {
256                         if (policyName.startsWith("Config_")) {
257                                 htmlOut.println("<p>This is a <b>config</b> policy set.</p>");
258                         } else if (policyName.startsWith("Action_")) {
259                                 htmlOut.println("<p>This is an <b>action</b> policy set.</p>");
260                         }
261                         htmlOut.println("<dl>");
262                 } else {
263                         if (policyName.startsWith("Config_")) {
264                                 htmlOut.println("<p>This is a <b>config</b> policy.</p>");
265                         } else if (policyName.startsWith("Action_")) {
266                                 htmlOut.println("<p>This is an <b>action</b> policy.</p>");
267                         }
268                         htmlOut.println("<ol>");
269                 }
270         }
271         
272         /**
273          * @return the attributeIdentifiersMap
274          */
275         public Map<String, AttributeIdentifiers> getAttributeIdentifiersMap() {
276                 return attributeIdentifiersMap;
277         }
278
279         @Override
280         public void onFinishScan(Object root) {
281                 if (LOGGER.isTraceEnabled())
282                         LOGGER.trace("ENTER");
283                 
284                 if (rootPolicyObject instanceof PolicySetType) {
285                         htmlOut.println("</dl>");
286                 } else {
287                         htmlOut.println("</ol>");
288                 }
289                 
290                 htmlOut.println("<hr>");
291                 
292                 htmlOut.println("<h3>Attribute Table:</h3>");
293                 
294                 htmlOut.println("<table border=\"3\" style=\"width:100%\">");
295                 htmlOut.println("<tr>");
296                 htmlOut.print("<th>Category</th>");
297                 htmlOut.print("<th>Type</th>");
298                 htmlOut.print("<th>Identifier</th>");
299                 htmlOut.println("</tr>");
300                 for(Map.Entry<String, AttributeIdentifiers> entry : this.attributeIdentifiersMap.entrySet()){
301                         AttributeIdentifiers value = entry.getValue();
302                         htmlOut.println("<tr>");
303                         htmlOut.print("<td><a name=\"" + entry.getKey() + "\"></a>" + value.category + "</td>");
304                         htmlOut.print("<td>" + value.getType() + "</td>");
305                         htmlOut.print("<td>" + value.id + "</td>");
306                         htmlOut.println("</tr>");
307                 }
308                 htmlOut.println("</table>");
309                 
310                 htmlOut.println("<p></p>");
311                 
312                 // Not necessary for the user, uncomment if desired at some point
313                 // writeRawXACML()
314                 
315                 super.onFinishScan(root);
316         }
317         
318         @SuppressWarnings("unused")
319         private void writeRawXACML() {
320                 if (LOGGER.isTraceEnabled())
321                         LOGGER.trace("ENTER");
322                 
323                 htmlOut.println("<hr>");                
324                 htmlOut.println("<h3>Raw XACML:</h3>");
325                 
326                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
327                 if (rootPolicyObject instanceof PolicySetType) {
328                         XACMLPolicyWriterWithPapNotify.writePolicyFile(bos, (PolicySetType) rootPolicyObject);
329                 } else if (rootPolicyObject instanceof PolicyType) {
330                         XACMLPolicyWriterWithPapNotify.writePolicyFile(bos, (PolicyType) rootPolicyObject);
331                 }
332                 
333                 String xacml = bos.toString();
334                 xacml = xacml.replaceAll("<", "&lt");
335                 xacml = xacml.replaceAll(">", "&gt");
336                 htmlOut.println("<pre>");
337                 htmlOut.println(xacml);
338                 htmlOut.println("</pre>");
339         }
340
341         @Override
342         public CallbackResult onPreVisitPolicySet(PolicySetType parent, PolicySetType policySet) {
343                 if (LOGGER.isTraceEnabled())
344                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Version: " + policySet.getVersion());
345                                 
346                 if (parent != null  && LOGGER.isTraceEnabled())
347                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + 
348                                              "Parent PolicySet: " + parent.getPolicySetId() + " Version: " + parent.getVersion());
349                 
350                 String description = policySet.getDescription();
351                 if (description != null && LOGGER.isTraceEnabled())
352                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + 
353                                              " Description: " + policySet.getDescription());
354                 
355                 if (parent == null) // root
356                         policySet(policySet, "dl");
357                 else
358                         policySet(policySet, "li");
359                 
360                 if (!policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty())
361                         htmlOut.println("<ol>");
362
363                 return super.onPreVisitPolicySet(parent, policySet);
364         }
365         
366         @Override
367         public CallbackResult onPostVisitPolicySet(PolicySetType parent, PolicySetType policySet) {
368                 if (LOGGER.isTraceEnabled())
369                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + " Version: " + policySet.getVersion());
370                                 
371                 if (parent != null  && LOGGER.isTraceEnabled())
372                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + 
373                                              "Parent PolicySet: " + parent.getPolicySetId() + " Version: " + parent.getVersion());
374                 
375                 String description = policySet.getDescription();
376                 if (description != null && LOGGER.isTraceEnabled())
377                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId() + 
378                                              " Description: " + policySet.getDescription());    
379                 
380                 if (!policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty())
381                         htmlOut.println("</ol>");
382                 
383                 htmlOut.println("<p></p>");
384                 
385                 return super.onPostVisitPolicySet(parent, policySet);
386         }
387         
388         public void policySet(PolicySetType policySet, String htmlListElement) {
389                 if (LOGGER.isTraceEnabled())
390                         LOGGER.trace("PolicySet: " + policySet.getPolicySetId());
391                 
392                 String combiningAlgorithm = "-";
393                 String id = "-";
394                 String version = "-";
395                 
396
397                 if (policySet.getPolicyCombiningAlgId() != null)
398                         combiningAlgorithm = extractLastIdentifier(policySet.getPolicyCombiningAlgId(), ":");
399                 
400                 if (policySet.getPolicySetId() != null)
401                         id = extractLastIdentifier(policySet.getPolicySetId(), ":");
402                 
403                 if (policySet.getVersion() != null)
404                         version = policySet.getVersion();
405                         
406                 
407                 htmlOut.println("<" + htmlListElement + "><b>Policy Set ID</b>: <i>" + id + 
408                                             "</i>  (v" + version + ") " + "</" + htmlListElement + ">");
409                 
410                 if (policySet.getTarget() == null || 
411                         policySet.getTarget().getAnyOf() == null ||
412                         policySet.getTarget().getAnyOf().size() <= 0) {
413                                 htmlOut.println("<p>This policy set applies to all requests.</p>");
414                 } else {        
415                         htmlOut.print("<p>");
416                         htmlOut.print("This policy set applies to requests with attributes ");
417                         
418                         List<AnyOfType> anyOf_s = policySet.getTarget().getAnyOf();
419                         target(anyOf_s);        
420                         htmlOut.println(".</p>");
421                 }
422                 
423                 if (policySet.getPolicySetOrPolicyOrPolicySetIdReference() != null &&
424                         !policySet.getPolicySetOrPolicyOrPolicySetIdReference().isEmpty()) {
425                         String algoDesc = combiningAlgo2human.get(combiningAlgorithm);
426                         if (algoDesc != null) {
427                                 algoDesc = algoDesc.replace("$placeholder$", "policy") + " (" + "<i>" + combiningAlgorithm + "</i>)";
428                         } else {
429                                 algoDesc = combiningAlgorithm;
430                         }
431                         
432                         htmlOut.println("<p>The result is " + algoDesc + ": </p>");
433                 }
434         }
435         
436         @Override
437         public CallbackResult onPreVisitPolicy(PolicySetType parent, PolicyType policy) {
438                 if (LOGGER.isTraceEnabled())
439                         LOGGER.trace("PolicySet: " + policy.getPolicyId() + " Version: " + policy.getVersion());
440                                 
441                 if (parent != null  && LOGGER.isTraceEnabled())
442                         LOGGER.trace("PolicySet: " + policy.getPolicyId() + 
443                                              "Parent PolicySet: " + parent.getPolicySetId() + " Version: " + parent.getVersion());
444                 
445                 String description = policy.getDescription();
446                 if (description != null && LOGGER.isTraceEnabled())
447                         LOGGER.trace("PolicySet: " + policy.getPolicyId() + 
448                                              " Description: " + policy.getDescription());
449                 
450                 policy(policy);
451                 
452                 if (!policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty())
453                         htmlOut.println("<ol type=\"i\">");
454                 
455                 return super.onPreVisitPolicy(parent, policy);
456         }
457         
458         @Override
459         public CallbackResult onPostVisitPolicy(PolicySetType parent, PolicyType policy) {
460                 if (LOGGER.isTraceEnabled())
461                         LOGGER.trace("PolicySet: " + policy.getPolicyId() + " Version: " + policy.getVersion());
462                                 
463                 if (parent != null  && LOGGER.isTraceEnabled())
464                         LOGGER.trace("PolicySet: " + policy.getPolicyId() + 
465                                              "Parent PolicySet: " + parent.getPolicySetId() + " Version: " + parent.getVersion());
466                 
467                 if (!policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty())
468                         htmlOut.println("</ol>");
469                 
470                 htmlOut.println("<p></p>");
471                 return super.onPostVisitPolicy(parent, policy);
472         }
473         
474         public void policy(PolicyType policy) {
475                 if (LOGGER.isTraceEnabled())
476                         LOGGER.trace("Policy: " + policy.getPolicyId());
477                 
478                 String combiningAlgorithm = "-";
479                 String id = "-";
480                 String version = "-";
481                 
482
483                 if (policy.getRuleCombiningAlgId() != null)
484                         combiningAlgorithm = extractLastIdentifier(policy.getRuleCombiningAlgId(), ":");
485                 
486                 if (policy.getPolicyId() != null)
487                         id = extractLastIdentifier(policy.getPolicyId(), ":");
488                 
489                 if (policy.getVersion() != null)
490                         version = policy.getVersion();
491                 
492                 htmlOut.println("<li><b>Policy ID</b>: <i>" + id + 
493                                     "</i>  (v" + version + ") " + "</li>");
494                 
495                 if (policy.getTarget() == null || 
496                         policy.getTarget().getAnyOf() == null ||
497                         policy.getTarget().getAnyOf().size() <= 0) {
498                         htmlOut.println("<p>This policy applies to all requests.</p>");
499                 } else {        
500                         htmlOut.print("<p>");
501                         htmlOut.print("This policy applies to requests with attributes ");
502                         
503                         List<AnyOfType> anyOf_s = policy.getTarget().getAnyOf();
504                         target(anyOf_s);        
505                         htmlOut.println(".</p>");
506                 }
507                 
508                 if (policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition() != null &&
509                         !policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().isEmpty()) {
510                         String algoDesc = combiningAlgo2human.get(combiningAlgorithm);
511                         if (algoDesc != null) {
512                                 algoDesc = algoDesc.replace("$placeholder$", "rule") + " (<i>" + combiningAlgorithm + "</i>)";
513                         } else {
514                                 algoDesc = combiningAlgorithm;
515                         }
516                         htmlOut.println("<p>The result is " + algoDesc + ": </p>");
517                 }
518         }
519         
520         
521         @Override
522         public CallbackResult onPreVisitRule(PolicyType parent, RuleType rule) {
523                 if (LOGGER.isTraceEnabled())
524                         LOGGER.trace("Rule: " + rule.getRuleId());
525                 
526                 if (parent != null && LOGGER.isTraceEnabled())
527                         LOGGER.trace("Parent Policy: " + parent.getPolicyId() + " Version: " + parent.getVersion());
528                 
529                 String description = rule.getDescription();
530                 if (description != null && LOGGER.isTraceEnabled()) {
531                         LOGGER.trace("Rule: " + rule.getRuleId() + 
532                                          " Description: " + rule.getDescription());
533                 }
534                 
535                 rule(rule);
536                 
537                 return super.onPreVisitRule(parent, rule);
538         }
539         
540         @Override
541         public CallbackResult onPostVisitRule(PolicyType parent, RuleType rule) {
542                 if (LOGGER.isTraceEnabled())
543                         LOGGER.trace("Rule: " + rule.getRuleId());
544                 
545                 if (parent != null && LOGGER.isTraceEnabled())
546                         LOGGER.trace("Parent Policy: " + parent.getPolicyId() + " Version: " + parent.getVersion());
547                 
548                 return super.onPostVisitRule(parent, rule);
549         }
550         
551         public void rule(RuleType rule) {
552                 if (LOGGER.isTraceEnabled())
553                         LOGGER.trace("Rule: " + rule.getRuleId());
554                 
555                 String id = "-";
556                 
557                 if (rule.getRuleId() != null)
558                         id = extractLastIdentifier(rule.getRuleId(), ":");
559                 
560                 htmlOut.println("<li><b>Rule ID</b>: <i>" + id + "</i></li>");
561                 
562                 htmlOut.println("<dl>");
563                 
564                 htmlOut.print("<p>");
565                 htmlOut.print(rule.getEffect().value());
566                 
567                 if (rule.getTarget() == null || 
568                                 rule.getTarget().getAnyOf() == null ||
569                                 rule.getTarget().getAnyOf().size() <= 0) {
570                                 htmlOut.print(" for all requests");
571                 } else {        
572                         List<AnyOfType> anyOf_s = rule.getTarget().getAnyOf();
573                         htmlOut.print(" for requests with attributes ");
574                         target(anyOf_s);        
575                 }
576                 
577                 if (rule.getCondition() != null) {
578                         htmlOut.print(" when ");
579                         htmlOut.println(this.stringifyCondition(rule.getCondition()) + " ");
580                 } else {
581                         htmlOut.print(" with no conditions ");
582                 }
583                 
584                 if (rule.getAdviceExpressions() != null) {
585                         advice(rule.getAdviceExpressions());
586                         if (rule.getObligationExpressions() != null)
587                                 htmlOut.println(" and ");
588                 }
589                 
590                 if (rule.getObligationExpressions() != null) {
591                         obligation(rule.getObligationExpressions());
592                 }
593                 
594                 htmlOut.println("</p>");
595         }
596         
597         private void advice(AdviceExpressionsType adviceExpressions) {
598                 if (LOGGER.isTraceEnabled())
599                         LOGGER.trace("ENTER");
600                 
601                 List<AdviceExpressionType> ae = adviceExpressions.getAdviceExpression();
602                 for (AdviceExpressionType expression : ae) {
603                         htmlOut.println(" with <b>advice</b> (<i>" + expression.getAdviceId() + "</i>) on <i>" +
604                                                 expression.getAppliesTo().value() + "</i>:" );
605                         htmlOut.println("<ol type=\"a\">");
606                         List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
607                         if (assignments != null) {
608                                 processAttributeAssignments(assignments);
609                         }
610                         htmlOut.println("</ol>");
611                 }
612         }
613         
614         private void obligation(ObligationExpressionsType obligationExpressions) {
615                 if (LOGGER.isTraceEnabled())
616                         LOGGER.trace("ENTER");
617                 
618                 List<ObligationExpressionType> oe = obligationExpressions.getObligationExpression();
619                 for (ObligationExpressionType expression : oe) {
620                         htmlOut.println(" with <b>obligations</b> (<i>" + expression.getObligationId() + "</i>) to be fullfilled on <i>" +
621                                                 expression.getFulfillOn().value() + "</i>:" );
622                         htmlOut.println("<ol type=\"a\">");
623                         List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
624                         if (assignments != null) {
625                                 processAttributeAssignments(assignments);
626                         }
627                         htmlOut.println("</ol>");
628                 }
629         }
630
631         /**
632          * @param assignments
633          */
634         private void processAttributeAssignments(List<AttributeAssignmentExpressionType> assignments) {
635                 if (LOGGER.isTraceEnabled())
636                         LOGGER.trace("ENTER");
637                 
638                 for (AttributeAssignmentExpressionType assignment : assignments) {
639                         String succintIdentifier = extractLastIdentifier(assignment.getCategory(), ":") +
640                                        ":" + extractLastIdentifier(assignment.getAttributeId(), ":");
641                         AttributeIdentifiers attributeIdentifiers = null;
642                         if (!this.attributeIdentifiersMap.containsKey(succintIdentifier)) {
643                                 // Note Attribute Assignments do not have an Attribute Type, assume string
644                                 // but note this case is unlikely since attributeMap should have been populated
645                                 // during parsing of target and conditions, and not in this case for Advice and
646                                 // Obligations.
647                                 attributeIdentifiers = new AttributeIdentifiers(assignment.getCategory(),
648                                                                                            "NA",
649                                                                                            assignment.getAttributeId());                        
650                                 this.attributeIdentifiersMap.put(succintIdentifier, attributeIdentifiers);
651                         }
652                         
653                         htmlOut.print("<li><i><a href=\"#" + succintIdentifier + "\">" + succintIdentifier + "</a></i> is ");
654                         // AttributeValueType
655                         JAXBElement<?> jaxbExp = assignment.getExpression();
656                         Object assignmentObject = jaxbExp.getValue();
657                         if (assignmentObject instanceof AttributeValueType) {
658                                 AttributeValueType avt = (AttributeValueType) assignmentObject;
659                                 if (attributeIdentifiers != null) {
660                                         attributeIdentifiers.setType(avt.getDataType());
661                                 }
662                                 int numContent = avt.getContent().size();
663                                 int countContent = 0;
664                                 for (Object c: avt.getContent()) {
665                                         countContent++;
666                                         htmlOut.print("<i>" + c + "</i>");
667                                         if (countContent < numContent) 
668                                                 htmlOut.print(" or ");
669                                 }
670                                 htmlOut.println("</li>");
671                         } else if (assignmentObject instanceof AttributeDesignatorType) {
672                                 htmlOut.println("NA");
673                         } else if (assignmentObject instanceof AttributeSelectorType) {
674                                 htmlOut.println("NA");
675                         } else if (assignmentObject instanceof ApplyType) {
676                                 htmlOut.println("NA");
677                         } else {                
678                                 htmlOut.println("Unexpected");
679                         }
680                 }
681         }
682
683         /**
684          * 
685          * @param anyOfList
686          */
687         public void target(List<AnyOfType> anyOfList) {
688                 if (LOGGER.isTraceEnabled())
689                         LOGGER.trace("ENTER");
690                 
691                 if (anyOfList != null) {
692                         Iterator<AnyOfType> iterAnyOf = anyOfList.iterator();
693                         String targetInHuman = "";
694                         while (iterAnyOf.hasNext()) {
695                                 AnyOfType anyOf = iterAnyOf.next();
696                                 List<AllOfType> allOfList = anyOf.getAllOf();
697                                 if (allOfList != null) {
698                                         Iterator<AllOfType> iterAllOf = allOfList.iterator();
699                                         while (iterAllOf.hasNext()) {
700                                                 AllOfType allOf = iterAllOf.next();
701                                                 List<MatchType> matchList = allOf.getMatch();
702                                                 if (matchList != null) {
703                                                         Iterator<MatchType> iterMatch = matchList.iterator();
704                                                         if (matchList.size() > 1)
705                                                                 targetInHuman += "(";
706                                                         while (iterMatch.hasNext()) {
707                                                                 MatchType match = iterMatch.next();
708                                                                 //
709                                                                 // Finally down to the actual attribute
710                                                                 //
711                                                                 StdAttribute attribute = null;
712                                                                 AttributeValueType value = match.getAttributeValue();
713                                                                 String attributeDataType;
714                                                                 if (match.getAttributeDesignator() != null && value != null) {
715                                                                         AttributeDesignatorType designator = match.getAttributeDesignator();
716                                                                         attribute = new StdAttribute(new IdentifierImpl(designator.getCategory()),
717                                                                                                                                                         new IdentifierImpl(designator.getAttributeId()),
718                                                                                                                                                         new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()), value.getContent()),
719                                                                                                                                                         designator.getIssuer(),
720                                                                                                                                                         false);
721                                                                         attributeDataType = designator.getDataType();
722                                                                 } else if (match.getAttributeSelector() != null && value != null) {
723                                                                         AttributeSelectorType selector = match.getAttributeSelector();
724                                                                         attribute = new StdAttribute(new IdentifierImpl(selector.getCategory()),
725                                                                                                                                                         new IdentifierImpl(selector.getContextSelectorId()),
726                                                                                                                                                         new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()), value.getContent()),
727                                                                                                                                                         null,
728                                                                                                                                                         false);
729                                                                         attributeDataType = selector.getDataType();
730                                                                 } else {
731                                                                         LOGGER.warn("NULL designator/selector or value for match.");
732                                                                         attributeDataType = "NA";
733                                                                 }
734                                                                 
735                                                                 String functionName = getHumanFunction(match.getMatchId());
736                                                                 if(attribute != null){
737                                                                         String succintIdentifier = extractLastIdentifier(attribute.getCategory().stringValue(), ":") +
738                                                                                         ":" + extractLastIdentifier(attribute.getAttributeId().stringValue(), ":");
739                                                                         AttributeIdentifiers ai = new AttributeIdentifiers(attribute.getCategory().stringValue(), 
740                                                                                         attributeDataType,
741                                                                                         attribute.getAttributeId().stringValue());
742                                                                         this.attributeIdentifiersMap.put(succintIdentifier,ai);
743
744                                                                         targetInHuman += "<i><a href=\"#" + succintIdentifier + "\">" + succintIdentifier + "</a></i> " + functionName + " ";
745
746                                                                         int numAttributes = attribute.getValues().size();
747                                                                         int count = 0;
748                                                                         for (AttributeValue<?> v: attribute.getValues()) {
749                                                                                 count++;                                                
750                                                                                 if (v.getValue() instanceof Collection<?>) {
751                                                                                         Collection<?> value_s = (Collection<?>) v.getValue();
752                                                                                         int numValues = value_s.size();
753                                                                                         int countValues = 0;
754                                                                                         for (Object o : value_s) {
755                                                                                                 countValues++;
756                                                                                                 targetInHuman += " <I>" + o + "</I>";
757                                                                                                 if (countValues < numValues) {
758                                                                                                         targetInHuman += ", or";
759                                                                                                 }
760                                                                                         }
761                                                                                 } else {
762                                                                                         targetInHuman += " <I>" + v.getValue() + "</I>";
763                                                                                         if (count < numAttributes) {
764                                                                                                 targetInHuman += ", or ";
765                                                                                         }
766                                                                                 }
767                                                                         }
768                                                                 }
769                                                                 
770                                                                 if (iterMatch.hasNext()) {
771                                                                         targetInHuman += " and ";
772                                                                 }
773                                                         } // end iterMatch
774                                                         if (matchList.size() > 1) {
775                                                                 targetInHuman += ")";
776                                                         }
777                                                 }
778                                                 if (iterAllOf.hasNext()) {
779                                                         targetInHuman += " or ";
780                                                 }
781                                         } // end iterAllOf 
782                                 }
783                                 if (iterAnyOf.hasNext()) {
784                                         targetInHuman = "(" + targetInHuman + ")" + " or ";
785                                 } else {
786                                         if (anyOfList.size() > 1) {
787                                                 targetInHuman += ")";
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.size() == 0)
859                                         return "";
860                                 else {
861                                         String forResult = "";
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                                                         // C: return stringifyExpression(v, result);
869                                                         forResult += stringifyExpression(v);
870                                                 }
871                                         }
872                                         return forResult; 
873                                 }
874                         }
875                         
876                         final int numExpr = (apply.getExpression() == null) ? -1 : apply.getExpression().size();                        
877                         if (numExpr <= 0) {
878                                 if (LOGGER.isDebugEnabled()) {
879                                         LOGGER.debug(functionName + " 0 expressions: " + numExpr);
880                                 }
881                                 return "";
882                         } else if (numExpr == 1) {
883                                 // eg: not
884                                 if (LOGGER.isDebugEnabled()) {
885                                         LOGGER.debug(functionName + " 1 expression: " + numExpr);
886                                 }
887                                 String applySubresult = "";
888                                 for (JAXBElement<?> e : apply.getExpression()) {
889                                         Object v = e.getValue();
890                                         if (v != null) {
891                                                 applySubresult += this.stringifyExpression(e.getValue());
892                                         }
893                                 }
894                                 return " " + removePrimitives(functionName) + " (" + applySubresult + ")";
895                         } else { 
896                                 // > 1 arguments
897                                 if (LOGGER.isDebugEnabled()) {
898                                         LOGGER.debug(functionName + " > 1 expressions: " + numExpr);
899                                 }
900                                 String applySubresult = "";
901                                 int exprCount = 0;
902                                 for (JAXBElement<?> e : apply.getExpression()) {
903                                         exprCount++;
904                                         Object ev = e.getValue();
905                                         if (ev != null) {
906                                                 if (ev instanceof ApplyType) {
907                                                         if (((ApplyType) ev).getFunctionId().contains("one-and-only")) {
908                                                                 applySubresult += this.stringifyExpression(e.getValue());
909                                                         } else {
910                                                                 applySubresult += "(" + this.stringifyExpression(e.getValue()) + ")";
911                                                         }
912                                                 } else {
913                                                         applySubresult += this.stringifyExpression(e.getValue());
914                                                 }
915                                                 
916                                                 if (exprCount < numExpr) {
917                                                         applySubresult += " " + removePrimitives(functionName) + " ";
918                                                 }
919                                         }
920                                 }
921                                 return applySubresult;
922                         }
923                 }
924                 if (expression instanceof AttributeDesignatorType) {
925                         AttributeDesignatorType adt = (AttributeDesignatorType) expression;
926                         
927                         String succintIdentifier = extractLastIdentifier(adt.getCategory(), ":") +
928                                            ":" + extractLastIdentifier(adt.getAttributeId(), ":");
929                         AttributeIdentifiers ai = new AttributeIdentifiers(adt.getCategory(), 
930                                                                                    adt.getDataType(),
931                                                                                    adt.getAttributeId());                       
932                         this.attributeIdentifiersMap.put(succintIdentifier,ai);
933
934                         return "<a href=\"#" + succintIdentifier + "\">" + succintIdentifier + "</a>";
935                 }
936                 if (expression instanceof AttributeSelectorType) {
937                         AttributeSelectorType ast = (AttributeSelectorType) expression;
938                         
939                         String attrName = ast.getPath();
940                         if (attrName == null || (attrName.length() == 0)) {
941                                 return "";
942                         }
943                         
944                         String textSelector = "/text()";
945                         if (attrName.endsWith(textSelector)) {
946                                 attrName = attrName.substring(0, attrName.length() - textSelector.length());
947                         }
948                         
949                         attrName = extractLastIdentifier(attrName, "/");
950                         attrName = extractLastIdentifier(attrName, ":");
951                         return " " + attrName;
952                 }
953                 if (expression instanceof AttributeValueType) {
954                         AttributeValueType avt = (AttributeValueType) expression;
955                         List<Object> content = avt.getContent();
956                         String value_s = "";
957                         for (Object o: content) {
958                                 value_s += " " + o.toString();
959                         }
960                         return " " + value_s;
961                 }
962                 if (expression instanceof VariableReferenceType) {
963                         //
964                         // Really unknown - the variable may or may not have been defined
965                         //
966                         return " VARIABLEREF-NOT-HANDLED";
967                 } else {
968                         throw new IllegalArgumentException("Unexpected input expression");
969                 }
970         }
971 }
972
973
974 class AttributeIdentifiers {
975         public final String category;
976         private String type;
977         public final String id;
978         
979         public AttributeIdentifiers(String category, String type, String id) {
980                 this.category = category;
981                 this.setType(type);
982                 this.id = id;
983         }
984
985         public String getType() {
986                 return type;
987         }
988
989         public void setType(String type) {
990                 this.type = type;
991         }
992 }