{-|
Module      : Data.RDF.Graph
Description : Representation and Incremental Processing of RDF Data
Copyright   : Travis Whitaker 2016
License     : MIT
Maintainer  : pi.boy.travis@gmail.com
Stability   : Provisional
Portability : Portable

This module provides conversion between RDF triples and @fgl@ graphs. Naturally
these functions will force the entire graph into memory.
-}

{-# LANGUAGE DeriveGeneric
           , DeriveAnyClass
           #-}

module Data.RDF.Graph (
    -- FGL Supporting Types
    GNode(..)
  , GEdge
    -- * Conversion to FGL Graphs
  , rdfGraph
  , triplesGraph
    -- * Conversion from FGL Graphs
  , graphRDF
  , graphTriples
  ) where

import Control.DeepSeq

import qualified Data.Graph.Inductive.Graph   as G
import qualified Data.Graph.Inductive.NodeMap as G

import Data.Maybe

import Data.RDF.Types

import GHC.Generics

-- | An RDF 'Subject' or 'Object' as a 'G.Graph' node. This common
--   representation is necessary because the 'Object' of one 'Triple' might be
--   the 'Subject' of another.
data GNode = IRIGNode     !IRI
           | BlankGNode   !BlankNode
           | LiteralGNode !Literal
          deriving ( GNode -> GNode -> Bool
(GNode -> GNode -> Bool) -> (GNode -> GNode -> Bool) -> Eq GNode
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GNode -> GNode -> Bool
$c/= :: GNode -> GNode -> Bool
== :: GNode -> GNode -> Bool
$c== :: GNode -> GNode -> Bool
Eq
                   , Eq GNode
Eq GNode
-> (GNode -> GNode -> Ordering)
-> (GNode -> GNode -> Bool)
-> (GNode -> GNode -> Bool)
-> (GNode -> GNode -> Bool)
-> (GNode -> GNode -> Bool)
-> (GNode -> GNode -> GNode)
-> (GNode -> GNode -> GNode)
-> Ord GNode
GNode -> GNode -> Bool
GNode -> GNode -> Ordering
GNode -> GNode -> GNode
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: GNode -> GNode -> GNode
$cmin :: GNode -> GNode -> GNode
max :: GNode -> GNode -> GNode
$cmax :: GNode -> GNode -> GNode
>= :: GNode -> GNode -> Bool
$c>= :: GNode -> GNode -> Bool
> :: GNode -> GNode -> Bool
$c> :: GNode -> GNode -> Bool
<= :: GNode -> GNode -> Bool
$c<= :: GNode -> GNode -> Bool
< :: GNode -> GNode -> Bool
$c< :: GNode -> GNode -> Bool
compare :: GNode -> GNode -> Ordering
$ccompare :: GNode -> GNode -> Ordering
$cp1Ord :: Eq GNode
Ord
                   , ReadPrec [GNode]
ReadPrec GNode
Int -> ReadS GNode
ReadS [GNode]
(Int -> ReadS GNode)
-> ReadS [GNode]
-> ReadPrec GNode
-> ReadPrec [GNode]
-> Read GNode
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [GNode]
$creadListPrec :: ReadPrec [GNode]
readPrec :: ReadPrec GNode
$creadPrec :: ReadPrec GNode
readList :: ReadS [GNode]
$creadList :: ReadS [GNode]
readsPrec :: Int -> ReadS GNode
$creadsPrec :: Int -> ReadS GNode
Read
                   , Int -> GNode -> ShowS
[GNode] -> ShowS
GNode -> String
(Int -> GNode -> ShowS)
-> (GNode -> String) -> ([GNode] -> ShowS) -> Show GNode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GNode] -> ShowS
$cshowList :: [GNode] -> ShowS
show :: GNode -> String
$cshow :: GNode -> String
showsPrec :: Int -> GNode -> ShowS
$cshowsPrec :: Int -> GNode -> ShowS
Show
                   , (forall x. GNode -> Rep GNode x)
-> (forall x. Rep GNode x -> GNode) -> Generic GNode
forall x. Rep GNode x -> GNode
forall x. GNode -> Rep GNode x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep GNode x -> GNode
$cfrom :: forall x. GNode -> Rep GNode x
Generic
                   , GNode -> ()
(GNode -> ()) -> NFData GNode
forall a. (a -> ()) -> NFData a
rnf :: GNode -> ()
$crnf :: GNode -> ()
NFData
                   )

