{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}

--------------------------------------------------------------------------------
--  See end of this file for licence information.
--------------------------------------------------------------------------------
-- |
--  Module      :  Graph
--  Copyright   :  (c) 2003, Graham Klyne, 2009 Vasili I Galchin,
--                 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2020 Douglas Burke
--  License     :  GPL V2
--
--  Maintainer  :  Douglas Burke
--  Stability   :  experimental
--  Portability :  CPP, FlexibleInstances, MultiParamTypeClasses, OverloadedStrings
--
--  This module defines a memory-based RDF graph instance. At present only
--  RDF 1.0 is explicitly supported; I have not gone through the RDF 1.1
--  changes to see how the code needs to be updated. This means that you
--  can have untyped strings in your graph that do not match the same content
--  but with an explicit @xsd:string@ datatype.
--
--  Note that the identifiers for blank nodes may /not/ be propogated when
--  a graph is written out using one of the formatters, such as
--  'Swish.RDF.Formatter.Turtle'. There is limited support for
--  generating new blank nodes from an existing set of triples; e.g.
--  'newNode' and 'newNodes'.
--
--------------------------------------------------------------------------------

------------------------------------------------------------
-- Simple labelled directed graph value
------------------------------------------------------------

module Swish.RDF.Graph
    ( 
      -- * Labels
      RDFLabel(..), ToRDFLabel(..), FromRDFLabel(..)
    , isLiteral, isUntypedLiteral, isTypedLiteral, isXMLLiteral
    , isDatatyped, isMemberProp, isUri, isBlank, isQueryVar
    , getLiteralText, getScopedName, makeBlank
    , quote
    , quoteT
      
      -- * RDF Graphs
    , RDFArcSet
    , RDFTriple
    , toRDFTriple, fromRDFTriple
    , NSGraph(..)
    , RDFGraph
    , toRDFGraph, emptyRDFGraph {-, updateRDFGraph-}
    , NamespaceMap
    , emptyNamespaceMap
    , LookupFormula(..), Formula, FormulaMap, emptyFormulaMap
    , addArc, merge
    , allLabels, allNodes, remapLabels, remapLabelList
    , newNode, newNodes
    , setNamespaces, getNamespaces
    , setFormulae, getFormulae, setFormula, getFormula
    , fmapNSGraph
    , traverseNSGraph
      
    -- * Re-export from GraphClass
    --
    -- | Note that @asubj@, @apred@ and @aobj@ have been
    -- removed in version @0.7.0.0@; use 'arcSubj', 'arcPred'
    -- or 'arcObj' instead.
    --
    , LDGraph(..), Label (..), Arc(..)
    , arc, Selector
      
    -- * Selected RDFLabel values
    --                                
    -- | The 'ToRDFLabel' instance of 'ScopedName' can also be used                                     
    -- to easily construct 'RDFLabel' versions of the terms defined
    -- in "Swish.RDF.Vocabulary".
    
    -- ** RDF terms                                          
    --                                          
    -- | These terms are described in <http://www.w3.org/TR/rdf-syntax-grammar/>;                                          
    -- the version used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-syntax-grammar-20040210/>.
    --                                          
    -- Some terms are listed within the RDF Schema terms below since their definition                                            
    -- is given within the RDF Schema document.                                          
    --                                          
    , resRdfRDF                                          
    , resRdfDescription      
    , resRdfID
    , resRdfAbout
    , resRdfParseType
    , resRdfResource
    , resRdfLi
    , resRdfNodeID
    , resRdfDatatype
    , resRdf1, resRdf2, resRdfn
    -- ** RDF Schema terms
    --                                 
    -- | These are defined by <http://www.w3.org/TR/rdf-schema/>; the version
    -- used is \"W3C Recommendation 10 February 2004\", <http://www.w3.org/TR/2004/REC-rdf-schema-20040210/>.
                        
    -- *** Classes
    --                                 
    -- | See the \"Classes\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.
    , resRdfsResource
    , resRdfsClass
    , resRdfsLiteral
    , resRdfsDatatype
    , resRdfXMLLiteral
    , resRdfProperty
    -- *** Properties
    --                                 
    -- | See the \"Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_classes> for more information.
    , resRdfsRange
    , resRdfsDomain
    , resRdfType
    , resRdfsSubClassOf
    , resRdfsSubPropertyOf
    , resRdfsLabel
    , resRdfsComment
    -- *** Containers
    --
    -- | See the \"Container Classes and Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_containervocab>.
    , resRdfsContainer
    , resRdfBag
    , resRdfSeq                                 
    , resRdfAlt  
    , resRdfsContainerMembershipProperty
    , resRdfsMember
    -- *** Collections
    --
    -- | See the \"Collections\" section at <http://www.w3.org/TR/rdf-schema/#ch_collectionvocab>.
    , resRdfList    
    , resRdfFirst
    , resRdfRest 
    , resRdfNil 
    -- *** Reification Vocabulary 
    --  
    -- | See the \"Reification Vocabulary\" section at <http://www.w3.org/TR/rdf-schema/#ch_reificationvocab>.
    , resRdfStatement  
    , resRdfSubject  
    , resRdfPredicate  
    , resRdfObject  
    -- *** Utility Properties 
    --  
    -- | See the \"Utility Properties\" section at <http://www.w3.org/TR/rdf-schema/#ch_utilvocab>.
    , resRdfsSeeAlso
    , resRdfsIsDefinedBy
    , resRdfValue  
    
    -- ** OWL     
    , resOwlSameAs
                    
    -- ** Miscellaneous     
    , resRdfdGeneralRestriction
    , resRdfdOnProperties, resRdfdConstraint, resRdfdMaxCardinality
    , resLogImplies
      
    -- * Exported for testing
    , grMatchMap, grEq
    , mapnode, maplist
    )
    where

import Swish.Namespace
    ( getNamespaceTuple
    , getScopedNameURI
    , ScopedName
    , getScopeLocal, getScopeNamespace
    , getQName
    , makeQNameScopedName
    , makeURIScopedName
    , nullScopedName
    )

import Swish.RDF.Vocabulary (LanguageTag)
import Swish.RDF.Vocabulary (fromLangTag, xsdBoolean, xsdDate, xsdDateTime, xsdDecimal, xsdDouble, xsdFloat, xsdInteger
                            , rdfType, rdfList, rdfFirst, rdfRest, rdfNil
                            , rdfsMember, rdfdGeneralRestriction, rdfdOnProperties, rdfdConstraint, rdfdMaxCardinality
                            , rdfsSeeAlso, rdfValue, rdfsLabel, rdfsComment, rdfProperty
                            , rdfsSubPropertyOf, rdfsSubClassOf, rdfsClass, rdfsLiteral
                            , rdfsDatatype, rdfXMLLiteral, rdfsRange, rdfsDomain, rdfsContainer
                            , rdfBag, rdfSeq, rdfAlt
                            , rdfsContainerMembershipProperty, rdfsIsDefinedBy
                            , rdfsResource, rdfStatement, rdfSubject, rdfPredicate, rdfObject
                            , rdfRDF, rdfDescription, rdfID, rdfAbout, rdfParseType
                            , rdfResource, rdfLi, rdfNodeID, rdfDatatype, rdfXMLLiteral
                            , rdf1, rdf2, rdfn
                            , owlSameAs, logImplies, namespaceRDF
                            )

import Swish.GraphClass (LDGraph(..), Label (..), Arc(..), ArcSet, Selector)
import Swish.GraphClass (arc, arcLabels, getComponents)
import Swish.GraphMatch (LabelMap, ScopedLabel(..))
import Swish.GraphMatch (graphMatch)
import Swish.QName (QName, getLName)

#if (!defined(__GLASGOW_HASKELL__)) || (__GLASGOW_HASKELL__ < 710)
import Control.Applicative (Applicative(pure), (<$>), (<*>))
import Data.Monoid (Monoid(..))
#endif

import Control.Arrow ((***))

import Network.URI (URI)

