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