packages feed

SourceGraph-0.3: Analyse/Everything.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.Software
   Description : Analyse Haskell software
   Copyright   : (c) Ivan Lazar Miljenovic 2008
   License     : GPL-3 or later.
   Maintainer  : Ivan.Miljenovic@gmail.com

   Analysis of the entire overall piece of software.
 -}
module Analyse.Everything (analyseEverything) where

import Parsing.Types
import Analyse.Utils

import Data.Graph.Analysis

import Data.List
import Data.Maybe
import Text.Printf
import System.Random

type CodeData = GraphData Function

-- | Performs analysis of the entire codebase.
analyseEverything :: (RandomGen g) => g -> [ModuleName] -> HaskellModules
                  -> DocElement
analyseEverything g exps hm = Section sec elems
    where
      cd = codeToGraph exps hm
      sec = Text "Analysis of the entire codebase"
      elems = catMaybes
              $ map ($cd) [ graphOf
                          , clustersOf g
                          , collapseAnal
                          , coreAnal
                          , cycleCompAnal
                          , rootAnal
                          , componentAnal
                          , cliqueAnal
                          , cycleAnal
                          , chainAnal
                          ]


codeToGraph          :: [ModuleName] -> HaskellModules -> CodeData
codeToGraph exps hms = importData params
    where
      exps' = concat . catMaybes $ map (fmap exports . getModule hms) exps
      fl = combineCalls .map functions $ hModulesIn hms
      params = Params { dataPoints    = functionsIn fl
                      , relationships = functionEdges fl
                      , roots         = exps'
                      , directed      = True
                      }

graphOf    :: CodeData -> Maybe DocElement
graphOf cd = Just $ Section sec [gc]
    where
      sec = Text "Visualisation of the entire software"
      gc = GraphImage $ applyAlg dg cd
      dg g = toGraph "code" lbl g
      lbl = "Software visualisation"

clustersOf      :: (RandomGen g) => g -> CodeData -> Maybe DocElement
clustersOf g cd = Just $ Section sec [ text, gc, textAfter
                                     , blank, cwMsg, cw, blank, rngMsg, rng]
    where
      blank = Paragraph [BlankSpace]
      sec = Text "Visualisation of overall function calls"
      gc = GraphImage $ applyAlg dg cd
      text = Paragraph
             [Text "Here is the current module grouping of functions:"]
      dg gr = toClusters "codeCluster" lbl gr
      lbl = "Module groupings"
      textAfter = Paragraph [Text "Here are two proposed module groupings:"]
      cwMsg = Paragraph [Emphasis $ Text "Using the Chinese Whispers algorithm:"]
      cw = GraphImage
           . toClusters "codeCW" "Chinese Whispers module suggestions"
           $ applyAlg (chineseWhispers g) cd
      rngMsg = Paragraph [Emphasis $ Text "Using the Relative Neighbourhood algorithm:"]
      rng = GraphImage
            . toClusters "codeRNG" "Relative Neighbourhood module suggestions"
            $ applyAlg relativeNeighbourhood cd

componentAnal :: CodeData -> Maybe DocElement
componentAnal cd
    | single comp = Nothing
    | otherwise   = Just $ Section sec [Paragraph [Text text]]
    where
      comp = applyAlg componentsOf cd
      len = length comp
      sec = Text "Function component analysis"
      text = printf "The functions are split up into %d components.  \
                     \You may wish to consider splitting the code up \
                     \into multiple libraries." len

cliqueAnal :: CodeData -> Maybe DocElement
cliqueAnal cd
    | null clqs = Nothing
    | otherwise = Just el
    where
      clqs = onlyCrossModule $ applyAlg cliquesIn cd
      clqs' = return . Itemized
              $ map (Paragraph . return . Text . showNodes) clqs
      text = Text "The code has the following cross-module cliques:"
      el = Section sec $ (Paragraph [text]) : clqs'
      sec = Text "Overall clique analysis"

cycleAnal :: CodeData -> Maybe DocElement
cycleAnal cd
    | null cycs = Nothing
    | otherwise = Just el
    where
      cycs = onlyCrossModule $ applyAlg uniqueCycles cd
      cycs' = return . Itemized
              $ map (Paragraph . return . Text . showCycle) cycs
      text = Text "The code has the following cross-module non-clique cycles:"
      el = Section sec $ (Paragraph [text]) : cycs'
      sec = Text "Overall cycle analysis"

chainAnal :: CodeData -> Maybe DocElement
chainAnal cd
    | null chns = Nothing
    | otherwise = Just el
    where
      chns = onlyCrossModule $ interiorChains cd
      chns' = return . Itemized
              $ map (Paragraph . return . Text . showPath) chns
      text = Text "The code has the following cross-module chains:"
      textAfter = Text "These chains can all be compressed down to \
                       \a single function."
      el = Section sec $
           [Paragraph [text]] ++ chns' ++ [Paragraph [textAfter]]
      sec = Text "Overall chain analysis"

rootAnal :: CodeData -> Maybe DocElement
rootAnal cd
    | asExpected = Nothing
    | otherwise  = Just $ Section sec ps
    where
      (wntd, ntRs, ntWd) = classifyRoots cd
      asExpected = (null ntRs) && (null ntWd)
      rpt (s,ns) = if (null ns)
                   then Nothing
                   else Just [ Paragraph
                               [Text
                                $ concat ["These functions are those that are "
                                         , s, ":"]]
                             , Paragraph [Emphasis . Text $ showNodes ns]]
      ps = concat . catMaybes
           $ map rpt [ ("available for use and roots",wntd)
                     , ("available for use but not roots",ntRs)
                     , ("not available for use but roots",ntWd)]
      sec = Text "Import root analysis"


cycleCompAnal    :: CodeData -> Maybe DocElement
cycleCompAnal cd = Just $ Section sec pars
    where
      cc = cyclomaticComplexity cd
      sec = Text "Overall Cyclomatic Complexity"
      pars = [Paragraph [text], Paragraph [textAfter, link]]
      text = Text
             $ printf "The overall cyclomatic complexity is: %d" 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")


coreAnal    :: CodeData -> Maybe DocElement
coreAnal cd = if (isEmpty core)
              then Nothing
              else Just $ Section sec [hdr, anal]
    where
      core = applyAlg coreOf cd
      p = "codeCore"
      lbl = "Overall core"
      hdr = Paragraph [Text "The core of software can be thought of as \
                             \the part where all the work is actually done."]
      anal = GraphImage (toGraph p lbl core)
      sec = Text "Overall Core analysis"


collapseAnal    :: CodeData -> Maybe DocElement
collapseAnal cd = if (trivialCollapse gc)
                  then Nothing
                  else Just $ Section sec [hdr, gr]
    where
      gc = applyAlg collapseGraph cd
      p = "codeCollapsed"
      lbl = "Collapsed view of the entire codebase"
      hdr = Paragraph [Text "The collapsed view of code collapses \
                            \down all cliques, cycles, chains, etc. to \
                            \make the graph tree-like." ]
      gr = GraphImage (toGraph p lbl gc)
      sec = Text "Collapsed view of the entire codebase"

-- | Only use those values that are in more than one module.
onlyCrossModule :: [LNGroup Function] -> [LNGroup Function]
onlyCrossModule = filter crossModule
    where
      crossModule = not . single . nub . map (inModule . label)