-- | A 'G.Graph' edge is an RDF 'Predicate'.
type GEdge = Predicate

-- | Convert a 'Subject' to a 'GNode'.
subjectNode :: Subject -> GNode
subjectNode :: Subject -> GNode
subjectNode (IRISubject IRI
i)   = IRI -> GNode
IRIGNode IRI
i
subjectNode (BlankSubject BlankNode
b) = BlankNode -> GNode
BlankGNode BlankNode
b

-- | Convert an 'Object' to a 'GNode'.
objectNode :: Object -> GNode
objectNode :: Object -> GNode
objectNode (IRIObject IRI
i)     = IRI -> GNode
IRIGNode IRI
i
objectNode (BlankObject BlankNode
b)   = BlankNode -> GNode
BlankGNode BlankNode
b
objectNode (LiteralObject Literal
l) = Literal -> GNode
LiteralGNode Literal
l

-- | Convert a 'GNode' to a 'Subject'. This will fail if the 'GNode' contains a
--   'Literal'.
nodeSubject :: GNode -> Either String Subject
nodeSubject :: GNode -> Either String Subject
nodeSubject (IRIGNode IRI
i)   = Subject -> Either String Subject
forall a b. b -> Either a b
Right (IRI -> Subject
IRISubject IRI
i)
nodeSubject (BlankGNode BlankNode
b) = Subject -> Either String Subject
forall a b. b -> Either a b
Right (BlankNode -> Subject
BlankSubject BlankNode
b)
nodeSubject GNode
_              = String -> Either String Subject
forall a b. a -> Either a b
Left String
"nodeSubject: subject must IRI or blank node."

-- | Convert a 'GNode' to an 'Object'.
nodeObject :: GNode -> Object
nodeObject :: GNode -> Object
nodeObject (IRIGNode IRI
i)     = IRI -> Object
IRIObject IRI
i
nodeObject (BlankGNode BlankNode
b)   = BlankNode -> Object
BlankObject BlankNode
b
nodeObject (LiteralGNode Literal
l) = Literal -> Object
LiteralObject Literal
l

-- | Convert an 'RDFGraph' into a 'G.DynGraph' and 'G.NodeMap'. The 'graphLabel'
--   is discarded.
rdfGraph :: G.DynGraph g => RDFGraph -> (g GNode GEdge, G.NodeMap GNode)
rdfGraph :: RDFGraph -> (g GNode GEdge, NodeMap GNode)
rdfGraph (RDFGraph Maybe IRI
_ [Triple]
ts) = [Triple] -> (g GNode GEdge, NodeMap GNode)
forall (g :: * -> * -> *).
DynGraph g =>
[Triple] -> (g GNode GEdge, NodeMap GNode)
triplesGraph [Triple]
ts

-- | Convert a list of 'Triple's into a 'G.DynGraph' and a 'G.NodeMap'.
triplesGraph :: G.DynGraph g => [Triple] -> (g GNode GEdge, G.NodeMap GNode)
triplesGraph :: [Triple] -> (g GNode GEdge, NodeMap GNode)
triplesGraph [Triple]
triples = [GNode]
-> [(GNode, GNode, GEdge)] -> (g GNode GEdge, NodeMap GNode)
forall a (g :: * -> * -> *) b.
(Ord a, DynGraph g) =>
[a] -> [(a, a, b)] -> (g a b, NodeMap a)
G.mkMapGraph [GNode]
nodes [(GNode, GNode, GEdge)]
edges
    where ([GNode]
nodes, [(GNode, GNode, GEdge)]
edges)                = ([GNode], [(GNode, GNode, GEdge)])
-> [Triple] -> ([GNode], [(GNode, GNode, GEdge)])
go ([],[]) [Triple]
triples
          go :: ([GNode], [(GNode, GNode, GEdge)])
