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