/**
     * Prints help.
     *
     * @return \ApiGen\Application
     */
    protected function printHelp()
    {
        $defaultTemplateConfig = Config\Helper::getDefaultTemplateConfigPath();
        $help = <<<HELP
Usage:
\tapigen --config <path> [options]
\tapigen --source <dir|file> --destination <dir> [options]

Options:
\t--config|-c        <file>      Config file
\t--source|-s        <dir|file>  Source file or directory to parse (can be used multiple times)
\t--destination|-d   <dir>       Directory where to save the generated documentation
\t--extensions       <list>      List of allowed file extensions, default "php"
\t--exclude          <mask>      Mask (case sensitive) to exclude file or directory from processing (can be used multiple times)
\t--skip-doc-path    <mask>      Don't generate documentation for elements from file or directory with this (case sensitive) mask (can be used multiple times)
\t--skip-doc-prefix  <value>     Don't generate documentation for elements with this (case sensitive) name prefix (can be used multiple times)
\t--charset          <list>      Character set of source files, default "auto"
\t--main             <value>     Main project name prefix
\t--title            <value>     Title of generated documentation
\t--base-url         <value>     Documentation base URL
\t--google-cse-id    <value>     Google Custom Search ID
\t--google-cse-label <value>     Google Custom Search label
\t--google-analytics <value>     Google Analytics tracking code
\t--template-config  <file>      Template config file, default "{$defaultTemplateConfig}"
\t--allowed-html     <list>      List of allowed HTML tags in documentation, default "b,i,a,ul,ol,li,p,br,var,samp,kbd,tt"
\t--groups           <value>     How should elements be grouped in the menu. Default value is "auto" (namespaces if available, packages otherwise)
\t--autocomplete     <list>      Element types for search input autocomplete. Default value is "@valuelasses,constants,functions"
\t--access-levels    <list>      Generate documentation for methods and properties with given access level, default "public,protected"
\t--internal         <yes|no>    Generate documentation for elements marked as internal and display internal documentation parts, default "no"
\t--php              <yes|no>    Generate documentation for PHP internal classes, default "yes"
\t--tree             <yes|no>    Generate tree view of classes, interfaces, traits and exceptions, default "yes"
\t--deprecated       <yes|no>    Generate documentation for deprecated elements, default "no"
\t--todo             <yes|no>    Generate documentation of tasks, default "no"
\t--source-code      <yes|no>    Generate highlighted source code files, default "yes"
\t--download         <yes|no>    Add a link to download documentation as a ZIP archive, default "no"
\t--report           <file>      Save a checkstyle report of poorly documented elements into a file
\t--wipeout          <yes|no>    Wipe out the destination directory first, default "yes"
\t--plugin-config    <file>      Plugin config file (can be used multiple times)
\t--quiet            <yes|no>    Don't display scaning and generating messages, default "no"
\t--progressbar      <yes|no>    Display progressbars, default "yes"
\t--colors           <yes|no>    Use colors, default "yes" in terminals with colors support
\t--update-check     <yes|no>    Check for update, default "yes"
\t--debug            <yes|no>    Display additional information in case of an error, default "no"
\t--help|-h                      Display this help

Only source and destination directories are required - either set explicitly or using a config file. Configuration parameters passed via command line have precedence over parameters from a config file.

Boolean options (those with possible values yes|no) do not have to have their values defined explicitly. Using --debug and --debug=yes is exactly the same.

Some options can have multiple values. You can do so either by using them multiple times or by separating values by a comma. That means that writing --source=file1.php --source=file2.php or --source=file1.php,file2.php is exactly the same.

Files or directories specified by --exclude will not be processed at all.
Elements from files within --skip-doc-path or with --skip-doc-prefix will be parsed but will not have their documentation generated. However if classes have any child classes, the full class tree will be generated and their inherited methods, properties and constants will be displayed (but will not be clickable).

HELP;
        call_user_func(array($this->logger, 'log'), $help);
        return $this;
    }
 /**
  * Checks ApiGen configuration.
  *
  * @param array $config Parsed configuration
  * @throws \ApiGen\Config\Exception If there is an error in configuration
  */
 private function checkConfiguration(array $config)
 {
     // Base configuration
     if (empty($config['source'])) {
         throw new ConfigException('Source is not set');
     }
     foreach ($config['source'] as $source) {
         if (!file_exists($source)) {
             throw new ConfigException(sprintf('Source "%s" doesn\'t exist', $source));
         }
     }
     if (empty($config['destination'])) {
         throw new ConfigException('Destination is not set');
     }
     foreach ($config['extensions'] as $extension) {
         if (!preg_match('~^[a-z\\d]+$~i', $extension)) {
             throw new ConfigException(sprintf('Invalid file extension "%s"', $extension));
         }
     }
     if (!empty($config['googleCseId']) && !preg_match('~^\\d{21}:[-a-z0-9_]{11}$~', $config['googleCseId'])) {
         throw new ConfigException(sprintf('Invalid Google Custom Search ID "%s"', $config['googleCseId']));
     }
     if (!empty($config['googleAnalytics']) && !preg_match('~^UA\\-\\d+\\-\\d+$~', $config['googleAnalytics'])) {
         throw new ConfigException(sprintf('Invalid Google Analytics tracking code "%s"', $config['googleAnalytics']));
     }
     if (empty($config['groups'])) {
         throw new ConfigException('No supported groups value given');
     }
     if (empty($config['autocomplete'])) {
         throw new ConfigException('No supported autocomplete value given');
     }
     if (empty($config['accessLevels'])) {
         throw new ConfigException('No supported access level given');
     }
     // Template configuration
     $require = $config['template']['require'];
     if (isset($require['min']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['min'])) {
         throw new ConfigException(sprintf('Invalid minimal version definition "%s"', $require['min']));
     }
     if (isset($require['max']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['max'])) {
         throw new ConfigException(sprintf('Invalid maximal version definition "%s"', $require['max']));
     }
     $isMinOk = function ($min) {
         $min .= str_repeat('.0', 2 - substr_count($min, '.'));
         return version_compare($min, Environment::getApplicationVersion(), '<=');
     };
     $isMaxOk = function ($max) {
         $max .= str_repeat('.0', 2 - substr_count($max, '.'));
         return version_compare($max, Environment::getApplicationVersion(), '>=');
     };
     if (isset($require['min'], $require['max']) && (!$isMinOk($require['min']) || !$isMaxOk($require['max']))) {
         throw new ConfigException(sprintf('The template requires version from "%s" to "%s", you are using version "%s"', $require['min'], $require['max'], Environment::getApplicationVersion()));
     } elseif (isset($require['min']) && !$isMinOk($require['min'])) {
         throw new ConfigException(sprintf('The template requires version "%s" or newer, you are using version "%s"', $require['min'], Environment::getApplicationVersion()));
     } elseif (isset($require['max']) && !$isMaxOk($require['max'])) {
         throw new ConfigException(sprintf('The template requires version "%s" or older, you are using version "%s"', $require['max'], Environment::getApplicationVersion()));
     }
     foreach (array('main', 'optional') as $section) {
         foreach ($config['template']['templates'][$section] as $type => $typeConfig) {
             if (!isset($typeConfig['filename'])) {
                 throw new ConfigException(sprintf('Filename for "%s" is not defined', $type));
             }
             if (!isset($typeConfig['template'])) {
                 throw new ConfigException(sprintf('Template for "%s" is not defined', $type));
             }
             if (null === Helper::getAbsoluteFilePath($typeConfig['template'], array(dirname($config['templateConfig'])))) {
                 throw new ConfigException(sprintf('Template for "%s" doesn\'t exist', $type));
             }
         }
     }
     // Plugins configuration
     foreach ($config['plugins'] as $pluginName => $definition) {
         if (!is_array($definition)) {
             throw new ConfigException(sprintf('Definition of plugin "%s" has to be an array', $pluginName));
         }
         if (!isset($definition['location'], $definition['class'])) {
             throw new ConfigException(sprintf('Plugin "%s" has to declare its location and class name', $pluginName));
         }
         foreach ($definition as $key => $value) {
             switch ($key) {
                 case 'location':
                 case 'class':
                     if (!is_string($value)) {
                         throw new ConfigException(sprintf('Parameter "%s" value has to be a string in plugin "%s" configuration', $key, $pluginName));
                     }
                     if ('location' === $key && !is_dir($value)) {
                         throw new ConfigException(sprintf('Plugin "%s" location "%s" does not exist', $pluginName, $value));
                     }
                     break;
                 case 'events':
                     if (!is_array($value)) {
                         throw new ConfigException(sprintf('Event hooks have to be defined as an array in plugin "%s" configuration', $pluginName));
                     }
                     foreach ($value as $index => $listenerDefinition) {
                         if (!preg_match(PluginsExtension::EVENT_LISTENER_FORMAT, $listenerDefinition, $matches)) {
                             throw new ConfigException(sprintf('Event hooks #%d definition is invalid in plugin "%s" configuration', $index + 1, $pluginName));
                         }
                     }
                     break;
                 case 'options':
                     if (!is_array($value)) {
                         throw new ConfigException(sprintf('Parameter "%s" value has to be an array in plugin "%s" configuration', $key, $pluginName));
                     }
                     break;
                 default:
                     throw new ConfigException(sprintf('Unknown plugin configuration option "%s" in plugin "%s" configuration', $key, $pluginName));
             }
         }
     }
 }