import Data.Maybe (mapMaybe)
import Data.Char (ord, isDigit)
import Data.Hashable (hashWithSalt)
import Data.List (intersect, union, foldl')
-- import Data.Ord (comparing)
import Data.Word (Word32)

import Data.String (IsString(..))

#if MIN_VERSION_time(1,5,0)
import Data.Time (UTCTime, Day, ParseTime, parseTimeM, formatTime, defaultTimeLocale)
#else
import Data.Time (UTCTime, Day, ParseTime, parseTime, formatTime)
import System.Locale (defaultTimeLocale)  
#endif

#if !(MIN_VERSION_base(4, 11, 0))
import Data.Semigroup
#endif

import Text.Printf

import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.Read as T
import qualified Data.Traversable as Traversable

-- | RDF graph node values
--
--  cf. <http://www.w3.org/TR/rdf-concepts/#section-Graph-syntax> version 1.0
--
--  This is extended from the RDF abstract graph syntax in the
--  following ways:
--
--  (a) a graph can be part of a resource node or blank node
--      (cf. Notation3 formulae)
--
--  (b) a \"variable\" node option is distinguished from a
--      blank node.
--      I have found this useful for encoding and handling
--      queries, even though query variables can be expressed
--      as blank nodes.
--
--  (c) a \"NoNode\" option is defined.
--      This might otherwise be handled by @Maybe (RDFLabel g)@.
--
-- Prior to version @0.7.0.0@, literals were represented by a
-- single constructor, @Lit@, with an optional argument. Language
-- codes for literals was also stored as a 'ScopedName' rather than
-- as a 'LanguageTag'.
--
data RDFLabel =
      Res ScopedName                    -- ^ resource
    | Lit T.Text                        -- ^ plain literal (<http://www.w3.org/TR/rdf-concepts/#dfn-plain-literal>)
    | LangLit T.Text LanguageTag        -- ^ plain literal
    | TypedLit T.Text ScopedName        -- ^ typed literal (<http://www.w3.org/TR/rdf-concepts/#dfn-typed-literal>)
    | Blank String                      -- ^ blank node
    | Var String                        -- ^ variable (not used in ordinary graphs)
    | NoNode                            -- ^ no node  (not used in ordinary graphs)

-- | Define equality of nodes possibly based on different graph types.
--
-- The equality of literals is taken from section 6.5.1 ("Literal
-- Equality") of the RDF Concepts and Abstract Document,
-- <http://www.w3.org/TR/2004/REC-rdf-concepts-20040210/#section-Literal-Equality>.
--
instance Eq RDFLabel where
    Res ScopedName
q1   == :: RDFLabel -> RDFLabel -> Bool
== Res ScopedName
q2   = ScopedName
q1 ScopedName -> ScopedName -> Bool
forall a. Eq a => a -> a -> Bool
== ScopedName
q2
    Blank String
b1 == Blank String
b2 = String
b1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
b2
    Var String
v1   == Var String
v2   = String
v1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
v2

    Lit Text
s1         == Lit Text
s2         = Text
s1 Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
s2
    LangLit Text
s1 LanguageTag
l1  == LangLit Text
s2 LanguageTag
l2  = Text
s1 Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
s2 Bool -> Bool -> Bool
&& LanguageTag
l1 LanguageTag -> LanguageTag -> Bool
forall a. Eq a => a -> a -> Bool
== LanguageTag
l2
    TypedLit Text
s1 ScopedName
t1 == TypedLit Text
s2 ScopedName
t2 = Text
s1 Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
s2 Bool -> Bool -> Bool
&& ScopedName
t1 ScopedName -> ScopedName -> Bool
forall a. Eq a => a -> a -> Bool
== ScopedName
t2

    RDFLabel
_  == RDFLabel
_ = Bool
False

instance Show RDFLabel where
    show :: RDFLabel -> String
show (Res ScopedName
sn)           = ScopedName -> String
forall a. Show a => a -> String
show ScopedName
sn
    show (Lit Text
st)           = Text -> String
quote1Str Text
st
    show (LangLit Text
st LanguageTag
lang)  = Text -> String
quote1Str Text
st String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"@"  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
T.unpack (LanguageTag -> Text
fromLangTag LanguageTag
lang)
    show (TypedLit Text
st ScopedName
dtype) 
        | ScopedName
dtype ScopedName -> [ScopedName] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ScopedName
xsdBoolean, ScopedName
xsdDouble, ScopedName
xsdDecimal, ScopedName
xsdInteger] = Text -> String
T.unpack Text
st
        | Bool
otherwise  = Text -> String
quote1Str Text
st String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"^^" String -> ShowS
forall a. [a] -> [a] -> [a]
++ ScopedName -> String
forall a. Show a => a -> String
show ScopedName
dtype

    {-
    show (Lit st (Just nam))
        | isLang nam = quote1Str st ++ "@"  ++ T.unpack (langTag nam)
        | nam `elem` [xsdBoolean, xsdDouble, xsdDecimal, xsdInteger] = T.unpack st
        | otherwise  = quote1Str st ++ "^^" ++ show nam
    -}

    show (Blank String
ln)         = String
"_:"String -> ShowS
forall a. [a] -> [a] -> [a]
++String
ln
    show (Var String
ln)           = Char
'?' Char -> ShowS
forall a. a -> [a] -> [a]
: String
ln
    show RDFLabel
NoNode             = String
"<NoNode>"

instance Ord RDFLabel where
    -- Order, from lowest to highest is
    --    Res, Lit, LangLit, TypedLit, Blank, Var, NoNode
    --
    compare :: RDFLabel -> RDFLabel -> Ordering
compare (Res ScopedName
sn1)        (Res ScopedName
sn2)        = ScopedName -> ScopedName -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ScopedName
sn1 ScopedName
sn2
    compare (Res ScopedName
_)          RDFLabel
_                = Ordering
LT
    compare RDFLabel
_                (Res ScopedName
_)          = Ordering
GT

    compare (Lit Text
s1)         (Lit Text
s2)         = Text -> Text -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Text
s1 Text
s2
    compare (Lit Text
_)          RDFLabel
_                = Ordering
LT
    compare RDFLabel
_                (Lit Text
_)          = Ordering
GT

    compare (LangLit Text
s1 LanguageTag
l1)  (LangLit Text
s2 LanguageTag
l2)  = (Text, LanguageTag) -> (Text, LanguageTag) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Text
s1,LanguageTag
l1) (Text
s2,LanguageTag
l2)
    compare (LangLit Text
_ LanguageTag
_)    RDFLabel
_                = Ordering
LT
    compare RDFLabel
_                (LangLit Text
_ LanguageTag
_)    = Ordering
GT

    compare (TypedLit Text
s1 ScopedName
t1) (TypedLit Text
s2 ScopedName
t2) = (Text, ScopedName) -> (Text, ScopedName) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Text
s1,ScopedName
t1) (Text
s2,ScopedName
t2)
    compare (TypedLit Text
_ ScopedName
_)   RDFLabel
_                = Ordering
LT
    compare RDFLabel
_                (TypedLit Text
_ ScopedName
_)   = Ordering
GT

    compare (Blank String
ln1)      (Blank String
ln2)      = String -> String -> Ordering
forall a. Ord a => a -> a -> Ordering
compare String
ln1 String
ln2
    compare (Blank String
_)        RDFLabel
_                = Ordering
LT
    compare RDFLabel
_                (Blank String
_)        = Ordering
GT

    compare (Var String
ln1)        (Var String
ln2)        = String -> String -> Ordering
forall a. Ord a => a -> a -> Ordering
compare String
ln1 String
ln2
    compare (Var String
_)          RDFLabel
NoNode           = Ordering
LT
    compare RDFLabel
_                (Var String
_)          = Ordering
GT

    compare RDFLabel
NoNode           RDFLabel
NoNode           = Ordering
EQ

instance Label RDFLabel where
    labelIsVar :: RDFLabel -> Bool
labelIsVar (Blank String
_)    = Bool
True
    labelIsVar (Var String
_)      = Bool
True
    labelIsVar RDFLabel
_            = Bool
False

    getLocal :: RDFLabel -> String
getLocal   (Blank String
loc)  = String
loc
    getLocal   (Var   String
loc)  = Char
'?'Char -> ShowS
forall a. a -> [a] -> [a]
:String
loc
    getLocal   (Res   ScopedName
sn)   = String
"Res_" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (Text -> String
T.unpack (Text -> String) -> (ScopedName -> Text) -> ScopedName -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LName -> Text
getLName (LName -> Text) -> (ScopedName -> LName) -> ScopedName -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScopedName -> LName
getScopeLocal) ScopedName
sn
    getLocal   RDFLabel
NoNode       = String
"None"
    getLocal   RDFLabel
_            = String
"Lit_"

    makeLabel :: String -> RDFLabel
makeLabel  (Char
'?':String
loc)    = String -> RDFLabel
Var String
loc
    makeLabel  String
loc          = String -> RDFLabel
Blank String
loc

    labelHash :: Int -> RDFLabel -> Int
labelHash Int
seed RDFLabel
lb       = Int -> String -> Int
forall a. Hashable a => Int -> a -> Int
hashWithSalt Int
seed (RDFLabel -> String
showCanon RDFLabel
lb)

instance IsString RDFLabel where
  fromString :: String -> RDFLabel
fromString = Text -> RDFLabel
Lit (Text -> RDFLabel) -> (String -> Text) -> String -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack

{-|
A type that can be converted to a RDF Label.

The String instance converts to an untyped literal
(so no language tag is assumed).

The `UTCTime` and `Day` instances assume values are in UTC.
 
The conversion for XSD types attempts to use the
canonical form described in section 2.3.1 of
<http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#lexical-space>.
  
Note that this is similar to
'Swish.RDF.Datatype.toRDFLabel';
the code should probably be combined at some point.
-}

class ToRDFLabel a where
  toRDFLabel :: a -> RDFLabel
  
{-|
A type that can be converted from a RDF Label,
with the possibility of failure.
 
The String instance converts from an untyped literal
(so it can not be used with a string with a language tag).

The following conversions are supported for common XSD
types (out-of-band values result in @Nothing@):

 - @xsd:boolean@ to @Bool@

 - @xsd:integer@ to @Int@ and @Integer@

 - @xsd:float@ to @Float@

 - @xsd:double@ to @Double@

 - @xsd:dateTime@ to @UTCTime@

 - @xsd:date@ to @Day@

Note that this is similar to
'Swish.RDF.Datatype.fromRDFLabel'; 
the code should probably be combined at some point.
-}

class FromRDFLabel a where
  fromRDFLabel :: RDFLabel -> Maybe a

-- instances for type conversion to/from RDFLabel
  
-- | This is just @id@.
instance ToRDFLabel RDFLabel where
  toRDFLabel :: RDFLabel -> RDFLabel
toRDFLabel = RDFLabel -> RDFLabel
forall a. a -> a
id
  
-- | This is just @Just@.
instance FromRDFLabel RDFLabel where
  fromRDFLabel :: RDFLabel -> Maybe RDFLabel
fromRDFLabel = RDFLabel -> Maybe RDFLabel
forall a. a -> Maybe a
Just
  
-- TODO: remove this hack when finished conversion to Text
maybeReadStr :: (Read a) => T.Text -> Maybe a  
maybeReadStr :: Text -> Maybe a
maybeReadStr Text
txt = case ReadS a
forall a. Read a => ReadS a
reads (Text -> String
T.unpack Text
txt) of
  [(a
val, String
"")] -> a -> Maybe a
forall a. a -> Maybe a
Just a
val
  [(a, String)]
_ -> Maybe a
forall a. Maybe a
Nothing
  
maybeRead :: T.Reader a -> T.Text -> Maybe a
maybeRead :: Reader a -> Text -> Maybe a
maybeRead Reader a
rdr Text
inTxt = 
  case Reader a
rdr Text
inTxt of
    Right (a
val, Text
"") -> a -> Maybe a
forall a. a -> Maybe a
Just a
val
    Either String (a, Text)
_ -> Maybe a
forall a. Maybe a
Nothing
    
fLabel :: (T.Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel :: (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe a
conv ScopedName
dtype (TypedLit Text
xs ScopedName
dt) | ScopedName
dt ScopedName -> ScopedName -> Bool
forall a. Eq a => a -> a -> Bool
== ScopedName
dtype = Text -> Maybe a
conv Text
xs
                                   | Bool
otherwise   = Maybe a
forall a. Maybe a
Nothing
fLabel Text -> Maybe a
_    ScopedName
_     RDFLabel
_ = Maybe a
forall a. Maybe a
Nothing
  
tLabel :: (Show a) => ScopedName -> (String -> T.Text) -> a -> RDFLabel                      
tLabel :: ScopedName -> (String -> Text) -> a -> RDFLabel
tLabel ScopedName
dtype String -> Text
conv = (Text -> ScopedName -> RDFLabel) -> ScopedName -> Text -> RDFLabel
forall a b c. (a -> b -> c) -> b -> a -> c
flip Text -> ScopedName -> RDFLabel
TypedLit ScopedName
dtype (Text -> RDFLabel) -> (a -> Text) -> a -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
conv (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show                      

-- | The character is converted to an untyped literal of length one.
instance ToRDFLabel Char where
  toRDFLabel :: Char -> RDFLabel
toRDFLabel = Text -> RDFLabel
Lit (Text -> RDFLabel) -> (Char -> Text) -> Char -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Text
T.singleton

-- | The label must be an untyped literal containing a single character.
instance FromRDFLabel Char where
  fromRDFLabel :: RDFLabel -> Maybe Char
fromRDFLabel (Lit Text
cs) | Text -> Int -> Ordering
T.compareLength Text
cs Int
1 Ordering -> Ordering -> Bool
forall a. Eq a => a -> a -> Bool
== Ordering
EQ = Char -> Maybe Char
forall a. a -> Maybe a
Just (Text -> Char
T.head Text
cs)
                        | Bool
otherwise = Maybe Char
forall a. Maybe a
Nothing
  fromRDFLabel RDFLabel
_ = Maybe Char
forall a. Maybe a
Nothing

-- | Strings are converted to untyped literals.
instance ToRDFLabel String where
  toRDFLabel :: String -> RDFLabel
toRDFLabel = Text -> RDFLabel
Lit (Text -> RDFLabel) -> (String -> Text) -> String -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack

-- | Only untyped literals are converted to strings.
instance FromRDFLabel String where
  fromRDFLabel :: RDFLabel -> Maybe String
fromRDFLabel (Lit Text
xs) = String -> Maybe String
forall a. a -> Maybe a
Just (Text -> String
T.unpack Text
xs)
  fromRDFLabel RDFLabel
_        = Maybe String
forall a. Maybe a
Nothing

textToBool :: T.Text -> Maybe Bool
textToBool :: Text -> Maybe Bool
textToBool Text
s | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"1", Text
"true"]  = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
             | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"0", Text
"false"] = Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
             | Bool
otherwise               = Maybe Bool
forall a. Maybe a
Nothing

-- | Converts to a literal with a @xsd:boolean@ datatype.
instance ToRDFLabel Bool where
  toRDFLabel :: Bool -> RDFLabel
toRDFLabel Bool
b = Text -> ScopedName -> RDFLabel
TypedLit (if Bool
b then Text
"true" else Text
"false") ScopedName
xsdBoolean
                                                 
-- | Converts from a literal with a @xsd:boolean@ datatype. The
-- literal can be any of the supported XSD forms - e.g. \"0\" or
-- \"true\".
instance FromRDFLabel Bool where
  fromRDFLabel :: RDFLabel -> Maybe Bool
fromRDFLabel = (Text -> Maybe Bool) -> ScopedName -> RDFLabel -> Maybe Bool
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe Bool
textToBool ScopedName
xsdBoolean

-- fromRealFloat :: (RealFloat a, Buildable a) => ScopedName -> a -> RDFLabel
fromRealFloat :: (RealFloat a, PrintfArg a) => ScopedName -> a -> RDFLabel
fromRealFloat :: ScopedName -> a -> RDFLabel
fromRealFloat ScopedName
dtype a
f | a -> Bool
forall a. RealFloat a => a -> Bool
isNaN a
f      = Text -> RDFLabel
toL Text
"NaN"
                      | a -> Bool
forall a. RealFloat a => a -> Bool
isInfinite a
f = Text -> RDFLabel
toL (Text -> RDFLabel) -> Text -> RDFLabel
forall a b. (a -> b) -> a -> b
$ if a
f a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
0.0 then Text
"INF" else Text
"-INF"
                      -- 
                      -- Would like to use Data.Text.Format.format but there are                                                                        
                      -- issues with this module; 0.3.0.2 doesn't build under
                      -- 6.12.3 due to a missing RelaxedPolyRec language extension
                      -- and it relies on double-conversion which has issues
                      -- when used in ghci due to a dlopen issue with libstdc++.
                      -- 
                      -- -- | otherwise    = toL $ L.toStrict $ format "{}" (Only f)  
                      -- 
                      | Bool
otherwise    = Text -> RDFLabel
toL (Text -> RDFLabel) -> Text -> RDFLabel
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String -> a -> String
forall r. PrintfType r => String -> r
printf String
"%E" a
f
                        
                        where
                          toL :: Text -> RDFLabel
toL = (Text -> ScopedName -> RDFLabel) -> ScopedName -> Text -> RDFLabel
forall a b c. (a -> b -> c) -> b -> a -> c
flip Text -> ScopedName -> RDFLabel
TypedLit ScopedName
dtype

-- textToRealFloat :: (RealFloat a) => (a -> Maybe a) -> T.Text -> Maybe a
textToRealFloat :: (RealFloat a, Read a) => (a -> Maybe a) -> T.Text -> Maybe a
textToRealFloat :: (a -> Maybe a) -> Text -> Maybe a
textToRealFloat a -> Maybe a
conv = Text -> Maybe a
rconv
    where
      rconv :: Text -> Maybe a
rconv Text
"NaN"  = a -> Maybe a
forall a. a -> Maybe a
Just (a
0.0a -> a -> a
forall a. Fractional a => a -> a -> a
/a
0.0) -- how best to create a NaN?
      rconv Text
"INF"  = a -> Maybe a
forall a. a -> Maybe a
Just (a
1.0a -> a -> a
forall a. Fractional a => a -> a -> a
/a
0.0) -- ditto for Infinity
      rconv Text
"-INF" = a -> Maybe a
forall a. a -> Maybe a
Just ((-a
1.0)a -> a -> a
forall a. Fractional a => a -> a -> a
/a
0.0)
      rconv Text
ival 
        -- xsd semantics allows "2." but Haskell syntax does not.
        | Text -> Bool
T.null Text
ival = Maybe a
forall a. Maybe a
Nothing
          
        | Bool
otherwise = case Text -> Maybe a
forall a. Read a => Text -> Maybe a
maybeReadStr Text
ival of
          Just a
val -> a -> Maybe a
conv a
val
          Maybe a
_        -> if Text -> Char
T.last Text
ival Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.' -- could drop the check
                      then Text -> Maybe a
forall a. Read a => Text -> Maybe a
maybeReadStr (Text -> Char -> Text
T.snoc Text
ival Char
'0') Maybe a -> (a -> Maybe a) -> Maybe a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= a -> Maybe a
conv
                      else Maybe a
forall a. Maybe a
Nothing
                               
        {-

        Unfortunately T.rational does not handle "3.01e4" the same
        as read; see https://bitbucket.org/bos/text/issue/7/

        | otherwise = case maybeRead T.rational ival of
          Just val -> conv val
          _        -> if T.last ival == '.' -- could drop the check
                      then maybeRead T.rational (T.snoc ival '0') >>= conv
                      else Nothing
        -}
                        
        -- not sure the above is any improvement on the following
        -- -- | T.last ival == '.' = maybeRead T.rational (T.snoc ival '0') >>= conv
        -- -- | otherwise          = maybeRead T.rational ival >>= conv
      
textToFloat :: T.Text -> Maybe Float
textToFloat :: Text -> Maybe Float
textToFloat = 
  let -- assume that an invalid value (NaN/Inf) from maybeRead means
      -- that the value is out of bounds for Float so we do not
      -- convert
      conv :: a -> Maybe a
conv a
f | a -> Bool
forall a. RealFloat a => a -> Bool
isNaN a
f Bool -> Bool -> Bool
|| a -> Bool
forall a. RealFloat a => a -> Bool
isInfinite a
f = Maybe a
forall a. Maybe a
Nothing
             | Bool
otherwise               = a -> Maybe a
forall a. a -> Maybe a
Just a
f
  in (Float -> Maybe Float) -> Text -> Maybe Float
forall a.
(RealFloat a, Read a) =>
(a -> Maybe a) -> Text -> Maybe a
textToRealFloat Float -> Maybe Float
forall a. RealFloat a => a -> Maybe a
conv

textToDouble :: T.Text -> Maybe Double      
textToDouble :: Text -> Maybe Double
textToDouble = (Double -> Maybe Double) -> Text -> Maybe Double
forall a.
(RealFloat a, Read a) =>
(a -> Maybe a) -> Text -> Maybe a
textToRealFloat Double -> Maybe Double
forall a. a -> Maybe a
Just

-- | Converts to a literal with a @xsd:float@ datatype.
instance ToRDFLabel Float where
  toRDFLabel :: Float -> RDFLabel
toRDFLabel = ScopedName -> Float -> RDFLabel
forall a. (RealFloat a, PrintfArg a) => ScopedName -> a -> RDFLabel
fromRealFloat ScopedName
xsdFloat
  
-- | Converts from a literal with a @xsd:float@ datatype.
-- The conversion will fail if the value is outside the valid range of
-- a Haskell `Float`.
instance FromRDFLabel Float where
  fromRDFLabel :: RDFLabel -> Maybe Float
fromRDFLabel = (Text -> Maybe Float) -> ScopedName -> RDFLabel -> Maybe Float
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe Float
textToFloat ScopedName
xsdFloat
                 
-- | Converts to a literal with a @xsd:double@ datatype.
instance ToRDFLabel Double where
  toRDFLabel :: Double -> RDFLabel
toRDFLabel = ScopedName -> Double -> RDFLabel
forall a. (RealFloat a, PrintfArg a) => ScopedName -> a -> RDFLabel
fromRealFloat ScopedName
xsdDouble
  
-- | Converts from a literal with a @xsd:double@ datatype.
instance FromRDFLabel Double where
  fromRDFLabel :: RDFLabel -> Maybe Double
fromRDFLabel = (Text -> Maybe Double) -> ScopedName -> RDFLabel -> Maybe Double
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe Double
textToDouble ScopedName
xsdDouble
  
-- TODO: are there subtypes of xsd::integer that are  
--       useful here?  
--         
-- TODO: add in support for Int8/..., Word8/...  
--  

-- | Converts to a literal with a @xsd:integer@ datatype.
instance ToRDFLabel Int where
  toRDFLabel :: Int -> RDFLabel
toRDFLabel = ScopedName -> (String -> Text) -> Int -> RDFLabel
forall a. Show a => ScopedName -> (String -> Text) -> a -> RDFLabel
tLabel ScopedName
xsdInteger String -> Text
T.pack

{-
Since decimal will just over/under-flow when converting to Int
we go via Integer and explicitly check for overflow.
-}

textToInt :: T.Text -> Maybe Int
textToInt :: Text -> Maybe Int
textToInt Text
s = 
  let conv :: Integer -> Maybe Int
      conv :: Integer -> Maybe Int
conv Integer
i = 
        let lb :: Integer
lb = Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
forall a. Bounded a => a
minBound :: Int)
            ub :: Integer
ub = Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
forall a. Bounded a => a
maxBound :: Int)
        in if (Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
lb) Bool -> Bool -> Bool
&& (Integer
i Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
ub) then Int -> Maybe Int
forall a. a -> Maybe a
Just (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
i) else Maybe Int
forall a. Maybe a
Nothing
  
  in Reader Integer -> Text -> Maybe Integer
