packages feed

SourceGraph-0.6.1.1: Analyse/Imports.hs

{-
Copyright (C) 2009 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.Imports
   Description : Analyse module imports.
   Copyright   : (c) Ivan Lazar Miljenovic 2009
   License     : GPL-3 or later.
   Maintainer  : Ivan.Miljenovic@gmail.com

   Analysis of Haskell module importing.
 -}
module Analyse.Imports (analyseImports) where

import Parsing.Types
import Analyse.Utils
import Analyse.GraphRepr
import Analyse.Visualise

import Data.Graph.Analysis

import Data.Maybe(mapMaybe)
import qualified Data.Map as M
import qualified Data.Set as S
import Text.Printf(printf)

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

-- | Analyse the imports present in the software.  Takes in a random seed
--   as well as a list of all modules exported.
analyseImports         :: [ModName] -> ParsedModules -> DocElement
analyseImports exps hm = Section sec elems
    where
      imd = importsToGraph exps hm
      sec = Text "Analysis of module imports"
      elems = mapMaybe ($imd) [ graphOf
                              , cycleCompAnal
                              , rootAnal
                              , componentAnal
                              , cycleAnal
                              , chainAnal
                              ]

importsToGraph          :: [ModName] -> ParsedModules -> MData
importsToGraph exps pms = mkMData $ importData params
    where
      params = ImpParams { dataPoints    = M.keys pms
                         , relationships = moduleImports pms
                         , roots         = exps
                         , directed      = True
                         }

moduleImports :: ParsedModules -> [Rel ModName ()]
moduleImports = concatMap imps . M.elems
    where
      imps pm = map (toRel (moduleName pm))
                . M.keys $ imports pm
      toRel m m' = (m,m',())

graphOf     :: MData -> Maybe DocElement
graphOf imd = Just $ Section sec [gi]
    where
      sec = Text "Visualisation of imports"
      gi = GraphImage $ DG "imports" (Text lbl) (drawModules lbl imd)
      lbl = "Import visualisation"

componentAnal :: MData -> Maybe DocElement
componentAnal imd
    | single comp = Nothing
    | otherwise   = Just el
    where
      comp = applyAlg componentsOf $ graphData imd
      len = length comp
      el = Section sec [Paragraph [Text text]]
      sec = Text "Import component analysis"
      text = printf "The imports have %d components.  \
                     \You may wish to consider splitting the code up." len

cycleAnal :: MData -> Maybe DocElement
cycleAnal imd
    | null cycs = Nothing
    | otherwise = Just el
    where
      cycs = applyAlg cyclesIn $ graphData imd
      cycs' = return . Itemized
              $ map (Paragraph . return . Text . showCycle' (nameOfModule' . snd)) cycs
      text = Text "The imports have the following cycles:"
      textAfter = Text "Whilst this is valid, it may make it difficult \
                       \to use in ghci, etc."
      el = Section sec
           $ Paragraph [text] : cycs' ++ [Paragraph [textAfter]]
      sec = Text "Cycle analysis of imports"

chainAnal :: MData -> Maybe DocElement
chainAnal imd
    | null chns = Nothing
    | otherwise = Just el
    where
      chns = interiorChains $ graphData imd
      chns' = return . Itemized
              $ map (Paragraph . return . Text . showPath' (nameOfModule . snd)) chns
      text = Text "The imports have the following chains:"
      textAfter = Text "These chains can all be compressed down to \
                       \a single module."
      el = Section sec $
           [Paragraph [text]] ++ chns' ++ [Paragraph [textAfter]]
      sec = Text "Import chain analysis"

rootAnal :: MData -> Maybe DocElement
rootAnal imd
    | asExpected = Nothing
    | otherwise  = Just $ Section sec inaccessible
    where
      imd' = graphData imd
      ntWd = S.toList . inaccessibleNodes $ imd'
      ntWd' = applyAlg getLabels imd' ntWd
      asExpected = null ntWd
      inaccessible = [ Paragraph [Text "These modules are those that are inaccessible:"]
                    , Paragraph [Emphasis . Text $ showNodes' nameOfModule ntWd']
                    ]
      sec = Text "Import root analysis"

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