/**
   * Private helper used by the function factory code to load a specific target, condition, or
   * general section.
   */
  private void functionParserHelper(Node root, FunctionFactory factory) throws ParsingException {
    // go through all elements in the section
    NodeList children = root.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      Node child = children.item(i);
      String name = child.getNodeName();

      if (name.equals("function")) {
        // a function section is a simple class element
        Function function = (Function) (loadClass("function", child));
        try {
          factory.addFunction(function);
        } catch (IllegalArgumentException iae) {
          throw new ParsingException("duplicate function", iae);
        }
      } else if (name.equals("abstractFunction")) {
        // an abstract function is a class with an identifier
        URI identifier = null;
        try {
          identifier = new URI(child.getAttributes().getNamedItem("identifier").getNodeValue());
        } catch (URISyntaxException urise) {
          throw new ParsingException("invalid function identifier", urise);
        }

        FunctionProxy proxy = (FunctionProxy) (loadClass("abstract function", child));
        try {
          factory.addAbstractFunction(proxy, identifier);
        } catch (IllegalArgumentException iae) {
          throw new ParsingException("duplicate abstract function", iae);
        }
      } else if (name.equals("functionCluster")) {
        // a cluster is a class that will give us a collection of
        // functions that need to be added one by one into the factory
        FunctionCluster cluster = (FunctionCluster) (loadClass("function cluster", child));

        for (Function function : cluster.getSupportedFunctions()) {
          try {
            factory.addFunction(function);
          } catch (IllegalArgumentException iae) {
            throw new ParsingException("duplicate function", iae);
          }
        }
      }
    }
  }
  /**
   * Registers all the supported factories with the given identifiers. If a given identifier is
   * already in use, then that factory is not registered. This method is provided only as a
   * convenience, and any registration that may involve identifier clashes should be done by
   * registering each factory individually.
   */
  public void registerFunctionFactories() {

    for (String id : functionMap.keySet()) {
      FunctionFactoryProxy ffp = (FunctionFactoryProxy) (functionMap.get(id));

      try {
        FunctionFactory.registerFactory(id, ffp);
      } catch (IllegalArgumentException iae) {
        logger.log(
            Level.WARNING, "Couldn't register FunctionFactory: " + id + " (already in use)", iae);
      }
    }
  }
  /**
   * Uses the default configuration to re-set the default factories used by the system (attribute,
   * combining algorithm, and function). If a default is not provided for a given factory, then that
   * factory will not be set as the system's default.
   */
  public void useDefaultFactories() {
    logger.fine("Switching to default factories from configuration");

    // set the default attribute factory, if it exists here
    if (defaultAttributeFactory != null) {
      AttributeFactory.setDefaultFactory(new AFProxy(defaultAttributeFactory));
    }

    // set the default combining algorithm factory, if it exists here
    if (defaultCombiningFactory != null) {
      CombiningAlgFactory.setDefaultFactory(new CAFProxy(defaultCombiningFactory));
    }

    // set the default function factories, if they exists here
    if (defaultFunctionFactoryProxy != null)
      FunctionFactory.setDefaultFactory(defaultFunctionFactoryProxy);
  }
  /**
   * Private helper function used by both constructors to actually load the configuration data. This
   * is the root of several private methods used to setup all the pdps and factories.
   */
  private void setupConfig(File configFile) throws ParsingException {
    logger.config("Loading runtime configuration");

    // load our classloader
    loader = getClass().getClassLoader();

    // get the root node from the configuration file
    Node root = getRootNode(configFile);

    // initialize all the maps
    pdpConfigMap = new HashMap<String, PDPConfig>();
    attributeMap = new HashMap<String, AttributeFactory>();
    combiningMap = new HashMap<String, CombiningAlgFactory>();
    functionMap = new HashMap<String, FunctionFactoryProxy>();

    // get the default names
    NamedNodeMap attrs = root.getAttributes();
    String defaultPDP = attrs.getNamedItem("defaultPDP").getNodeValue();
    String defaultAF = getDefaultFactory(attrs, "defaultAttributeFactory");
    String defaultCAF = getDefaultFactory(attrs, "defaultCombiningAlgFactory");
    String defaultFF = getDefaultFactory(attrs, "defaultFunctionFactory");

    // loop through all the root-level elements, for each one getting its
    // name and then loading the right kind of element
    NodeList children = root.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      Node child = children.item(i);
      String childName = child.getNodeName();
      String elementName = null;

      // get the element's name
      if (child.getNodeType() == Node.ELEMENT_NODE)
        elementName = child.getAttributes().getNamedItem("name").getNodeValue();

      // see if this is a pdp or a factory, and load accordingly,
      // putting the new element into the respective map...make sure
      // that we're never loading something with the same name twice
      if (childName.equals("pdp")) {
        if (logger.isLoggable(Level.CONFIG)) logger.config("Loading PDP: " + elementName);
        if (pdpConfigMap.containsKey(elementName))
          throw new ParsingException("more that one pdp with " + "name \"" + elementName + "\"");
        pdpConfigMap.put(elementName, parsePDPConfig(child));
      } else if (childName.equals("attributeFactory")) {
        if (logger.isLoggable(Level.CONFIG))
          logger.config("Loading AttributeFactory: " + elementName);
        if (attributeMap.containsKey(elementName))
          throw new ParsingException(
              "more that one " + "attributeFactory with name " + elementName + "\"");
        attributeMap.put(elementName, parseAttributeFactory(child));
      } else if (childName.equals("combiningAlgFactory")) {
        if (logger.isLoggable(Level.CONFIG))
          logger.config("Loading CombiningAlgFactory: " + elementName);
        if (combiningMap.containsKey(elementName))
          throw new ParsingException(
              "more that one " + "combiningAlgFactory with " + "name \"" + elementName + "\"");
        combiningMap.put(elementName, parseCombiningAlgFactory(child));
      } else if (childName.equals("functionFactory")) {
        if (logger.isLoggable(Level.CONFIG))
          logger.config("Loading FunctionFactory: " + elementName);
        if (functionMap.containsKey(elementName))
          throw new ParsingException(
              "more that one functionFactory" + " with name \"" + elementName + "\"");
        functionMap.put(elementName, parseFunctionFactory(child));
      }
    }

    // finally, extract the default elements
    defaultPDPConfig = (PDPConfig) (pdpConfigMap.get(defaultPDP));

    defaultAttributeFactory = (AttributeFactory) (attributeMap.get(defaultAF));
    if (defaultAttributeFactory == null) {
      try {
        defaultAttributeFactory = AttributeFactory.getInstance(defaultAF);
      } catch (Exception e) {
        throw new ParsingException("Unknown AttributeFactory", e);
      }
    }

    defaultCombiningFactory = (CombiningAlgFactory) (combiningMap.get(defaultCAF));
    if (defaultCombiningFactory == null) {
      try {
        defaultCombiningFactory = CombiningAlgFactory.getInstance(defaultCAF);
      } catch (Exception e) {
        throw new ParsingException("Unknown CombininAlgFactory", e);
      }
    }

    defaultFunctionFactoryProxy = (FunctionFactoryProxy) (functionMap.get(defaultFF));
    if (defaultFunctionFactoryProxy == null) {
      try {
        defaultFunctionFactoryProxy = FunctionFactory.getInstance(defaultFF);
      } catch (Exception e) {
        throw new ParsingException("Unknown FunctionFactory", e);
      }
    }
  }
  @Override
  protected Condition createCondition() {
    URI designatorType = null;
    URI designatorId = null;
    try {
      designatorType = new URI("http://www.w3.org/2001/XMLSchema#integer");
      designatorId = new URI(getId());
    } catch (URISyntaxException ex) {
      log.fatal(null, ex);
    }
    FunctionFactory factory = FunctionFactory.getConditionInstance();
    Function conditionFunction = null;
    try {
      conditionFunction = factory.createFunction(globalFunction.getFunctionName());
    } catch (UnknownIdentifierException ex) {
      log.fatal(null, ex);
    } catch (FunctionTypeException ex) {
      log.fatal(null, ex);
    }

    if (this.values.isEmpty()) {
      List tmp = new ArrayList();
      tmp.add(BooleanAttribute.getInstance(false));
      return new Condition(conditionFunction, tmp);
    }

    Function conditionFunction2 = null;
    try {
      conditionFunction2 = factory.createFunction("urn:oasis:names:tc:xacml:1.0:function:" + "and");
    } catch (UnknownIdentifierException ex) {
      log.fatal(null, ex);
    } catch (FunctionTypeException ex) {
      log.fatal(null, ex);
    }

    factory = FunctionFactory.getGeneralInstance();
    Function greaterFunction;
    try {
      greaterFunction =
          factory.createFunction(ComparisonFunction.NAME_INTEGER_GREATER_THAN_OR_EQUAL);
    } catch (Exception e) {
      log.fatal("Exception in condition creation", e);
      return null;
    }

    Function lessFunction;
    try {
      lessFunction = factory.createFunction(ComparisonFunction.NAME_INTEGER_LESS_THAN_OR_EQUAL);
    } catch (Exception e) {
      log.fatal("Exception in condition creation", e);
      return null;
    }

    URI quantityDesignatorId = null;
    try {
      quantityDesignatorId = new URI("urn:oasis:names:tc:xacml:1.0:resource:quantity-id");
    } catch (URISyntaxException ex) {
      log.error(null, ex);
    }

    Function quantityFunction;
    try {
      quantityFunction =
          factory.createFunction("urn:oasis:names:tc:xacml:1.0:function:" + "integer-one-and-only");
    } catch (Exception e) {
      log.fatal("Exception in SCQuantity condition", e);
      return null;
    }

    List applyCondArgs = new ArrayList();
    List conditionArgs = new ArrayList();
    for (Object quantitiesOb : values) {
      applyCondArgs.clear();
      List quantities = (List) quantitiesOb;
      List applyArgs = new ArrayList();
      List applyArgsUser = new ArrayList();

      List greatApplyArgs = new ArrayList();
      List greatApplyArgsUser = new ArrayList();

      AttributeDesignator lowDesignator =
          new AttributeDesignator(
              AttributeDesignator.RESOURCE_TARGET,
              designatorType,
              quantityDesignatorId,
              false,
              null);
      applyArgsUser.add(lowDesignator);
      Apply lowApplyUser = new Apply(quantityFunction, applyArgsUser);
      IntegerAttribute lowQuantityAttribute = new IntegerAttribute((Long) quantities.get(0));
      applyArgs.add(lowApplyUser);
      applyArgs.add(lowQuantityAttribute);
      Apply lowApply = new Apply(greaterFunction, applyArgs);
      applyCondArgs.add(lowApply);

      AttributeDesignator greatDesignator =
          new AttributeDesignator(
              AttributeDesignator.RESOURCE_TARGET,
              designatorType,
              quantityDesignatorId,
              false,
              null);
      greatApplyArgsUser.add(greatDesignator);
      Apply greatApplyUser = new Apply(quantityFunction, greatApplyArgsUser);
      IntegerAttribute greatQuantityAttribute = new IntegerAttribute((Long) quantities.get(1));
      greatApplyArgs.add(greatApplyUser);
      greatApplyArgs.add(greatQuantityAttribute);
      Apply applyCondition = new Apply(lessFunction, greatApplyArgs);
      applyCondArgs.add(applyCondition);
      Apply applyCond = new Apply(conditionFunction2, applyCondArgs);
      conditionArgs.add(applyCond);
    }
    Condition condition = new Condition(conditionFunction, conditionArgs);
    return condition;
  }