Coverage Report - org.openpermis.policy.io.xml.PermisXmlReader
 
Classes in this File Line Coverage Branch Coverage Complexity
PermisXmlReader
85%
398/465
77%
181/234
6.314
 
 1  
 /*
 2  
  * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport
 3  
  *                     (http://www.vbs.admin.ch)
 4  
  * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch)
 5  
  * All rights reserved.
 6  
  *
 7  
  * Licensed under the Open Permis License which accompanies this distribution,
 8  
  * and is available at http://www.openpermis.org/BSDlicenceKent.txt
 9  
  */
 10  
 package org.openpermis.policy.io.xml;
 11  
 
 12  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ABSOLUTE_PERIOD_ELEMENT;
 13  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ACTION_POLICY_ELEMENT;
 14  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ACTION_REF_ELEMENT;
 15  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ACTION_SPEC_ELEMENT;
 16  
 import static org.openpermis.policy.io.xml.PermisXmlTags.AND_ELEMENT;
 17  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ARGUMENT_REF_ELEMENT;
 18  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ARGUMENT_SPEC_ELEMENT;
 19  
 import static org.openpermis.policy.io.xml.PermisXmlTags.BOOLEAN_TYPE;
 20  
 import static org.openpermis.policy.io.xml.PermisXmlTags.CONSTANT_ELEMENT;
 21  
 import static org.openpermis.policy.io.xml.PermisXmlTags.CURRENT_TIME_ELEMENT;
 22  
 import static org.openpermis.policy.io.xml.PermisXmlTags.DELEGATE_ELEMENT;
 23  
 import static org.openpermis.policy.io.xml.PermisXmlTags.DEPTH_ATTRIBUTE;
 24  
 import static org.openpermis.policy.io.xml.PermisXmlTags.DOUBLE_TYPE;
 25  
 import static org.openpermis.policy.io.xml.PermisXmlTags.DURATION_TYPE;
 26  
 import static org.openpermis.policy.io.xml.PermisXmlTags.END_ATTRIBUTE;
 27  
 import static org.openpermis.policy.io.xml.PermisXmlTags.EQUAL_ELEMENT;
 28  
 import static org.openpermis.policy.io.xml.PermisXmlTags.GREATER_EQUAL_ELEMENT;
 29  
 import static org.openpermis.policy.io.xml.PermisXmlTags.GREATER_THAN_ELEMENT;
 30  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ID_ATTRIBUTE;
 31  
 import static org.openpermis.policy.io.xml.PermisXmlTags.IF_ELEMENT;
 32  
 import static org.openpermis.policy.io.xml.PermisXmlTags.INCLUDE_ELEMENT;
 33  
 import static org.openpermis.policy.io.xml.PermisXmlTags.INTEGER_TYPE;
 34  
 import static org.openpermis.policy.io.xml.PermisXmlTags.LDAPDN_ATTRIBUTE;
 35  
 import static org.openpermis.policy.io.xml.PermisXmlTags.LESS_EQUAL_ELEMENT;
 36  
 import static org.openpermis.policy.io.xml.PermisXmlTags.LESS_THAN_ELEMENT;
 37  
 import static org.openpermis.policy.io.xml.PermisXmlTags.MAXIMUM_VALID_UP_TO_ELEMENT;
 38  
 import static org.openpermis.policy.io.xml.PermisXmlTags.MINIMUM_VALID_FROM_ELEMENT;
 39  
 import static org.openpermis.policy.io.xml.PermisXmlTags.MINIMUM_VALID_UP_TO_ELEMENT;
 40  
 import static org.openpermis.policy.io.xml.PermisXmlTags.NAME_ATTRIBUTE;
 41  
 import static org.openpermis.policy.io.xml.PermisXmlTags.NON_NULL_INTERSECTION_ELEMENT;
 42  
 import static org.openpermis.policy.io.xml.PermisXmlTags.NOT_ELEMENT;
 43  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OR_ELEMENT;
 44  
 import static org.openpermis.policy.io.xml.PermisXmlTags.PERMIS_POLICY_ELEMENT;
 45  
 import static org.openpermis.policy.io.xml.PermisXmlTags.PRESENT_ELEMENT;
 46  
 import static org.openpermis.policy.io.xml.PermisXmlTags.RESOURCE_DOMAIN_POLICY_ELEMENT;
 47  
 import static org.openpermis.policy.io.xml.PermisXmlTags.RESOURCE_DOMAIN_REF_ELEMENT;
 48  
 import static org.openpermis.policy.io.xml.PermisXmlTags.RESOURCE_DOMAIN_SPEC_ELEMENT;
 49  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_ASSIGNMENT_POLICY_ELEMENT;
 50  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_ASSIGNMENT_RULE_ELEMENT;
 51  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_ELEMENT;
 52  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_HIERARCHY_ELEMENT;
 53  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_HIERARCHY_POLICY_ELEMENT;
 54  
 import static org.openpermis.policy.io.xml.PermisXmlTags.ROLE_LIST_ELEMENT;
 55  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OBLIGATION_POLICY_ELEMENT;
 56  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OBLIGATION_SPEC_ELEMENT;
 57  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OBLIGATION_LIST_ELEMENT;
 58  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OBLIGATION_REF_ELEMENT;
 59  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TEXT_ATTRIBUTE;
 60  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SET_ELEMENT;
 61  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SOA_ELEMENT;
 62  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SOA_POLICY_ELEMENT;
 63  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SOA_SPEC_ELEMENT;
 64  
 import static org.openpermis.policy.io.xml.PermisXmlTags.START_ATTRIBUTE;
 65  
 import static org.openpermis.policy.io.xml.PermisXmlTags.STRING_TYPE;
 66  
 import static org.openpermis.policy.io.xml.PermisXmlTags.OBJECT_TYPE;
 67  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUBJECT_DOMAIN_POLICY_ELEMENT;
 68  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUBJECT_DOMAIN_REF_ELEMENT;
 69  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUBJECT_DOMAIN_SPEC_ELEMENT;
 70  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUBSET_ELEMENT;
 71  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUBSTRING_OF_ELEMENT;
 72  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUB_ROLE_ELEMENT;
 73  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUPERSET_ELEMENT;
 74  
 import static org.openpermis.policy.io.xml.PermisXmlTags.SUPER_ROLE_ELEMENT;
 75  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_ACCESS_POLICY_ELEMENT;
 76  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_ACCESS_RULE_ELEMENT;
 77  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_LIST_ELEMENT;
 78  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_POLICY_ELEMENT;
 79  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_REF_ELEMENT;
 80  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TARGET_SPEC_ELEMENT;
 81  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TIME_TYPE;
 82  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TIME_ZONE_ATTRIBUTE;
 83  
 import static org.openpermis.policy.io.xml.PermisXmlTags.TYPE_ATTRIBUTE;
 84  
 import static org.openpermis.policy.io.xml.PermisXmlTags.URL_ATTRIBUTE;
 85  
 import static org.openpermis.policy.io.xml.PermisXmlTags.VALIDITY_ELEMENT;
 86  
 import static org.openpermis.policy.io.xml.PermisXmlTags.VALUE_ATTRIBUTE;
 87  
 
 88  
 import java.io.Reader;
 89  
 import java.io.UnsupportedEncodingException;
 90  
 import java.net.URI;
 91  
 import java.net.URISyntaxException;
 92  
 import java.net.URLEncoder;
 93  
 import java.util.ArrayList;
 94  
 import java.util.Collection;
 95  
 import java.util.HashMap;
 96  
 import java.util.HashSet;
 97  
 import java.util.List;
 98  
 import java.util.Map;
 99  
 import java.util.Set;
 100  
 
 101  
 import javax.xml.stream.Location;
 102  
 import javax.xml.stream.XMLInputFactory;
 103  
 import javax.xml.stream.XMLStreamConstants;
 104  
 import javax.xml.stream.XMLStreamException;
 105  
 import javax.xml.stream.XMLStreamReader;
 106  
 
 107  
 import org.joda.time.DateTimeZone;
 108  
 import org.joda.time.Period;
 109  
 
 110  
 import org.openpermis.basic.AbsoluteTimePeriod;
 111  
 import org.openpermis.basic.PartialTime;
 112  
 import org.openpermis.basic.TimePeriod;
 113  
 import org.openpermis.basic.TimePeriodConstraint;
 114  
 import org.openpermis.policy.Action;
 115  
 import org.openpermis.policy.ActionCollection;
 116  
 import org.openpermis.policy.Authority;
 117  
 import org.openpermis.policy.Domain;
 118  
 import org.openpermis.policy.Obligation;
 119  
 import org.openpermis.policy.ObligationCollection;
 120  
 import org.openpermis.policy.ParameterList;
 121  
 import org.openpermis.policy.Policy;
 122  
 import org.openpermis.policy.Predicate;
 123  
 import org.openpermis.policy.Role;
 124  
 import org.openpermis.policy.RoleAssignmentRule;
 125  
 import org.openpermis.policy.RoleAssignmentRuleCollection;
 126  
 import org.openpermis.policy.RoleHierarchyCollection;
 127  
 import org.openpermis.policy.Target;
 128  
 import org.openpermis.policy.TargetAccessRule;
 129  
 import org.openpermis.policy.TargetAccessRuleCollection;
 130  
 import org.openpermis.policy.TargetCollection;
 131  
 import org.openpermis.policy.bean.DomainBean;
 132  
 import org.openpermis.policy.bean.PartBeanFactory;
 133  
 import org.openpermis.policy.bean.RoleHierarchyBean;
 134  
 import org.openpermis.policy.io.PolicyException;
 135  
 import org.openpermis.policy.io.PolicyReader;
 136  
 import org.openpermis.policy.io.ProblemReporter;
 137  
 import org.openpermis.policy.io.ProblemType;
 138  
 import org.openpermis.policy.predicate.And;
 139  
 import org.openpermis.policy.predicate.Argument;
 140  
 import org.openpermis.policy.predicate.Constant;
 141  
 import org.openpermis.policy.predicate.CurrentTime;
 142  
 import org.openpermis.policy.predicate.Not;
 143  
 import org.openpermis.policy.predicate.Or;
 144  
 import org.openpermis.policy.predicate.Present;
 145  
 import org.openpermis.policy.predicate.SubstringOf;
 146  
 import org.openpermis.policy.predicate.TimeConstant;
 147  
 import org.openpermis.policy.predicate.Value;
 148  
 import org.openpermis.policy.predicate.ValueRelationalPredicate;
 149  
 import org.openpermis.policy.predicate.ValueSet;
 150  
 import org.openpermis.policy.predicate.ValueSetRelationalPredicate;
 151  
 import org.openpermis.policy.predicate.ValueRelationalPredicate.Relation;
 152  
 import org.openpermis.policy.predicate.ValueSetRelationalPredicate.SetRelation;
 153  
 
 154  
 /**
 155  
  * Policy reader implementation for Permis XML policies.
 156  
  * @since 0.1.0
 157  
  */
 158  
 public class PermisXmlReader
 159  
         implements PolicyReader
 160  
 {
 161  
 
 162  
         //---- Static
 163  
 
 164  
         /**
 165  
          * @since 0.1.0
 166  
          */
 167  
         private static final int START = XMLStreamConstants.START_ELEMENT;
 168  
 
 169  
         /**
 170  
          * @since 0.1.0
 171  
          */
 172  
         private static final int END = XMLStreamConstants.END_ELEMENT;
 173  
 
 174  
         //---- State
 175  
 
 176  
         /**
 177  
          * @since 0.1.0
 178  
          */
 179  
         private final XMLStreamReader reader;
 180  
 
 181  
         /**
 182  
          * @since 0.1.0
 183  
          */
 184  
         private final PartBeanFactory partFactory;
 185  
 
 186  
         /**
 187  
          * @since 0.3.0
 188  
          */
 189  
         private final ProblemReporter problemReporter;
 190  
 
 191  
         /**
 192  
          * @since 0.1.0
 193  
          */
 194  
         private DateTimeZone zone;
 195  
 
 196  
         /**
 197  
          * @since 0.1.0
 198  
          */
 199  
         private Map<String, Class<?>> parameterMap;
 200  
 
 201  
         //---- Constructors
 202  
 
 203  
         /**
 204  
          * Creates a new policy reader that uses the specified reader but has not problem reporter.
 205  
          * <p>Readers created by this constructor will throw a {@link PolicyException} if a problem
 206  
          * is encountered that could have been reported to a {@link ProblemReporter}.</p>
 207  
          * @param reader the reader to read the policy from.
 208  
          * @param partFactory the {@link PartBeanFactory} to use for creating policy parts.
 209  
          * @throws PolicyException if the XML reader cannot be created.
 210  
          * @see #PermisXmlReader(Reader, PartBeanFactory, ProblemReporter)
 211  
          * @since 0.1.0
 212  
          */
 213  
         public PermisXmlReader (Reader reader, PartBeanFactory partFactory) throws PolicyException {
 214  269
                 this(reader, partFactory, null);
 215  269
         }
 216  
 
 217  
         /**
 218  
          * Creates a new policy reader that uses the specified reader.
 219  
          * <p>Readers created with a <code>null</code> {@link ProblemReporter} will throw a
 220  
          * {@link PolicyException} if a non-fatal problem is encountered that could have been
 221  
          * reported to a {@link ProblemReporter}.</p>
 222  
          * @param reader the reader to read the policy from.
 223  
          * @param partFactory the {@link PartBeanFactory} to use for creating policy parts.
 224  
          * @param problemReporter reporter for serialization problems, may be <code>null</code>.
 225  
          * @throws PolicyException if the XML reader cannot be created.
 226  
          * @since 0.3.0
 227  
          */
 228  
         public PermisXmlReader (
 229  
                 Reader reader, PartBeanFactory partFactory, ProblemReporter problemReporter
 230  
         )
 231  
                 throws PolicyException
 232  271
         {
 233  271
                 this.problemReporter = problemReporter;
 234  271
                 if (reader == null) {
 235  0
                         throw new IllegalArgumentException();
 236  
                 }
 237  271
                 final XMLInputFactory factory = XMLInputFactory.newInstance();
 238  
                 try {
 239  271
                         this.reader  = factory.createXMLStreamReader(reader);
 240  0
                 } catch (XMLStreamException e) {
 241  0
                         throw new PolicyException("Could not create stream.", e);
 242  271
                 }
 243  271
                 this.partFactory  = partFactory;
 244  271
         }
 245  
 
 246  
         //---- Methods
 247  
 
 248  
         /**
 249  
          * Returns the current reader location in format &quot;line:column&quot;.
 250  
          * @return the current reader location as string.
 251  
          * @since 0.3.0
 252  
          */
 253  
         protected String getLocation () {
 254  
                 try {
 255  609
                         final Location location = this.reader.getLocation();
 256  609
                         final StringBuilder sb = new StringBuilder();
 257  609
                         sb.append("line ").
 258  
                                 append(location.getLineNumber()).
 259  
                                 append(", column ").
 260  
                                 append(location.getColumnNumber());
 261  609
                         return sb.toString();
 262  0
                 } catch (Exception e) {
 263  
                         // Always return a location, in case of an internal error return 0:0.
 264  0
                         return "0:0";
 265  
                 }
 266  
         }
 267  
 
 268  
         /**
 269  
          * Reports a problem of the specified type with the given parameters.
 270  
          * <p>The current reader location is passed as the first parameter.
 271  
          * @param type the problem type to report.
 272  
          * @param parameters the parameters to pass on.
 273  
          * @throws PolicyException if there is no problem reporter available.
 274  
          * @since 0.3.0
 275  
          */
 276  
         protected void reportProblem (ProblemType type, Object... parameters) throws PolicyException {
 277  7
                 final Object[] newParameters = new Object[parameters.length + 1];
 278  7
                 newParameters[0] = getLocation();
 279  7
                 System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
 280  7
                 if (this.problemReporter == null) {
 281  2
                         throw new PolicyException(type.getMessage(newParameters));
 282  
                 }
 283  5
                 this.problemReporter.reportProblem(type, newParameters);
 284  5
         }
 285  
 
 286  
         /**
 287  
          * Sets the time zone of this policy.
 288  
          * @param zone a {@link DateTimeZone}.
 289  
          * @since 0.1.0
 290  
          */
 291  
         protected void setZone (DateTimeZone zone) {
 292  34
                 this.zone = zone;
 293  34
         }
 294  
 
 295  
         /**
 296  
          * Defines the relation (actionname, parametername, type).
 297  
          * @param parameterMap the parameters.
 298  
          * @since 0.1.0
 299  
          */
 300  
         protected void setParameterMap (Map<String, Class<?>> parameterMap) {
 301  6
                 this.parameterMap = parameterMap;
 302  6
         }
 303  
 
 304  
         /**
 305  
          * Moves the cursor of parser to the next element, it may be a starting or ending element.
 306  
          * @throws PolicyException if next element could not be found.
 307  
          */
 308  
         protected void nextElement () throws PolicyException {
 309  
                 try {
 310  14721
                         this.reader.nextTag();
 311  0
                 } catch (XMLStreamException e) {
 312  0
                         throw new PolicyException(getLocation() + ": Parsing exception.", e);
 313  14721
                 }
 314  14721
         }
 315  
 
 316  
         /**
 317  
          * Returns true if the cursor points to the element of type and name.
 318  
          * @return true if the cursor points to the element of type and name.
 319  
          * @throws PolicyException if next element could not be found.
 320  
          */
 321  
         protected boolean isElement (int type, String name) throws PolicyException {
 322  6224
                 if (this.reader.getEventType() == type &&
 323  
                         this.reader.getName().getLocalPart().equals(name)
 324  
                 ) {
 325  1813
                         return true;
 326  
                 }
 327  4411
                 return false;
 328  
         }
 329  
 
 330  
         /**
 331  
          * Moves the cursor of parser to the next position of the given {@link XMLStreamConstants}
 332  
          * <code>type</code>.
 333  
          * @param type type of the next position.
 334  
          * @throws PolicyException if next position could not be found.
 335  
          */
 336  
         protected void next (int type) throws PolicyException {
 337  
                 try {
 338  12620
                         while (this.reader.hasNext()) {
 339  12620
                                 this.reader.next();
 340  12620
                                 if (this.reader.getEventType() == type) {
 341  5995
                                         return;
 342  
                                 }
 343  
                         }
 344  0
                         throw new PolicyException(getLocation() + ": Abrupt end of document.");
 345  0
                 } catch (XMLStreamException e) {
 346  0
                         throw new PolicyException(getLocation() + ": Parsing exception.", e);
 347  
                 }
 348  
         }
 349  
 
 350  
         /**
 351  
          * Moves the cursor of parser to the next element of type <code>findType</code> and element
 352  
          * name <code>findName</code>.
 353  
          * @param findType type of the next element.
 354  
          * @param findName name of the next element.
 355  
          * @throws PolicyException if next element with name could not be found.
 356  
          */
 357  
         protected void nextElement (int findType, String findName) throws PolicyException {
 358  
                 while (true) {
 359  5518
                         next(findType);
 360  5518
                         final String elementName = this.reader.getName().getLocalPart();
 361  5518
                         if (elementName.equals(findName)) {
 362  5515
                                 return;
 363  
                         }
 364  3
                         reportProblem(ProblemType.ELEMENT_SKIPPED, elementName);
 365  2
                 }
 366  
         }
 367  
 
 368  
         /**
 369  
          * Moves the cursor of parser to the next element of type <code>findType</code> and element
 370  
          * name <code>findName</code> but the cursor will stop on <code>stopType</code> and
 371  
          * <code>stopName</code>.
 372  
          * @param findType type of the next element to find.
 373  
          * @param findName name of the next element to find.
 374  
          * @param stopType type of the next element to stop.
 375  
          * @param stopName name of the next element to stop.
 376  
          * @return true if cursor on find position, false if cursor on stop position.
 377  
          * @throws PolicyException if no next element could not be found.
 378  
          */
 379  
         protected boolean nextElement (int findType, String findName, int stopType, String stopName)
 380  
                 throws PolicyException
 381  
         {
 382  
                 while (true) {
 383  13153
                         nextElement();
 384  13153
                         final int eventType = this.reader.getEventType();
 385  13153
                         final String elementName = this.reader.getName().getLocalPart();
 386  13153
                         if (eventType == findType && elementName.equals(findName)) {
 387  4725
                                 return true;
 388  8428
                         } else if (eventType == stopType && elementName.equals(stopName)) {
 389  4885
                                 return false;
 390  
                         }
 391  3543
                         if (eventType != END) {
 392  
                                 // Only report problems for tags that are not read,
 393  
                                 // the parser does not explicitly handle the closing of all tags.
 394  0
                                 reportProblem(ProblemType.ELEMENT_SKIPPED, elementName);
 395  
                         }
 396  3543
                 }
 397  
         }
 398  
 
 399  
         /**
 400  
          * Returns the value of the attribute of the current element.
 401  
          * @param attribute name of the attribute.
 402  
          * @return String the attribute value.
 403  
          * @throws PolicyException if no such attribute exists.
 404  
          * @since 0.1.0
 405  
          */
 406  
         protected String expectAttribute (String attribute) throws PolicyException {
 407  8374
                 final String value = this.reader.getAttributeValue(null, attribute);
 408  8374
                 if (value == null) {
 409  599
                         throw new PolicyException(getLocation() + ": Missing attribute '" + attribute + "'.");
 410  
                 }
 411  7775
                 return value;
 412  
         }
 413  
 
 414  
         /**
 415  
          * Returns the value of the optional attribute, <code>null</code> if attribute does not exist.
 416  
          * @param attribute name of the attribute.
 417  
          * @return String the attribute value if exist, <code>null</code> otherwise.
 418  
          * @since 0.1.0
 419  
          */
 420  
         protected String optionalAttribute (String attribute) {
 421  
                 String result;
 422  
                 try {
 423  1638
                         result = expectAttribute(attribute);
 424  599
                 } catch (PolicyException e) {
 425  599
                         result = null;
 426  1039
                 }
 427  1638
                 return result;
 428  
         }
 429  
 
 430  
         /**
 431  
          * Encodes an LDAP DN to a valid URI name.
 432  
          * @param ldapDn the LDAP DN to encode.
 433  
          * @return the encoded LDAP DN.
 434  
          * @throws UnsupportedEncodingException if encoding fails.
 435  
          * @since 0.3.0
 436  
          */
 437  
         protected String encode (String ldapDn) throws UnsupportedEncodingException {
 438  4
                 final String encoded = URLEncoder.encode(ldapDn, "UTF-8");
 439  
                 // Do not encode '=' and ',' since this is not required for LDAP URIs
 440  
                 // and readability is greatly enhanced. Also use '%20' for spaces instead
 441  
                 // of '+' to improve compatibility.
 442  4
                 return encoded.replaceAll("%3D", "=").replaceAll("%2C", ",").replaceAll("\\+", "%20");
 443  
         }
 444  
 
 445  
         /**
 446  
          * Creates an URI for the specified input string.
 447  
          * <p>If the URI cannot be created the string is first converted to a valid URI name
 448  
          * and then converted. This conversion will issue a warning.</p>
 449  
          * @param string the string to be converted.
 450  
          * @param element the element for which to create the URI.
 451  
          * @param attribute the attribute that holds the URI.
 452  
          * @return the URI requested.
 453  
          * @throws URISyntaxException if the conversion fails and an URI cannot be created.
 454  
          * @throws PolicyException if a conversion takes place and there is no problem reporter.
 455  
          * @since 0.3.0
 456  
          */
 457  
         protected URI createUri (
 458  
                 String string, String element, String attribute
 459  
         )
 460  
                 throws URISyntaxException, PolicyException
 461  
         {
 462  
                 try {
 463  698
                         return new URI(string);
 464  4
                 } catch (URISyntaxException e) {
 465  
                         try {
 466  4
                                 final URI uri = new URI(encode(string));
 467  4
                                 reportProblem(ProblemType.ID_CONVERTED, element, attribute, string, uri);
 468  3
                                 return uri;
 469  0
                         } catch (UnsupportedEncodingException e2) {
 470  0
                                 throw e;
 471  
                         }
 472  
                 }
 473  
         }
 474  
 
 475  
         /**
 476  
          * Parses the stream and build the policy.
 477  
          * @return the policy.
 478  
          * @throws PolicyException signals a problem in reading the policy.
 479  
          * @since 0.1.0
 480  
          */
 481  
         public Policy readPolicy () throws PolicyException {
 482  237
                 nextElement(START, PERMIS_POLICY_ELEMENT);
 483  
 
 484  
                 // parse time zone
 485  237
                 final String timeZoneString = optionalAttribute(TIME_ZONE_ATTRIBUTE);
 486  237
                 if (timeZoneString != null && !timeZoneString.equals("")) {
 487  
                         try {
 488  24
                                 this.zone = DateTimeZone.forID(timeZoneString);
 489  0
                         } catch (IllegalArgumentException e) {
 490  0
                                 throw new PolicyException(
 491  
                                         getLocation() + ": Invalid time zone '" + timeZoneString + "'."
 492  
                                 );
 493  24
                         }
 494  
                 }
 495  
 
 496  
                 // parse role assignment rules
 497  237
                 final Map<String, DomainBean> subjectDomains = readSubjectDomainPolicy();
 498  235
                 final Map<String, RoleHierarchyBean> hierarchies = readRoleHierarchyPolicy();
 499  235
                 final Map<String, Authority> soas = readSoaPolicy();
 500  235
                 final RoleAssignmentRuleCollection roleAssignmentRules =
 501  
                         readRoleAssignmentPolicy(subjectDomains, hierarchies, soas);
 502  
 
 503  
                 // parse domain policy
 504  235
                 final Map<String, Domain> domains = readResourceDomainPolicy();
 505  
 
 506  
                 // parse action policy
 507  235
                 this.parameterMap = new HashMap<String, Class<?>>();
 508  235
                 final Map<String, Action> actions = readActionPolicy(this.parameterMap);
 509  
 
 510  
                 // parse target policy
 511  235
                 final Map<String, Target> targets = readTargetPolicy(domains, actions);
 512  
 
 513  
                 // parse obligation policy
 514  235
                 final Map<String, Obligation> obligations = readObligationPolicy();
 515  
                 
 516  
                 // parse target access policy
 517  235
                 final TargetAccessRuleCollection targetAccessRules =
 518  
                         readTargetAccessPolicy(domains, hierarchies, actions, targets, obligations);
 519  
 
 520  
                 // create role hierarchies
 521  235
                 final RoleHierarchyCollection roleHierarchies =
 522  
                         this.partFactory.createRoleHierarchyCollection(hierarchies.values());
 523  
 
 524  
                 // read end
 525  235
                 nextElement(END, PERMIS_POLICY_ELEMENT);
 526  
 
 527  
                 // create policy
 528  235
                 return this.partFactory.createPolicy(
 529  
                         this.zone, roleHierarchies, targetAccessRules, roleAssignmentRules
 530  
                 );
 531  
         }
 532  
 
 533  
         /**
 534  
          * Reads a subject domain policy.
 535  
          * @return a map of subject domains.
 536  
          * @throws PolicyException signals a problem in reading the policy.
 537  
          * @since 0.1.0
 538  
          */
 539  
         protected Map<String, DomainBean> readSubjectDomainPolicy () throws PolicyException {
 540  239
                 final Map<String, DomainBean> result = new HashMap<String, DomainBean>();
 541  239
                 nextElement(START, SUBJECT_DOMAIN_POLICY_ELEMENT);
 542  403
                 while (nextElement(
 543  
                         START, SUBJECT_DOMAIN_SPEC_ELEMENT, END, SUBJECT_DOMAIN_POLICY_ELEMENT)) {
 544  
                         // Subject Domain Spec
 545  166
                         final String id = expectAttribute(ID_ATTRIBUTE);
 546  166
                         final URI uri = readIncludeElement(SUBJECT_DOMAIN_SPEC_ELEMENT);
 547  165
                         final DomainBean subjectDomain = this.partFactory.createDomain(uri);
 548  165
                         if (result.containsKey(id)) {
 549  0
                                 throw new PolicyException(
 550  
                                         getLocation() + ": Same ID for two different subject domains."
 551  
                                 );
 552  
                         }
 553  165
                         result.put(id, subjectDomain);
 554  165
                 }
 555  
 
 556  237
                 return result;
 557  
         }
 558  
 
 559  
         /**
 560  
          * Reads an include element.
 561  
          * @return an {@link URI}.
 562  
          * @throws PolicyException signals a problem in reading the policy.
 563  
          * @since 0.3.0
 564  
          */
 565  
         protected URI readIncludeElement (String domainSpecElementType) throws PolicyException {
 566  331
                 nextElement(START, INCLUDE_ELEMENT);
 567  
                 URI uri;
 568  
                 try {
 569  331
                         final String ldap = optionalAttribute(LDAPDN_ATTRIBUTE);
 570  331
                         final String url = optionalAttribute(URL_ATTRIBUTE);
 571  331
                         if (url != null && ldap != null) {
 572  0
                                 throw new PolicyException(getLocation() + ": Only one attribute is allowed.");
 573  331
                         } else if (url != null) {
 574  0
                                 uri = createUri(url, domainSpecElementType, URL_ATTRIBUTE);
 575  331
                         } else if (ldap != null) {
 576  331
                                 uri = createUri(ldap, domainSpecElementType, LDAPDN_ATTRIBUTE);
 577  
                         } else {
 578  0
                                 throw new PolicyException(getLocation() + ": No attribute is set.");
 579  
                         }
 580  
 
 581  0
                 } catch (URISyntaxException e) {
 582  0
                         throw new PolicyException(getLocation() + ": LDAP DN is not an URI.");
 583  330
                 }
 584  330
                 return uri;
 585  
         }
 586  
 
 587  
         /**
 588  
          * Reads a role hierarchy policy.
 589  
          * @return a map of role hierarchies.
 590  
          * @throws PolicyException signals a problem in reading the policy.
 591  
          * @since 0.1.0
 592  
          */
 593  
         protected Map<String, RoleHierarchyBean> readRoleHierarchyPolicy () throws PolicyException {
 594  245
                 final Map<String, RoleHierarchyBean> result = new HashMap<String, RoleHierarchyBean>();
 595  245
                 nextElement(START, ROLE_HIERARCHY_POLICY_ELEMENT);
 596  444
                 while (nextElement(START, ROLE_HIERARCHY_ELEMENT, END, ROLE_HIERARCHY_POLICY_ELEMENT)) {
 597  
 
 598  
                         // Role Spec
 599  202
                         final String id = expectAttribute(ID_ATTRIBUTE);
 600  202
                         final String type = expectAttribute(TYPE_ATTRIBUTE);
 601  
 
 602  
                         // check attributes
 603  
                         URI typeUri;
 604  
                         try {
 605  202
                                 typeUri = createUri(type, ROLE_HIERARCHY_ELEMENT, TYPE_ATTRIBUTE);
 606  0
                         } catch (URISyntaxException e1) {
 607  0
                                 throw new PolicyException(getLocation() + ": Type is not an URI.");
 608  202
                         }
 609  202
                         if (result.containsKey(id)) {
 610  1
                                 throw new PolicyException(getLocation() + ": Hierarchy ID already exists.");
 611  
                         }
 612  201
                         for (RoleHierarchyBean hierarchy : result.values()) {
 613  16
                                 if (hierarchy.getIdentity().equals(typeUri)) {
 614  1
                                         throw new PolicyException(
 615  
                                                 getLocation() + ": Hierarchy type already exists."
 616  
                                         );
 617  
                                 }
 618  
                         }
 619  
 
 620  
                         // create role hierarchy
 621  200
                         final RoleHierarchyBean hierarchy = this.partFactory.createRoleHierarchy(typeUri);
 622  200
                         final Set<String> undefinedSuperRoles = new HashSet<String>();
 623  200
                         result.put(id, hierarchy);
 624  651
                         while (nextElement(START, SUPER_ROLE_ELEMENT, END, ROLE_HIERARCHY_ELEMENT)) {
 625  
 
 626  
                                 // Sup Role
 627  451
                                 final String superRoleName = expectAttribute(VALUE_ATTRIBUTE);
 628  451
                                 if (hierarchy.roleExists(superRoleName)) {
 629  2
                                         if (undefinedSuperRoles.contains(superRoleName)) {
 630  2
                                                 undefinedSuperRoles.remove(superRoleName);
 631  
                                         } else {
 632  0
                                                 throw new PolicyException(
 633  
                                                         getLocation() + ": Same role name for two different roles."
 634  
                                                 );
 635  
                                         }
 636  
                                 } else {
 637  449
                                         hierarchy.addRole(superRoleName);
 638  
                                 }
 639  566
                                 while (nextElement(START, SUB_ROLE_ELEMENT, END, SUPER_ROLE_ELEMENT)) {
 640  
 
 641  
                                         // Sub Role
 642  115
                                         final String subRoleName = expectAttribute(VALUE_ATTRIBUTE);
 643  115
                                         if (!hierarchy.roleExists(subRoleName)) {
 644  3
                                                 hierarchy.addRole(subRoleName);
 645  3
                                                 undefinedSuperRoles.add(subRoleName);
 646  
                                         }
 647  115
                                         hierarchy.connectRoles(superRoleName, subRoleName);
 648  115
                                 }
 649  451
                         }
 650  200
                         if (!undefinedSuperRoles.isEmpty()) {
 651  1
                                 throw new PolicyException(
 652  
                                         getLocation() + ": No super role definition for sub role."
 653  
                                 );
 654  
                         }
 655  199
                 }
 656  
 
 657  242
                 return result;
 658  
         }
 659  
 
 660  
         /**
 661  
          * Reads a soa policy.
 662  
          * @return a map of subjects.
 663  
          * @throws PolicyException signals a problem in reading the policy.
 664  
          * @since 0.1.0
 665  
          */
 666  
         protected Map<String, Authority> readSoaPolicy () throws PolicyException {
 667  237
                 final Map<String, Authority> result = new HashMap<String, Authority>();
 668  237
                 nextElement(START, SOA_POLICY_ELEMENT);
 669  402
                 while (nextElement(START, SOA_SPEC_ELEMENT, END, SOA_POLICY_ELEMENT)) {
 670  
 
 671  
                         // Soa Spec
 672  165
                         final String id = expectAttribute(ID_ATTRIBUTE);
 673  165
                         final String ldapDn = expectAttribute(LDAPDN_ATTRIBUTE);
 674  
                         final Authority authority;
 675  
                         try {
 676  165
                                 authority = this.partFactory.createAuthority(
 677  
                                         createUri(ldapDn, SOA_SPEC_ELEMENT, LDAPDN_ATTRIBUTE)
 678  
                                 );
 679  0
                         } catch (URISyntaxException e) {
 680  0
                                 throw new PolicyException(getLocation() + ": ID is not an URI.");
 681  165
                         }
 682  165
                         if (result.containsKey(id)) {
 683  0
                                 throw new PolicyException(getLocation() + ": Same ID for two different SOA's.");
 684  
                         }
 685  165
                         result.put(id, authority);
 686  165
                 }
 687  237
                 return result;
 688  
         }
 689  
 
 690  
         /**
 691  
          * Reads a role assignment policy.
 692  
          * @param subjectDomains existing subject domains.
 693  
          * @param hierachies existing role hierarchies.
 694  
          * @param soas existing soas.
 695  
          * @return a role assignment rule collection.
 696  
          * @throws PolicyException signals a problem in reading the policy.
 697  
          * @since 0.3.0
 698  
          */
 699  
         protected RoleAssignmentRuleCollection readRoleAssignmentPolicy (
 700  
                 Map<String, DomainBean> subjectDomains,
 701  
                 Map<String, RoleHierarchyBean> hierachies,
 702  
                 Map<String, Authority> soas
 703  
         )
 704  
                 throws PolicyException
 705  
         {
 706  236
                 final List<RoleAssignmentRule> roleAssignmentRules = new ArrayList<RoleAssignmentRule>();
 707  236
                 nextElement(START, ROLE_ASSIGNMENT_POLICY_ELEMENT);
 708  447
                 while (nextElement(
 709  
                         START, ROLE_ASSIGNMENT_RULE_ELEMENT, END, ROLE_ASSIGNMENT_POLICY_ELEMENT)) {
 710  
 
 711  
                         // Subject Domain
 712  211
                         nextElement(START, SUBJECT_DOMAIN_REF_ELEMENT);
 713  211
                         final String subjectDomainId = expectAttribute(ID_ATTRIBUTE);
 714  211
                         final DomainBean subjectDomain = subjectDomains.get(subjectDomainId);
 715  211
                         if (subjectDomain == null) {
 716  0
                                 throw new PolicyException(getLocation() + ": Subject domain ID does not exist.");
 717  
                         }
 718  
 
 719  
                         // Role List
 720  211
                         final Collection<Role> roleSet = readRoleList(hierachies);
 721  
 
 722  
                         // Delegate
 723  211
                         nextElement(START, DELEGATE_ELEMENT);
 724  
                         int delegateDepth;
 725  
                         try {
 726  211
                                 delegateDepth = Integer.parseInt(expectAttribute(DEPTH_ATTRIBUTE));
 727  211
                                 if (delegateDepth < 0) {
 728  0
                                         throw new PolicyException(
 729  
                                                 getLocation() + ": Delegation depth must be positive."
 730  
                                         );
 731  
                                 }
 732  0
                         } catch (NumberFormatException e) {
 733  0
                                 throw new PolicyException(
 734  
                                         getLocation() + ": Attribute Depth must be of type integer"
 735  
                                 );
 736  211
                         }
 737  
 
 738  
 
 739  
                         // SOA
 740  211
                         nextElement(START, SOA_ELEMENT);
 741  211
                         final String soaId = expectAttribute(ID_ATTRIBUTE);
 742  211
                         final Authority authority = soas.get(soaId);
 743  211
                         if (authority == null) {
 744  0
                                 throw new PolicyException(getLocation() + ": Soa ID does not exist.");
 745  
                         }
 746  
 
 747  
                         // Validity
 748  211
                         final TimePeriodConstraint validity = readValidity(this.zone);
 749  
 
 750  
                         // create the role assignment rule
 751  211
                         roleAssignmentRules.add(
 752  
                                 this.partFactory.createRoleAssignmentRule(
 753  
                                         authority,
 754  
                                         subjectDomain,
 755  
                                         roleSet,
 756  
                                         validity,
 757  
                                         delegateDepth
 758  
                                 )
 759  
                         );
 760  211
                 }
 761  236
                 return this.partFactory.createRoleAssignmentRuleCollection(roleAssignmentRules);
 762  
         }
 763  
 
 764  
         /**
 765  
          * Reads a resource domain policy.
 766  
          * @return a map of resouce domains.
 767  
          * @throws PolicyException signals a problem in reading the policy.
 768  
          * @since 0.1.0
 769  
          */
 770  
         protected Map<String, Domain> readResourceDomainPolicy () throws PolicyException {
 771  237
                 final Map<String, Domain> result = new HashMap<String, Domain>();
 772  237
                 nextElement(START, RESOURCE_DOMAIN_POLICY_ELEMENT);
 773  402
                 while (nextElement(START, RESOURCE_DOMAIN_SPEC_ELEMENT, END,
 774  
                         RESOURCE_DOMAIN_POLICY_ELEMENT)) {
 775  
 
 776  
                         // Target Domain Spec
 777  165
                         final String id = expectAttribute(ID_ATTRIBUTE);
 778  165
                         final URI uri = readIncludeElement(RESOURCE_DOMAIN_SPEC_ELEMENT);
 779  165
                         final Domain resourceDomain = this.partFactory.createDomain(uri);
 780  165
                         if (result.containsKey(id)) {
 781  0
                                 throw new PolicyException(
 782  
                                         getLocation() + ": Same ID for two different domains."
 783  
                                 );
 784  
                         }
 785  165
                         result.put(id, resourceDomain);
 786  165
                 }
 787  237
                 return result;
 788  
         }
 789  
 
 790  
 
 791  
         /**
 792  
          * Reads a target policy.
 793  
          * @param domains read Domains.
 794  
          * @param actions read Actions.
 795  
          * @return a map of targets.
 796  
          * @throws PolicyException signals a problem in reading the policy.
 797  
          * @since 0.3.0
 798  
          */
 799  
         protected Map<String, Target> readTargetPolicy (
 800  
                 Map<String, Domain> domains, Map<String, Action> actions
 801  
         ) 
 802  
                 throws PolicyException 
 803  
         {
 804  
                 // init
 805  236
                 final Map<String, Target> result = new HashMap<String, Target>();
 806  
 
 807  
                 // loop over targets
 808  236
                 nextElement(START, TARGET_POLICY_ELEMENT);
 809  696
                 while (nextElement(START, TARGET_SPEC_ELEMENT, END, TARGET_POLICY_ELEMENT)) {
 810  
 
 811  
                         // read id
 812  460
                         final String id = expectAttribute(ID_ATTRIBUTE);
 813  
 
 814  
                         // look ahead
 815  460
                         nextElement();
 816  
 
 817  
                         // read domain
 818  
                         Domain targetDomain;
 819  460
                         if (isElement(START, RESOURCE_DOMAIN_REF_ELEMENT)) {
 820  424
                                 final String targetDomainId = expectAttribute(ID_ATTRIBUTE);
 821  424
                                 targetDomain = domains.get(targetDomainId);
 822  424
                                 if (targetDomain == null) {
 823  0
                                         throw new PolicyException(getLocation() + ": Target domain ID does not exist.");
 824  
                                 }
 825  424
                                 nextElement(START, ACTION_REF_ELEMENT, END, TARGET_SPEC_ELEMENT);
 826  424
                         } else {
 827  36
                                 targetDomain = null;
 828  
                         }
 829  
 
 830  
                         // read actions
 831  460
                         final List<Action> allowedActionsList = new ArrayList<Action>();
 832  896
                         while (isElement(START, ACTION_REF_ELEMENT)) {
 833  436
                                 final String allowedActionId = expectAttribute(ID_ATTRIBUTE);
 834  436
                                 final Action action = actions.get(allowedActionId);
 835  436
                                 if (action == null) {
 836  0
                                         throw new PolicyException(getLocation() + ": Action ID does not exist.");
 837  
                                 }
 838  436
                                 if (!allowedActionsList.add(action)) {
 839  0
                                         throw new PolicyException(
 840  
                                                 getLocation() + ": Action already exists for this target."
 841  
                                         );
 842  
                                 }
 843  436
                                 nextElement(START, ACTION_REF_ELEMENT, END, TARGET_SPEC_ELEMENT);
 844  436
                         }
 845  460
                         final ActionCollection allowedActions =
 846  
                                 this.partFactory.createActionCollection(allowedActionsList);
 847  
 
 848  
                         // create target
 849  460
                         Target newTarget = this.partFactory.createTarget(targetDomain, allowedActions);
 850  
 
 851  
                         // add target to list
 852  460
                         if (result.containsKey(id)) {
 853  0
                                 throw new PolicyException(
 854  
                                         getLocation() + ": Same ID for two different targets."
 855  
                                 );
 856  
                         }
 857  460
                         result.put(id, newTarget);
 858  460
                 }
 859  
 
 860  
                 // return
 861  236
                 return result;
 862  
         }
 863  
 
 864  
 
 865  
         /**
 866  
          * Reads an action policy and fill map with parameter name and type.
 867  
          * @param map a mapping of parameter name and type.
 868  
          * @return a map of actions.
 869  
          * @throws PolicyException signals a problem in reading the policy.
 870  
          * @since 0.1.0
 871  
          */
 872  
         protected Map<String, Action> readActionPolicy (Map<String, Class<?>> map)
 873  
                 throws PolicyException
 874  
         {
 875  237
                 final Map<String, Action> result = new HashMap<String, Action>();
 876  237
                 nextElement(START, ACTION_POLICY_ELEMENT);
 877  676
                 while (nextElement(START, ACTION_SPEC_ELEMENT, END, ACTION_POLICY_ELEMENT)) {
 878  
 
 879  
                         // Action
 880  439
                         final String actionId = expectAttribute(ID_ATTRIBUTE);
 881  439
                         final String actionName = expectAttribute(NAME_ATTRIBUTE);
 882  439
                         if (result.containsKey(actionId)) {
 883  0
                                 throw new PolicyException(getLocation() + ": Same ID for two different actions.");
 884  
                         }
 885  
 
 886  
                         // Arguments
 887  
                         // Ordering of the arguments is important and should not be lost.
 888  439
                         final ParameterList parameters = new ParameterList();
 889  537
                         while (nextElement(START, ARGUMENT_SPEC_ELEMENT, END, ACTION_SPEC_ELEMENT)) {
 890  98
                                 final String parameterName = expectAttribute(NAME_ATTRIBUTE);
 891  98
                                 final String parameterType = expectAttribute(TYPE_ATTRIBUTE);
 892  98
                                 if (parameters.contains(parameterName)) {
 893  0
                                         throw new PolicyException(
 894  
                                                 getLocation() + ": Same name for different parameters in action."
 895  
                                         );
 896  
                                 }
 897  
                                 final Class<?> type;
 898  98
                                 if (INTEGER_TYPE.equals(parameterType)) {
 899  74
                                         type = Integer.class;
 900  24
                                 } else if (DOUBLE_TYPE.equals(parameterType)) {
 901  0
                                         type = Double.class;
 902  24
                                 } else if (BOOLEAN_TYPE.equals(parameterType)) {
 903  24
                                         type = Boolean.class;
 904  0
                                 } else if (STRING_TYPE.equals(parameterType)) {
 905  0
                                         type = String.class;
 906  0
                                 } else if (TIME_TYPE.equals(parameterType)) {
 907  0
                                         type = TimeConstant.class;
 908  0
                                 } else if (OBJECT_TYPE.equals(parameterType)) {
 909  0
                                         type = Object.class;
 910  
                                 } else {
 911  0
                                         throw new PolicyException(getLocation() + ": Unknown type for argument.");
 912  
                                 }
 913  
 
 914  
                                 // add to parameterlist
 915  98
                                 parameters.add(parameterName, type);
 916  
 
 917  
                                 // add to parameterMap
 918  98
                                 if (map.containsKey(parameterName)) {
 919  0
                                         if (map.get(parameterName) != type) {
 920  0
                                                 throw new PolicyException(getLocation() +
 921  
                                                         ": Parameter '" + parameterName + "' defined with different types.");
 922  
                                         }
 923  
                                 }
 924  98
                                 map.put(parameterName, type);
 925  98
                         }
 926  
 
 927  439
                         final Action action = this.partFactory.createAction(actionName, parameters);
 928  439
                         result.put(actionId, action);
 929  
 
 930  439
                 }
 931  
 
 932  237
                 return result;
 933  
         }
 934  
 
 935  
         /**
 936  
          * Reads a target access policy.
 937  
          * @param domains existing target domains.
 938  
          * @param hierarchies existing role hierarchies.
 939  
          * @param actions existing actions.
 940  
          * @param targets existing targets.
 941  
          * @return collection of target access rules.
 942  
          * @throws PolicyException signals a problem in reading the policy.
 943  
          * @since 0.3.0
 944  
          */
 945  
         protected TargetAccessRuleCollection readTargetAccessPolicy (
 946  
                 Map<String, Domain> domains,
 947  
                 Map<String, RoleHierarchyBean> hierarchies,
 948  
                 Map<String, Action> actions,
 949  
                 Map<String, Target> targets,
 950  
                 Map<String, Obligation> obligations
 951  
         )
 952  
                 throws PolicyException
 953  
         {
 954  236
                 final List<TargetAccessRule> result = new ArrayList<TargetAccessRule>();
 955  236
                 nextElement(START, TARGET_ACCESS_POLICY_ELEMENT);
 956  
 
 957  
                 // Target Access Elements
 958  705
                 while (nextElement(START, TARGET_ACCESS_RULE_ELEMENT, END, TARGET_ACCESS_POLICY_ELEMENT)) {
 959  
 
 960  
                         // Role List
 961  469
                         final Collection<Role> roles = readRoleList(hierarchies);
 962  469
                         nextElement(START, TARGET_LIST_ELEMENT);
 963  
 
 964  
                         // Target List
 965  469
                         final List<Target> targetList = new ArrayList<Target>();
 966  926
                         while (nextElement(START, TARGET_REF_ELEMENT, END, TARGET_LIST_ELEMENT)) {
 967  457
                                 final String targetId = expectAttribute(ID_ATTRIBUTE);
 968  457
                                 if (!targets.containsKey(targetId)) {
 969  0
                                         throw new PolicyException(getLocation() +
 970  
                                                 ": Reference to a undefined target."
 971  
                                         );
 972  
                                 }
 973  457
                                 Target target = targets.get(targetId);
 974  457
                                 targetList.add(target);
 975  457
                         }
 976  469
                         final TargetCollection targetCollection =
 977  
                                 this.partFactory.createTargetCollection(targetList);
 978  
                         
 979  
                         // Expecting an optional IF and/or Obligation Element.
 980  469
                         Predicate condition = Predicate.TRUE;
 981  469
                         ObligationCollection obligationCollection = 
 982  
                                 this.partFactory.createObligationCollection(null);
 983  
                         while (
 984  
                                 !isElement(END, TARGET_ACCESS_RULE_ELEMENT) &&
 985  938
                                 !isElement(START, IF_ELEMENT) &&
 986  
                                 !isElement(START, OBLIGATION_LIST_ELEMENT)
 987  
                         ) {
 988  469
                                 nextElement();
 989  
                         }
 990  469
                         if (isElement(START, IF_ELEMENT)) {
 991  24
                                 condition  = readCondition();
 992  
                                 
 993  
                                 // Expecting an optional Obligation Element.
 994  
                                 while (
 995  48
                                         !isElement(END, TARGET_ACCESS_RULE_ELEMENT) &&
 996  
                                         !isElement(START, OBLIGATION_LIST_ELEMENT)
 997  
                                 ) {
 998  24
                                         nextElement();
 999  
                                 }
 1000  24
                                 if (isElement(START, OBLIGATION_LIST_ELEMENT)) {
 1001  12
                                         obligationCollection = readObligationList(obligations);
 1002  
                                 }
 1003  
                                 
 1004  445
                         } else if (isElement(START, OBLIGATION_LIST_ELEMENT)) {
 1005  62
                                 obligationCollection = readObligationList(obligations);
 1006  
                         }
 1007  
 
 1008  
                         // Target Access Rule
 1009  469
                         result.add(
 1010  
                                 this.partFactory.createTargetAccessRule(
 1011  
                                         targetCollection, roles, condition, obligationCollection
 1012  
                                 )
 1013  
                         );
 1014  469
                 }
 1015  236
                 return this.partFactory.createTargetAccessRuleCollection(result);
 1016  
         }
 1017  
 
 1018  
         /**
 1019  
          * Reads an obligation policy.
 1020  
          * @return a map of obligations.
 1021  
          * @throws PolicyException signals a problem in reading the policy.
 1022  
          * @since 0.3.0
 1023  
          */
 1024  
         protected Map<String, Obligation> readObligationPolicy () throws PolicyException {
 1025  236
                 final Map<String, Obligation> result = new HashMap<String, Obligation>();
 1026  
 
 1027  236
                 nextElement(START, OBLIGATION_POLICY_ELEMENT);
 1028  312
                 while (nextElement(START, OBLIGATION_SPEC_ELEMENT, END, OBLIGATION_POLICY_ELEMENT)) {
 1029  76
                         final String id = expectAttribute(ID_ATTRIBUTE);
 1030  76
                         final String text = expectAttribute(TEXT_ATTRIBUTE);
 1031  
                         
 1032  76
                         if (result.containsKey(id)) {
 1033  0
                                 throw new PolicyException(
 1034  
                                         getLocation() + ": Same ID for two different obligations."
 1035  
                                 );
 1036  
                         }
 1037  76
                         result.put(id, this.partFactory.createObligation(text));
 1038  76
                 }
 1039  236
                 return result;
 1040  
         }
 1041  
         
 1042  
         /**
 1043  
          * Reads a list of obligations.
 1044  
          * @param obligations existing obligations.
 1045  
          * @return an {@link ObligationCollection}.
 1046  
          * @since 0.3.0
 1047  
          */
 1048  
         protected ObligationCollection readObligationList (Map<String, Obligation> obligations
 1049  
         ) 
 1050  
                 throws PolicyException 
 1051  
         {
 1052  75
                 if (!isElement(START, OBLIGATION_LIST_ELEMENT)) {
 1053  1
                         nextElement(START, OBLIGATION_LIST_ELEMENT);
 1054  
                 }
 1055  
 
 1056  75
                 final List<Obligation> obligationList = new ArrayList<Obligation>();
 1057  163
                 while (nextElement(START, OBLIGATION_REF_ELEMENT, END, OBLIGATION_LIST_ELEMENT)) {
 1058  88
                         final String obligationId = expectAttribute(ID_ATTRIBUTE);
 1059  88
                         final Obligation obligation = obligations.get(obligationId);
 1060  88
                         if (obligation == null) {
 1061  0
                                 throw new PolicyException(
 1062  
                                         getLocation() + ": Obligation with ID" + obligationId + " does not exist."
 1063  
                                 );
 1064  
                         }
 1065  88
                         obligationList.add(obligation);
 1066  88
                 }
 1067  75
                 return this.partFactory.createObligationCollection(obligationList);
 1068  
         }
 1069  
 
 1070  
         /**
 1071  
          * Reads the validity of a role assignment rule.
 1072  
          * @return a {@link TimePeriodConstraint}.
 1073  
          * @throws PolicyException signals a problem in reading the policy.
 1074  
          * @since 0.3.0
 1075  
          */
 1076  
         protected TimePeriodConstraint readValidity (DateTimeZone defaultZone) throws PolicyException {
 1077  212
                 nextElement(START, VALIDITY_ELEMENT);
 1078  
 
 1079  212
                 TimePeriod absolut = null;
 1080  212
                 Period maxValidUpTo = null;
 1081  212
                 Period minValidUpTo = null;
 1082  212
                 Period minValidFrom = null;
 1083  528
                 while (!isElement(END, VALIDITY_ELEMENT)) {
 1084  316
                         nextElement();
 1085  316
                         if (isElement(START, ABSOLUTE_PERIOD_ELEMENT)) {
 1086  13
                                 final PartialTime start =
 1087  
                                         new PartialTime(expectAttribute(START_ATTRIBUTE), defaultZone, false);
 1088  13
                                 final PartialTime end =
 1089  
                                         new PartialTime(expectAttribute(END_ATTRIBUTE), defaultZone, false);
 1090  13
                                 absolut = new AbsoluteTimePeriod(start, end);
 1091  13
                         } else if (isElement(START, MAXIMUM_VALID_UP_TO_ELEMENT)) {
 1092  13
                                 maxValidUpTo = TimeUtility.parseDuration(expectAttribute(DURATION_TYPE));
 1093  290
                         } else if (isElement(START, MINIMUM_VALID_FROM_ELEMENT)) {
 1094  13
                                 minValidFrom = TimeUtility.parseDuration(expectAttribute(DURATION_TYPE));
 1095  277
                         } else if (isElement(START, MINIMUM_VALID_UP_TO_ELEMENT)) {
 1096  13
                                 minValidUpTo = TimeUtility.parseDuration(expectAttribute(DURATION_TYPE));
 1097  
                         }
 1098  
                 }
 1099  212
                 return new TimePeriodConstraint(absolut, minValidFrom, minValidUpTo, maxValidUpTo);
 1100  
         }
 1101  
 
 1102  
         /**
 1103  
          * Reads a role list element.
 1104  
          * @param hierarchies the existing role hierarchies.
 1105  
          * @return set of roles.
 1106  
          * @throws PolicyException signals a problem in reading the policy.
 1107  
          * @since 0.1.0
 1108  
          */
 1109  
         protected Collection<Role> readRoleList (Map<String, RoleHierarchyBean> hierarchies)
 1110  
                 throws PolicyException
 1111  
         {
 1112  681
                 final Collection<Role> result = new ArrayList<Role>();
 1113  681
                 nextElement(START, ROLE_LIST_ELEMENT);
 1114  1420
                 while (nextElement(START, ROLE_ELEMENT, END, ROLE_LIST_ELEMENT)) {
 1115  
 
 1116  
                         // Role
 1117  739
                         final String type = expectAttribute(ID_ATTRIBUTE);
 1118  739
                         final String value = optionalAttribute(VALUE_ATTRIBUTE);
 1119  739
                         final RoleHierarchyBean hierarchy = hierarchies.get(type);
 1120  739
                         if (hierarchy == null) {
 1121  0
                                 throw new PolicyException(getLocation() + ": Role type does not exist.");
 1122  
                         }
 1123  739
                         if (value == null) {
 1124  
                                 // add all roles of this type
 1125  55
                                 for (String role : hierarchy.getRoles()) {
 1126  110
                                         result.add(Role.create(hierarchy, role));
 1127  
                                 }
 1128  
                         } else {
 1129  
                                 // add single role
 1130  684
                                 if (!hierarchy.roleExists(value)) {
 1131  0
                                         throw new PolicyException(getLocation() + ": Role ID does not exist.");
 1132  
                                 }
 1133  684
                                 result.add(Role.create(hierarchy, value));
 1134  
                         }
 1135  739
                 }
 1136  681
                 return result;
 1137  
         }
 1138  
         
 1139  
         /**
 1140  
          * Reads a if statement condition and returns it as a {@link Predicate}.
 1141  
          * @return the condition as {@link Predicate}.
 1142  
          * @throws PolicyException signals a problem in reading the policy.
 1143  
          * @since 0.3.0
 1144  
          */
 1145  
         protected Predicate readCondition () throws PolicyException {
 1146  33
                 if (!isElement(START, IF_ELEMENT)) {
 1147  9
                         nextElement(START, IF_ELEMENT);
 1148  
                 }
 1149  33
                 next(START);
 1150  33
                 final Predicate predicate = readConditionRec();
 1151  
                 
 1152  33
                 if (!predicate.isValid()) {
 1153  0
                         throw new PolicyException(getLocation() + ": Predicate is not valid.");
 1154  
                 }
 1155  33
                 nextElement(END, IF_ELEMENT);
 1156  33
                 return predicate;
 1157  
         }
 1158  
 
 1159  
         /**
 1160  
          * @since 0.1.0
 1161  
          */
 1162  
         private Predicate readConditionRec () throws PolicyException {
 1163  208
                 final String tag = this.reader.getName().getLocalPart();
 1164  
                 final Predicate result;
 1165  
 
 1166  
                 // And
 1167  208
                 if (AND_ELEMENT.equals(tag)) {
 1168  26
                         result = new And(readPredicateList().toArray(new Predicate[]{}));
 1169  
 
 1170  
                 // Or
 1171  182
                 } else if (OR_ELEMENT.equals(tag)) {
 1172  13
                         result = new Or(readPredicateList().toArray(new Predicate[]{}));
 1173  
 
 1174  
                 // Not
 1175  169
                 } else if (NOT_ELEMENT.equals(tag)) {
 1176  1
                         final List<Predicate> list = readPredicateList();
 1177  1
                         if (list.size() == 1) {
 1178  1
                                 result = new Not(list.get(0));
 1179  
                         } else {
 1180  0
                                 throw new PolicyException(getLocation() + 
 1181  
                                         ": Not doesn't conatin exactly one predicate."
 1182  
                                 );
 1183  
                         }
 1184  
                         
 1185  
                 // Present
 1186  1
                 } else if (PRESENT_ELEMENT.equals(tag)) {
 1187  12
                         result = new Present(readNextValue());
 1188  12
                         nextElement(END, PRESENT_ELEMENT);
 1189  
 
 1190  
                 // GreaterThan
 1191  156
                 } else if (GREATER_THAN_ELEMENT.equals(tag)) {
 1192  42
                         result = new ValueRelationalPredicate(Relation.GreaterThan, readNextValue(),
 1193  
                                 readNextValue());
 1194  42
                         nextElement(END, GREATER_THAN_ELEMENT);
 1195  
 
 1196  
                 // GreaterEqual
 1197  114
                 } else if (GREATER_EQUAL_ELEMENT.equals(tag)) {
 1198  36
                         result = new ValueRelationalPredicate(Relation.GreaterEqual, readNextValue(),
 1199  
                                 readNextValue());
 1200  36
                         nextElement(END, GREATER_EQUAL_ELEMENT);
 1201  
 
 1202  
                 // Equal
 1203  78
                 } else if (EQUAL_ELEMENT.equals(tag)) {
 1204  13
                         result = new ValueRelationalPredicate(Relation.Equal, readNextValue(), readNextValue());
 1205  13
                         nextElement(END, EQUAL_ELEMENT);
 1206  
 
 1207  
                 // LessThan
 1208  65
                 } else if (LESS_THAN_ELEMENT.equals(tag)) {
 1209  12
                         result = new ValueRelationalPredicate(Relation.LessThan, readNextValue(),
 1210  
                                 readNextValue());
 1211  12
                         nextElement(END, LESS_THAN_ELEMENT);
 1212  
 
 1213  
                 // LessEqual
 1214  53
                 } else if (LESS_EQUAL_ELEMENT.equals(tag)) {
 1215  12
                         result = new ValueRelationalPredicate(Relation.LessEqual, readNextValue(),
 1216  
                                 readNextValue());
 1217  12
                         nextElement(END, LESS_EQUAL_ELEMENT);
 1218  
 
 1219  
                 // NonNullIntersection
 1220  41
                 } else if (NON_NULL_INTERSECTION_ELEMENT.equals(tag)) {
 1221  13
                         result = new ValueSetRelationalPredicate(SetRelation.NonNullIntersection,
 1222  
                                 readValueSet(), readValueSet());
 1223  13
                         nextElement(END, NON_NULL_INTERSECTION_ELEMENT);
 1224  
 
 1225  
                 // Subset
 1226  28
                 } else if (SUBSET_ELEMENT.equals(tag)) {
 1227  14
                         result = new ValueSetRelationalPredicate(SetRelation.Subset, readValueSet(),
 1228  
                                 readValueSet());
 1229  14
                         nextElement(END, SUBSET_ELEMENT);
 1230  
 
 1231  
                 // Superset
 1232  14
                 } else if (SUPERSET_ELEMENT.equals(tag)) {
 1233  13
                         result = new ValueSetRelationalPredicate(SetRelation.Superset, readValueSet(),
 1234  
                                 readValueSet());
 1235  13
                         nextElement(END, SUPERSET_ELEMENT);
 1236  
 
 1237  
                 // SubstringOf
 1238  1
                 } else if (SUBSTRING_OF_ELEMENT.equals(tag)) {
 1239  1
                         result = new SubstringOf(readNextValue(), readNextValue());
 1240  1
                         nextElement(END, SUBSTRING_OF_ELEMENT);
 1241  
 
 1242  
                 } else {
 1243  0
                         throw new PolicyException(
 1244  
                                 getLocation() + ": Unknown element tag = " + tag + " in IF Statement."
 1245  
                         );
 1246  
                 }
 1247  208
                 return result;
 1248  
         }
 1249  
 
 1250  
         /**
 1251  
          * @since 0.1.0
 1252  
          */
 1253  
         private List<Predicate> readPredicateList () throws PolicyException {
 1254  40
                 final List<Predicate> result = new ArrayList<Predicate>();
 1255  40
                 next(START);
 1256  
                 do {
 1257  175
                         result.add(readConditionRec());
 1258  175
                         nextElement();
 1259  175
                 } while (this.reader.getEventType() == START);
 1260  40
                 return result;
 1261  
         }
 1262  
 
 1263  
         /**
 1264  
          * @since 0.3.0
 1265  
          */
 1266  
         private ValueSet readValueSet () throws PolicyException {
 1267  80
                 final List<Value<?>> values = new ArrayList<Value<?>>();
 1268  80
                 next(START);
 1269  80
                 final String tag = this.reader.getName().getLocalPart();
 1270  80
                 if (SET_ELEMENT.equals(tag)) {
 1271  80
                         next(START);
 1272  
                         do {
 1273  124
                                 values.add(readValue());
 1274  124
                                 nextElement();
 1275  124
                         } while (this.reader.getEventType() == START);
 1276  80
                         return new ValueSet(values);
 1277  
                 }
 1278  0
                 throw new PolicyException(getLocation() + ": Unknown constant type");
 1279  
         }
 1280  
 
 1281  
         /**
 1282  
          * @since 0.3.0
 1283  
          */
 1284  
         private Value<?> readNextValue () throws PolicyException {
 1285  244
                 next(START);
 1286  244
                 return readValue();
 1287  
         }
 1288  
 
 1289  
         /**
 1290  
          * @since 0.1.0
 1291  
          */
 1292  
         private Value<?> readValue () throws PolicyException {
 1293  368
                 final String tag = this.reader.getName().getLocalPart();
 1294  
                 final Value<?> result;
 1295  368
                 if (CONSTANT_ELEMENT.equals(tag)) {
 1296  235
                         final String type = expectAttribute(TYPE_ATTRIBUTE);
 1297  235
                         final String value = expectAttribute(VALUE_ATTRIBUTE);
 1298  
 
 1299  235
                         if (TIME_TYPE.equals(type)) {
 1300  100
                                 result = new TimeConstant(new PartialTime(value, this.zone, true));
 1301  
 
 1302  135
                         } else if (INTEGER_TYPE.equals(type)) {
 1303  132
                                 result = new Constant<Integer>(new Integer(value));
 1304  
 
 1305  3
                         } else if (BOOLEAN_TYPE.equals(type)) {
 1306  0
                                 result = new Constant<Boolean>(Boolean.valueOf(value));
 1307  
 
 1308  3
                         } else if (DOUBLE_TYPE.equals(type)) {
 1309  1
                                 result = new Constant<Double>(new Double(value));
 1310  
 
 1311  2
                         } else if (STRING_TYPE.equals(type)) {
 1312  2
                                 result = new Constant<String>(value);
 1313  
 
 1314  
                         } else {
 1315  0
                                 throw new PolicyException(getLocation() + ": Constant type is not supported.");
 1316  
                         }
 1317  
 
 1318  235
                 } else if (ARGUMENT_REF_ELEMENT.equals(tag)) {
 1319  
 
 1320  107
                         final String argumentName = expectAttribute(NAME_ATTRIBUTE);
 1321  
 
 1322  
                         // test if a parameter exists
 1323  107
                         final Class<?> parameterType = this.parameterMap.get(argumentName);
 1324  107
                         if (parameterType == null) {
 1325  0
                                 throw new PolicyException(
 1326  
                                         getLocation() + ": Reference to non-existent action argument.");
 1327  
                         }
 1328  
 
 1329  
                         // get type
 1330  107
                         final String type = parameterType.getSimpleName();
 1331  
 
 1332  107
                         if (INTEGER_TYPE.equals(type)) {
 1333  92
                                 result = new Argument<Integer>(argumentName, Integer.class);
 1334  
 
 1335  15
                         } else if (BOOLEAN_TYPE.equals(type)) {
 1336  14
                                 result = new Argument<Boolean>(argumentName, Boolean.class);
 1337  
 
 1338  1
                         } else if (DOUBLE_TYPE.equals(type)) {
 1339  1
                                 result = new Argument<Double>(argumentName, Double.class);
 1340  
 
 1341  0
                         } else if (STRING_TYPE.equals(type)) {
 1342  0
                                 result = new Argument<String>(argumentName, String.class);
 1343  
 
 1344  0
                         } else if (OBJECT_TYPE.equals(type)) {
 1345  0
                                 result = new Argument<Object>(argumentName, Object.class);
 1346  
 
 1347  
                         } else {
 1348  0
                                 throw new PolicyException(getLocation() + ": Argument type is not supported.");
 1349  
                         }
 1350  
 
 1351  107
                 } else if (CURRENT_TIME_ELEMENT.equals(tag)) {
 1352  
 
 1353  26
                         result = new CurrentTime();
 1354  
 
 1355  
                 } else {
 1356  0
                         throw new PolicyException(getLocation() + ": Unknown value type.");
 1357  
                 }
 1358  368
                 nextElement(END, tag);
 1359  368
                 return result;
 1360  
         }
 1361  
 }