packages feed

SourceGraph-0.3: Analyse/Module.hs

{-
Copyright (C) 2008 Ivan Lazar Miljenovic <Ivan.Miljenovic@gmail.com>

This file is part of SourceGraph.

SourceGraph is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{- |
   Module      : Analyse.Module
   Description : Analyse modules.
   Copyright   : (c) Ivan Lazar Miljenovic 2008
   License     : GPL-3 or later.
   Maintainer  : Ivan.Miljenovic@gmail.com

   Analysis of Haskell modules.
 -}
module Analyse.Module(analyseModules) where

import Parsing.Types
import Analyse.Utils

import Data.Graph.Analysis

import Data.Maybe
import Text.Printf

-- -----------------------------------------------------------------------------

-- Helper types

-- | Shorthand type
type FunctionData = (String, GraphData AString)

-- -----------------------------------------------------------------------------

-- Analysing.

-- | Performs analysis of all modules present in the 'HaskellModules' provided.
analyseModules :: HaskellModules -> DocElement
analyseModules = Section (Text "Analysis of each module")
                 . catMaybes . map analyseModule . hModulesIn

-- | Performs analysis of the given 'HaskellModule'.
analyseModule    :: HaskellModule -> Maybe DocElement
analyseModule hm = if (n > 1)
                   then Just $  Section sec elems
                   else Nothing
    where
      m = show $ moduleName hm
      (n,fd) = moduleToGraph hm
      elems = catMaybes
              $ map ($fd) [ graphOf
                          , collapseAnal
                          , coreAnal
                          , cycleCompAnal
                          , rootAnal
                          , componentAnal
                          , cliqueAnal
                          , cycleAnal
                          , chainAnal
                          ]
      sec = Grouping [ Text "Analysis of"
                     , Emphasis (Text m)]

-- | Convert the module to the /Graphalyze/ format.
moduleToGraph    :: HaskellModule -> (Int,FunctionData)
moduleToGraph hm = (n,(show $ moduleName hm, fd'))
    where
      n = applyAlg noNodes fd'
      fd' = manipulateNodes (AS . name) fd
      fd = importData params
      funcs = functions hm
      params = Params { dataPoints    = functionsIn funcs
                      , relationships = functionEdges funcs
                      , roots         = exports hm
                      , directed      = True
                      }

graphOf        :: FunctionData -> Maybe DocElement
graphOf (m,fd) = Just $ Section sec [gi]
    where
      sec = Grouping [ Text "Visualisation of"
                     , Emphasis (Text m)]
      gi = GraphImage $ applyAlg dg fd
      dg g = toGraph m lbl g
      lbl = unwords ["Diagram of:", m]

componentAnal :: FunctionData -> Maybe DocElement
componentAnal (m,fd)
    | single comp = Nothing
    | otherwise   = Just el
    where
      comp = applyAlg componentsOf fd
      len = length comp
      el = Section sec [Paragraph [Text text]]
      sec = Grouping [ Text "Component analysis of"
                     , Emphasis (Text m)]
      text = printf "The module %s has %d components.  \
                     \You may wish to consider splitting it up." m len

cliqueAnal :: FunctionData -> Maybe DocElement
cliqueAnal (m,fd)
    | null clqs = Nothing
    | otherwise = Just el
    where
      clqs = applyAlg cliquesIn fd
      clqs' = return . Itemized
              $ map (Paragraph . return . Text . showNodes) clqs
      text = Text $ printf "The module %s has the following cliques:" m
      el = Section sec $ (Paragraph [text]) : clqs'
      sec = Grouping [ Text "Clique analysis of"
                     , Emphasis (Text m)]

cycleAnal :: FunctionData -> Maybe DocElement
cycleAnal (m,fd)
    | null cycs = Nothing
    | otherwise = Just el
    where
      cycs = applyAlg uniqueCycles fd
      cycs' = return . Itemized
              $ map (Paragraph . return . Text . showCycle) cycs
      text = Text $ printf "The module %s has the following non-clique \
                            \cycles:" m
      el = Section sec $ (Paragraph [text]) : cycs'
      sec = Grouping [ Text "Cycle analysis of"
                     , Emphasis (Text m)]

chainAnal :: FunctionData -> Maybe DocElement
chainAnal (m,fd)
    | null chns = Nothing
    | otherwise = Just el
    where
      chns = interiorChains fd
      chns' = return . Itemized
              $ map (Paragraph . return . Text . showPath) chns
      text = Text $ printf "The module %s has the following chains:" m
      textAfter = Text "These chains can all be compressed down to \
                       \a single function."
      el = Section sec
           $ [Paragraph [text]] ++ chns' ++ [Paragraph [textAfter]]
      sec = Grouping [ Text "Chain analysis of"
                     , Emphasis (Text m)]

rootAnal :: FunctionData -> Maybe DocElement
rootAnal (m,fd)
    | asExpected = Nothing
    | otherwise  = Just el
    where
      (wntd, ntRs, ntWd) = classifyRoots fd
      asExpected = (null ntRs) && (null ntWd)
      rpt (s,ns) = if (null ns)
                   then Nothing
                   else Just [ Paragraph
                               [Text
                                $ concat ["These nodes are those that are "
                                         , s, ":"]]
                             , Paragraph [Emphasis . Text $ showNodes ns]]
      ps = concat . catMaybes
           $ map rpt [ ("in the export list and roots",wntd)
                     , ("in the export list but not roots",ntRs)
                     , ("not in the export list but roots",ntWd)]
      el = Section sec ps
      sec = Grouping [ Text "Root analysis of"
                     , Emphasis (Text m)]

coreAnal        :: FunctionData -> Maybe DocElement
coreAnal (m,fd) = if (isEmpty core)
                  then Nothing
                  else Just el
    where
      core = applyAlg coreOf fd
      p = m ++ "_core"
      lbl = unwords ["Core of", m]
      hdr = Paragraph [Text "The core of a module can be thought of as \
                             \the part where all the work is actually done."]
      anal = GraphImage (toGraph p lbl core)
      el = Section sec [hdr, anal]
      sec = Grouping [ Text "Core analysis of"
                     , Emphasis (Text m)]

collapseAnal        :: FunctionData -> Maybe DocElement
collapseAnal (m,fd) = if (trivialCollapse gc)
                      then Nothing
                      else Just el
    where
      gc = applyAlg collapseGraph fd
      p = m ++ "_collapsed"
      lbl = unwords ["Collapsed view of", m]
      hdr = Paragraph [Text "The collapsed view of a module collapses \
                            \down all cliques, cycles, chains, etc. to \
                            \make the graph tree-like." ]
      gr = GraphImage (toGraph p lbl gc)
      el = Section sec [hdr, gr]
      sec = Grouping [ Text "Collapsed view of"
                     , Emphasis (Text m)]


cycleCompAnal        :: FunctionData -> Maybe DocElement
cycleCompAnal (m,fd) = Just $ Section sec pars
    where
      cc = cyclomaticComplexity fd
      sec = Grouping [ Text "Cyclomatic Complexity of"
                       , Emphasis (Text m)]
      pars = [Paragraph [text], Paragraph [textAfter, link]]
      text = Text
             $ printf "The cyclomatic complexity of %s is: %d." m cc
      textAfter = Text "For more information on cyclomatic complexity, \
                       \please see: "
      link = DocLink (Text "Wikipedia: Cyclomatic Complexity")
                     (URL "http://en.wikipedia.org/wiki/Cyclomatic_complexity")