forall a. Reader a -> Text -> Maybe a
maybeRead (Reader Integer -> Reader Integer
forall a. Num a => Reader a -> Reader a
T.signed Reader Integer
forall a. Integral a => Reader a
T.decimal) Text
s Maybe Integer -> (Integer -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Integer -> Maybe Int
conv

-- | Converts from a literal with a @xsd:integer@ datatype.
-- The conversion will fail if the value is outside the valid range of
-- a Haskell `Int`.
instance FromRDFLabel Int where
  fromRDFLabel :: RDFLabel -> Maybe Int
fromRDFLabel = (Text -> Maybe Int) -> ScopedName -> RDFLabel -> Maybe Int
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe Int
textToInt ScopedName
xsdInteger

-- | Converts to a literal with a @xsd:integer@ datatype.
instance ToRDFLabel Integer where
  toRDFLabel :: Integer -> RDFLabel
toRDFLabel = ScopedName -> (String -> Text) -> Integer -> RDFLabel
forall a. Show a => ScopedName -> (String -> Text) -> a -> RDFLabel
tLabel ScopedName
xsdInteger String -> Text
T.pack

-- | Converts from a literal with a @xsd:integer@ datatype.
instance FromRDFLabel Integer where
  fromRDFLabel :: RDFLabel -> Maybe Integer
fromRDFLabel = (Text -> Maybe Integer) -> ScopedName -> RDFLabel -> Maybe Integer
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel (Reader Integer -> Text -> Maybe Integer
forall a. Reader a -> Text -> Maybe a
maybeRead (Reader Integer -> Reader Integer
forall a. Num a => Reader a -> Reader a
T.signed Reader Integer
forall a. Integral a => Reader a
T.decimal)) ScopedName
xsdInteger

{-
Support an ISO-8601 style format supporting

  2005-02-28T00:00:00Z
  2004-12-31T19:01:00-05:00
  2005-07-14T03:18:56.234+01:00

fromUTCFormat is used to convert UTCTime to a string
for storage within a Lit.

toUTCFormat is used to convert a string into UTCTime;
we have to support 
   no time zone
   Z
   +/-HH:MM

which means a somewhat messy convertor, which is written
for clarity rather than speed.
-}

fromUTCFormat :: UTCTime -> String
fromUTCFormat :: UTCTime -> String
fromUTCFormat = TimeLocale -> String -> UTCTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%FT%T%QZ"
  
fromDayFormat :: Day -> String
fromDayFormat :: Day -> String
fromDayFormat = TimeLocale -> String -> Day -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%FZ"
  
toTimeFormat :: (ParseTime a) => String -> String -> Maybe a
toTimeFormat :: String -> String -> Maybe a
toTimeFormat String
fmt String
inVal =
  let fmtHHMM :: String
fmtHHMM = String
fmt String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"%z"
      fmtZ :: String
fmtZ = String
fmt String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"Z"
#if MIN_VERSION_time(1,5,0)
      pt :: String -> m t
pt String
f = Bool -> TimeLocale -> String -> String -> m t
forall (m :: * -> *) t.
(MonadFail m, ParseTime t) =>
Bool -> TimeLocale -> String -> String -> m t
parseTimeM Bool
True TimeLocale
defaultTimeLocale String
f String
inVal
#else
      pt f = parseTime defaultTimeLocale f inVal
#endif
  in case String -> Maybe a
forall (m :: * -> *) t. (MonadFail m, ParseTime t) => String -> m t
pt String
fmtHHMM of
    o :: Maybe a
o@(Just a
_) -> Maybe a
o
    Maybe a
_ -> case String -> Maybe a
forall (m :: * -> *) t. (MonadFail m, ParseTime t) => String -> m t
pt String
fmtZ of
      o :: Maybe a
o@(Just a
_) -> Maybe a
o
      Maybe a
_ -> String -> Maybe a
forall (m :: * -> *) t. (MonadFail m, ParseTime t) => String -> m t
pt String
fmt 
  
toUTCFormat :: T.Text -> Maybe UTCTime
toUTCFormat :: Text -> Maybe UTCTime
toUTCFormat = String -> String -> Maybe UTCTime
forall a. ParseTime a => String -> String -> Maybe a
toTimeFormat String
"%FT%T%Q" (String -> Maybe UTCTime)
-> (Text -> String) -> Text -> Maybe UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
    
toDayFormat :: T.Text -> Maybe Day
toDayFormat :: Text -> Maybe Day
toDayFormat = String -> String -> Maybe Day
forall a. ParseTime a => String -> String -> Maybe a
toTimeFormat String
"%F" (String -> Maybe Day) -> (Text -> String) -> Text -> Maybe Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
    
-- | Converts to a literal with a @xsd:datetime@ datatype.
instance ToRDFLabel UTCTime where
  toRDFLabel :: UTCTime -> RDFLabel
toRDFLabel = (Text -> ScopedName -> RDFLabel) -> ScopedName -> Text -> RDFLabel
forall a b c. (a -> b -> c) -> b -> a -> c
flip Text -> ScopedName -> RDFLabel
TypedLit ScopedName
xsdDateTime (Text -> RDFLabel) -> (UTCTime -> Text) -> UTCTime -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (UTCTime -> String) -> UTCTime -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> String
fromUTCFormat
  
-- | Converts from a literal with a @xsd:datetime@ datatype.
instance FromRDFLabel UTCTime where
  fromRDFLabel :: RDFLabel -> Maybe UTCTime
fromRDFLabel = (Text -> Maybe UTCTime) -> ScopedName -> RDFLabel -> Maybe UTCTime
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe UTCTime
toUTCFormat ScopedName
xsdDateTime
  
-- | Converts to a literal with a @xsd:date@ datatype.
instance ToRDFLabel Day where
  toRDFLabel :: Day -> RDFLabel
toRDFLabel = (Text -> ScopedName -> RDFLabel) -> ScopedName -> Text -> RDFLabel
forall a b c. (a -> b -> c) -> b -> a -> c
flip Text -> ScopedName -> RDFLabel
TypedLit ScopedName
xsdDate (Text -> RDFLabel) -> (Day -> Text) -> Day -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (Day -> String) -> Day -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> String
fromDayFormat

-- | Converts from a literal with a @xsd:date@ datatype.
instance FromRDFLabel Day where
  fromRDFLabel :: RDFLabel -> Maybe Day
fromRDFLabel = (Text -> Maybe Day) -> ScopedName -> RDFLabel -> Maybe Day
forall a. (Text -> Maybe a) -> ScopedName -> RDFLabel -> Maybe a
fLabel Text -> Maybe Day
toDayFormat ScopedName
xsdDate
  
-- | Converts to a Resource.
instance ToRDFLabel ScopedName where  
  toRDFLabel :: ScopedName -> RDFLabel
toRDFLabel = ScopedName -> RDFLabel
Res

-- | Converts from a Resource.
instance FromRDFLabel ScopedName where
  fromRDFLabel :: RDFLabel -> Maybe ScopedName
fromRDFLabel (Res ScopedName
sn) = ScopedName -> Maybe ScopedName
forall a. a -> Maybe a
Just ScopedName
sn
  fromRDFLabel RDFLabel
_        = Maybe ScopedName
forall a. Maybe a
Nothing
  
-- | Converts to a Resource.
instance ToRDFLabel QName where  
  toRDFLabel :: QName -> RDFLabel
toRDFLabel = ScopedName -> RDFLabel
Res (ScopedName -> RDFLabel)
-> (QName -> ScopedName) -> QName -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Text -> QName -> ScopedName
makeQNameScopedName Maybe Text
forall a. Maybe a
Nothing
  
-- | Converts from a Resource.
instance FromRDFLabel QName where
  fromRDFLabel :: RDFLabel -> Maybe QName
fromRDFLabel (Res ScopedName
sn) = QName -> Maybe QName
forall a. a -> Maybe a
Just (QName -> Maybe QName) -> QName -> Maybe QName
forall a b. (a -> b) -> a -> b
$ ScopedName -> QName
getQName ScopedName
sn
  fromRDFLabel RDFLabel
_        = Maybe QName
forall a. Maybe a
Nothing
  
-- | Converts to a Resource.
instance ToRDFLabel URI where  
  toRDFLabel :: URI -> RDFLabel
toRDFLabel = ScopedName -> RDFLabel
Res (ScopedName -> RDFLabel) -> (URI -> ScopedName) -> URI -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. URI -> ScopedName
makeURIScopedName
  
-- | Converts from a Resource.
instance FromRDFLabel URI where
  fromRDFLabel :: RDFLabel -> Maybe URI
fromRDFLabel (Res ScopedName
sn) = URI -> Maybe URI
forall a. a -> Maybe a
Just (URI -> Maybe URI) -> URI -> Maybe URI
forall a b. (a -> b) -> a -> b
$ ScopedName -> URI
getScopedNameURI ScopedName
sn
  fromRDFLabel RDFLabel
_        = Maybe URI
forall a. Maybe a
Nothing

-- | Get the canonical string for RDF label.
--
--  This is used for hashing, so that equivalent labels always return
--  the same hash value.
--
--  TODO: can remove the use of quote1Str as all we care about is
--  a unique output, not that it is valid in any RDF format. Also
--  rename to showForHash or something like that, since it is only used
--  for this purpose.
--
showCanon :: RDFLabel -> String
showCanon :: RDFLabel -> String
showCanon (Res ScopedName
sn)           = String
"<"String -> ShowS
forall a. [a] -> [a] -> [a]
++URI -> String
forall a. Show a => a -> String
show (ScopedName -> URI
getScopedNameURI ScopedName
sn)String -> ShowS
forall a. [a] -> [a] -> [a]
++String
">"
showCanon (Lit Text
st)           = Text -> String
forall a. Show a => a -> String
show Text
st
showCanon (LangLit Text
st LanguageTag
lang)  = Text -> String
quote1Str Text
st String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"@"  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
T.unpack (LanguageTag -> Text
fromLangTag LanguageTag
lang)
showCanon (TypedLit Text
st ScopedName
dt)   = Text -> String
quote1Str Text
st String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"^^" String -> ShowS
forall a. [a] -> [a] -> [a]
++ URI -> String
forall a. Show a => a -> String
show (ScopedName -> URI
getScopedNameURI ScopedName
dt)
showCanon RDFLabel
s                  = RDFLabel -> String
forall a. Show a => a -> String
show RDFLabel
s

-- The Data.Text API points out issues with processing a text
-- character-by-character, but it's not clear to me how to avoid
-- that here.
--
-- One assumption would be that the strings aren't likely to be large,
-- so that several calls to T.find or similar could be made to
-- simplify certain cases.
--
-- Is it worth scanning through the text to look for characters like \n
-- or #, or to look for sequences like '##'?

-- Is it worth sending in a flag to indicate the different modes for
-- handling \n characters, or just leave this complexity in 'quoteT False'?
--
processChar ::
  Char
  -> (T.Text, Bool) -- ^ the boolean is @True@ if the returned text has been
  -- expanded so that it begins with @\\@
processChar :: Char -> (Text, Bool)
processChar Char
'"'  = (Text
"\\\"", Bool
True)
processChar Char
'\\' = (Text
"\\\\", Bool
True)
processChar Char
'\n' = (Text
"\\n", Bool
True)
processChar Char
'\r' = (Text
"\\r", Bool
True)
processChar Char
'\t' = (Text
"\\t", Bool
True)
processChar Char
'\b' = (Text
"\\b", Bool
True)
-- processChar '\f' = ("\\f", True)
-- Using the above I get invalid output according to
-- rapper version 2.0.9, so use the following for now
-- (changed at version 0.9.0.6)
processChar Char
'\f' = (Text
"\\u000C", Bool
True) -- 
processChar Char
c =
  let nc :: Int
nc = Char -> Int
ord Char
c
      -- lazy ways to convert to hex-encoded strings
      four :: Text
four = Text -> Text -> Text
T.append Text
"\\u" (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String -> Int -> String
forall r. PrintfType r => String -> r
printf String
"%04X" Int
nc
      eight :: Text
eight = Text -> Text -> Text
T.append Text
"\\U" (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String -> Int -> String
forall r. PrintfType r => String -> r
printf String
"%08X" Int
nc
  in if Int
nc Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x20
     then (Text
four, Bool
True)
     else if Int
nc Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x7f
          then (Char -> Text
T.singleton Char
c, Bool
False)
          else if Int
nc Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0x10000
               then (Text
four, Bool
True)
               else (Text
eight, Bool
True)

convertChar :: Char -> T.Text
convertChar :: Char -> Text
convertChar = (Text, Bool) -> Text
forall a b. (a, b) -> a
fst ((Text, Bool) -> Text) -> (Char -> (Text, Bool)) -> Char -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> (Text, Bool)
processChar

-- | See `quote`.
quoteT :: Bool -> T.Text -> T.Text
quoteT :: Bool -> Text -> Text
quoteT Bool
True Text
txt =
  -- Output is to be used as "..."
  let go :: (Text -> p) -> Text -> p
go Text -> p
dl Text
x =
        case Text -> Maybe (Char, Text)
T.uncons Text
x of
          Just (Char
c, Text
xs) -> (Text -> p) -> Text -> p
go (Text -> p
dl (Text -> p) -> (Text -> Text) -> Text -> p
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append (Char -> Text
convertChar Char
c)) Text
xs
          Maybe (Char, Text)
_ -> Text -> p
dl Text
T.empty
                          
  in (Text -> Text) -> Text -> Text
forall p. (Text -> p) -> Text -> p
go (Text -> Text -> Text
T.append Text
T.empty) Text
txt

-- One complexity here is my reading of the Turtle grammar
--    STRING_LITERAL_LONG_QUOTE ::=	'"""' (('"' | '""')? [^"\] | ECHAR | UCHAR)* '"""'
-- which says that any un-protected double-quote characters can not
-- be followed by a \ character. One option would be to always use the
-- 'quoteT True' behavior.
--
quoteT Bool
_ Text
txt =
  -- Output is to be used as """...""""
  let go :: (Text -> c) -> Text -> c
go Text -> c
dl Text
x =
        case Text -> Maybe (Char, Text)
T.uncons Text
x of
          Just (Char
'"', Text
xs) -> (Text -> c) -> Text -> c
go1 Text -> c
dl Text
xs
          Just (Char
'\n', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Text -> Text
T.cons Char
'\n') Text
xs
          Just (Char
c, Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append (Char -> Text
convertChar Char
c)) Text
xs
          Maybe (Char, Text)
_ -> Text -> c
dl Text
T.empty

      -- Seen one double quote
      go1 :: (Text -> c) -> Text -> c
go1 Text -> c
dl Text
x =
        case Text -> Maybe (Char, Text)
T.uncons Text
x of
          Just (Char
'"', Text
xs) -> (Text -> c) -> Text -> c
go2 Text -> c
dl Text
xs
          Just (Char
'\n', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\"\n") Text
xs
          Just (Char
'\\', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\\\"\\\\") Text
xs
          Just (Char
c, Text
xs) ->
            let (Text
t, Bool
f) = Char -> (Text, Bool)
processChar Char
c
                dl' :: Text -> Text
dl' = if Bool
f then Text -> Text -> Text
T.append Text
"\\\"" else Char -> Text -> Text
T.cons Char
'"'
            in (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
dl' (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
t) Text
xs
          Maybe (Char, Text)
_ -> Text -> c
dl Text
"\\\""
          
      -- Seen two double quotes
      go2 :: (Text -> c) -> Text -> c
go2 Text -> c
dl Text
x =
        case Text -> Maybe (Char, Text)
T.uncons Text
x of
          Just (Char
'"', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\\\"\\\"\\\"") Text
xs
          Just (Char
'\n', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\"\"\n") Text
xs
          Just (Char
'\\', Text
xs) -> (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\\\"\\\"\\\\") Text
xs
          Just (Char
c, Text
xs) ->
            let (Text
t, Bool
f) = Char -> (Text, Bool)
processChar Char
c
                dl' :: Text -> Text
dl' = Text -> Text -> Text
T.append (if Bool
f then Text
"\\\"\\\"" else Text
"\"\"")
            in (Text -> c) -> Text -> c
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
dl' (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
t) Text
xs
          Maybe (Char, Text)
_ -> Text -> c
dl Text
"\\\"\\\""

      -- at the start of the string we have 3 quotes, so any
      -- starting quote characters must be quoted.
      go0 :: (Text -> c) -> Text -> c
go0 Text -> c
dl Text
x =
        case Text -> Maybe (Char, Text)
T.uncons Text
x of
          Just (Char
'"', Text
xs) -> (Text -> c) -> Text -> c
go0 (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append Text
"\\\"") Text
xs
          Just (Char
'\n', Text
xs) -> (Text -> c) -> Text -> c
forall p. (Text -> p) -> Text -> p
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Text -> Text
T.cons Char
'\n') Text
xs
          Just (Char
c, Text
xs) -> (Text -> c) -> Text -> c
forall p. (Text -> p) -> Text -> p
go (Text -> c
dl (Text -> c) -> (Text -> Text) -> Text -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> Text
T.append (Char -> Text
convertChar Char
c)) Text
xs
          Maybe (Char, Text)
_ -> Text -> c
dl Text
T.empty
      
  in (Text -> Text) -> Text -> Text
forall p. (Text -> p) -> Text -> p
go0 (Text -> Text -> Text
T.append Text
T.empty) Text
txt
        
-- | Turtle-style quoting rules for a string.
--
--   At present the choice is between using one or three
--   double quote (@\"@) characters to surround the string; i.e. using
--   single quote (@'@)  characters is not supported.
-- 
--   As of Swish 0.9.0.6, the @\\f@ character is converted to
--   @\\u000C@ rather than left as is to aid interoperability
--   with some other tools.
--   
quote :: 
  Bool  -- ^ @True@ if the string is to be displayed using one rather than three quotes.
  -> String -- ^ String to quote.
  -> String -- ^ The string does /not/ contain the surrounding quote marks.
quote :: Bool -> ShowS
quote Bool
f = Text -> String
T.unpack (Text -> String) -> (String -> Text) -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Text -> Text
quoteT Bool
f (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack

{-
quote _     []           = ""
quote False s@(c:'"':[]) | c == '\\'  = s -- handle triple-quoted strings ending in "
                         | otherwise  = [c, '\\', '"']

quote True  ('"': st)    = '\\':'"': quote True  st
quote True  ('\n':st)    = '\\':'n': quote True  st
quote True  ('\t':st)    = '\\':'t': quote True  st

quote False ('"': st)    =      '"': quote False st
quote False ('\n':st)    =     '\n': quote False st
quote False ('\t':st)    =     '\t': quote False st
quote f ('\r':st)    = '\\':'r': quote f st
quote f ('\\':st)    = '\\':'\\': quote f st -- not sure about this
quote f (c:st) = 
  let nc = ord c
      rst = quote f st
      
      -- lazy way to convert to a string
      hstr = printf "%08X" nc
      ustr = hstr ++ rst

  in if nc > 0xffff 
     then '\\':'U': ustr
     else if nc > 0x7e || nc < 0x20
          then '\\':'u': drop 4 ustr
          else c : rst

-}

-- surround a string with a single double-quote mark at each end,
-- e.g. "...".
quote1Str :: T.Text -> String
quote1Str :: Text -> String
quote1Str Text
t = Char
'"' Char -> ShowS
forall a. a -> [a] -> [a]
: Text -> String
T.unpack (Bool -> Text -> Text
quoteT Bool
True Text
t) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""

---------------------------------------------------------
--  Selected RDFLabel values
---------------------------------------------------------

-- | @rdf:type@ from <http://www.w3.org/TR/rdf-schema/#ch_type>.
resRdfType :: RDFLabel
resRdfType :: RDFLabel
resRdfType = ScopedName -> RDFLabel
Res ScopedName
rdfType 

-- | @rdf:List@ from <http://www.w3.org/TR/rdf-schema/#ch_list>.
resRdfList :: RDFLabel
resRdfList :: RDFLabel
resRdfList = ScopedName -> RDFLabel
Res ScopedName
rdfList

-- | @rdf:first@ from <http://www.w3.org/TR/rdf-schema/#ch_first>.
resRdfFirst :: RDFLabel
resRdfFirst :: RDFLabel
resRdfFirst = ScopedName -> RDFLabel
Res ScopedName
rdfFirst 

-- | @rdf:rest@ from <http://www.w3.org/TR/rdf-schema/#ch_rest>.
resRdfRest :: RDFLabel
resRdfRest :: RDFLabel
resRdfRest = ScopedName -> RDFLabel
Res ScopedName
rdfRest

-- | @rdf:nil@ from <http://www.w3.org/TR/rdf-schema/#ch_nil>.
resRdfNil :: RDFLabel
resRdfNil :: RDFLabel
resRdfNil = ScopedName -> RDFLabel
Res ScopedName
rdfNil

-- | @rdfs:member@ from <http://www.w3.org/TR/rdf-schema/#ch_member>.
resRdfsMember :: RDFLabel
resRdfsMember :: RDFLabel
resRdfsMember = ScopedName -> RDFLabel
Res ScopedName
rdfsMember

-- | @rdfd:GeneralRestriction@.
resRdfdGeneralRestriction :: RDFLabel
resRdfdGeneralRestriction :: RDFLabel
resRdfdGeneralRestriction = ScopedName -> RDFLabel
Res ScopedName
rdfdGeneralRestriction

-- | @rdfd:onProperties@.
resRdfdOnProperties :: RDFLabel
resRdfdOnProperties :: RDFLabel
resRdfdOnProperties       = ScopedName -> RDFLabel
Res ScopedName
rdfdOnProperties

-- | @rdfd:constraint@.
resRdfdConstraint :: RDFLabel
resRdfdConstraint :: RDFLabel
resRdfdConstraint         = ScopedName -> RDFLabel
Res ScopedName
rdfdConstraint

-- | @rdfd:maxCardinality@.
resRdfdMaxCardinality :: RDFLabel
resRdfdMaxCardinality :: RDFLabel
resRdfdMaxCardinality     = ScopedName -> RDFLabel
Res ScopedName
rdfdMaxCardinality

-- | @rdfs:seeAlso@ from <http://www.w3.org/TR/rdf-schema/#ch_seealso>.
resRdfsSeeAlso :: RDFLabel
resRdfsSeeAlso :: RDFLabel
resRdfsSeeAlso = ScopedName -> RDFLabel
Res ScopedName
rdfsSeeAlso

-- | @rdf:value@ from <http://www.w3.org/TR/rdf-schema/#ch_value>.
resRdfValue :: RDFLabel
resRdfValue :: RDFLabel
resRdfValue = ScopedName -> RDFLabel
Res ScopedName
rdfValue

-- | @owl:sameAs@.
resOwlSameAs :: RDFLabel
resOwlSameAs :: RDFLabel
resOwlSameAs = ScopedName -> RDFLabel
Res ScopedName
owlSameAs

-- | @log:implies@.
resLogImplies :: RDFLabel
resLogImplies :: RDFLabel
resLogImplies = ScopedName -> RDFLabel
Res ScopedName
logImplies

-- | @rdfs:label@ from <http://www.w3.org/TR/rdf-schema/#ch_label>.
resRdfsLabel :: RDFLabel
resRdfsLabel :: RDFLabel
resRdfsLabel = ScopedName -> RDFLabel
Res ScopedName
rdfsLabel

-- | @rdfs:comment@ from <http://www.w3.org/TR/rdf-schema/#ch_comment>.
resRdfsComment :: RDFLabel
resRdfsComment :: RDFLabel
resRdfsComment = ScopedName -> RDFLabel
Res ScopedName
rdfsComment

-- | @rdf:Property@ from <http://www.w3.org/TR/rdf-schema/#ch_property>.
resRdfProperty :: RDFLabel
resRdfProperty :: RDFLabel
resRdfProperty = ScopedName -> RDFLabel
Res ScopedName
rdfProperty

-- | @rdfs:subPropertyOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subpropertyof>.
resRdfsSubPropertyOf :: RDFLabel
resRdfsSubPropertyOf :: RDFLabel
resRdfsSubPropertyOf = ScopedName -> RDFLabel
Res ScopedName
rdfsSubPropertyOf

-- | @rdfs:subClassOf@ from <http://www.w3.org/TR/rdf-schema/#ch_subclassof>.
resRdfsSubClassOf :: RDFLabel
resRdfsSubClassOf :: RDFLabel
resRdfsSubClassOf = ScopedName -> RDFLabel
Res ScopedName
rdfsSubClassOf

-- | @rdfs:Class@ from <http://www.w3.org/TR/rdf-schema/#ch_class>.
resRdfsClass :: RDFLabel
resRdfsClass :: RDFLabel
resRdfsClass = ScopedName -> RDFLabel
Res ScopedName
rdfsClass

-- | @rdfs:Literal@ from <http://www.w3.org/TR/rdf-schema/#ch_literal>.
resRdfsLiteral :: RDFLabel
resRdfsLiteral :: RDFLabel
resRdfsLiteral = ScopedName -> RDFLabel
Res ScopedName
rdfsLiteral

-- | @rdfs:Datatype@ from <http://www.w3.org/TR/rdf-schema/#ch_datatype>.
resRdfsDatatype :: RDFLabel
resRdfsDatatype :: RDFLabel
resRdfsDatatype = ScopedName -> RDFLabel
Res ScopedName
rdfsDatatype

-- | @rdf:XMLLiteral@ from <http://www.w3.org/TR/rdf-schema/#ch_xmlliteral>.
resRdfXMLLiteral :: RDFLabel
resRdfXMLLiteral :: RDFLabel
resRdfXMLLiteral = ScopedName -> RDFLabel
Res ScopedName
rdfXMLLiteral

-- | @rdfs:range@ from <http://www.w3.org/TR/rdf-schema/#ch_range>.
resRdfsRange :: RDFLabel
resRdfsRange :: RDFLabel
resRdfsRange = ScopedName -> RDFLabel
Res ScopedName
rdfsRange

-- | @rdfs:domain@ from <http://www.w3.org/TR/rdf-schema/#ch_domain>.
resRdfsDomain :: RDFLabel
resRdfsDomain :: RDFLabel
resRdfsDomain = ScopedName -> RDFLabel
Res ScopedName
rdfsDomain

-- | @rdfs:Container@ from <http://www.w3.org/TR/rdf-schema/#ch_container>.
resRdfsContainer :: RDFLabel
resRdfsContainer :: RDFLabel
resRdfsContainer = ScopedName -> RDFLabel
Res ScopedName
rdfsContainer

-- | @rdf:Bag@ from <http://www.w3.org/TR/rdf-schema/#ch_bag>.
resRdfBag :: RDFLabel
resRdfBag :: RDFLabel
resRdfBag = ScopedName -> RDFLabel
Res ScopedName
rdfBag

-- | @rdf:Seq@ from <http://www.w3.org/TR/rdf-schema/#ch_seq>.
resRdfSeq :: RDFLabel
resRdfSeq :: RDFLabel
resRdfSeq = ScopedName -> RDFLabel
Res ScopedName
rdfSeq

-- | @rdf:Alt@ from <http://www.w3.org/TR/rdf-schema/#ch_alt>.
resRdfAlt :: RDFLabel
resRdfAlt :: RDFLabel
resRdfAlt = ScopedName -> RDFLabel
Res ScopedName
rdfAlt

-- | @rdfs:ContainerMembershipProperty@ from <http://www.w3.org/TR/rdf-schema/#ch_containermembershipproperty>.
resRdfsContainerMembershipProperty :: RDFLabel
resRdfsContainerMembershipProperty :: RDFLabel
resRdfsContainerMembershipProperty = ScopedName -> RDFLabel
Res ScopedName
rdfsContainerMembershipProperty

-- | @rdfs:isDefinedBy@ from <http://www.w3.org/TR/rdf-schema/#ch_isdefinedby>.
resRdfsIsDefinedBy :: RDFLabel
resRdfsIsDefinedBy :: RDFLabel
resRdfsIsDefinedBy = ScopedName -> RDFLabel
Res ScopedName
rdfsIsDefinedBy

-- | @rdfs:Resource@ from <http://www.w3.org/TR/rdf-schema/#ch_resource>.
resRdfsResource :: RDFLabel
resRdfsResource :: RDFLabel
resRdfsResource = ScopedName -> RDFLabel
Res ScopedName
rdfsResource

-- | @rdf:Statement@ from <http://www.w3.org/TR/rdf-schema/#ch_statement>.
resRdfStatement :: RDFLabel
resRdfStatement :: RDFLabel
resRdfStatement = ScopedName -> RDFLabel
Res ScopedName
rdfStatement

-- | @rdf:subject@ from <http://www.w3.org/TR/rdf-schema/#ch_subject>.
resRdfSubject :: RDFLabel
resRdfSubject :: RDFLabel
resRdfSubject = ScopedName -> RDFLabel
Res ScopedName
rdfSubject

-- | @rdf:predicate@ from <http://www.w3.org/TR/rdf-schema/#ch_predicate>.
resRdfPredicate :: RDFLabel
resRdfPredicate :: RDFLabel
resRdfPredicate = ScopedName -> RDFLabel
Res ScopedName
rdfPredicate

-- | @rdf:object@ from <http://www.w3.org/TR/rdf-schema/#ch_object>.
resRdfObject :: RDFLabel
resRdfObject :: RDFLabel
resRdfObject = ScopedName -> RDFLabel
Res ScopedName
rdfObject

-- | @rdf:RDF@.
resRdfRDF :: RDFLabel
resRdfRDF :: RDFLabel
resRdfRDF = ScopedName -> RDFLabel
Res ScopedName
rdfRDF

-- | @rdf:Description@.
resRdfDescription :: RDFLabel
resRdfDescription :: RDFLabel
resRdfDescription = ScopedName -> RDFLabel
Res ScopedName
rdfDescription

-- | @rdf:ID@.
resRdfID :: RDFLabel
resRdfID :: RDFLabel
resRdfID = ScopedName -> RDFLabel
Res ScopedName
rdfID

-- | @rdf:about@.
resRdfAbout :: RDFLabel
resRdfAbout :: RDFLabel
resRdfAbout = ScopedName -> RDFLabel
Res ScopedName
rdfAbout

-- | @rdf:parseType@.
resRdfParseType :: RDFLabel
resRdfParseType :: RDFLabel
resRdfParseType = ScopedName -> RDFLabel
Res ScopedName
rdfParseType

-- | @rdf:resource@.
resRdfResource :: RDFLabel
resRdfResource :: RDFLabel
resRdfResource = ScopedName -> RDFLabel
Res ScopedName
rdfResource

-- | @rdf:li@.
resRdfLi :: RDFLabel
resRdfLi :: RDFLabel
resRdfLi = ScopedName -> RDFLabel
Res ScopedName
rdfLi

-- | @rdf:nodeID@.
resRdfNodeID :: RDFLabel
resRdfNodeID :: RDFLabel
resRdfNodeID = ScopedName -> RDFLabel
Res ScopedName
rdfNodeID

-- | @rdf:datatype@.
resRdfDatatype :: RDFLabel
resRdfDatatype :: RDFLabel
resRdfDatatype = ScopedName -> RDFLabel
Res ScopedName
rdfDatatype

-- | @rdf:_1@.
resRdf1 :: RDFLabel
resRdf1 :: RDFLabel
resRdf1 = ScopedName -> RDFLabel
Res ScopedName
rdf1

-- | @rdf:_2@.
resRdf2 :: RDFLabel
resRdf2 :: RDFLabel
resRdf2 = ScopedName -> RDFLabel
Res ScopedName
rdf2

-- | Create a @rdf:_n@ entity.
--
-- There is no check that the argument is not @0@.
resRdfn :: Word32 -> RDFLabel
resRdfn :: Word32 -> RDFLabel
resRdfn = ScopedName -> RDFLabel
Res (ScopedName -> RDFLabel)
-> (Word32 -> ScopedName) -> Word32 -> RDFLabel
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> ScopedName
rdfn

---------------------------------------------------------
--  Additional functions on RDFLabel values
---------------------------------------------------------

-- |Test if supplied labal is a URI resource node
isUri :: RDFLabel -> Bool
isUri :: RDFLabel -> Bool
isUri (Res ScopedName
_) = Bool
True
isUri  RDFLabel
_      = Bool
False

-- |Test if supplied labal is a literal node
-- ('Lit', 'LangLit', or 'TypedLit').
isLiteral :: RDFLabel -> Bool
isLiteral :: RDFLabel -> Bool
isLiteral (Lit Text
_)        = Bool
True
isLiteral (LangLit Text
_ LanguageTag
_)  = Bool
True
isLiteral (TypedLit Text
_ ScopedName
_) = Bool
True
isLiteral  RDFLabel
_             = Bool
False

-- |Test if supplied labal is an untyped literal node (either
-- 'Lit' or 'LangLit').
isUntypedLiteral :: RDFLabel -> Bool
isUntypedLiteral :: RDFLabel -> Bool
isUntypedLiteral (Lit Text
_)       = Bool
True
isUntypedLiteral (LangLit Text
_ LanguageTag
_) = Bool
True
isUntypedLiteral  RDFLabel
_            = Bool
False

-- |Test if supplied labal is a typed literal node ('TypedLit').
isTypedLiteral :: RDFLabel -> Bool
isTypedLiteral :: RDFLabel -> Bool
isTypedLiteral (TypedLit Text
_ ScopedName
_) = Bool
True
isTypedLiteral  RDFLabel
_             = Bool
False

-- |Test if supplied labal is a XML literal node
isXMLLiteral :: RDFLabel -> Bool
isXMLLiteral :: RDFLabel -> Bool
isXMLLiteral = ScopedName -> RDFLabel -> Bool
isDatatyped ScopedName
rdfXMLLiteral

-- |Test if supplied label is a typed literal node of a given datatype
isDatatyped :: ScopedName -> RDFLabel -> Bool
isDatatyped :: ScopedName -> RDFLabel -> Bool
isDatatyped ScopedName
d  (TypedLit Text
_ ScopedName
dt) = ScopedName
d ScopedName -> ScopedName -> Bool
forall a. Eq a => a -> a -> Bool
== ScopedName
dt
isDatatyped ScopedName
_  RDFLabel
_               = Bool
False

-- |Test if supplied label is a container membership property
--
--  Check for namespace is RDF namespace and
--  first character of local name is '_' and
--  remaining characters of local name are all digits
isMemberProp :: RDFLabel -> Bool
isMemberProp :: RDFLabel -> Bool
isMemberProp (Res ScopedName
sn) =
  ScopedName -> Namespace
getScopeNamespace ScopedName
sn Namespace -> Namespace -> Bool
forall a. Eq a => a -> a -> Bool
== Namespace
namespaceRDF Bool -> Bool -> Bool
&&
  case Text -> Maybe (Char, Text)
T.uncons (LName -> Text
getLName (ScopedName -> LName
getScopeLocal ScopedName
sn)) of
    Just (Char
'_', Text
t) -> (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isDigit Text
t
    Maybe (Char, Text)
_ -> Bool
False
isMemberProp RDFLabel
_        = Bool
False

-- |Test if supplied labal is a blank node
isBlank :: RDFLabel -> Bool
isBlank :: RDFLabel -> Bool
isBlank (Blank String
_) = Bool
True
isBlank  RDFLabel
_        = Bool
False

-- |Test if supplied labal is a query variable
isQueryVar :: RDFLabel -> Bool
isQueryVar :: RDFLabel -> Bool
isQueryVar (Var String
_) = Bool
True
isQueryVar  RDFLabel
_      = Bool
False

-- |Extract text value from a literal node (including the
-- Language and Typed variants). The empty string is returned
-- for other nodes.
getLiteralText :: RDFLabel -> T.Text
getLiteralText :: RDFLabel -> Text
getLiteralText (Lit Text
s)        = Text
s
getLiteralText (LangLit Text
s LanguageTag
_)  = Text
s
getLiteralText (TypedLit Text
s ScopedName
_) = Text
s
getLiteralText  RDFLabel
_             = Text
""

-- |Extract the ScopedName value from a resource node ('nullScopedName'
-- is returned for non-resource nodes).
getScopedName :: RDFLabel -> ScopedName
getScopedName :: RDFLabel -> ScopedName
getScopedName (Res ScopedName
sn) = ScopedName
sn
getScopedName  RDFLabel
_       = ScopedName
nullScopedName

-- |Make a blank node from a supplied query variable,
--  or return the supplied label unchanged.
--  (Use this in when substituting an existential for an
--  unsubstituted query variable.)
makeBlank :: RDFLabel -> RDFLabel
makeBlank :: RDFLabel -> RDFLabel
makeBlank  (Var String
loc)    = String -> RDFLabel
Blank String
loc
makeBlank  RDFLabel
lb           = RDFLabel
lb

-- | RDF Triple (statement)
-- 
--   At present there is no check or type-level
--   constraint that stops the subject or
--   predicate of the triple from being a literal.
--
type RDFTriple = Arc RDFLabel

-- | A set of RDF triples.
type RDFArcSet = ArcSet RDFLabel

-- | Convert 3 RDF labels to a RDF triple.
--
--   See also @Swish.RDF.GraphClass.arcFromTriple@.
toRDFTriple :: 
  (ToRDFLabel s, ToRDFLabel p, ToRDFLabel o) 
  => s -- ^ Subject 
  -> p -- ^ Predicate
  -> o -- ^ Object
  -> RDFTriple
toRDFTriple :: s -> p -> o -> RDFTriple
toRDFTriple s
s p
p o
o = 
  RDFLabel -> RDFLabel -> RDFLabel -> RDFTriple
forall lb. lb -> lb -> lb -> Arc lb
Arc (s -> RDFLabel
forall a. ToRDFLabel a => a -> RDFLabel
toRDFLabel s
s) (p -> RDFLabel
forall a. ToRDFLabel a => a -> RDFLabel
toRDFLabel p
p) (o -> RDFLabel
forall a. ToRDFLabel a => a -> RDFLabel
toRDFLabel o
o)

-- | Extract the contents of a RDF triple.
--
--   See also @Swish.RDF.GraphClass.arcToTriple@.
fromRDFTriple :: 
  (FromRDFLabel s, FromRDFLabel p, FromRDFLabel o) 
  => RDFTriple 
  -> Maybe (s, p, o) -- ^ The conversion only succeeds if all three
                     --   components can be converted to the correct
                     --   Haskell types.
fromRDFTriple :: RDFTriple -> Maybe (s, p, o)
fromRDFTriple (Arc RDFLabel
s RDFLabel
p RDFLabel
o) = 
  (,,) (s -> p -> o -> (s, p, o))
-> Maybe s -> Maybe (p -> o -> (s, p, o))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RDFLabel -> Maybe s
forall a. FromRDFLabel a => RDFLabel -> Maybe a
fromRDFLabel RDFLabel
s Maybe (p -> o -> (s, p, o)) -> Maybe p -> Maybe (o -> (s, p, o))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> RDFLabel -> Maybe p
forall a. FromRDFLabel a => RDFLabel -> Maybe a
fromRDFLabel RDFLabel
p Maybe (o -> (s, p, o)) -> Maybe o -> Maybe (s, p, o)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> RDFLabel -> Maybe o
forall a. FromRDFLabel a => RDFLabel -> Maybe a
fromRDFLabel RDFLabel
o
  
-- | Namespace prefix list entry

-- | A map for name spaces (key is the prefix).
type NamespaceMap = M.Map (Maybe T.Text) URI -- TODO: should val be URI or namespace?

-- | Create an empty namespace map.
emptyNamespaceMap :: NamespaceMap
emptyNamespaceMap :: NamespaceMap
emptyNamespaceMap = NamespaceMap
forall k a. Map k a
M.empty

-- | Graph formula entry

data LookupFormula lb gr = Formula
    { LookupFormula lb gr -> lb
formLabel :: lb -- ^ The label for the formula
    , LookupFormula lb gr -> gr
formGraph :: gr -- ^ The contents of the formula
    }

instance (Eq lb, Eq gr) => Eq (LookupFormula lb gr) where
    LookupFormula lb gr
f1 == :: LookupFormula lb gr -> LookupFormula lb gr -> Bool
== LookupFormula lb gr
f2 = LookupFormula lb gr -> lb
forall lb gr. LookupFormula lb gr -> lb
formLabel LookupFormula lb gr
f1 lb -> lb -> Bool
forall a. Eq a => a -> a -> Bool
== LookupFormula lb gr -> lb
forall lb gr. LookupFormula lb gr -> lb
formLabel LookupFormula lb gr
f2 Bool -> Bool -> Bool
&&
               LookupFormula lb gr -> gr
forall lb gr. LookupFormula lb gr -> gr
formGraph LookupFormula lb gr
f1 gr -> gr -> Bool
forall a. Eq a => a -> a -> Bool
== LookupFormula lb gr -> gr
forall lb gr. LookupFormula lb gr -> gr
formGraph LookupFormula lb gr
f2

instance (Ord lb, Ord gr) => Ord (LookupFormula lb gr) where
    (Formula lb
a1 gr
b1) compare :: LookupFormula lb gr -> LookupFormula lb gr -> Ordering
`compare` (Formula lb
a2 gr
b2) =
        (lb
a1,gr
b1) (lb, gr) -> (lb, gr) -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` (lb
a2,gr
b2)

-- | A named formula.
type Formula lb = LookupFormula lb (NSGraph lb)

instance (Label lb) => Show (Formula lb)
    where
        show :: Formula lb -> String
show (Formula lb
l NSGraph lb
g) = lb -> String
forall a. Show a => a -> String
show lb
l String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" :- { " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String -> NSGraph lb -> String
forall lb. Label lb => String -> NSGraph lb -> String
showArcs String
"    " NSGraph lb
g String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" }"

-- | A map for named formulae.
type FormulaMap lb = M.Map lb (NSGraph lb)

-- | Create an empty formula map.
emptyFormulaMap :: FormulaMap RDFLabel
emptyFormulaMap :: FormulaMap RDFLabel
emptyFormulaMap = FormulaMap RDFLabel
forall k a. Map k a
M.empty

-- fmapFormulaMap :: (Ord a, Ord b) => (a -> b) -> FormulaMap a -> FormulaMap b
fmapFormulaMap :: (Ord a) => (a -> a) -> FormulaMap a -> FormulaMap a
fmapFormulaMap :: (a -> a) -> FormulaMap a -> FormulaMap a
fmapFormulaMap a -> a
f FormulaMap a
m = [(a, NSGraph a)] -> FormulaMap a
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(a, NSGraph a)] -> FormulaMap a)
-> [(a, NSGraph a)] -> FormulaMap a
forall a b. (a -> b) -> a -> b
$ ((a, NSGraph a) -> (a, NSGraph a))
-> [(a, NSGraph a)] -> [(a, NSGraph a)]
forall a b. (a -> b) -> [a] -> [b]
map (a -> a
f (a -> a)
-> (NSGraph a -> NSGraph a) -> (a, NSGraph a) -> (a, NSGraph a)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** (a -> a) -> NSGraph a -> NSGraph a
forall lb. Ord lb => (lb -> lb) -> NSGraph lb -> NSGraph lb
fmapNSGraph a -> a
f) ([(a, NSGraph a)] -> [(a, NSGraph a)])
-> [(a, NSGraph a)] -> [(a, NSGraph a)]
forall a b. (a -> b) -> a -> b
$ FormulaMap a -> [(a, NSGraph a)]
forall k a. Map k a -> [(k, a)]
M.assocs FormulaMap a
m

-- TODO: how to traverse formulamaps now?

{-
traverseFormulaMap :: 
    (Applicative f, Ord a, Ord b) 
    => (a -> f b) -> FormulaMap a -> f (FormulaMap b)
-}
traverseFormulaMap :: 
    (Applicative f, Ord a) 
    => (a -> f a) -> FormulaMap a -> f (FormulaMap a)
traverseFormulaMap :: (a -> f a) -> FormulaMap a -> f (FormulaMap a)
traverseFormulaMap a -> f a
f = (NSGraph a -> f (NSGraph a)) -> FormulaMap a -> f (FormulaMap a)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
Traversable.traverse ((a -> f a) -> NSGraph a -> f (NSGraph a)
forall (f :: * -> *) a.
(Applicative f, Ord a) =>
(a -> f a) -> NSGraph a -> f (NSGraph a)
traverseFormula a -> f a
f)

{-
traverseFormula :: 
    (Applicative f, Ord a, Ord b)
    => (a -> f b) -> Formula a -> f (Formula b)
-}
{-
traverseFormula :: 
    (Applicative f, Ord a)
    => (a -> f a) -> Formula a -> f (Formula a)
traverseFormula f (Formula k gr) = Formula <$> f k <*> traverseNSGraph f gr
-}

traverseFormula ::
    (Applicative f, Ord a)
    => (a -> f a) -> NSGraph a -> f (NSGraph a)

{-
traverseFormula ::
    (Applicative f, Ord a, Ord b)
    => (a -> f b) -> NSGraph a -> f (NSGraph b)
-}

traverseFormula :: (a -> f a) -> NSGraph a -> f (NSGraph a)
traverseFormula = (a -> f a) -> NSGraph a -> f (NSGraph a)
forall (f :: * -> *) a.
(Applicative f, Ord a) =>
(a -> f a) -> NSGraph a -> f (NSGraph a)
traverseNSGraph

{-
formulaeMapM ::
    (Monad m) => (lb -> m l2) -> FormulaMap lb -> m (FormulaMap l2)
formulaeMapM f = T.mapM (formulaEntryMapM f)

formulaEntryMapM ::
    (Monad m)
    => (lb -> m l2)
    -> Formula lb
    -> m (Formula l2)
formulaEntryMapM f (Formula k gr) =
  Formula `liftM` f k `ap` T.mapM f gr
    
-}

{-|

Memory-based graph with namespaces and subgraphs.

The primary means for adding arcs to an existing graph
are: 

 - `setArcs` from the `LDGraph` instance, which replaces the 
    existing set of arcs and does not change the namespace 
    map.

 - `addArc` which checks that the arc is unknown before
    adding it but does not change the namespace map or
    re-label any blank nodes in the arc.

-}
data NSGraph lb = NSGraph
    { NSGraph lb -> NamespaceMap
namespaces :: NamespaceMap      -- ^ the namespaces to use
    , NSGraph lb -> FormulaMap lb
formulae   :: FormulaMap lb     -- ^ any associated formulae 
                                      --   (a.k.a. sub- or named- graps)
    , NSGraph lb -> ArcSet lb
statements :: ArcSet lb         -- ^ the statements in the graph
    }

instance LDGraph NSGraph lb where
    emptyGraph :: NSGraph lb
emptyGraph   = NamespaceMap -> FormulaMap lb -> ArcSet lb -> NSGraph lb
forall lb. NamespaceMap -> FormulaMap lb -> ArcSet lb -> NSGraph lb
NSGraph NamespaceMap
emptyNamespaceMap FormulaMap lb
forall k a. Map k a
M.empty ArcSet lb
forall a. Set a
S.empty
    getArcs :: NSGraph lb -> ArcSet lb
getArcs      = NSGraph lb -> ArcSet lb
forall lb. NSGraph lb -> ArcSet lb
statements 
    setArcs :: NSGraph lb -> ArcSet lb -> NSGraph lb
setArcs NSGraph lb
g ArcSet lb
as = NSGraph lb
g { statements :: ArcSet lb
statements=ArcSet lb
as }

-- | The '<>' operation uses 'merge' rather than 'addGraphs'.
instance (Label lb) => Semigroup (NSGraph lb) where
    <> :: NSGraph lb -> NSGraph lb -> NSGraph lb
(<>) = NSGraph lb -> NSGraph lb -> NSGraph lb
forall lb. Label lb => NSGraph lb -> NSGraph lb -> NSGraph lb
merge

-- | The 'mappend' operation uses the Semigroup instance
--   (so 'merge' rather than 'addGraphs').
instance (Label lb) => Monoid (NSGraph lb) where
    mempty :: NSGraph lb
mempty  = NSGraph lb
forall (lg :: * -> *) lb. LDGraph lg lb => lg lb
emptyGraph
#if !(MIN_VERSION_base(4, 11, 0))
    mappend = (<>)
#endif

-- fmapNSGraph :: (Ord lb1, Ord lb2) => (lb1 -> lb2) -> NSGraph lb1 -> NSGraph lb2
  
-- | 'fmap' for 'NSGraph' instances.
fmapNSGraph :: (Ord lb) => (lb -> lb) -> NSGraph lb -> NSGraph lb
fmapNSGraph :: (lb -> lb) -> NSGraph lb -> NSGraph lb
fmapNSGraph lb -> lb
f (NSGraph NamespaceMap
ns FormulaMap lb
fml ArcSet lb
stmts) = 
    NamespaceMap -> FormulaMap lb -> ArcSet lb -> NSGraph lb
forall lb. NamespaceMap -> FormulaMap lb -> ArcSet lb -> NSGraph lb
NSGraph NamespaceMap
ns ((lb -> lb) -> FormulaMap lb -> FormulaMap lb
forall a. Ord a => (a -> a) -> FormulaMap a -> FormulaMap a
fmapFormulaMap lb -> lb
f FormulaMap lb
fml) (((Arc lb -> Arc lb) -> ArcSet lb -> ArcSet lb
forall b a. Ord b => (a -> b) -> Set a -> Set b
S.map ((Arc lb -> Arc lb) -> ArcSet lb -> ArcSet lb)
-> (Arc lb -> Arc lb) -> ArcSet lb -> ArcSet lb
forall a b. (a -> b) -> a -> b
$ (lb -> lb) -> Arc lb -> Arc lb
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap lb -> lb
f) ArcSet lb
stmts)

{-
traverseNSGraph :: 
    (Applicative f, Ord a, Ord b) 
    => (a -> f b) -> NSGraph a -> f (NSGraph b)
-}

-- | 'Data.Traversable.traverse' for 'NSGraph' instances.
traverseNSGraph :: 
    (Applicative f, Ord a) 
    => (a -> f a) -> NSGraph a -> f (NSGraph a)
traverseNSGraph :: (a -> f a) -> NSGraph a -> f (NSGraph a)
traverseNSGraph a -> f a
f (NSGraph NamespaceMap
ns FormulaMap a
fml ArcSet a
stmts) = 
    NamespaceMap -> FormulaMap a -> ArcSet a -> NSGraph a
forall lb. NamespaceMap -> FormulaMap lb -> ArcSet lb -> NSGraph lb
NSGraph NamespaceMap
ns (FormulaMap a -> ArcSet a -> NSGraph a)
-> f (FormulaMap a) -> f (ArcSet a -> NSGraph a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (a -> f a) -> FormulaMap a -> f (FormulaMap a)
forall (f :: * -> *) a.
(Applicative f, Ord a) =>
(a -> f a) -> FormulaMap a -> f (FormulaMap a)
traverseFormulaMap a -> f a
f FormulaMap a
fml f (ArcSet a -> NSGraph a) -> f (ArcSet a) -> f (NSGraph a)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ((Arc a -> f (Arc a)) -> ArcSet a -> f (ArcSet a)
forall (f :: * -> *) b a.
(Applicative f, Ord b) =>
(a -> f b) -> Set a -> f (Set b)
traverseSet ((Arc a -> f (Arc a)) -> ArcSet a -> f (ArcSet a))
-> (Arc a -> f (Arc a)) -> ArcSet a -> f (ArcSet a)
forall a b. (a -> b) -> a -> b
$ (a -> f a) -> Arc a -> f (Arc a)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
Traversable.traverse a -> f a
f) ArcSet a
stmts

traverseSet ::
    (Applicative f, Ord b)
    => (a -> f b) -> S.Set a -> f (S.Set b)
traverseSet :: (a -> f b) -> Set a -> f (Set b)
traverseSet a -> f b
f = (a -> f (Set b) -> f (Set b)) -> f (Set b) -> Set a -> f (Set b)
forall a b. (a -> b -> b) -> b -> Set a -> b
S.foldr a -> f (Set b) -> f (Set b)
cons (Set b -> f (Set b)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Set b
forall a. Set a
S.empty)
    where
      cons :: a -> f (Set b) -> f (Set b)
cons a
x f (Set b)
s = b -> Set b -> Set b
forall a. Ord a => a -> Set a -> Set a
S.insert (b -> Set b -> Set b) -> f b -> f (Set b -> Set b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> f b
f a
x f (Set b -> Set b) -> f (Set b) -> f (Set b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> f (Set b)
s

instance (Label lb) => Eq (NSGraph lb) where
    == :: NSGraph lb -> NSGraph lb -> Bool
(==) = NSGraph lb -> NSGraph lb -> Bool
forall lb. Label lb => NSGraph lb -> NSGraph lb -> Bool
grEq

-- The namespaces are not used in the ordering since this could
-- lead to identical graphs not being considered the same when
-- ordering.
--
instance (Label lb) => Ord (NSGraph lb) where
    (NSGraph NamespaceMap
_ FormulaMap lb
fml1 ArcSet lb
stmts1) compare :: NSGraph lb -> NSGraph lb -> Ordering
`compare` (NSGraph NamespaceMap
_ FormulaMap lb
fml2 ArcSet lb
stmts2) =
        (FormulaMap lb
fml1,ArcSet lb
stmts1) (FormulaMap lb, ArcSet lb)
-> (FormulaMap lb, ArcSet lb) -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` (FormulaMap lb
fml2,ArcSet lb
stmts2)

instance (Label lb) => Show (NSGraph lb) where
    show :: NSGraph lb -> String
show     = String -> NSGraph lb -> String
forall lb. Label lb => String -> NSGraph lb -> String
grShow String
""
    showList :: [NSGraph lb] -> ShowS
showList = String -> [NSGraph lb] -> ShowS
forall lb. Label lb => String -> [NSGraph lb] -> ShowS
grShowList String
""

-- | Retrieve the namespace map in the graph.
getNamespaces :: NSGraph lb -> NamespaceMap
getNamespaces :: NSGraph lb -> NamespaceMap
getNamespaces = NSGraph lb -> NamespaceMap
forall lb. NSGraph lb -> NamespaceMap
namespaces

-- | Replace the namespace information in the graph.
setNamespaces      :: NamespaceMap -> NSGraph lb -> NSGraph lb
setNamespaces :: NamespaceMap -> NSGraph lb -> NSGraph lb
setNamespaces NamespaceMap
ns NSGraph lb
g = NSGraph lb
g { namespaces :: NamespaceMap
namespaces=NamespaceMap
ns }

-- | Retrieve the formulae in the graph.
getFormulae :: NSGraph lb -> FormulaMap lb
getFormulae :: NSGraph lb -> FormulaMap lb
getFormulae = NSGraph lb -> FormulaMap lb
forall lb. NSGraph lb -> FormulaMap lb
formulae

-- | Replace the formulae in the graph.
setFormulae      :: FormulaMap lb -> NSGraph lb -> NSGraph lb
setFormulae :: FormulaMap lb -> NSGraph lb -> NSGraph lb
setFormulae FormulaMap lb
fs NSGraph lb
g = NSGraph lb
g { formulae :: FormulaMap lb
formulae=FormulaMap lb
fs }

-- | Find a formula in the graph, if it exists.
getFormula     :: (Label lb) => NSGraph lb -> lb -> Maybe (NSGraph lb)
-- getFormula g l = fmap formGraph $ M.lookup l (formulae g)
getFormula :: NSGraph lb -> lb -> Maybe (NSGraph lb)
getFormula NSGraph lb
g lb
l = lb -> Map lb (NSGraph lb) -> Maybe (NSGraph lb)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup lb
l (NSGraph lb -> Map lb (NSGraph lb)
forall lb. NSGraph lb -> FormulaMap lb
formulae NSGraph lb
g)

-- | Add (or replace) a formula.
setFormula     :: (Label lb) => Formula lb -> NSGraph lb -> NSGraph lb
-- setFormula f g = g { formulae = M.insert (formLabel f) f (formulae g) }
setFormula :: Formula lb -> NSGraph lb -> NSGraph lb
setFormula (Formula lb
fn NSGraph lb
fg) NSGraph lb
g = NSGraph lb
g { formulae :: FormulaMap lb
formulae = lb -> NSGraph lb -> FormulaMap lb -> FormulaMap lb
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert lb
fn NSGraph lb
fg (NSGraph lb -> FormulaMap lb
forall lb. NSGraph lb -> FormulaMap lb
formulae NSGraph lb
g) }

{-|
Add an arc to the graph. It does not relabel any blank nodes in the input arc,
nor does it change the namespace map, 
but it does ensure that the arc is unknown before adding it.
-}
addArc :: (Label lb) => Arc lb -> NSGraph lb -> NSGraph lb
addArc :: Arc lb -> NSGraph lb -> NSGraph lb
addArc Arc lb
ar = (ArcSet lb -> ArcSet lb) -> NSGraph lb -> NSGraph lb
forall (lg :: * -> *) lb.
LDGraph lg lb =>
(ArcSet lb -> ArcSet lb) -> lg lb -> lg lb
update (Arc lb -> ArcSet lb -> ArcSet lb
forall a. Ord a => a -> Set a -> Set a
S.insert Arc lb
ar)

grShowList :: (Label lb) => String -> [NSGraph lb] -> String -> String
grShowList :: String -> [NSGraph lb] -> ShowS
grShowList String
_ []     = String -> ShowS
showString String
"[no graphs]"
grShowList String
p (NSGraph lb
g:[NSGraph lb]
gs) = Char -> ShowS
showChar Char
'[' ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString (String -> NSGraph lb -> String
forall lb. Label lb => String -> NSGraph lb -> String
grShow String
pp NSGraph lb
g) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [NSGraph lb] -> ShowS
forall lb. Label lb => [NSGraph lb] -> ShowS
showl [NSGraph lb]
gs
    where
        showl :: [NSGraph lb] -> ShowS
showl []     = Char -> ShowS
showChar Char
']' -- showString $ "\n" ++ p ++ "]"
        showl (NSGraph lb
h:[NSGraph lb]
hs) = String -> ShowS
showString (String
",\n "String -> ShowS
forall a. [a] -> [a] -> [a]
++String
pString -> ShowS
forall a. [a] -> [a] -> [a]
++String -> NSGraph lb -> String
forall lb. Label lb => String -> NSGraph lb -> String
grShow String
pp NSGraph lb
h) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [NSGraph lb] -> ShowS
showl [NSGraph lb]
hs
        pp :: String
pp           = Char
' 'Char -> ShowS
forall a. a -> [a] -> [a]
:String
p

grShow   :: (Label lb) => String -> NSGraph lb -> String
grShow :: String -> NSGraph lb -> String
grShow String
p NSGraph lb
g =
    String
"Graph, formulae: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
showForm String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++
    String
p String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"arcs: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String -> NSGraph lb -> String
forall lb. Label lb => String -> NSGraph lb -> String
showArcs String
p NSGraph lb
g
    where
        showForm :: String
showForm = (LookupFormula lb (NSGraph lb) -> String)
-> [LookupFormula lb (NSGraph lb)] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((String
pp String -> ShowS
forall a. [a] -> [a] -> [a]
++) ShowS
-> (LookupFormula lb (NSGraph lb) -> String)
-> LookupFormula lb (NSGraph lb)
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LookupFormula lb (NSGraph lb) -> String
forall a. Show a => a -> String
show) [LookupFormula lb (NSGraph lb)]
fml
        fml :: [LookupFormula lb (NSGraph lb)]
fml = ((lb, NSGraph lb) -> LookupFormula lb (NSGraph lb))
-> [(lb, NSGraph lb)] -> [LookupFormula lb (NSGraph lb)]
forall a b. (a -> b) -> [a] -> [b]
map ((lb -> NSGraph lb -> LookupFormula lb (NSGraph lb))
-> (lb, NSGraph lb) -> LookupFormula lb (NSGraph lb)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry lb -> NSGraph lb -> LookupFormula lb (NSGraph lb)
forall lb gr. lb -> gr -> LookupFormula lb gr
Formula) ([(lb, NSGraph lb)] -> [LookupFormula lb (NSGraph lb)])
-> [(lb, NSGraph lb)] -> [LookupFormula lb (NSGraph lb)]
forall a b. (a -> b) -> a -> b
$ Map lb (NSGraph lb) -> [(lb, NSGraph lb)]
forall k a. Map k a -> [(k, a)]
M.assocs (NSGraph lb -> Map lb (NSGraph lb)
forall lb. NSGraph lb -> FormulaMap lb
getFormulae NSGraph lb
g) -- NOTE: want to just show 'name :- graph'
        pp :: String
pp = String
"\n    " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
p

showArcs :: (Label lb) => String -> NSGraph lb -> String
showArcs :: String -> NSGraph lb -> String
showArcs String
p NSGraph lb
g = (Arc lb -> ShowS) -> String -> Set (Arc lb) -> String
forall a b. (a -> b -> b) -> b -> Set a -> b
S.foldr (String -> ShowS
forall a. [a] -> [a] -> [a]
(++) (String -> ShowS) -> (Arc lb -> String) -> Arc lb -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
pp String -> ShowS
forall a. [a] -> [a] -> [a]
++) ShowS -> (Arc lb -> String) -> Arc lb -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Arc lb -> String
forall a. Show a => a -> String
show) String
"" (NSGraph lb -> Set (Arc lb)
forall (lg :: * -> *) lb. LDGraph lg lb => lg lb -> ArcSet lb
getArcs NSGraph lb
g)
    where
        pp :: String
pp = String
"\n    " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
p

-- | Graph equality.
grEq :: (Label lb) => NSGraph lb -> NSGraph lb -> Bool
grEq :: NSGraph lb -> NSGraph lb -> Bool
grEq NSGraph lb
g1 = (Bool, LabelMap (ScopedLabel lb)) -> Bool
forall a b. (a, b) -> a
fst ((Bool, LabelMap (ScopedLabel lb)) -> Bool)
-> (NSGraph lb -> (Bool, LabelMap (ScopedLabel lb)))
-> NSGraph lb
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
forall lb.
Label lb =>
NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
grMatchMap NSGraph lb
g1

-- | Match graphs, returning `True` if they are equivalent,
-- with a map of labels to equivalence class identifiers
-- (see 'graphMatch' for further details).
grMatchMap :: (Label lb) =>
    NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
grMatchMap :: NSGraph lb -> NSGraph lb -> (Bool, LabelMap (ScopedLabel lb))
grMatchMap NSGraph lb
g1 NSGraph lb
g2 =
    (lb -> lb -> Bool)
-> ArcSet lb -> ArcSet lb -> (Bool, LabelMap (ScopedLabel lb))
forall lb.
Label lb =>
(lb -> lb -> Bool)
-> ArcSet lb -> ArcSet lb -> (Bool, LabelMap (ScopedLabel lb))
graphMatch lb -> lb -> Bool
matchable (NSGraph lb -> ArcSet lb
forall (lg :: * -> *) lb. LDGraph lg lb => lg lb -> ArcSet lb
getArcs NSGraph lb
g1) (NSGraph lb -> ArcSet lb
forall (lg :: * -> *) lb. LDGraph lg lb => lg lb -> ArcSet lb
getArcs NSGraph lb
g2)
    where
        matchable :: lb -> lb -> Bool
matchable lb
l1 lb
l2 = NSGraph lb -> lb -> Maybe (NSGraph lb)
forall k. Ord k => NSGraph k -> k -> Maybe (NSGraph k)
mapFormula NSGraph lb
g1 lb
l1 Maybe (NSGraph lb) -> Maybe (NSGraph lb) -> Bool
forall a. Eq a => a -> a -> Bool
== NSGraph lb -> lb -> Maybe (NSGraph lb)
forall k. Ord k => NSGraph k -> k -> Maybe (NSGraph k)
mapFormula NSGraph lb
g2 lb
l2
        -- hmmm, if we compare the formula, rather then graph,
        -- a lot of tests fail (when the formulae are named by blank
        -- nodes). Presumably because the quality check for Formula forces
        -- the label to be identical, which it needn't be with bnodes
        -- for the match to hold.
        -- mapFormula g l  = M.lookup l (getFormulae g)
        -- mapFormula g l  = fmap formGraph $ M.lookup l (getFormulae g)
        -- the above discussion is hopefully moot now storing graph directly
        mapFormula :: NSGraph k -> k -> Maybe (NSGraph k)
mapFormula NSGraph k
g k
l  = k -> Map k (NSGraph k) -> Maybe (NSGraph k)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup k
l (NSGraph k -> Map k (NSGraph k)
forall lb. NSGraph lb -> FormulaMap lb
getFormulae NSGraph k
g)

-- |Merge RDF graphs, renaming blank and query variable nodes as
--  needed to neep variable nodes from the two graphs distinct in
--  the resulting graph.
--        
--  Currently formulae are not guaranteed to be preserved across a
--  merge.
--        
merge :: (Label lb) => NSGraph lb -> NSGraph lb -> NSGraph lb
merge :: NSGraph lb -> NSGraph lb -> NSGraph lb
merge NSGraph lb
gr1 NSGraph lb
gr2 =
    let bn1 :: [lb]
bn1   = Set lb -> [lb]
forall a. Set a -> [a]
S.toList (Set lb -> [lb]) -> Set lb -> [lb]
forall a b. (a -> b) -> a -> b
$ (lb -> Bool) -> NSGraph lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> NSGraph lb -> Set lb
allLabels lb -> Bool
forall lb. Label lb => lb -> Bool
labelIsVar NSGraph lb
gr1
        bn2 :: [lb]
bn2   = Set lb -> [lb]
forall a. Set a -> [a]
S.toList (Set lb -> [lb]) -> Set lb -> [lb]
forall a b. (a -> b) -> a -> b
$ (lb -> Bool) -> NSGraph lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> NSGraph lb -> Set lb
allLabels lb -> Bool
forall lb. Label lb => lb -> Bool
labelIsVar NSGraph lb
gr2
        dupbn :: [lb]
dupbn = [lb] -> [lb] -> [lb]
forall a. Eq a => [a] -> [a] -> [a]
intersect [lb]
bn1 [lb]
bn2
        allbn :: [lb]
allbn = [lb] -> [lb] -> [lb]
forall a. Eq a => [a] -> [a] -> [a]
union [lb]
bn1 [lb]
bn2
    in NSGraph lb -> NSGraph lb -> NSGraph lb
forall (lg :: * -> *) lb.
(LDGraph lg lb, Ord lb) =>
lg lb -> lg lb -> lg lb
addGraphs NSGraph lb
gr1 ([lb] -> [lb] -> (lb -> lb) -> NSGraph lb -> NSGraph lb
forall lb.
Label lb =>
[lb] -> [lb] -> (lb -> lb) -> NSGraph lb -> NSGraph lb
remapLabels [lb]
dupbn [lb]
allbn lb -> lb
forall a. a -> a
id NSGraph lb
gr2)

-- |Return list of all labels (including properties) in the graph
--  satisfying a supplied filter predicate. This routine
--  includes the labels in any formulae.
allLabels :: (Label lb) => (lb -> Bool) -> NSGraph lb -> S.Set lb
allLabels :: (lb -> Bool) -> NSGraph lb -> Set lb
allLabels lb -> Bool
p NSGraph lb
gr = (lb -> Bool) -> Set lb -> Set lb
forall a. (a -> Bool) -> Set a -> Set a
S.filter lb -> Bool
p ((lb -> Bool) -> Set lb -> Set lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> Set lb -> Set lb -> Set lb
unionNodes lb -> Bool
p ((lb -> Bool) -> NSGraph lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> NSGraph lb -> Set lb
formulaNodes lb -> Bool
p NSGraph lb
gr) (NSGraph lb -> Set lb
forall (lg :: * -> *) lb.
(LDGraph lg lb, Ord lb) =>
lg lb -> Set lb
labels NSGraph lb
gr) ) 
                 
{- TODO: is the leading 'filter p' needed in allLabels?
-}

-- |Return list of all subjects and objects in the graph
--  satisfying a supplied filter predicate.
allNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> S.Set lb
allNodes :: (lb -> Bool) -> NSGraph lb -> Set lb
allNodes lb -> Bool
p = (lb -> Bool) -> Set lb -> Set lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> Set lb -> Set lb -> Set lb
unionNodes lb -> Bool
p Set lb
forall a. Set a
S.empty (Set lb -> Set lb)
-> (NSGraph lb -> Set lb) -> NSGraph lb -> Set lb
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NSGraph lb -> Set lb
forall (lg :: * -> *) lb.
(LDGraph lg lb, Ord lb) =>
lg lb -> Set lb
nodes

-- | List all nodes in graph formulae satisfying a supplied predicate
formulaNodes :: (Label lb) => (lb -> Bool) -> NSGraph lb -> S.Set lb
formulaNodes :: (lb -> Bool) -> NSGraph lb -> Set lb
formulaNodes lb -> Bool
p NSGraph lb
gr = (Set lb -> Set lb -> Set lb) -> Set lb -> [Set lb] -> Set lb
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' ((lb -> Bool) -> Set lb -> Set lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> Set lb -> Set lb -> Set lb
unionNodes lb -> Bool
p) Set lb
fkeys ((NSGraph lb -> Set lb) -> [NSGraph lb] -> [Set lb]
forall a b. (a -> b) -> [a] -> [b]
map ((lb -> Bool) -> NSGraph lb -> Set lb
forall lb. Label lb => (lb -> Bool) -> NSGraph lb -> Set lb
allLabels lb -> Bool
p) [NSGraph lb]
fvals)
    where
        fm :: FormulaMap lb
fm    = NSGraph lb -> FormulaMap lb
forall lb. NSGraph lb -> FormulaMap lb
formulae NSGraph lb
gr
        -- fvals = map formGraph $ M.elems fm
        fvals :: [NSGraph lb]
fvals = FormulaMap lb -> [NSGraph lb]
forall k a. Map k a -> [a]
M.elems FormulaMap lb
fm
        -- TODO: can this conversion be improved?
        fkeys :: Set lb
fkeys = (lb -> Bool) -> Set lb -> Set lb
forall a. (a -> Bool) -> Set a -> Set a
S.filter lb -> Bool
p (Set lb -> Set lb) -> Set lb -> Set lb
forall a b. (a -> b) -> a -> b
$ [lb] -> Set lb
forall a. Ord a => [a] -> Set a
S.fromList ([lb] -> Set lb) -> [lb] -> Set lb
forall a b. (a -> b) -> a -> b
$ FormulaMap lb -> [lb]
forall k a. Map k a -> [k]
M.keys FormulaMap lb
fm

-- | Helper to filter variable nodes and merge with those found so far
unionNodes :: (Label lb) => (lb -> Bool) -> S.Set lb -> S.Set lb -> S.Set lb
unionNodes :: (lb -> Bool) -> Set lb -> Set lb -> Set lb
unionNodes lb -> Bool
p Set lb
ls1 Set lb
ls2 = Set lb
ls1 Set lb -> Set lb -> Set lb
forall a. Ord a => Set a -> Set a -> Set a
`S.union` (lb -> Bool) -> Set lb -> Set lb
forall a. (a -> Bool) -> Set a -> Set a
S.filter lb -> Bool
p Set lb
ls2

-- TODO: use S.Set lb rather than [lb] in the following

-- |Remap selected nodes in graph.
--
--  This is the node renaming operation that prevents graph-scoped
--  variable nodes from being merged when two graphs are merged.
remapLabels ::
    (Label lb)
    => [lb] -- ^ variable nodes to be renamed (@dupbn@)
    -> [lb] -- ^ variable nodes used that must be avoided (@allbn@)
    -> (lb -> lb) -- ^ node conversion function that is applied to nodes
    -- from @dupbn@ in the graph that are to be replaced by
    -- new blank nodes.  If no such conversion is required,
    -- supply @id@.  The function 'makeBlank' can be used to convert
    -- RDF query nodes into RDF blank nodes.
    -> NSGraph lb -- ^ graph in which nodes are to be renamed
    -> NSGraph lb

remapLabels :: [lb] -> [lb] -> (lb -> lb) -> NSGraph lb -> NSGraph lb
remapLabels [lb]
dupbn [lb]
allbn lb -> lb
cnvbn =
    (lb -> lb) -> NSGraph lb -> NSGraph lb
forall lb. Ord lb => (lb -> lb) -> NSGraph lb -> NSGraph lb
fmapNSGraph ([lb] -> [lb] -> (lb -> lb) -> lb -> lb
forall lb. Label lb => [lb] -> [lb] -> (lb -> lb) -> lb -> lb
mapnode [lb]
dupbn [lb]
allbn lb -> lb
cnvbn)

-- |Externally callable function to construct a list of (old,new)
--  values to be used for graph label remapping.
--
remapLabelList ::
    (Label lb)
    => [lb] -- ^ labels to be remaped
    -> [lb] -- ^ labels to be avoided by the remapping
    -> [(lb,lb)]
remapLabelList :: [lb] -> [lb] -> [(lb, lb)]
remapLabelList [lb]
remap [lb]
avoid = [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
forall lb.
Label lb =>
[lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
maplist [lb]
remap [lb]
avoid lb -> lb
forall a. a -> a
id []

-- | Remap a single graph node.
--
--  If the node is not one of those to be remapped,
--  the supplied value is returned unchanged.
mapnode ::
    (Label lb) => [lb] -> [lb] -> (lb -> lb) -> lb -> lb
mapnode :: [lb] -> [lb] -> (lb -> lb) -> lb -> lb
mapnode [lb]
dupbn [lb]
allbn lb -> lb
cnvbn lb
nv =
    lb -> lb -> Map lb lb -> lb
forall k a. Ord k => a -> k -> Map k a -> a
M.findWithDefault lb
nv lb
nv (Map lb lb -> lb) -> Map lb lb -> lb
forall a b. (a -> b) -> a -> b
$ [(lb, lb)] -> Map lb lb
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(lb, lb)] -> Map lb lb) -> [(lb, lb)] -> Map lb lb
forall a b. (a -> b) -> a -> b
$ [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
forall lb.
Label lb =>
[lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
maplist [lb]
dupbn [lb]
allbn lb -> lb
cnvbn []

-- | Construct a list of (oldnode,newnode) values to be used for
--  graph label remapping.  The function operates recursively, adding
--  new nodes generated to the accumulator and also to the
--  list of nodes to be avoided.
maplist ::
    (Label lb) 
    => [lb]       -- ^ oldnode values
    -> [lb]       -- ^ nodes to be avoided
    -> (lb -> lb) 
    -> [(lb,lb)]  -- ^ accumulator
    -> [(lb,lb)]
maplist :: [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
maplist []         [lb]
_     lb -> lb
_     [(lb, lb)]
mapbn = [(lb, lb)]
mapbn
maplist (lb
dn:[lb]
dupbn) [lb]
allbn lb -> lb
cnvbn [(lb, lb)]
mapbn = [lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
forall lb.
Label lb =>
[lb] -> [lb] -> (lb -> lb) -> [(lb, lb)] -> [(lb, lb)]
maplist [lb]
dupbn [lb]
allbn' lb -> lb
cnvbn [(lb, lb)]
mapbn'
    where
        dnmap :: lb
dnmap  = lb -> [lb] -> lb
forall lb. Label lb => lb -> [lb] -> lb
newNode (lb -> lb
cnvbn lb
dn) [lb]
allbn
        mapbn' :: [(lb, lb)]
mapbn' = (lb
dn,lb
dnmap)(lb, lb) -> [(lb, lb)] -> [(lb, lb)]
forall a. a -> [a] -> [a]
:[(lb, lb)]
mapbn
        allbn' :: [lb]
allbn' = lb
dnmaplb -> [lb] -> [lb]
forall a. a -> [a] -> [a]
:[lb]
allbn

--  TODO: optimize this for common case @nnn@ and @_nnn@:
--    always generate @_nnn@ and keep track of last allocated
--

-- |Given a node and a list of existing nodes, find a new node for
--  the supplied node that does not clash with any existing node.
--  (Generates an non-terminating list of possible replacements, and
--  picks the first one that isn't already in use.)
--
newNode :: (Label lb) => lb -> [lb] -> lb
newNode :: lb -> [lb] -> lb
newNode lb
dn [lb]
existnodes =
    [lb] -> lb
forall a. [a] -> a
head ([lb] -> lb) -> [lb] -> lb
forall a b. (a -> b) -> a -> b
$ lb -> [lb] -> [lb]
forall lb. Label lb => lb -> [lb] -> [lb]
newNodes lb
dn [lb]
existnodes

-- |Given a node and a list of existing nodes, generate a list of new
--  nodes for the supplied node that do not clash with any existing node.
newNodes :: (Label lb) => lb -> [lb] -> [lb]
newNodes :: lb -> [lb] -> [lb]
newNodes lb
dn [lb]
existnodes =
    (lb -> Bool) -> [lb] -> [lb]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (lb -> Bool) -> lb -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (lb -> [lb] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [lb]
existnodes)) ([lb] -> [lb]) -> [lb] -> [lb]
forall a b. (a -> b) -> a -> b
$ (String, Word32) -> [lb]
forall lb. Label lb => (String, Word32) -> [lb]
trynodes (lb -> (String, Word32)
forall lb. Label lb => lb -> (String, Word32)
noderootindex lb
dn)

{- 

For now go with a 32-bit integer (since Int on my machine uses 32-bit
values). We could instead use a Whole class constraint from
Numeric.Natural (in semigroups), but it is probably better to
specialize here. The idea for using Word<X> rather than Int is to
make it obvious that we are only interested in values >= 0.

-}

noderootindex :: (Label lb) => lb -> (String, Word32)
noderootindex :: lb -> (String, Word32)
noderootindex lb
dn = (String
nh,Word32
nx) where
    (String
nh,String
nt) = String -> (String, String)
splitnodeid (String -> (String, String)) -> String -> (String, String)
forall a b. (a -> b) -> a -> b
$ lb -> String
forall lb. Label lb => lb -> String
getLocal lb
dn
    nx :: Word32
nx      = if String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
nt then Word32
0 else String -> Word32
forall a. Read a => String -> a
read String
nt

splitnodeid :: String -> (String,String)
splitnodeid :: String -> (String, String)
splitnodeid = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isDigit

trynodes :: (Label lb) => (String, Word32) -> [lb]
trynodes :: (String, Word32) -> [lb]
trynodes (String
nr,Word32
nx) = [ String -> lb
forall lb. Label lb => String -> lb
makeLabel (String
nrString -> ShowS
forall a. [a] -> [a] -> [a]
++Word32 -> String
forall a. Show a => a -> String
show Word32
n) | Word32
n <- (Word32 -> Word32) -> Word32 -> [Word32]
forall a. (a -> a) -> a -> [a]
iterate (Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
+Word32
1) Word32
nx ]

{-
trybnodes :: (Label lb) => (String,Int) -> [lb]
trybnodes (nr,nx) = [ makeLabel (nr++show n) | n <- iterate (+1) nx ]
-}

-- | Memory-based RDF graph type

type RDFGraph = NSGraph RDFLabel

-- |Create a new RDF graph from a supplied set of arcs.
--
-- This version will attempt to fill up the namespace map
-- of the graph based on the input labels (including datatypes
-- on literals). For faster
-- creation of a graph you can use:
--
-- > emptyRDFGraph { statements = arcs }
--
-- which is how this routine was defined in version @0.3.1.1@
-- and earlier.
--
toRDFGraph :: 
    RDFArcSet
    -> RDFGraph
toRDFGraph :: RDFArcSet -> RDFGraph
toRDFGraph RDFArcSet
arcs = 
  let lbls :: Set RDFLabel
lbls = (RDFTriple -> [RDFLabel]) -> RDFArcSet -> Set RDFLabel
forall b a. Ord b => (a -> [b]) -> Set a -> Set b
getComponents RDFTriple -> [RDFLabel]
forall lb. Arc lb -> [lb]
arcLabels RDFArcSet
arcs
      
      getNS :: RDFLabel -> Maybe ScopedName
getNS (Res ScopedName
s) = ScopedName -> Maybe ScopedName
forall a. a -> Maybe a
Just ScopedName
s
      getNS (TypedLit Text
_ ScopedName
dt) = ScopedName -> Maybe ScopedName
forall a. a -> Maybe a
Just ScopedName
dt
      getNS RDFLabel
_ = Maybe ScopedName
forall a. Maybe a
Nothing

      ns :: [Namespace]
ns = (RDFLabel -> Maybe Namespace) -> [RDFLabel] -> [Namespace]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((ScopedName -> Namespace) -> Maybe ScopedName -> Maybe Namespace
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ScopedName -> Namespace
getScopeNamespace (Maybe ScopedName -> Maybe Namespace)
-> (RDFLabel -> Maybe ScopedName) -> RDFLabel -> Maybe Namespace
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RDFLabel -> Maybe ScopedName
getNS) ([RDFLabel] -> [Namespace]) -> [RDFLabel] -> [Namespace]
forall a b. (a -> b) -> a -> b
$ Set RDFLabel -> [RDFLabel]
forall a. Set a -> [a]
S.toList Set RDFLabel
lbls
      nsmap :: NamespaceMap
nsmap = (NamespaceMap -> Namespace -> NamespaceMap)
-> NamespaceMap -> [Namespace] -> NamespaceMap
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl'
              (\NamespaceMap
m Namespace
ins -> let (Maybe Text
p,URI
u) = Namespace -> (Maybe Text, URI)
getNamespaceTuple Namespace
ins
                         in (URI -> URI -> URI)
-> Maybe Text -> URI -> NamespaceMap -> NamespaceMap
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
M.insertWith ((URI -> URI) -> URI -> URI -> URI
forall a b. a -> b -> a
const URI -> URI
forall a. a -> a
id) Maybe Text
p URI
u NamespaceMap
m)
              NamespaceMap
emptyNamespaceMap [Namespace]
ns
  
  in RDFGraph
forall a. Monoid a => a
mempty { namespaces :: NamespaceMap
namespaces = NamespaceMap
nsmap, statements :: RDFArcSet
statements = RDFArcSet
arcs }

-- |Create a new, empty RDF graph (it is just 'mempty').
--
emptyRDFGraph :: RDFGraph
emptyRDFGraph :: RDFGraph
emptyRDFGraph = RDFGraph
forall a. Monoid a => a
mempty 

{-
-- |Update an RDF graph using a supplied list of arcs, keeping
--  prefix definitions and formula definitions from the original.
--
--  [[[TODO:  I think this may be redundant - the default graph
--  class has an update method which accepts a function to update
--  the arcs, not touching other parts of the graph value.]]]
updateRDFGraph :: RDFGraph -> [RDFTriple] -> RDFGraph
updateRDFGraph gr as = gr { statements=as }
-}

--------------------------------------------------------------------------------
--
--  Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin,
--    2011, 2012, 2013, 2014, 2015, 2016, 2018, 2020 Douglas Burke
--  All rights reserved.
--
--  This file is part of Swish.
--
--  Swish 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 2 of the License, or
--  (at your option) any later version.
--
--  Swish 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 Swish; if not, write to:
--    The Free Software Foundation, Inc.,
--    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--
--------------------------------------------------------------------------------