-> [Triple] -> ([GNode], [(GNode, GNode, GEdge)])
go ([GNode]
ns, [(GNode, GNode, GEdge)]
es) []                = ([GNode]
ns, [(GNode, GNode, GEdge)]
es)
          go ([GNode]
ns, [(GNode, GNode, GEdge)]
es) (Triple Subject
s GEdge
p Object
o:[Triple]
ts) = let s' :: GNode
s' = Subject -> GNode
subjectNode Subject
s
                                              o' :: GNode
o' = Object -> GNode
objectNode Object
o
                                          in ([GNode], [(GNode, GNode, GEdge)])
-> [Triple] -> ([GNode], [(GNode, GNode, GEdge)])
go (GNode
s'GNode -> [GNode] -> [GNode]
forall a. a -> [a] -> [a]
:GNode
o'GNode -> [GNode] -> [GNode]
forall a. a -> [a] -> [a]
:[GNode]
ns, (GNode
s', GNode
o', GEdge
p)(GNode, GNode, GEdge)
-> [(GNode, GNode, GEdge)] -> [(GNode, GNode, GEdge)]
forall a. a -> [a] -> [a]
:[(GNode, GNode, GEdge)]
es) [Triple]
ts

-- | Convert a 'G.Graph' into an 'RDFGraph'. This will fail if the graph
--   contains any 'LiteralGNode's with an outward degree greater than zero,
--   since such a graph is illegal in RDF.
graphRDF :: G.Graph g => Maybe IRI -> g GNode GEdge -> Either String RDFGraph
graphRDF :: Maybe IRI -> g GNode GEdge -> Either String RDFGraph
graphRDF Maybe IRI
l = (Maybe IRI -> [Triple] -> RDFGraph
RDFGraph Maybe IRI
l ([Triple] -> RDFGraph)
-> Either String [Triple] -> Either String RDFGraph
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>) (Either String [Triple] -> Either String RDFGraph)
-> (g GNode GEdge -> Either String [Triple])
-> g GNode GEdge
-> Either String RDFGraph
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  g GNode GEdge -> Either String [Triple]
forall (g :: * -> * -> *).
Graph g =>
g GNode GEdge -> Either String [Triple]
graphTriples

-- | Convert a 'G.Graph' into a list of 'Triple's. This will fail if the graph
--   contains any 'LiteralGNode's with an outward degree greater than zero,
--   since such a graph is illegal in RDF.
graphTriples :: G.Graph g => g GNode GEdge -> Either String [Triple]
graphTriples :: g GNode GEdge -> Either String [Triple]
graphTriples g GNode GEdge
g = [(Int, Int, GEdge)] -> Either String [Triple]
go (g GNode GEdge -> [(Int, Int, GEdge)]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LEdge b]
G.labEdges g GNode GEdge
g)
          -- The use of fromJust is safe here, since labEdges will never return
          -- an edge to a node not present in the graph.
    where go :: [(Int, Int, GEdge)] -> Either String [Triple]
go []               = [Triple] -> Either String [Triple]
forall a b. b -> Either a b
Right []
          go ((Int
si, Int
oi, GEdge
p):[(Int, Int, GEdge)]
ts) = let s :: Either String Subject
s = GNode -> Either String Subject
nodeSubject (Maybe GNode -> GNode
forall a. HasCallStack => Maybe a -> a
fromJust (g GNode GEdge -> Int -> Maybe GNode
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Int -> Maybe a
G.lab g GNode GEdge
g Int
si))
                                    o :: Object
o = GNode -> Object
nodeObject (Maybe GNode -> GNode
forall a. HasCallStack => Maybe a -> a
fromJust (g GNode GEdge -> Int -> Maybe GNode
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Int -> Maybe a
G.lab g GNode GEdge
g Int
oi))
                                in ((\Subject
s' -> (Subject -> GEdge -> Object -> Triple
Triple Subject
s' GEdge
p Object
oTriple -> [Triple] -> [Triple]
forall a. a -> [a] -> [a]
:)) (Subject -> [Triple] -> [Triple])
-> Either String Subject -> Either String ([Triple] -> [Triple])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either String Subject
s) Either String ([Triple] -> [Triple])
-> Either String [Triple] -> Either String [Triple]
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [(Int, Int, GEdge)] -> Either String [Triple]
go [(Int, Int, GEdge)]
ts