rdf4h 1.2.7 → 1.3.0
raw patch · 9 files changed
+160/−83 lines, 9 filesdep +ghc-primdep +networkdep ~network-uriPVP ok
version bump matches the API change (PVP)
Dependencies added: ghc-prim, network
Dependency ranges changed: network-uri
API changes (from Hackage documentation)
- Data.RDF.Query: removeDupes :: Triples -> Triples
- Data.RDF.Query: sortTriples :: Triples -> Triples
+ Data.RDF: uniqTriplesOf :: RDF rdf => rdf -> Triples
+ Data.RDF.MGraph: uniqTriplesOf :: RDF rdf => rdf -> Triples
+ Data.RDF.Query: expandNode :: PrefixMappings -> Node -> Node
+ Data.RDF.Query: expandTriple :: PrefixMappings -> Triple -> Triple
+ Data.RDF.Query: expandURI :: PrefixMappings -> Text -> Text
+ Data.RDF.TriplesGraph: uniqTriplesOf :: RDF rdf => rdf -> Triples
+ Data.RDF.Types: absolutizeUrl :: Maybe BaseUrl -> Maybe Text -> Text -> Text
+ Data.RDF.Types: isAbsoluteUri :: Text -> Bool
+ Data.RDF.Types: mkAbsoluteUrl :: Text -> Text -> Text
+ Data.RDF.Types: resolveQName :: Maybe BaseUrl -> Text -> PrefixMappings -> Maybe Text
+ Data.RDF.Types: uniqTriplesOf :: RDF rdf => rdf -> Triples
Files
- rdf4h.cabal +34/−6
- src/Data/RDF/MGraph.hs +6/−1
- src/Data/RDF/Namespace.hs +1/−2
- src/Data/RDF/Query.hs +44/−19
- src/Data/RDF/TriplesGraph.hs +8/−3
- src/Data/RDF/Types.hs +55/−3
- src/Text/RDF/RDF4H/ParserUtils.hs +4/−4
- src/Text/RDF/RDF4H/TurtleParser.hs +4/−41
- testsuite/tests/Test.hs +4/−4
rdf4h.cabal view
@@ -1,5 +1,5 @@ name: rdf4h-version: 1.2.7+version: 1.3.0 synopsis: A library for RDF processing in Haskell description: 'RDF for Haskell' is a library for working with RDF in Haskell.@@ -8,8 +8,8 @@ for triples containing a particular subject, predicate, or object, or selecting triples that satisfy an arbitrary predicate function. -author: Calvin Smith-copyright: (c) Calvin Smith, Rob Stewart+author: Calvin Smith, Rob Stewart, Slava Kravchenko+copyright: (c) Calvin Smith, Rob Stewart, Slava Kravchenko maintainer: Rob Stewart <robstewart@gmail.com> homepage: https://github.com/robstewart57/rdf4h bug-reports: https://github.com/robstewart57/rdf4h/issues@@ -35,6 +35,10 @@ description: Use HPC for tests default: True +flag network-uri+ description: Get Network.URI from the network-uri package+ default: True+ library exposed-modules: Data.RDF , Data.RDF.Namespace@@ -57,7 +61,15 @@ , text , unordered-containers , hashable- , network-uri++ if impl(ghc < 7.6)+ build-depends: ghc-prim++ if flag(network-uri)+ build-depends: network-uri >= 2.6, network >= 2.6+ else+ build-depends: network-uri < 2.6, network < 2.6+ other-modules: Text.RDF.RDF4H.ParserUtils , Text.RDF.RDF4H.Interact hs-source-dirs: src@@ -76,7 +88,15 @@ , containers , text , hashable- , network-uri++ if impl(ghc < 7.6)+ build-depends: ghc-prim++ if flag(network-uri)+ build-depends: network-uri >= 2.6, network >= 2.6+ else+ build-depends: network-uri < 2.6, network < 2.6+ hs-source-dirs: src extensions: BangPatterns RankNTypes ScopedTypeVariables MultiParamTypeClasses OverloadedStrings ghc-options: -Wall -fno-warn-unused-do-bind -funbox-strict-fields@@ -101,7 +121,15 @@ , knob , unordered-containers , hashable- , network-uri++ if impl(ghc < 7.6)+ build-depends: ghc-prim++ if flag(network-uri)+ build-depends: network-uri >= 2.6, network >= 2.6+ else+ build-depends: network-uri < 2.6, network < 2.6+ other-modules: Data.RDF , Data.RDF.Namespace , Data.RDF.MGraph
src/Data/RDF/MGraph.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TupleSections #-} -- |A simple graph implementation backed by 'Data.HashMap'. -module Data.RDF.MGraph(MGraph, empty, mkRdf, triplesOf, select, query)+module Data.RDF.MGraph(MGraph, empty, mkRdf, triplesOf, uniqTriplesOf, select, query) where @@ -40,6 +40,7 @@ empty = empty' mkRdf = mkRdf' triplesOf = triplesOf'+ uniqTriplesOf = uniqTriplesOf' select = select' query = query' @@ -105,6 +106,10 @@ triplesOf' :: MGraph -> Triples triplesOf' (MGraph ((spoMap, _), _, _)) = concatMap (uncurry tripsSubj) subjPredMaps where subjPredMaps = HashMap.toList spoMap++-- naive implementation for now+uniqTriplesOf' :: MGraph -> Triples+uniqTriplesOf' = nub . expandTriples tripsSubj :: Subject -> AdjacencyMap -> Triples tripsSubj s adjMap = concatMap (uncurry (tfsp s)) (HashMap.toList adjMap)
src/Data/RDF/Namespace.hs view
@@ -12,8 +12,7 @@ -- * Predefined namespace values rdf, rdfs, dc, dct, owl, xsd, skos, foaf, ex, ex2, standard_ns_mappings, ns_mappings-)-where+) where import qualified Data.Text as T import Data.RDF.Types
src/Data/RDF/Query.hs view
@@ -1,24 +1,25 @@- module Data.RDF.Query ( -- * Query functions- sortTriples, equalSubjects, equalPredicates, equalObjects,+ equalSubjects, equalPredicates, equalObjects, subjectOf, predicateOf, objectOf, isEmpty,- rdfContainsNode, tripleContainsNode, removeDupes,+ rdfContainsNode, tripleContainsNode, listSubjectsWithPredicate, listObjectsOfPredicate, -- * RDF graph functions- isIsomorphic, expandTriples, fromEither+ isIsomorphic, expandTriples, fromEither, + -- * Miscellaneous functions+ expandTriple, expandNode, expandURI+ ) where import Prelude hiding (pred) import Data.List import Data.RDF.Types---- |Answer the given list of triples in sorted order.-sortTriples :: Triples -> Triples-sortTriples = sort+import qualified Data.RDF.Namespace as NS (toPMList, uriOf, rdf)+import qualified Data.Text as T+import Data.Maybe (catMaybes) -- |Answer the subject node of the triple. {-# INLINE subjectOf #-}@@ -43,21 +44,24 @@ in elem True xs -- |Answer if triple contains node.+-- Note that it doesn't perform namespace expansion! tripleContainsNode :: Node -> Triple -> Bool {-# INLINE tripleContainsNode #-} tripleContainsNode node t = subjectOf t == node || predicateOf t == node || objectOf t == node - -- |Determine whether two triples have equal subjects.+-- Note that it doesn't perform namespace expansion! equalSubjects :: Triple -> Triple -> Bool equalSubjects (Triple s1 _ _) (Triple s2 _ _) = s1 == s2 -- |Determine whether two triples have equal predicates.+-- Note that it doesn't perform namespace expansion! equalPredicates :: Triple -> Triple -> Bool equalPredicates (Triple _ p1 _) (Triple _ p2 _) = p1 == p2 -- |Determine whether two triples have equal objects.+-- Note that it doesn't perform namespace expansion! equalObjects :: Triple -> Triple -> Bool equalObjects (Triple _ _ o1) (Triple _ _ o2) = o1 == o2 @@ -91,12 +95,6 @@ (Left err) -> error (show err) (Right rdf) -> rdf --- |Remove duplicate triples, returning unique triples. This --- function may return the triples in a different order than --- given.-removeDupes :: Triples -> Triples-removeDupes = map head . group . sort- -- |This determines if two RDF representations are equal regardless of blank -- node names, triple order and prefixes. In math terms, this is the \simeq -- latex operator, or ~=@@ -113,7 +111,34 @@ expandTriples' :: Triples -> Maybe BaseUrl -> PrefixMappings -> Triples -> Triples expandTriples' acc _ _ [] = acc expandTriples' acc baseURL prefixMaps (t:rest) = expandTriples' (normalize baseURL prefixMaps t : acc) baseURL prefixMaps rest- where normalize baseURL' prefixMaps' = expandPrefixes prefixMaps' . expandBaseUrl baseURL'- expandBaseUrl (Just _) triple' = triple'- expandBaseUrl Nothing triple' = triple'- expandPrefixes _ triple' = triple'+ where normalize baseURL' prefixMaps' = absolutizeTriple baseURL' . expandTriple prefixMaps'++-- |Expand the triple with the prefix map.+expandTriple :: PrefixMappings -> Triple -> Triple+expandTriple pms t = triple (expandNode pms $ subjectOf t) (expandNode pms $ predicateOf t) (expandNode pms $ objectOf t)++-- |Expand the node with the prefix map.+-- Only UNodes are expanded, other kinds of nodes are returned as-is.+expandNode :: PrefixMappings -> Node -> Node+expandNode pms (UNode n) = unode $ expandURI pms n+expandNode _ n' = n'++-- |Expand the URI with the prefix map.+-- Also expands "a" to "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".+expandURI :: PrefixMappings -> T.Text -> T.Text+expandURI _ "a" = T.append (NS.uriOf NS.rdf) "type"+expandURI pms' x = firstExpandedOrOriginal x $ catMaybes $ map (resourceTail x) (NS.toPMList pms')+ where resourceTail :: T.Text -> (T.Text, T.Text) -> Maybe T.Text+ resourceTail x' (p', u') = T.stripPrefix (T.append p' ":") x' >>= Just . T.append u'+ firstExpandedOrOriginal :: a -> [a] -> a+ firstExpandedOrOriginal orig' [] = orig'+ firstExpandedOrOriginal _ (e:_) = e++-- |Prefixes relative URIs in the triple with BaseUrl.+absolutizeTriple :: Maybe BaseUrl -> Triple -> Triple+absolutizeTriple base t = triple (absolutizeNode base $ subjectOf t) (absolutizeNode base $ predicateOf t) (absolutizeNode base $ objectOf t)++-- |Prepends BaseUrl to UNodes with relative URIs.+absolutizeNode :: Maybe BaseUrl -> Node -> Node+absolutizeNode (Just (BaseUrl b')) (UNode u') = unode $ mkAbsoluteUrl b' u'+absolutizeNode _ n = n
src/Data/RDF/TriplesGraph.hs view
@@ -8,7 +8,7 @@ -- functions of this graph (select, query) remove duplicates from their -- result triples (but triplesOf does not) since it is usually cheap -- to do so.-module Data.RDF.TriplesGraph(TriplesGraph, empty, mkRdf, triplesOf, select, query)+module Data.RDF.TriplesGraph(TriplesGraph, empty, mkRdf, triplesOf, uniqTriplesOf, select, query) where @@ -17,6 +17,7 @@ import Data.RDF.Namespace import Data.RDF.Query import Data.RDF.Types+import Data.List (nub) -- |A simple implementation of the 'RDF' type class that represents -- the graph internally as a list of triples.@@ -46,6 +47,7 @@ empty = empty' mkRdf = mkRdf' triplesOf = triplesOf'+ uniqTriplesOf = uniqTriplesOf' select = select' query = query' @@ -76,11 +78,14 @@ triplesOf' :: TriplesGraph -> Triples triplesOf' (TriplesGraph (ts, _, _)) = ts +uniqTriplesOf' :: TriplesGraph -> Triples+uniqTriplesOf' = nub . expandTriples+ select' :: TriplesGraph -> NodeSelector -> NodeSelector -> NodeSelector -> Triples-select' (TriplesGraph (ts, _, _)) s p o = removeDupes $ filter (matchSelect s p o) ts+select' g s p o = filter (matchSelect s p o) $ triplesOf g query' :: TriplesGraph -> Maybe Subject -> Maybe Predicate -> Maybe Object -> Triples-query' (TriplesGraph (ts, _, _)) s p o = removeDupes $ filter (matchPattern s p o) ts+query' g s p o = filter (matchPattern s p o) $ triplesOf g matchSelect :: NodeSelector -> NodeSelector -> NodeSelector -> Triple -> Bool matchSelect s p o t =
src/Data/RDF/Types.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveGeneric, OverloadedStrings #-} module Data.RDF.Types ( @@ -14,8 +14,11 @@ -- * Node query function isUNode,isLNode,isBNode, + -- * Miscellaneous+ resolveQName, absolutizeUrl, isAbsoluteUri, mkAbsoluteUrl,+ -- * RDF Type- RDF(baseUrl,prefixMappings,addPrefixMappings,empty,mkRdf,triplesOf,select,query),+ RDF(baseUrl,prefixMappings,addPrefixMappings,empty,mkRdf,triplesOf,uniqTriplesOf,select,query), -- * Parsing RDF RdfParser(parseString,parseFile,parseURL),@@ -41,6 +44,7 @@ import Data.Hashable(Hashable) import qualified Data.List as List import qualified Data.Map as Map+import qualified Network.URI as Network (isURI) ------------------- -- LValue and constructor functions@@ -86,7 +90,7 @@ -- node ('BNode'), or a literal node ('LNode'). data Node = - -- |An RDF URI reference. See+ -- |An RDF URI reference. URIs conform to the RFC3986 standard. See -- <http://www.w3.org/TR/rdf-concepts/#section-Graph-URIref> for more -- information. UNode !T.Text@@ -172,6 +176,10 @@ isLNode (LNode _) = True isLNode _ = False +{-# INLINE isAbsoluteUri #-}+isAbsoluteUri :: T.Text -> Bool+isAbsoluteUri = Network.isURI . T.unpack+ -- |A type class for ADTs that expose views to clients. class View a b where view :: a -> b@@ -206,8 +214,16 @@ mkRdf :: Triples -> Maybe BaseUrl -> PrefixMappings -> rdf -- |Return all triples in the RDF, as a list.+ --+ -- Note that this function returns a list of triples in the RDF as they+ -- were added, without removing duplicates and without expanding namespaces. triplesOf :: rdf -> Triples + -- |Return unique triples in the RDF, as a list.+ --+ -- This function performs namespace expansion and removal of duplicates.+ uniqTriplesOf :: rdf -> Triples+ -- |Select the triples in the RDF that match the given selectors. -- -- The three NodeSelector parameters are optional functions that match@@ -479,6 +495,42 @@ deriving (Eq, Ord) instance Show PrefixMapping where show (PrefixMapping (prefix, uri)) = printf "PrefixMapping (%s, %s)" (show prefix) (show uri)++-----------------+-- Miscellaneous helper functions used throughout the project++-- Resolve a prefix using the given prefix mappings and base URL. If the prefix is+-- empty, then the base URL will be used if there is a base URL and if the map+-- does not contain an entry for the empty prefix.+resolveQName :: Maybe BaseUrl -> T.Text -> PrefixMappings -> Maybe T.Text+resolveQName mbaseUrl prefix (PrefixMappings pms') =+ case (mbaseUrl, T.null prefix) of+ (Just (BaseUrl base), True) -> Just $ Map.findWithDefault base T.empty pms'+ (Nothing, True) -> Nothing+ (_, _ ) -> Map.lookup prefix pms'++-- Resolve a URL fragment found on the right side of a prefix mapping+-- by converting it to an absolute URL if possible.+absolutizeUrl :: Maybe BaseUrl -> Maybe T.Text -> T.Text -> T.Text+absolutizeUrl mbUrl mdUrl urlFrag =+ if isAbsoluteUri urlFrag then urlFrag else+ (case (mbUrl, mdUrl) of+ (Nothing, Nothing) -> urlFrag+ (Just (BaseUrl bUrl), Nothing) -> bUrl `T.append` urlFrag+ (Nothing, Just dUrl) -> if isHash urlFrag then+ dUrl `T.append` urlFrag else urlFrag+ (Just (BaseUrl bUrl), Just dUrl) -> (if isHash urlFrag then dUrl+ else bUrl)+ `T.append` urlFrag)+ where+ isHash bs' = bs' == "#"++{-# INLINE mkAbsoluteUrl #-}+-- Make an absolute URL by returning as is if already an absolute URL and otherwise+-- appending the URL to the given base URL.+mkAbsoluteUrl :: T.Text -> T.Text -> T.Text+mkAbsoluteUrl base url =+ if isAbsoluteUri url then url else base `T.append` url -----------------
src/Text/RDF/RDF4H/ParserUtils.hs view
@@ -6,7 +6,7 @@ import Network.URI import Network.HTTP-import Data.Char(intToDigit)+import Data.Char (intToDigit) import Data.Text.Encoding (decodeUtf8) import qualified Data.ByteString.Char8 as B import qualified Data.Text as T@@ -23,8 +23,8 @@ justTriples = map (fromMaybe (error "ParserUtils.justTriples")) . filter (/= Nothing) --- | pars contents at URL in to and 'RDF'-_parseURL :: RDF rdf => (T.Text -> Either ParseFailure rdf) -> String -> IO (Either ParseFailure rdf)+-- | Parse contents at URL into an 'RDF'.+_parseURL :: RDF rdf => (T.Text -> Either ParseFailure rdf) -> String -> IO (Either ParseFailure rdf) _parseURL parseFunc url = maybe (return (Left (ParseFailure $ "Unable to parse URL: " ++ url)))@@ -41,7 +41,7 @@ (2, 0, 0) -> return $ parseFunc (decodeUtf8 (rspBody res)) _ -> return (errResult $ "couldn't retrieve from URL: " ++ httpError res) --- | construct HTTP GET request.+-- | Construct HTTP GET request. request :: URI -> HTTPRequest B.ByteString request uri = Request { rqURI = uri, rqMethod = GET,
src/Text/RDF/RDF4H/TurtleParser.hs view
@@ -107,7 +107,9 @@ char ':' name <- option T.empty t_name (bUrl, _, _, pms, _, _, _, _) <- getState- return $ resolveQName bUrl pre pms `T.append` name+ case resolveQName bUrl pre pms of+ Just n -> return $ n `T.append` name+ Nothing -> error ("Cannot resolve QName prefix: " ++ T.unpack pre) t_subject :: GenParser ParseState () t_subject =@@ -398,48 +400,9 @@ in_range :: Char -> [(Char, Char)] -> Bool in_range c = any (\(c1, c2) -> c >= c1 && c <= c2) --- Resolve a prefix using the given prefix mappings and base URL. If the prefix is--- empty, then the base URL will be used if there is a base URL and if the map--- does not contain an entry for the empty prefix.-resolveQName :: Maybe BaseUrl -> T.Text -> PrefixMappings -> T.Text-resolveQName mbaseUrl prefix (PrefixMappings pms') =- case (mbaseUrl, T.null prefix) of- (Just (BaseUrl base), True) -> Map.findWithDefault base T.empty pms'- (Nothing, True) -> err1- (_, _ ) -> Map.findWithDefault err2 prefix pms'- where- err1 = error "Cannot resolve empty QName prefix to a Base URL."- err2 = error ("Cannot resolve QName prefix: " ++ T.unpack prefix)---- Resolve a URL fragment found on the right side of a prefix mapping by converting it to an absolute URL if possible.-absolutizeUrl :: Maybe BaseUrl -> Maybe T.Text -> T.Text -> T.Text-absolutizeUrl mbUrl mdUrl urlFrag =- if isAbsoluteUri urlFrag then urlFrag else- (case (mbUrl, mdUrl) of- (Nothing, Nothing) -> urlFrag- (Just (BaseUrl bUrl), Nothing) -> bUrl `T.append` urlFrag- (Nothing, Just dUrl) -> if isHash urlFrag then- dUrl `T.append` urlFrag else urlFrag- (Just (BaseUrl bUrl), Just dUrl) -> (if isHash urlFrag then dUrl- else bUrl)- `T.append` urlFrag)- where- isHash bs' = T.length bs' == 1 && T.head bs' == '#'--{-# INLINE isAbsoluteUri #-}-isAbsoluteUri :: T.Text -> Bool-isAbsoluteUri = T.isInfixOf (T.pack [':'])- newBaseUrl :: Maybe BaseUrl -> T.Text -> BaseUrl-newBaseUrl Nothing url = BaseUrl url+newBaseUrl Nothing url = BaseUrl url newBaseUrl (Just (BaseUrl bUrl)) url = BaseUrl $! mkAbsoluteUrl bUrl url--{-# INLINE mkAbsoluteUrl #-}--- Make an absolute URL by returning as is if already an absolute URL and otherwise--- appending the URL to the given base URL.-mkAbsoluteUrl :: T.Text -> T.Text -> T.Text-mkAbsoluteUrl base url =- if isAbsoluteUri url then url else base `T.append` url currBaseUrl :: GenParser ParseState (Maybe BaseUrl) currBaseUrl = getState >>= \(bUrl, _, _, _, _, _, _, _) -> return bUrl
testsuite/tests/Test.hs view
@@ -6,11 +6,11 @@ import qualified Data.RDF.MGraph_Test as MGraph import qualified Text.RDF.RDF4H.XmlParser_Test as XmlParser import qualified Text.RDF.RDF4H.TurtleParser_ConformanceTest as TurtleParser+import Data.RDF.GraphTestUtils -main :: IO () -main = defaultMain ( TriplesGraph.tests- ++ MGraph.tests+main :: IO ()+main = defaultMain ( graphTests "TriplesGraph" TriplesGraph.triplesOf' TriplesGraph.uniqTriplesOf' TriplesGraph.empty' TriplesGraph.mkRdf'+ ++ graphTests "MGraph" MGraph.triplesOf' MGraph.uniqTriplesOf' MGraph.empty' MGraph.mkRdf' ++ TurtleParser.tests ++ XmlParser.tests )-