/**
   * Generates entity classes.
   *
   * @param distPath directory to generates the code at
   */
  public void generate(String distPath) throws IOException {
    // Remove all the previously generate classes, so that classes corresponding to types that have
    // been
    // removed from the XML schema will be later removed from the source code repository:
    File packageDir = new File(distPath, ENTITIES_PACKAGE.replace('.', File.separatorChar));
    if (packageDir.exists()) {
      FileUtils.cleanDirectory(packageDir);
    }

    // Run the XJC compiler to generate all the classes:
    System.setProperty("javax.xml.accessExternalSchema", "all");
    try {
      com.sun.tools.xjc.Driver.run(
          new String[] {
            "-extension",
            "-no-header",
            "-enableIntrospection",
            "-d",
            distPath,
            "-p",
            ENTITIES_PACKAGE,
            "-b",
            xjbFile.getAbsolutePath(),
            xsdFile.getAbsolutePath()
          },
          System.out,
          System.err);
    } catch (Exception exception) {
      throw new IOException(exception);
    }
  }
  /** {@inheritDoc} */
  @Override
  protected boolean performExecution() throws MojoExecutionException, MojoFailureException {

    boolean updateStaleFileTimestamp = false;

    try {

      // Setup the Tool's execution environment
      ToolExecutionEnvironment environment = null;
      try {

        // Create a LocaleFacet if the user has configured an explicit Locale for the tool.
        final LocaleFacet localeFacet =
            locale == null ? null : LocaleFacet.createFor(locale, getLog());

        // Create the ToolExecutionEnvironment
        environment =
            new ToolExecutionEnvironment(
                getLog(),
                ThreadContextClassLoaderBuilder.createFor(this.getClass(), getLog())
                    .addPaths(getClasspath()),
                LoggingHandlerEnvironmentFacet.create(getLog(), getClass(), getEncoding(false)),
                localeFacet);

        // Add any extra configured EnvironmentFacets, as configured in the POM.
        if (extraFacets != null) {
          for (EnvironmentFacet current : extraFacets) {
            environment.add(current);
          }
        }

        // Setup the environment.
        environment.setup();

        // Compile the XJC arguments
        final String[] xjcArguments =
            getXjcArguments(environment.getClassPathAsArgument(), STANDARD_EPISODE_FILENAME);

        // Ensure that the outputDirectory exists, but only clear it if does not already
        FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir);

        // Do we need to re-create the episode file's parent directory.
        final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir;
        if (reCreateEpisodeFileParentDirectory) {
          getEpisodeFile(STANDARD_EPISODE_FILENAME);
        }

        // Check the system properties.
        logSystemPropertiesAndBasedir();

        // Fire XJC
        if (XJC_COMPLETED_OK != Driver.run(xjcArguments, new XjcLogAdapter(getLog()))) {

          final StringBuilder errorMsgBuilder = new StringBuilder();
          errorMsgBuilder.append("\n+=================== [XJC Error]\n");
          errorMsgBuilder.append("|\n");

          final List<URL> sourceXSDs = getSources();
          for (int i = 0; i < sourceXSDs.size(); i++) {
            errorMsgBuilder
                .append("| " + i + ": ")
                .append(sourceXSDs.get(i).toString())
                .append("\n");
          }

          errorMsgBuilder.append("|\n");
          errorMsgBuilder.append("+=================== [End XJC Error]\n");
          throw new MojoExecutionException(errorMsgBuilder.toString());
        }

        // Indicate that the output directory was updated.
        getBuildContext().refresh(getOutputDirectory());

        // Update the modification timestamp of the staleFile.
        updateStaleFileTimestamp = true;

      } finally {

        if (environment != null) {
          environment.restore();
        }
      }

      // Add the generated source root to the project, enabling tooling and other plugins to see
      // them.
      addGeneratedSourcesToProjectSourceRoot();

      // Copy all source XSDs to the resulting artifact?
      if (xsdPathWithinArtifact != null) {

        final String buildOutputDirectory = getProject().getBuild().getOutputDirectory();
        final File targetXsdDirectory = new File(buildOutputDirectory, xsdPathWithinArtifact);
        FileUtils.forceMkdir(targetXsdDirectory);

        for (URL current : getSources()) {

          String fileName = null;
          if ("file".equalsIgnoreCase(current.getProtocol())) {
            fileName = new File(current.getPath()).getName();
          } else if ("jar".equalsIgnoreCase(current.getProtocol())) {

            // Typical JAR path
            // jar:file:/path/to/aJar.jar!/some/path/xsd/aResource.xsd
            final int bangIndex = current.toString().indexOf("!");
            if (bangIndex == -1) {
              throw new MojoExecutionException(
                  "Illegal JAR URL [" + current.toString() + "]: lacks a '!'");
            }

            final String internalPath = current.toString().substring(bangIndex + 1);
            fileName = new File(internalPath).getName();
          } else {
            throw new MojoExecutionException(
                "Could not extract FileName from URL [" + current + "]");
          }

          final File targetFile = new File(targetXsdDirectory, fileName);
          if (targetFile.exists()) {

            // TODO: Should we throw an exception here instead?
            getLog()
                .warn(
                    "File ["
                        + FileSystemUtilities.getCanonicalPath(targetFile)
                        + "] already exists. Not copying XSD file ["
                        + current.getPath()
                        + "] to it.");
          }
          IOUtil.copy(current.openStream(), new FileWriter(targetFile));
        }

        // Refresh the BuildContext
        getBuildContext().refresh(targetXsdDirectory);
      }
    } catch (MojoExecutionException e) {
      throw e;
    } catch (NoSchemasException e) {
      if (failOnNoSchemas) {
        throw new MojoExecutionException("", e);
      }
    } catch (Exception e) {
      throw new MojoExecutionException(e.getMessage(), e);
    }

    // All done.
    return updateStaleFileTimestamp;
  }