/** Private helper that handles the attributeFactory elements. */
  private AttributeFactory parseAttributeFactory(Node root) throws ParsingException {
    AttributeFactory factory = null;

    // check if we're starting with the standard factory setup
    if (useStandard(root, "useStandardDatatypes")) {
      logger.config("Starting with standard Datatypes");

      factory = StandardAttributeFactory.getNewFactory();
    } else {
      factory = new BaseAttributeFactory();
    }

    // now look for all datatypes specified for this factory, adding
    // them as we go
    NodeList children = root.getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      Node child = children.item(i);

      if (child.getNodeName().equals("datatype")) {
        // a datatype is a class with an identifier
        String identifier = child.getAttributes().getNamedItem("identifier").getNodeValue();
        AttributeProxy proxy = (AttributeProxy) (loadClass("datatype", child));

        try {
          factory.addDatatype(identifier, proxy);
        } catch (IllegalArgumentException iae) {
          throw new ParsingException("duplicate datatype: " + identifier, iae);
        }
      }
    }

    return factory;
  }
  /**
   * 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 registerAttributeFactories() {
    for (String id : attributeMap.keySet()) {
      AttributeFactory af = attributeMap.get(id);

      try {
        AttributeFactory.registerFactory(id, new AFProxy(af));
      } catch (IllegalArgumentException iae) {
        logger.log(
            Level.WARNING, "Couldn't register AttributeFactory:" + 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);
      }
    }
  }