-------------------------------------------------------------------------------- -- See end of this file for licence information. -------------------------------------------------------------------------------- -- | -- Module : QName -- Copyright : (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 Douglas Burke -- License : GPL V2 -- -- Maintainer : Douglas Burke -- Stability : experimental -- Portability : H98 -- -- This module defines an algebraic datatype for qualified names (QNames). -- -------------------------------------------------------------------------------- module Swish.Utils.QName ( QName(..) -- , maybeQnEq , newQName, qnameFromPair, qnameFromURI , getNamespace, getLocalName, getQNameURI , splitURI , qnameFromFilePath ) where import Data.Char (isAlpha, isAlphaNum) import System.Directory (canonicalizePath) import System.FilePath (splitDirectories) import Data.String (IsString(..)) import Data.List (intercalate) ------------------------------------------------------------ -- Qualified name ------------------------------------------------------------ -- -- cf. http://www.w3.org/TR/REC-xml-names/ data QName = QName { qnNsuri, qnLocal :: String } instance IsString QName where fromString = qnameFromURI instance Eq QName where (==) = qnEq instance Ord QName where (QName u1 l1) <= (QName u2 l2) = if up1 /= up2 then up1 <= up2 else (ur1++l1) <= (ur2++l2) where n = min (length u1) (length u2) (up1,ur1) = splitAt n u1 (up2,ur2) = splitAt n u2 instance Show QName where show (QName ns ln) = "<" ++ ns ++ ln ++ ">" newQName :: String -> String -> QName newQName = QName qnameFromPair :: (String,String) -> QName qnameFromPair (ns,ln) = QName ns ln qnameFromURI :: String -> QName qnameFromURI = qnameFromPair . splitURI getNamespace :: QName -> String getNamespace = qnNsuri getLocalName :: QName -> String getLocalName = qnLocal getQNameURI :: QName -> String getQNameURI (QName ns ln) = ns++ln -- Original used comparison of concatenated strings, -- but that was very inefficient. This version does the -- comparison without constructing new values qnEq :: QName -> QName -> Bool qnEq (QName n1 l1) (QName n2 l2) = qnEq1 n1 n2 l1 l2 where qnEq1 (c1:ns1) (c2:ns2) ln1 ln2 = c1==c2 && qnEq1 ns1 ns2 ln1 ln2 qnEq1 [] ns2 ln1@(_:_) ln2 = qnEq1 ln1 ns2 [] ln2 qnEq1 ns1 [] ln1 ln2@(_:_) = qnEq1 ns1 ln2 ln1 [] qnEq1 [] [] [] [] = True qnEq1 _ _ _ _ = False {- -- Define equality of (Maybe QName) maybeQnEq :: (Maybe QName) -> (Maybe QName) -> Bool maybeQnEq Nothing Nothing = True maybeQnEq (Just q1) (Just q2) = q1 == q2 maybeQnEq _ _ = False -} -- Separate URI string into namespace URI and local name splitURI :: String -> ( String, String ) -- splitQname "http://example.org/aaa#bbb" = ("http://example.org/aaa#","bbb") -- splitQname "http://example.org/aaa/bbb" = ("http://example.org/aaa/","bbb") -- splitQname "http://example.org/aaa/" = ("http://example.org/aaa/","") splitURI qn = splitAt (scanURI qn (-1) 0) qn -- helper function for splitQName -- Takes 3 arguments: -- QName to scan -- index of last name-start char, or (-1) -- number of characters scanned so far -- Returns index of start of name, or length of list -- scanURI :: String -> Int -> Int -> Int scanURI (nextch:more) (-1) nc | isNameStartChar nextch = scanURI more nc (nc+1) | otherwise = scanURI more (-1) (nc+1) scanURI (nextch:more) ns nc | not (isNameChar nextch) = scanURI more (-1) (nc+1) | otherwise = scanURI more ns (nc+1) scanURI "" (-1) nc = nc scanURI "" ns _ = ns -- Definitions here per XML namespaces, NCName production, -- restricted to characters used in URIs. -- cf. http://www.w3.org/TR/REC-xml-names/ isNameStartChar :: Char -> Bool isNameStartChar c = isAlpha c || c == '_' isNameChar :: Char -> Bool isNameChar c = isAlphaNum c || c `elem` ".-_" {-| Convert a filepath to a file: URI stored in a QName. If the input file path is relative then the working directory is used to convert it into an absolute path. If the input represents a directory then it *must* end in the directory separator - e.g. @\"\/foo\/bar\/\"@ rather than @\"\/foo\/bar\"@ for Posix systems. This has not been tested on Windows. -} qnameFromFilePath :: FilePath -> IO QName qnameFromFilePath = fmap qnameFromURI . filePathToURI filePathToURI :: FilePath -> IO String filePathToURI fname = do ipath <- canonicalizePath fname let paths = splitDirectories ipath txt = intercalate "/" $ case paths of "/":rs -> rs _ -> paths return $ "file:///" ++ txt -------------------------------------------------------------------------------- -- -- Copyright (c) 2003, Graham Klyne, 2009 Vasili I Galchin, 2011 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 -- --------------------------------------------------------------------------------