XPath.hs revision 133498fca44f178d10e2eb6d965ce3442d2e2e32
97a9a944b5887e91042b019776c41d5dd74557aferikabeleModule : $Header$
fd9abdda70912b99b24e3bf1a38f26fde908a74cndDescription : XPath utilities
fd9abdda70912b99b24e3bf1a38f26fde908a74cndCopyright : (c) Christian Maeder, DFKI GmbH 2010
fd9abdda70912b99b24e3bf1a38f26fde908a74cndLicense : similar to LGPL, see HetCATS/LICENSE.txt or LIZENZ.txt
8c81ebbdeed67059e7a1106d7c617543e901c3abyoshikiMaintainer : Christian.Maeder@dfki.de
8c81ebbdeed67059e7a1106d7c617543e901c3abyoshikiStability : provisional
8c81ebbdeed67059e7a1106d7c617543e901c3abyoshikiPortability : portable
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletcXPath utilities independent of xml package.
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndreferences:
2e545ce2450a9953665f701bb05350f0d3f26275nd<http://www.fh-wedel.de/~si/HXmlToolbox/hxpath/diplomarbeit.pdf>
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen(modules XPathParser, XPathDataTypes)
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd(modules Text.XML.HXT.DOM.Unicode, Text.XML.HXT.Parser.XmlCharParser)
3f08db06526d6901aa08c110b5bc7dde6bc39905nd'ncName' from 'OWL.Parse' allows `+' in names. Unicode is not
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndfully supported. A qualified name is an ncName or two ncNames
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndseparated by a colon (different from OWL uris).
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd-- * data types and pretty printing (via show)
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd-- | axis specifier
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd = Ancestor Bool -- ^ or self?
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd | Attribute
8c81ebbdeed67059e7a1106d7c617543e901c3abyoshiki | Descendant Bool -- ^ or self?
8c81ebbdeed67059e7a1106d7c617543e901c3abyoshiki | Following Bool -- ^ sibling?
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd | Namespace
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd | Preceding Bool -- ^ sibling?
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd | Self deriving Show
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd-- | all possible values
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndallAxis :: [Axis]
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndallAxis = let bl = [True, False] in
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd [ Attribute
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd , Namespace
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd ++ [ Ancestor b | b <- bl ]
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd ++ [ Descendant b | b <- bl ]
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd ++ [ Following b | b <- bl ]
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd ++ [ Preceding b | b <- bl ]
f61ec47ec494705bd2d02b5b6fe021e5c193f2c8noodl-- | utility to show (constant) constructors as lower case strings
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndlowerShow :: Show a => a -> String
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndlowerShow = map toLower . show
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd-- | proper string representation (do not use show)
06ba4a61654b3763ad65f52283832ebf058fdf1csliveshowAxis :: Axis -> String
06ba4a61654b3763ad65f52283832ebf058fdf1csliveshowAxis a =
06ba4a61654b3763ad65f52283832ebf058fdf1cslive let s = takeWhile isAlpha $ lowerShow a
06ba4a61654b3763ad65f52283832ebf058fdf1cslive orSelf b = if b then s ++ "-or-self" else s
06ba4a61654b3763ad65f52283832ebf058fdf1cslive sibl b = if b then s ++ "-sibling" else s
06ba4a61654b3763ad65f52283832ebf058fdf1cslive in case a of
06ba4a61654b3763ad65f52283832ebf058fdf1cslive Ancestor c -> orSelf c
06ba4a61654b3763ad65f52283832ebf058fdf1cslive Descendant c -> orSelf c
06ba4a61654b3763ad65f52283832ebf058fdf1cslive Following c -> sibl c
06ba4a61654b3763ad65f52283832ebf058fdf1cslive Preceding c -> sibl c
06ba4a61654b3763ad65f52283832ebf058fdf1cslivedata NodeTest
06ba4a61654b3763ad65f52283832ebf058fdf1cslive = NameTest String -- ^ optional prefix and local part (possibly *)
06ba4a61654b3763ad65f52283832ebf058fdf1cslive | PI String -- ^ processing-instruction node type with optional literal
06ba4a61654b3763ad65f52283832ebf058fdf1cslive deriving Show
06ba4a61654b3763ad65f52283832ebf058fdf1cslive-- | all node types without processing-instruction
06ba4a61654b3763ad65f52283832ebf058fdf1cslivenodeTypes :: [NodeTest]
06ba4a61654b3763ad65f52283832ebf058fdf1cslivenodeTypes = [Node, Comment, Text]
06ba4a61654b3763ad65f52283832ebf058fdf1cslive-- | the processing-instruction string
06ba4a61654b3763ad65f52283832ebf058fdf1cslivepIS :: String
06ba4a61654b3763ad65f52283832ebf058fdf1cslivepIS = "processing-instruction"
06ba4a61654b3763ad65f52283832ebf058fdf1cslive-- | put parens arount a string
06ba4a61654b3763ad65f52283832ebf058fdf1csliveparen :: String -> String
06ba4a61654b3763ad65f52283832ebf058fdf1csliveparen = ('(' :) . (++ ")")
06ba4a61654b3763ad65f52283832ebf058fdf1cslive-- | proper string representation (do not use show)
82178a3043043b8813c0d7288a06ca1b7d110d4atakashishowNodeTest :: NodeTest -> String
06ba4a61654b3763ad65f52283832ebf058fdf1csliveshowNodeTest t = case t of
06ba4a61654b3763ad65f52283832ebf058fdf1cslive NameTest q -> q
e1e8390280254f7f0580d701e583f670643d4f3fnilgun PI s -> pIS ++ paren s
e1e8390280254f7f0580d701e583f670643d4f3fnilgun _ -> lowerShow t ++ paren ""
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | the stuff of a path between the slashes
e1e8390280254f7f0580d701e583f670643d4f3fnilgundata Step = Step Axis NodeTest [Expr] -- ^ with predicate list
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | string representation considering abbreviations
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowStep :: Step -> String
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowStep (Step a n ps) =
e1e8390280254f7f0580d701e583f670643d4f3fnilgun let t = showNodeTest n in
e1e8390280254f7f0580d701e583f670643d4f3fnilgun case (a, n) of
e1e8390280254f7f0580d701e583f670643d4f3fnilgun (Attribute, _) -> '@' : t
e1e8390280254f7f0580d701e583f670643d4f3fnilgun (Child, _) -> t
e1e8390280254f7f0580d701e583f670643d4f3fnilgun (Self, Node) -> "."
e1e8390280254f7f0580d701e583f670643d4f3fnilgun (Parent, Node) -> ".."
e1e8390280254f7f0580d701e583f670643d4f3fnilgun _ -> showAxis a ++ "::" ++ t
e1e8390280254f7f0580d701e583f670643d4f3fnilgun ++ concatMap showPred ps
e1e8390280254f7f0580d701e583f670643d4f3fnilguninstance Show Step where
e1e8390280254f7f0580d701e583f670643d4f3fnilgun show = showStep
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | test for @descendant-or-self::node()@ step
e1e8390280254f7f0580d701e583f670643d4f3fnilgunisDescOrSelfNode :: Step -> Bool
e1e8390280254f7f0580d701e583f670643d4f3fnilgunisDescOrSelfNode (Step a n _) = case (a, n) of
e1e8390280254f7f0580d701e583f670643d4f3fnilgun (Descendant True, Node) -> True
3a6531f1fd50efd78381e5800802a1449096781eslive-- | only absolute paths may be empty
3a6531f1fd50efd78381e5800802a1449096781eslivedata Path = Path Bool [Step] -- ^ absolute?
3a6531f1fd50efd78381e5800802a1449096781eslive-- | show a path abbreviating @\/descendant-or-self::node()\/@
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowSteps :: Bool -> [Step] -> String
06ba4a61654b3763ad65f52283832ebf058fdf1csliveshowSteps abso sts = let h = if abso then "/" else "" in case sts of
06ba4a61654b3763ad65f52283832ebf058fdf1cslive s : r -> let f = h ++ showStep s in case r of
e1e8390280254f7f0580d701e583f670643d4f3fnilgun _ -> if abso && isDescOrSelfNode s then "//" ++ showSteps False r
06ba4a61654b3763ad65f52283832ebf058fdf1cslive else f ++ showSteps True r
4aa603e6448b99f9371397d439795c91a93637eandinstance Show Path where
17ade6df5ec233536985eb1c130a906c725dd614humbedooh show (Path abso sts) = showSteps abso sts
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | indicator for primary expressions
e1e8390280254f7f0580d701e583f670643d4f3fnilgundata PrimKind
06ba4a61654b3763ad65f52283832ebf058fdf1cslive = Var -- ^ leading dollar
20f499565e77defe9dab24dd85c02f38a1175855nd | Literal -- ^ single or double quotes
17ade6df5ec233536985eb1c130a906c725dd614humbedooh | Number -- ^ digits possibly with decimal point
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | expressions where function calls, unary and infix expressions are generic
e06675c51d084791089d79c3ac18aeae8dd465fcrbb = GenExpr Bool String [Expr] -- ^ infix?, op or fct, and arguments
4a56677aad9b66a36f3dc9fddbca8dc1230ad471rbowen | PathExpr (Maybe Expr) Path -- ^ optional filter and path
17ade6df5ec233536985eb1c130a906c725dd614humbedooh | FilterExpr Expr [Expr] -- ^ primary expression with predicates
e1e8390280254f7f0580d701e583f670643d4f3fnilgun | PrimExpr PrimKind String
e1e8390280254f7f0580d701e583f670643d4f3fnilguninstance Show Expr where
e1e8390280254f7f0580d701e583f670643d4f3fnilgun show = showExpr
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | put square brackets around an expression
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowPred :: Expr -> String
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowPred e = '[' : showExpr e ++ "]"
e1e8390280254f7f0580d701e583f670643d4f3fnilgun-- | show expression with minimal parens
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowExpr :: Expr -> String
e1e8390280254f7f0580d701e583f670643d4f3fnilgunshowExpr e = case e of
e1e8390280254f7f0580d701e583f670643d4f3fnilgun GenExpr infx op args ->
e1e8390280254f7f0580d701e583f670643d4f3fnilgun if infx then
e1e8390280254f7f0580d701e583f670643d4f3fnilgun showInfixExpr op args
e1e8390280254f7f0580d701e583f670643d4f3fnilgun else op ++ paren (intercalate "," $ map showExpr args)
25d005c58102474e06fb67d748273b161d7198d0pquerna PathExpr m p -> case m of
a5a2e98043365d3f5a8bb3c3cdfc252a4f11bf02nd Nothing -> ""
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf Just f -> showExpr f
a5a2e98043365d3f5a8bb3c3cdfc252a4f11bf02nd FilterExpr pe ps ->
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd (if isPrimExpr pe then id else paren) (showExpr pe)
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd ++ concatMap showPred ps
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd PrimExpr _ s -> s
2f46ce2a814d7f2b126dfb9f1b25fd64e2fbdc11rbowen{- | show arguments with minimal parens interspersed with the infix operator.
e1e8390280254f7f0580d701e583f670643d4f3fnilgunAlso treat the unary minus where the argument list is a singleton.
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndAlphanumeric operators are shown with spaces, which looks bad for @mod@ and
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4nd@div@ in conjunction with additive, relational, or equality operators. The
0c4abc32c00611fe1d52c9661f5cc79a3f74c6d4ndunary minus gets a leading blank if the preceding character is a
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd'ncNameChar'. -}
ad74a0524a06bfe11b7de9e3b4ce7233ab3bd3f7ndshowInfixExpr :: String -> [Expr] -> String
7f5b59ccc63c0c0e3e678a168f09ee6a2f51f9d0ndshowInfixExpr op args = case args of
e1e8390280254f7f0580d701e583f670643d4f3fnilgun [] -> op -- cannot happen
f086b4b402fa9a2fefc7dda85de2a3cc1cd0a654rjung [arg] -> -- unary minus
727872d18412fc021f03969b8641810d8896820bhumbedooh let s = showExpr arg
0d0ba3a410038e179b695446bb149cce6264e0abnd in op ++ case arg of
727872d18412fc021f03969b8641810d8896820bhumbedooh GenExpr True aOp _ -> case aOp of
0d0ba3a410038e179b695446bb149cce6264e0abnd _ -> paren s
727872d18412fc021f03969b8641810d8896820bhumbedooh arg : rargs ->
0d0ba3a410038e179b695446bb149cce6264e0abnd let mi = findIndex (elem op) inOps
0d0ba3a410038e179b695446bb149cce6264e0abnd f = parenExpr False mi arg
0d0ba3a410038e179b695446bb149cce6264e0abnd padOp = if any isAlpha op then ' ' : op ++ " " else
ac082aefa89416cbdc9a1836eaf3bed9698201c8humbedooh if elem op addOps && not (null f) && ncNameChar (last f)
0d0ba3a410038e179b695446bb149cce6264e0abnd then ' ' : op else op
0d0ba3a410038e179b695446bb149cce6264e0abnd in f ++ concatMap ((padOp ++) . parenExpr True mi) rargs
727872d18412fc021f03969b8641810d8896820bhumbedooh{- | put parens around arguments that have a lower precedence or
0d0ba3a410038e179b695446bb149cce6264e0abnd equal precendence if they are a right argument. -}
0d0ba3a410038e179b695446bb149cce6264e0abndparenExpr :: Bool -> Maybe Int -> Expr -> String
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedoohparenExpr rst mi e =
205f749042ed530040a4f0080dbcb47ceae8a374rjung let s = showExpr e
af33a4994ae2ff15bc67d19ff1a7feb906745bf8rbowen in case e of
0d0ba3a410038e179b695446bb149cce6264e0abnd GenExpr True op (_ : _ : _) ->
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd let mj = findIndex (elem op) inOps
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd putPar = case (mi, mj) of
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd (Just i, Just j) -> i > j || rst && i == j