-- ------------------------------------------------------------ {- | Module : Text.XML.HXT.DOM.Util Copyright : Copyright (C) 2008 Uwe Schmidt License : MIT Maintainer : Uwe Schmidt (uwe@fh-wedel.de) Stability : stable Portability: portable Little useful things for strings, lists and other values -} -- ------------------------------------------------------------ module Text.XML.HXT.DOM.Util ( stringTrim , stringToLower , stringToUpper , stringAll , stringFirst , stringLast , normalizeNumber , normalizeWhitespace , normalizeBlanks , escapeURI , textEscapeXml , stringEscapeXml , attrEscapeXml , stringToInt , stringToHexString , charToHexString , intToHexString , hexStringToInt , decimalStringToInt , doubles , singles , noDoubles , swap , partitionEither , toMaybe , uncurry3 , uncurry4 ) where import Data.Char import Data.List import Data.Maybe -- ------------------------------------------------------------ -- | -- remove leading and trailing whitespace with standard Haskell predicate isSpace stringTrim :: String -> String stringTrim = reverse . dropWhile isSpace . reverse . dropWhile isSpace -- | -- convert string to uppercase with standard Haskell toUpper function stringToUpper :: String -> String stringToUpper = map toUpper -- | -- convert string to lowercase with standard Haskell toLower function stringToLower :: String -> String stringToLower = map toLower -- | find all positions where a string occurs within another string stringAll :: (Eq a) => [a] -> [a] -> [Int] stringAll x = map fst . filter ((x `isPrefixOf`) . snd) . zip [0..] . tails -- | find the position of the first occurence of a string stringFirst :: (Eq a) => [a] -> [a] -> Maybe Int stringFirst x = listToMaybe . stringAll x -- | find the position of the last occurence of a string stringLast :: (Eq a) => [a] -> [a] -> Maybe Int stringLast x = listToMaybe . reverse . stringAll x -- ------------------------------------------------------------ -- | Removes leading \/ trailing whitespaces and leading zeros normalizeNumber :: String -> String normalizeNumber = reverse . dropWhile (== ' ') . reverse . dropWhile (\x -> x == '0' || x == ' ') -- | Reduce whitespace sequences to a single whitespace normalizeWhitespace :: String -> String normalizeWhitespace = unwords . words -- | replace all whitespace chars by blanks normalizeBlanks :: String -> String normalizeBlanks = map (\ x -> if isSpace x then ' ' else x) -- ------------------------------------------------------------ -- | Escape all disallowed characters in URI -- references (see ) escapeURI :: String -> String escapeURI ref = concatMap replace ref where notAllowed :: Char -> Bool notAllowed c = c < '\31' || c `elem` ['\DEL', ' ', '<', '>', '\"', '{', '}', '|', '\\', '^', '`' ] replace :: Char -> String replace c | notAllowed c = '%' : charToHexString c | otherwise = [c] -- ------------------------------------------------------------ escapeXml :: String -> String -> String escapeXml escSet = concatMap esc where esc c | c `elem` escSet = "&#" ++ show (fromEnum c) ++ ";" | otherwise = [c] -- | -- escape XML chars <, >, ", and ampercent by transforming them into character references -- -- see also : 'attrEscapeXml' stringEscapeXml :: String -> String stringEscapeXml = escapeXml "<>\"\'&" -- | -- escape XML chars < and ampercent by transforming them into character references, used for escaping text nodes -- -- see also : 'attrEscapeXml' textEscapeXml :: String -> String textEscapeXml = escapeXml "<&" -- | -- escape XML chars in attribute values, same as stringEscapeXml, but none blank whitespace -- is also escaped -- -- see also : 'stringEscapeXml' attrEscapeXml :: String -> String attrEscapeXml = escapeXml "<>\"\'&\n\r\t" stringToInt :: Int -> String -> Int stringToInt base digits = sign * (foldl acc 0 $ concatMap digToInt digits1) where splitSign ('-' : ds) = ((-1), ds) splitSign ('+' : ds) = ( 1 , ds) splitSign ds = ( 1 , ds) (sign, digits1) = splitSign digits digToInt c | c >= '0' && c <= '9' = [ord c - ord '0'] | c >= 'A' && c <= 'Z' = [ord c - ord 'A' + 10] | c >= 'a' && c <= 'z' = [ord c - ord 'a' + 10] | otherwise = [] acc i1 i0 = i1 * base + i0 -- | -- convert a string of hexadecimal digits into an Int hexStringToInt :: String -> Int hexStringToInt = stringToInt 16 -- | -- convert a string of digits into an Int decimalStringToInt :: String -> Int decimalStringToInt = stringToInt 10 -- | -- convert a string into a hexadecimal string applying charToHexString -- -- see also : 'charToHexString' stringToHexString :: String -> String stringToHexString = concatMap charToHexString -- | -- convert a char (byte) into a 2-digit hexadecimal string -- -- see also : 'stringToHexString', 'intToHexString' charToHexString :: Char -> String charToHexString c = [ fourBitsToChar (c' `div` 16) , fourBitsToChar (c' `mod` 16) ] where c' = fromEnum c -- | -- convert a none negative Int into a hexadecimal string -- -- see also : 'charToHexString' intToHexString :: Int -> String intToHexString i | i == 0 = "0" | i > 0 = intToStr i | otherwise = error ("intToHexString: negative argument " ++ show i) where intToStr 0 = "" intToStr i' = intToStr (i' `div` 16) ++ [fourBitsToChar (i' `mod` 16)] fourBitsToChar :: Int -> Char fourBitsToChar i = "0123456789ABCDEF" !! i -- ------------------------------------------------------------ -- | -- take all elements of a list which occur more than once. The result does not contain doubles. -- (doubles . doubles == doubles) doubles :: Eq a => [a] -> [a] doubles = doubles' [] where doubles' acc [] = acc doubles' acc (e : s) | e `elem` s && e `notElem` acc = doubles' (e:acc) s | otherwise = doubles' acc s -- | -- drop all elements from a list which occur more than once. singles :: Eq a => [a] -> [a] singles = singles' [] where singles' acc [] = acc singles' acc (e : s) | e `elem` s || e `elem` acc = singles' acc s | otherwise = singles' (e : acc) s -- | -- remove duplicates from list noDoubles :: Eq a => [a] -> [a] noDoubles [] = [] noDoubles (e : s) | e `elem` s = noDoubles s | otherwise = e : noDoubles s -- ------------------------------------------------------------ swap :: (a,b) -> (b,a) swap (x,y) = (y,x) partitionEither :: [Either a b] -> ([a], [b]) partitionEither = foldr (\x ~(ls,rs) -> either (\l -> (l:ls,rs)) (\r -> (ls,r:rs)) x) ([],[]) toMaybe :: Bool -> a -> Maybe a toMaybe False _ = Nothing toMaybe True x = Just x -- ------------------------------------------------------------ -- | mothers little helpers for to much curry uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d uncurry3 f ~(a, b, c) = f a b c uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e uncurry4 f ~(a, b, c, d) = f a b c d -- ------------------------------------------------------------