#!/usr/bin/env python
#
# $Id: happydoc_class.py,v 1.34 2001/06/02 19:04:20 doughellmann Exp $
#
# Time-stamp: <01/04/26 12:48:14 dhellmann>
#
# Copyright Doug Hellmann 2000
#
#                         All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

"""HappyDoc Class for HappyDoc Application.

"""

__rcs_info__ = {
    #
    #  Creation Information
    #
    'module_name'  : '$RCSfile: happydoc_class.py,v $',
    'rcs_id'       : '$Id: happydoc_class.py,v 1.34 2001/06/02 19:04:20 doughellmann Exp $',
    'creator'      : 'Doug Hellmann <doughellmann@bigfoot.com>',
    'project'      : 'HappyDoc',
    'created'      : 'Sun, 13-Aug-2000 11:27:00 EDT',

    #
    #  Current Information
    #
    'author'       : '$Author: doughellmann $',
    'version'      : '$Revision: 1.34 $',
    'date'         : '$Date: 2001/06/02 19:04:20 $',
}

#
# Import system modules
#
import os
import glob
import sys
import types
import string
import re
import parser
import traceback

#
# Import Local modules
#
from parseinfo import getDocs
import parseinfo
from CommandLineApp import CommandLineApp
from prettyast import astListFixNames
import docset
import hdformatter
from cvsversion import cvs_product_version
import hdpath
import optiontools
import happydocset
import happyformatter

#
# Module
#

True = 1
False = None
    
