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