private void handleProject(final Element projectElement) throws CruiseControlException {

    final String projectName = getProjectName(projectElement);

    if (projects.containsKey(projectName)) {
      final String duplicateEntriesMessage =
          "Duplicate entries in config file for project name " + projectName;
      throw new CruiseControlException(duplicateEntriesMessage);
    }

    // property handling is a little bit dirty here.
    // we have a set of properties mostly resolved in the rootProperties
    // and a child set of properties
    // it is possible that the rootProperties contain references to child
    // properties
    // in particular the project.name one
    final MapWithParent nonFullyResolvedProjectProperties = new MapWithParent(rootProperties);
    // Register the project's name as a built-in property
    LOG.debug("Setting property \"project.name\" to \"" + projectName + "\".");
    nonFullyResolvedProjectProperties.put("project.name", projectName);

    // handle project templates properties
    final List projectTemplateProperties = templatePluginProperties.get(projectElement.getName());
    if (projectTemplateProperties != null) {
      for (final Object projectTemplateProperty : projectTemplateProperties) {
        final Element element = (Element) projectTemplateProperty;
        ProjectXMLHelper.registerProperty(
            nonFullyResolvedProjectProperties, element, FAIL_UPON_MISSING_PROPERTY);
      }
    }

    // Register any project specific properties
    for (final Object o : projectElement.getChildren("property")) {
      final Element propertyElement = (Element) o;
      ProjectXMLHelper.registerProperty(
          nonFullyResolvedProjectProperties, propertyElement, FAIL_UPON_MISSING_PROPERTY);
    }

    // add the resolved rootProperties to the project's properties
    final Map<String, String> thisProperties = nonFullyResolvedProjectProperties.thisMap;
    for (final String key : rootProperties.keySet()) {
      if (!thisProperties.containsKey(key)) {
        final String value = rootProperties.get(key);
        thisProperties.put(key, Util.parsePropertiesInString(thisProperties, value, false));
      }
    }

    // Parse the entire element tree, expanding all property macros
    ProjectXMLHelper.parsePropertiesInElement(
        projectElement, thisProperties, FAIL_UPON_MISSING_PROPERTY);

    // Register any custom plugins
    final PluginRegistry projectPlugins = PluginRegistry.createRegistry(rootPlugins);
    for (final Object o : projectElement.getChildren("plugin")) {
      final Element element = (Element) o;
      // final PluginPlugin plugin = (PluginPlugin)
      new ProjectXMLHelper().configurePlugin(element, false);
      // projectPlugins.register(plugin);
      projectPlugins.register(element);
      // add(plugin);
    }

    projectElement.removeChildren("property");
    projectElement.removeChildren("plugin");

    LOG.debug("**************** configuring project " + projectName + " *******************");
    ProjectHelper projectHelper =
        new ProjectXMLHelper(thisProperties, projectPlugins, fileResolver, controller);

    final ProjectInterface project;
    try {
      project = (ProjectInterface) projectHelper.configurePlugin(projectElement, false);
    } catch (CruiseControlException e) {
      throw new CruiseControlException("error configuring project " + projectName, e);
    }

    // Why call method that is a no-op, and exists only for gendoc purposes?
    // add(project);

    project.validate();
    LOG.debug("**************** end configuring project " + projectName + " *******************");

    this.projects.put(projectName, project);
    this.projectPluginRegistries.put(projectName, projectPlugins);
  }