class HappyDoc(CommandLineApp):
    """
    HappyDoc is a documentation generation/extraction tool which does
    not depend on being able to import modules.

    This app is based on the Demos/parser/example.py module distributed
    with the Python source distribution.

    """

    include_private_names = True
    include_comments = True
    output_directory = './doc'
    output = None
    author_name = 'Doug Hellmann <doughellmann@bigfoot.com>'
    app_home = 'http://HappyDoc.sourceforge.net/'
    package_description_file = 'README.txt'
    recurse_into_subdirs=True

    docset_type = None
    docset_title = 'HappyDoc Generated Documentation'

    #
    # Define the formats supported
    #
    supported_formats = hdformatter.plugins

    #
    # Define the documentation set types supported
    #
    supported_docset_types = docset.plugins

    def appInit(self):
        self._app_name = self.__class__.__name__
        self._app_version = cvs_product_version()
        
        self.statusMessage('%s version %s' % (self._app_name,
                                              self._app_version))
        
        self.set_docset_type('multifile_docset')
        self.set_format('htmltable')
        
        self._ignore_dir_patterns = []
        self.addIgnoreDirectoryPattern('CVS', 'dist', 'build', 'doc', 'docs')
        return

    def addIgnoreDirectoryPattern(self, *dirNamePatterns):
        "Add one or more directory name patterns to the list which should be ignored."
        for dir_name_pattern in dirNamePatterns:
            if dir_name_pattern not in self._ignore_dir_patterns:
                self._ignore_dir_patterns.append(dir_name_pattern)
                self.statusMessage('Ignoring %s' % dir_name_pattern, 2)
        return

    def ignoreDirectoryTest(self, dirName):
        """Determines whether 'dirName' matches pattern to be ignored.

        Arguments

          'dirName' -- Full path of the directory to be tested.

        Returns true value if 'dirName' should be ignored, false value
        otherwise.

        """
        dir_base = hdpath.basename(dirName) 
        if dir_base in self._ignore_dir_patterns:
            return 1
        else:
            return 0
    
    def set_format(self, format):
        "Set the formatter to be used."
        self.format = format
        try:
            self.formatter_factory = self.supported_formats[format]
        except KeyError:
            raise ValueError('format must be one of %s' \
                             % self.supported_formats.keys(),
                             format)
        return

    def set_docset_type(self, docset_type):
        "Set the docset to be used."
        self.docset_type = docset_type
        try:
            self.docset_factory = self.supported_docset_types[docset_type]
        except KeyError:
            raise ValueError('docset_type must be one of %s' % \
                             self.supported_docset_types.keys(),
                             docset_type)
        return

    def optionHandler_no_private_names(self):
        "Do not include names beginning with _."
        self.include_private_names = False
        return

    def optionHandler_no_comments(self):
        """Do not include comment text as though it was
           a __doc__ string.
        """
        self.include_comments = False
        return
    
    def optionHandler_d(self, outputDirectory):
        """Specify an outputDirectory.

        Defaults to './doc'."""
        self.output_directory = outputDirectory
        return

    def optionHandler_i(self, ignoreDirectory):
        """Specify a directory basename to be ignored.

        Use just the base name of the directory.
        For instance, to ignore all directories
        with the name CVS, specify: -i CVS.

        Defaults to ignore::
        
          CVS, dist, build, doc, docs.
          
        """
        ignoreDirectory=string.strip(ignoreDirectory)
        self.statusMessage('Adding ignore directive for %s' % ignoreDirectory)
        self.addIgnoreDirectoryPattern(ignoreDirectory)
        return

    def optionHandler_r(self):
        "Disable recursion into subdirectories."
        self.recurse_into_subdirs = False
        return

    def optionHandler_o(self):
        "Specify that output should go to stdout."
        self.set_docset_type('stdout')
        return

    def optionHandler_t(self, title):
        "Specify a title for the documentation set."
        self.docset_title = title
        return

    def optionHandler_p(self, packageDescriptionFile):
        """Specify a file with a description of the package.

        The default packageDescriptionFile is README.txt.
        """
        self.package_description_file = packageDescriptionFile
        return

    def optionHandler_author(self, authorNameAndEmail):
        """Specify the author identification to be inserted for
        references.
        """
        self.author_name = authorNameAndEmail
        return

    def optionHandler_F(self, format):
        """Specify the output format.

        Defaults to 'htmltable'."""
        self.set_format(format)
        return

    def optionHandler_T(self, docset_type):
        """Specify the documentation set type.

        Defaults to 'multifile_docset'."""
        self.set_docset_type(docset_type)
        return

    def showVerboseSyntaxHelp(self):
        "Overloaded to show supported docset and format types."
        CommandLineApp.showVerboseSyntaxHelp(self)

        print 'SUPPORTED FORMATS for -F Option:\n'
        items = self.supported_formats.items()
        items.sort()
        for i in items:
            print '  FORMATTER TYPE %s: %s' % (i[0], i[1].__doc__)

        print '\nSUPPORTED DOCSET TYPES for -T Option:\n'
        print '  %s' % happydocset.DocSet.__doc__
        print
        items = self.supported_docset_types.items()
        items.sort()
        for i in items:
            print '  DOCSET TYPE: %s: %s' % (i[0], i[1].__doc__)

        print
        return

    
    def main(self, *args):
        
        #
        # Debug info about where the docsets and hdformatters come from
        #
        self.statusMessage('Docsets list from %s' % docset.__path__[0], 1)
        self.statusMessage('Formatters from %s' % hdformatter.__path__[0], 1)
        #
        # Find DocSet arguments
        #
        self.statusMessage('Looking for docset parameters', 2)
        args, docset_params = optiontools.getParameters('docset', args)
        self.statusMessage('DEBUG: Docset parameters:', 3)
        for p, v in docset_params.items():
            self.statusMessage('DEBUG: \t%s:%s' % (p,v), 3)
        #
        # Find Formatter parameters
        #
        self.statusMessage('Looking for formatter parameters', 2)
        args, formatter_params = optiontools.getParameters('formatter', args)
        self.statusMessage('DEBUG: Formatter parameters:', 3)
        for p, v in formatter_params.items():
            self.statusMessage('DEBUG: \t%s:%s' % (p,v), 3)
        #
        # Get the list of modules to input
        #
        input_modules = args
        if not input_modules:
            #
            # No files specified, print a help message and exit.
            #
            self.showHelp('Specify input file(s) to be processed.')
            raise self.HelpRequested, 'No input file(s) specified.'
        #
        # Create output directory
        #
        if not self.output:
            od = self.output_directory
            self.statusMessage('Output directory is %s' % self.output_directory, 2)
            if (od[0] != '/'):
                od = hdpath.join( hdpath.cwd(), od )
                self.statusMessage('Setting output directory to %s' % od, 2)
            od = hdpath.normpath(od)
            self.statusMessage('Creating output directory %s' % od, 2)
            hdpath.rmkdir(od)
            self.output_directory = od
        #
        # Create the docset
        #
        docset_init_params = {
            'formatterFactory':self.formatter_factory,
            'parserFunc':parseinfo.getDocs,
            'inputModuleNames':input_modules,
            
            'author':self.author_name,
            'outputBaseDirectory':self.output_directory,
            'descriptionFilename':self.package_description_file,
            'formatterParameters':formatter_params,
            'ignoreDirFunc':self.ignoreDirectoryTest,
            'includeComments':self.include_comments,
            'includePrivateNames':self.include_private_names,
            'statusMessageFunc':self.statusMessage,
            'title':self.docset_title,
            'useRecursion':self.recurse_into_subdirs,

            }
        docset_init_params.update(docset_params)
        parsed_modules = apply( self.docset_factory, (), docset_init_params)
        #
        # Tell the docset to output its results
        #
        parsed_modules.write()
        #
        # Clean up
        #
        parsed_modules = None
        return

