@@ -1,25 +1,31 @@
1{-# LANGUAGE Rank2Types, FlexibleContexts #-}
1{-# LANGUAGE Rank2Types, FlexibleContexts #-}
2+{-# OPTIONS_GHC -fno-warn-orphans #-}
2-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
4-- Module : Text.XML.Lens
5-- Module : Text.XML.Lens
5−-- Copyright : (C) 2013 Fumiaki Kinoshita
6+-- Copyright : (C) 2015 Fumiaki Kinoshita
6-- License : BSD-style (see the file LICENSE)
7-- License : BSD-style (see the file LICENSE)
8-- Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>
9-- Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>
9−-- Stability : experimental
10+-- Stability : provisional
10-- Portability : non-portable
11-- Portability : non-portable
11−-- Useful traversals inspired by XPath
12+--
12----------------------------------------------------------------------------
13----------------------------------------------------------------------------
13module Text.XML.Lens (
14module Text.XML.Lens (
14 -- * Lenses, traversals for 'Element'
15 -- * Lenses, traversals for 'Element'
16 -- ** Names
18 -- ** Names
20 -- ** Attributes
24 -- ** Attributes
21 , attributeIs
25 , attributeIs
22 , attributeSatisfies
26 , attributeSatisfies
27+ , attributeSatisfies'
24 , attribute
30 , attribute
@@ -27,68 +33,75 @@ module Text.XML.Lens (
29 -- ** Children
35 -- ** Children
32 -- * Prisms for 'Node'
37 -- * Prisms for 'Node'
33 , _Element
39 , _Element
34 , _Content
40 , _Content
35 , AsInstruction(..)
41 , AsInstruction(..)
36 , AsComment(..)
42 , AsComment(..)
37 -- * Lenses for 'Document'
43 -- * Lenses for 'Document'
39 , prologue
46 , prologue
40 , epilogue
47 , epilogue
42 -- * Lenses for 'Name'
49 -- * Lenses for 'Name'
43 , _nameLocalName
51 , _nameLocalName
44 , _nameNamespace
52 , _nameNamespace
45 , _namePrefix
53 , _namePrefix
46 -- * Lenses for 'Instruction'
54 -- * Lenses for 'Instruction'
47 , _instructionTarget
56 , _instructionTarget
48 , _instructionData
57 , _instructionData
50− , module Control.Lens
53import Text.XML
59import Text.XML
54import Control.Lens
60import Control.Lens
55import Data.Text (Text)
61import Data.Text (Text)
56import Data.Map (Map)
62import Data.Map (Map)
57−import Control.Applicative
63+import qualified Data.CaseInsensitive as CI
58−
64+import Data.Maybe (isNothing)
61prologue :: Lens' Document Prologue
66prologue :: Lens' Document Prologue
62prologue f doc = fmap (\p -> doc { documentPrologue = p} ) $ f $ documentPrologue doc
67prologue f doc = fmap (\p -> doc { documentPrologue = p} ) $ f $ documentPrologue doc
68+{-# INLINE prologue #-}
64-- | The root element of the document.
70-- | The root element of the document.
65root :: Lens' Document Element
71root :: Lens' Document Element
66root f doc = fmap (\p -> doc { documentRoot = p} ) $ f $ documentRoot doc
72root f doc = fmap (\p -> doc { documentRoot = p} ) $ f $ documentRoot doc
68epilogue :: Lens' Document [Miscellaneous]
75epilogue :: Lens' Document [Miscellaneous]
69epilogue f doc = fmap (\p -> doc { documentEpilogue = p} ) $ f $ documentEpilogue doc
76epilogue f doc = fmap (\p -> doc { documentEpilogue = p} ) $ f $ documentEpilogue doc
77+{-# INLINE epilogue #-}
71doctype :: Lens' Prologue (Maybe Doctype)
79doctype :: Lens' Prologue (Maybe Doctype)
72doctype f doc = fmap (\p -> doc { prologueDoctype = p} ) $ f $ prologueDoctype doc
80doctype f doc = fmap (\p -> doc { prologueDoctype = p} ) $ f $ prologueDoctype doc
81+{-# INLINE doctype #-}
74class AsInstruction t where
83class AsInstruction t where
75 _Instruction :: Prism' t Instruction
84 _Instruction :: Prism' t Instruction
77_instructionTarget :: Lens' Instruction Text
86_instructionTarget :: Lens' Instruction Text
78_instructionTarget f (Instruction t d) = f t <&> \t' -> Instruction t' d
87_instructionTarget f (Instruction t d) = f t <&> \t' -> Instruction t' d
88+{-# INLINE _instructionTarget #-}
80_instructionData :: Lens' Instruction Text
90_instructionData :: Lens' Instruction Text
81_instructionData f (Instruction t d) = f d <&> \d' -> Instruction t d'
91_instructionData f (Instruction t d) = f d <&> \d' -> Instruction t d'
92+{-# INLINE _instructionData #-}
83instance AsInstruction Node where
94instance AsInstruction Node where
84 _Instruction = prism' NodeInstruction $ \s -> case s of
95 _Instruction = prism' NodeInstruction $ \s -> case s of
85 NodeInstruction e -> Just e
96 NodeInstruction e -> Just e
86 _ -> Nothing
97 _ -> Nothing
98+ {-# INLINE _Instruction #-}
88instance AsInstruction Miscellaneous where
100instance AsInstruction Miscellaneous where
89 _Instruction = prism' MiscInstruction $ \s -> case s of
101 _Instruction = prism' MiscInstruction $ \s -> case s of
90 MiscInstruction e -> Just e
102 MiscInstruction e -> Just e
91 _ -> Nothing
103 _ -> Nothing
104+ {-# INLINE _Instruction #-}
93class AsComment t where
106class AsComment t where
94 _Comment :: Prism' t Text
107 _Comment :: Prism' t Text
@@ -97,84 +110,107 @@ instance AsComment Node where
97 _Comment = prism' NodeComment $ \s -> case s of
110 _Comment = prism' NodeComment $ \s -> case s of
98 NodeComment e -> Just e
111 NodeComment e -> Just e
99 _ -> Nothing
112 _ -> Nothing
113+ {-# INLINE _Comment #-}
101instance AsComment Miscellaneous where
115instance AsComment Miscellaneous where
102 _Comment = prism' MiscComment $ \s -> case s of
116 _Comment = prism' MiscComment $ \s -> case s of
103 MiscComment e -> Just e
117 MiscComment e -> Just e
104 _ -> Nothing
118 _ -> Nothing
119+ {-# INLINE _Comment #-}
106_nameLocalName :: Lens' Name Text
121_nameLocalName :: Lens' Name Text
107−_nameLocalName f n = fmap (\x -> n { nameLocalName = x }) $ f $ nameLocalName n
122+_nameLocalName f n = f (nameLocalName n) <&> \x -> n { nameLocalName = x }
123+{-# INLINE _nameLocalName #-}
109_nameNamespace :: Lens' Name (Maybe Text)
125_nameNamespace :: Lens' Name (Maybe Text)
110−_nameNamespace f n = fmap (\x -> n { nameNamespace = x }) $ f $ nameNamespace n
126+_nameNamespace f n = f (nameNamespace n) <&> \x -> n { nameNamespace = x }
127+{-# INLINE _nameNamespace #-}
112_namePrefix :: Lens' Name (Maybe Text)
129_namePrefix :: Lens' Name (Maybe Text)
113−_namePrefix f n = fmap (\x -> n { namePrefix = x }) $ f $ namePrefix n
130+_namePrefix f n = f (namePrefix n) <&> \x -> n { namePrefix = x }
131+{-# INLINE _namePrefix #-}
115_Element :: Prism' Node Element
133_Element :: Prism' Node Element
116_Element = prism' NodeElement $ \s -> case s of
134_Element = prism' NodeElement $ \s -> case s of
117 NodeElement e -> Just e
135 NodeElement e -> Just e
118 _ -> Nothing
136 _ -> Nothing
137+{-# INLINE _Element #-}
120_Content :: Prism' Node Text
139_Content :: Prism' Node Text
121_Content = prism' NodeContent $ \s -> case s of
140_Content = prism' NodeContent $ \s -> case s of
122 NodeContent e -> Just e
141 NodeContent e -> Just e
123 _ -> Nothing
142 _ -> Nothing
143+{-# INLINE _Content #-}
125name :: Lens' Element Name
145name :: Lens' Element Name
126−name f e = fmap (\x -> e { elementName = x }) $ f $ elementName e
146+name f e = f (elementName e) <&> \x -> e { elementName = x }
149+localName :: Lens' Element Text
150+localName = name . _nameLocalName
151+{-# INLINE localName #-}
128attrs :: Lens' Element (Map Name Text)
153attrs :: Lens' Element (Map Name Text)
129attrs f e = fmap (\x -> e { elementAttributes = x }) $ f $ elementAttributes e
154attrs f e = fmap (\x -> e { elementAttributes = x }) $ f $ elementAttributes e
131nodes :: Lens' Element [Node]
157nodes :: Lens' Element [Node]
132nodes f e = fmap (\x -> e { elementNodes = x }) $ f $ elementNodes e
158nodes f e = fmap (\x -> e { elementNodes = x }) $ f $ elementNodes e
134−attr :: Name -> IndexedTraversal' Name Element Text
161+attr :: Name -> Traversal' Element Text
135attr n = attrs . ix n
162attr n = attrs . ix n
137−attribute :: Name -> IndexedLens' Name Element (Maybe Text)
165+attribute :: Name -> Lens' Element (Maybe Text)
138attribute n = attrs . at n
166attribute n = attrs . at n
167+{-# INLINE attribute #-}
140−-- | Traverse itself with its all children.
169+-- | Traverse elements which has the specified *local* name (case-insensitive).
141−entire :: Traversal' Element Element
170+named :: CI.CI Text -> Traversal' Element Element
142−entire f e@(Element _ _ ns) = com <$> f e <*> traverse (_Element (entire f)) ns where
171+named n f s
143− com (Element n a _) = Element n a
172+ | CI.mk (nameLocalName (elementName s)) == n = f s
173+ | otherwise = pure s
176+-- | Old name for 'named'
177+ell :: Text -> Traversal' Element Element
178+ell n = named $ CI.mk n
145-- | Traverse elements which has the specified name.
180-- | Traverse elements which has the specified name.
146el :: Name -> Traversal' Element Element
181el :: Name -> Traversal' Element Element
148 | elementName s == n = f s
183 | elementName s == n = f s
149 | otherwise = pure s
184 | otherwise = pure s
150−
185+{-# DEPRECATED el "Use named instead" #-}
151−-- | Traverse elements which has the specified *local* name.
152−ell :: Text -> Traversal' Element Element
154− | nameLocalName (elementName s) == n = f s
155− | otherwise = pure s
157attributeSatisfies :: Name -> (Text -> Bool) -> Traversal' Element Element
187attributeSatisfies :: Name -> (Text -> Bool) -> Traversal' Element Element
158−attributeSatisfies n p = filtered (maybe False p . preview (attrs . ix n))
188+attributeSatisfies n p = attributeSatisfies' n (maybe False p)
189+{-# INLINE attributeSatisfies #-}
191+attributeSatisfies' :: Name -> (Maybe Text -> Bool) -> Traversal' Element Element
192+attributeSatisfies' n p = filtered (p . preview (attrs . ix n))
193+{-# INLINE attributeSatisfies' #-}
195+withoutAttribute :: Name -> Traversal' Element Element
196+withoutAttribute n = attributeSatisfies' n isNothing
197+{-# INLINE withoutAttribute #-}
160attributeIs :: Name -> Text -> Traversal' Element Element
199attributeIs :: Name -> Text -> Traversal' Element Element
161attributeIs n v = attributeSatisfies n (==v)
200attributeIs n v = attributeSatisfies n (==v)
201+{-# INLINE attributeIs #-}
163-- | Traverse all contents of the element.
203-- | Traverse all contents of the element.
164text :: Traversal' Element Text
204text :: Traversal' Element Text
165text = nodes . traverse . _Content
205text = nodes . traverse . _Content
167-- | Traverse all comments of the element.
208-- | Traverse all comments of the element.
168comment :: Traversal' Element Text
209comment :: Traversal' Element Text
169comment = nodes . traverse . _Comment
210comment = nodes . traverse . _Comment
211+{-# INLINE comment #-}
213+-- | 'plate' traverses over its sub-elements.
171instance Plated Element where
214instance Plated Element where
172 plate = nodes . traverse . _Element
215 plate = nodes . traverse . _Element
174−-- | Combine two 'Traversal's just like XPath's slash.
177−-- l ./ m ≡ l . 'plate' . m
179−(./) :: Plated a => Traversal s t a a -> Traversal a a u v -> Traversal s t u v
180−l ./ m = l . plate . m
216+ {-# INLINE plate #-}