cpsa-2.5.2: src/CPSA/Graph/ExpandedView.hs
-- Generates an expanded view of CPSA output as a compound document
-- that contains SVG within XHTML.
-- Copyright (c) 2009 The MITRE Corporation
--
-- This program is free software: you can redistribute it and/or
-- modify it under the terms of the BSD License as published by the
-- University of California.
module CPSA.Graph.ExpandedView (expandedView, treelessView) where
import Data.List (intersperse)
import System.IO
import CPSA.Lib.CPSA
import CPSA.Graph.XMLOutput
import CPSA.Graph.Config
import CPSA.Graph.SVG
import CPSA.Graph.Loader
import CPSA.Graph.Preskeleton
import CPSA.Graph.Tree
type Printer a = Int -> Int -> SExpr a -> String
expandedView :: Handle -> Config -> Int -> [SExpr Pos] -> [Preskel] -> IO ()
expandedView h conf margin cmts ks =
do
hPutList h (header (scripts conf) cmts ks)
let pp = printer conf
comments h margin pp cmts
case forest ks of
[t] ->
tdrawer h conf margin pp False t
f ->
do
toc h f
mapM_ (tdrawer h conf margin pp True) f
hPutList h closer
hClose h
header :: Bool -> [SExpr Pos] -> [Preskel] -> [String]
header addScript cmts ks =
["<html xmlns=\"http://www.w3.org/1999/xhtml\">",
"<head>",
" <title>" ++ title cmts ks ++ "</title>",
" <meta http-equiv=\"content-type\"" ++
" content=\"application/xhtml+xml; charset=UTF-8\" />",
" <style>",
" svg.diagram { border-width: 1px; border-style: solid }",
" </style>"]
++ maybeScript addScript ++
["</head>",
"<body>"]
maybeScript :: Bool -> [String]
maybeScript False = []
maybeScript True =
[ " <script>"]
++ javascript ++
[" </script>"]
-- Find title in a herald form, but if absent use a protocol name.
title :: [SExpr Pos] -> [Preskel] -> String
title [] ks = protocolTitle ks
title ((L _ (S _ "herald" : S _ title : _)) : _) _ = title
title ((L _ (S _ "herald" : Q _ title : _)) : _) _ = title
title (_ : cmts) ks = title cmts ks
protocolTitle :: [Preskel] -> String
protocolTitle [] = "CPSA"
protocolTitle (k : _) = protocol k
comments :: Handle -> Int -> Printer Pos -> [SExpr Pos] -> IO ()
comments h margin pp cmts =
do
hPutStrLn h ""
let xs = concat $ intersperse "\n" $ map (pp margin indent) cmts
hPutStrLn h $ show $ mc "pre" [] xs
closer :: [String]
closer =
["", "</body>", "</html>"]
hPutList :: Handle -> [String] -> IO ()
hPutList h xs = mapM_ (hPutStrLn h) xs
-- Generates a list of trees within the document when there are more
-- than one.
toc :: Handle -> Forest -> IO ()
toc h f =
do
hPutStrLn h ""
hPutStr h $ "<p id=\"" ++ topid ++ "\">Trees:"
mapM_ (anchor h treeid . label . vertex) f
hPutStrLn h ".</p>"
topid :: String
topid = "top"
anchor :: Handle -> (Int -> String) -> Int -> IO ()
anchor h id n =
hPutStr h $ " <a href=\"#" ++ id n ++ "\">" ++ show n ++ "</a>"
-- Generates an SVG document root and puts it into a div element.
-- When scripting is enabled, it places all elements into a g element
-- that is used as the target of scaling actions.
docRoot :: Config -> Float -> Float -> [Element] -> Element
docRoot conf w h es =
ec "div" [] div
where
attrs = [("class", "diagram"),
("width", showL w ++ units conf),
("height", showL h ++ units conf),
("xmlns", "http://www.w3.org/2000/svg"),
("version", "1.1"),
("viewBox", viewbox),
("font-size", showL (font conf))]
viewbox = "0 0 " ++ showL w ++ " " ++ showL h
svg = ec "svg" attrs (if scripts conf then [ec "g" [] es] else es)
div = if scripts conf then [zoomControl, br, svg] else [svg]
br = ec "br" [] []
zoomControl :: Element
zoomControl =
ec "select" [("onchange", "zoom(event)")]
[mc "option" [("value", "1.0")] "1.0",
mc "option" [("value", "0.8")] "0.8",
mc "option" [("value", "0.6")] "0.6",
mc "option" [("value", "0.4")] "0.4",
mc "option" [("value", "0.2")] "0.2"]
-- Draws one tree
tdrawer :: Handle -> Config -> Int -> Printer Pos -> Bool -> Tree -> IO ()
tdrawer h conf margin pp toc t =
do
hPutStrLn h ""
let id = label (vertex t)
hPutStr h $ "<p id=\"" ++ treeid id ++ "\">Tree"
case toc of
True -> anchor h (\_ -> topid) id
False -> hPutStr h $ " " ++ show id
hPutStrLn h ".</p>"
hPutStrLn h ""
let (width, height, es) = tree conf t
hPutStrLn h $ show $ docRoot conf width height es
hPutSExpr h margin pp (protSrc (vertex t))
kdrawer h conf margin pp id [t]
treeid :: Int -> String
treeid label = tid (show label)
-- Draws the first item in the queue
kdrawer :: Handle -> Config -> Int -> Printer Pos -> Int -> [Tree] -> IO ()
kdrawer _ _ _ _ _ [] = return ()
kdrawer h conf margin pp tid (t:ts) =
do
hPutStrLn h ""
let k = vertex t
let id = label k
hPutStr h $ "<p id=\"" ++ itemid id ++ "\">Item"
anchor h (\_ -> treeid tid) id
case parent k of
Nothing -> return ()
Just p ->
do
hPutStr h ", Parent:"
anchor h itemid p
titledList h "Child" "Children" $ map (label . vertex) (children t)
titledList h "Seen Child" "Seen Children" $ seen k
hPutStrLn h ".</p>"
hPutStrLn h ""
let (width, height, es) = skel conf k
hPutStrLn h $ show $ docRoot conf width height (defs conf : es)
hPutSExpr h margin pp (purgeTraces conf $ preskelSrc k)
-- Use a breadth first ordering by appending children to the queue
kdrawer h conf margin pp tid (ts ++ children t)
itemid :: Int -> String
itemid label = kid (show label)
-- Handle singular vs. plural.
titledList :: Handle -> String -> String -> [Int] -> IO ()
titledList _ _ _ [] =
return ()
titledList h singular _ [id] =
do
hPutStr h $ ", " ++ singular ++ ":"
anchor h itemid id
titledList h _ plural ls =
do
hPutStr h $ ", " ++ plural ++ ":"
mapM_ (anchor h itemid) ls
hPutSExpr :: Handle -> Int -> Printer Pos -> SExpr Pos -> IO ()
hPutSExpr h margin pp sexpr =
do
hPutStrLn h ""
hPutStrLn h $ show $ mc "pre" [] (pp margin indent sexpr)
-- S-expression pretty print parameters
indent :: Int
indent = 2
purgeTraces :: Config -> SExpr Pos -> SExpr Pos
purgeTraces conf x | purge conf =
strip "traces" x
purgeTraces _ x = x
strip :: String -> SExpr Pos -> SExpr Pos
strip key (L p xs) =
L p (filter f xs)
where
f (L _ (S _ s : _)) | s == key = False
f _ = True
strip _ x = x
-- Encoded from src/zoom.js using src/js2hs
javascript :: [String]
javascript =
["<![CDATA[",
"// Zoom support for use with cpsagraph and option --zoom",
"",
"// Find the g element within a near by svg element and apply a transform",
"function zoom(evt) {",
" var g = evt.target.parentNode.lastElementChild.firstElementChild;",
" g.setAttribute(\"transform\", \"scale(\"+evt.target.value+\")\");",
"}",
"]]>"]
-- Treeless View -- fast because we don't generated derivation trees.
treelessView :: Handle -> Config -> Int -> [SExpr Pos] ->
Preskel -> State -> IO ()
treelessView h conf margin cmts k s =
do
hPutList h (header (scripts conf) cmts [k])
let pp = printer conf
comments h margin pp cmts
body h conf margin pp k s
body :: Handle -> Config -> Int -> Printer Pos -> Preskel -> State -> IO ()
body h conf margin pp k s =
do
case parent k of
Nothing ->
do
hPutStrLn h ""
hPutStrLn h $ show $ mc "p" [] ("Tree " ++ show (label k))
hPutSExpr h margin pp (protSrc k)
Just _ -> return ()
hPutStrLn h ""
hPutStrLn h $ show $ mc "p" [] ("Item " ++ show (label k))
hPutStrLn h ""
let (width, height, es) = skel conf k
hPutStrLn h $ show $ docRoot conf width height (defs conf : es)
hPutSExpr h margin pp (purgeTraces conf $ preskelSrc k)
n <- loadNext s
case n of
Nothing -> -- EOF
do
hPutList h closer
hClose h
Just (k, s) ->
body h conf margin pp k s