{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Text.CSL.Input.Bibtex
( readBibtex
, readBibtexString
, Lang(..)
, langToLocale
, getLangFromEnv
)
where
import Prelude
import Control.Applicative
import qualified Control.Exception as E
import Control.Monad
import Control.Monad.RWS hiding ((<>))
import qualified Data.ByteString as B
import Data.Char (isAlphaNum, isDigit, isUpper, toLower,
toUpper)
import Data.List (foldl', intercalate)
import Data.List.Split (splitOn, splitWhen, wordsBy)
import qualified Data.Map as Map
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import System.Environment (getEnvironment)
import Text.CSL.Compat.Pandoc (readLaTeX)
import Text.CSL.Exception (CiteprocException (ErrorReadingBib, ErrorReadingBibFile))
import Text.CSL.Parser (parseLocale)
import Text.CSL.Reference
import Text.CSL.Style (Agent (..), emptyAgent, CslTerm (..),
Formatted (..), Locale (..))
import Text.CSL.Util (onBlocks, protectCase, safeRead,
splitWhen, splitStrWhen, trim,
unTitlecase, addSpaceAfterPeriod)
import Text.Pandoc.Definition
import qualified Text.Pandoc.Walk as Walk
import Text.Parsec hiding (State, many, (<|>))
blocksToFormatted :: [Block] -> Bib Formatted
blocksToFormatted bs =
case bs of
[Plain xs] -> inlinesToFormatted xs
[Para xs] -> inlinesToFormatted xs
_ -> inlinesToFormatted $ Walk.query (:[]) bs
adjustSpans :: Lang -> Inline -> [Inline]
adjustSpans _ (Span ("",[],[]) xs) = xs
adjustSpans lang (RawInline (Format "latex") s)
| s == "\\hyphen" || s == "\\hyphen " = [Str "-"]
| otherwise = Walk.walk (concatMap (adjustSpans lang))
$ parseRawLaTeX lang s
adjustSpans _ x = [x]
parseRawLaTeX :: Lang -> Text -> [Inline]
parseRawLaTeX lang (T.stripPrefix "\\" -> Just xs) =
case latex' contents of
[Para ys] -> f command ys
[Plain ys] -> f command ys
_ -> []
where (command', contents') = T.break (=='{') xs
command = trim command'
contents = T.drop 1 $ T.dropEnd 1 contents'
f "mkbibquote" ils = [Quoted DoubleQuote ils]
f "mkbibemph" ils = [Emph ils]
f "mkbibitalic" ils = [Emph ils]
f "mkbibbold" ils = [Strong ils]
f "mkbibparens" ils = [Str "("] ++ ils ++ [Str ")"]
f "mkbibbrackets" ils = [Str "["] ++ ils ++ [Str "]"]
f "autocap" ils = ils
f "textnormal" ils = [Span ("",["nodecor"],[]) ils]
f "bibstring" [Str s] = [Str $ resolveKey' lang s]
f _ ils = [Span nullAttr ils]
parseRawLaTeX _ _ = []
inlinesToFormatted :: [Inline] -> Bib Formatted
inlinesToFormatted ils = do
lang <- gets localeLanguage
return $ Formatted $ Walk.walk (concatMap (adjustSpans lang)) ils
data Item = Item{ identifier :: Text
, entryType :: Text
, fields :: Map.Map Text Text
}
getLangFromEnv :: IO Lang
getLangFromEnv = do
env <- getEnvironment
return $ case lookup "LANG" env of
Just x -> case Text.CSL.Util.splitWhen (\c -> c == '_' || c == '-')
(T.takeWhile (/='.') (T.pack x)) of
(w:z:_) -> Lang w z
[w] | not (T.null w) -> Lang w mempty
_ -> Lang "en" "US"
Nothing -> Lang "en" "US"
readBibtex :: (Text -> Bool) -> Bool -> Bool -> FilePath -> IO [Reference]
readBibtex idpred isBibtex caseTransform f = do
contents <- decodeUtf8 <$> B.readFile f
E.catch (readBibtexString idpred isBibtex caseTransform contents)
(\e -> case e of
ErrorReadingBib es -> E.throwIO $ ErrorReadingBibFile f es
_ -> E.throwIO e)
readBibtexString :: (Text -> Bool) -> Bool -> Bool -> Text
-> IO [Reference]
readBibtexString idpred isBibtex caseTransform contents = do
lang <- getLangFromEnv
locale <- parseLocale (langToLocale lang)
case runParser (bibEntries <* eof) Map.empty "stdin" contents of
Left err -> E.throwIO $ ErrorReadingBib $ drop 8 $ show err
Right xs -> return $ mapMaybe
(itemToReference lang locale isBibtex caseTransform)
(filter (idpred . identifier)
(resolveCrossRefs isBibtex
xs))
type BibParser = Parsec Text (Map.Map Text Text)
bibEntries :: BibParser [Item]
bibEntries = do
skipMany nonEntry
many (bibItem <* skipMany nonEntry)
where nonEntry = bibSkip <|>
try (char '@' >>
(bibComment <|> bibPreamble <|> bibString))
bibSkip :: BibParser ()
bibSkip = skipMany1 (satisfy (/='@'))
bibComment :: BibParser ()
bibComment = do
cistring "comment"
spaces
void inBraces <|> bibSkip <|> return ()
bibPreamble :: BibParser ()
bibPreamble = do
cistring "preamble"
spaces
void inBraces
bibString :: BibParser ()
bibString = do
cistring "string"
spaces
char '{'
spaces
(k,v) <- entField
char '}'
updateState (Map.insert k v)
return ()
inBraces :: BibParser Text
inBraces = try $ do
char '{'
res <- manyTill
( (T.pack <$> many1 (noneOf "{}\\"))
<|> (char '\\' >> ( (char '{' >> return "\\{")
<|> (char '}' >> return "\\}")
<|> return "\\"))
<|> (braced <$> inBraces)
) (char '}')
return $ T.concat res
braced :: Text -> Text
braced = T.cons '{' . flip T.snoc '}'
inQuotes :: BibParser Text
inQuotes = do
char '"'
T.concat <$> manyTill ( (T.pack <$> many1 (noneOf "\"\\{"))
<|> (char '\\' >> T.cons '\\' . T.singleton <$> anyChar)
<|> braced <$> inBraces
) (char '"')
fieldName :: BibParser Text
fieldName = resolveAlias . T.toLower . T.pack
<$> many1 (letter <|> digit <|> oneOf "-_:+")
isBibtexKeyChar :: Char -> Bool
isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]*&" :: String)
bibItem :: BibParser Item
bibItem = do
char '@'
enttype <- map toLower <$> many1 letter
spaces
char '{'
spaces
entid <- many1 (satisfy isBibtexKeyChar)
spaces
char ','
spaces
entfields <- entField `sepEndBy` (char ',' >> spaces)
spaces
char '}'
return $ Item (T.pack entid) (T.pack enttype) (Map.fromList entfields)
entField :: BibParser (Text, Text)
entField = do
k <- fieldName
spaces
char '='
spaces
vs <- (expandString <|> inQuotes <|> inBraces <|> rawWord) `sepBy`
try (spaces >> char '#' >> spaces)
spaces
return (k, T.concat vs)
resolveAlias :: Text -> Text
resolveAlias "archiveprefix" = "eprinttype"
resolveAlias "primaryclass" = "eprintclass"
resolveAlias s = s
rawWord :: BibParser Text
rawWord = T.pack <$> many1 alphaNum
expandString :: BibParser Text
expandString = do
k <- fieldName
strs <- getState
case Map.lookup k strs of
Just v -> return v
Nothing -> return k
cistring :: Text -> BibParser Text
cistring s = try (go s)
where go t = case T.uncons t of
Nothing -> return ""
Just (c,cs) -> do
x <- char (toLower c) <|> char (toUpper c)
xs <- go cs
return (T.cons x xs)
resolveCrossRefs :: Bool -> [Item] -> [Item]
resolveCrossRefs isBibtex entries =
map (resolveCrossRef isBibtex entries) entries
splitKeys :: Text -> [Text]
splitKeys = filter (not . T.null) . T.split (\c -> c == ' ' || c == ',')
getXrefFields :: Bool -> Item -> [Item] -> Text -> [(Text, Text)]
getXrefFields isBibtex baseEntry entries keys = do
let keys' = splitKeys keys
xrefEntry <- [e | e <- entries, identifier e `elem` keys']
(k, v) <- Map.toList $ fields xrefEntry
if k == "crossref" || k == "xdata"
then do
xs <- mapM (getXrefFields isBibtex baseEntry entries)
(splitKeys v)
(x, y) <- xs
guard $ isNothing $ Map.lookup x $ fields xrefEntry
return (x, y)
else do
k' <- if isBibtex
then return k
else transformKey (entryType xrefEntry) (entryType baseEntry) k
guard $ isNothing $ Map.lookup k' $ fields baseEntry
return (k',v)
resolveCrossRef :: Bool -> [Item] -> Item -> Item
resolveCrossRef isBibtex entries entry =
Map.foldrWithKey go entry (fields entry)
where go key val entry' =
if key == "crossref" || key == "xdata"
then entry'{ fields = fields entry' <>
Map.fromList (getXrefFields isBibtex
entry entries val) }
else entry'
transformKey :: Text -> Text -> Text -> [Text]
transformKey _ _ "ids" = []
transformKey _ _ "crossref" = []
transformKey _ _ "xref" = []
transformKey _ _ "entryset" = []
transformKey _ _ "entrysubtype" = []
transformKey _ _ "execute" = []
transformKey _ _ "label" = []
transformKey _ _ "options" = []
transformKey _ _ "presort" = []
transformKey _ _ "related" = []
transformKey _ _ "relatedoptions" = []
transformKey _ _ "relatedstring" = []
transformKey _ _ "relatedtype" = []
transformKey _ _ "shorthand" = []
transformKey _ _ "shorthandintro" = []
transformKey _ _ "sortkey" = []
transformKey x y "author"
| x `elem` ["mvbook", "book"] &&
y `elem` ["inbook", "bookinbook", "suppbook"] = ["bookauthor", "author"]
transformKey x y "author"
| x == "mvbook" && y == "book" = ["bookauthor", "author"]
transformKey "mvbook" y z
| y `elem` ["book", "inbook", "bookinbook", "suppbook"] = standardTrans z
transformKey x y z
| x `elem` ["mvcollection", "mvreference"] &&
y `elem` ["collection", "reference", "incollection", "inreference",
"suppcollection"] = standardTrans z
transformKey "mvproceedings" y z
| y `elem` ["proceedings", "inproceedings"] = standardTrans z
transformKey "book" y z
| y `elem` ["inbook", "bookinbook", "suppbook"] = bookTrans z
transformKey x y z
| x `elem` ["collection", "reference"] &&
y `elem` ["incollection", "inreference", "suppcollection"] = bookTrans z
transformKey "proceedings" "inproceedings" z = bookTrans z
transformKey "periodical" y z
| y `elem` ["article", "suppperiodical"] =
case z of
"title" -> ["journaltitle"]
"subtitle" -> ["journalsubtitle"]
"shorttitle" -> []
"sorttitle" -> []
"indextitle" -> []
"indexsorttitle" -> []
_ -> [z]
transformKey _ _ x = [x]
standardTrans :: Text -> [Text]
standardTrans z =
case z of
"title" -> ["maintitle"]
"subtitle" -> ["mainsubtitle"]
"titleaddon" -> ["maintitleaddon"]
"shorttitle" -> []
"sorttitle" -> []
"indextitle" -> []
"indexsorttitle" -> []
_ -> [z]
bookTrans :: Text -> [Text]
bookTrans z =
case z of
"title" -> ["booktitle"]
"subtitle" -> ["booksubtitle"]
"titleaddon" -> ["booktitleaddon"]
"shorttitle" -> []
"sorttitle" -> []
"indextitle" -> []
"indexsorttitle" -> []
_ -> [z]
data Lang = Lang Text Text
langToLocale :: Lang -> Text
langToLocale (Lang x y) = x <> (if T.null y then "" else T.cons '-' y)
resolveKey :: Lang -> Formatted -> Formatted
resolveKey lang (Formatted ils) = Formatted (Walk.walk go ils)
where go (Str s) = Str $ resolveKey' lang s
go x = x
resolveKey' :: Lang -> Text -> Text
resolveKey' (Lang "ca" "AD") k =
case T.toLower k of
"inpreparation" -> "en preparació"
"submitted" -> "enviat"
"forthcoming" -> "disponible en breu"
"inpress" -> "a impremta"
"prepublished" -> "pre-publicat"
"mathesis" -> "tesi de màster"
"phdthesis" -> "tesi doctoral"
"candthesis" -> "tesi de candidatura"
"techreport" -> "informe tècnic"
"resreport" -> "informe de recerca"
"software" -> "programari"
"datacd" -> "CD de dades"
"audiocd" -> "CD d’àudio"
"patent" -> "patent"
"patentde" -> "patent alemana"
"patenteu" -> "patent europea"
"patentfr" -> "patent francesa"
"patentuk" -> "patent britànica"
"patentus" -> "patent estatunidenca"
"patreq" -> "soŀlicitud de patent"
"patreqde" -> "soŀlicitud de patent alemana"
"patreqeu" -> "soŀlicitud de patent europea"
"patreqfr" -> "soŀlicitud de patent francesa"
"patrequk" -> "soŀlicitud de patent britànica"
"patrequs" -> "soŀlicitud de patent estatunidenca"
"countryde" -> "Alemanya"
"countryeu" -> "Unió Europea"
"countryep" -> "Unió Europea"
"countryfr" -> "França"
"countryuk" -> "Regne Unit"
"countryus" -> "Estats Units d’Amèrica"
"newseries" -> "sèrie nova"
"oldseries" -> "sèrie antiga"
_ -> k
resolveKey' (Lang "da" "DK") k =
case T.toLower k of
"forthcoming" -> "kommende"
"inpress" -> "i tryk"
"mathesis" -> "speciale"
"phdthesis" -> "ph.d.-afhandling"
"candthesis" -> "kandidatafhandling"
"techreport" -> "teknisk rapport"
"resreport" -> "forskningsrapport"
"software" -> "software"
"datacd" -> "data-cd"
"audiocd" -> "lyd-cd"
"patent" -> "patent"
"patentde" -> "tysk patent"
"patenteu" -> "europæisk patent"
"patentfr" -> "fransk patent"
"patentuk" -> "britisk patent"
"patentus" -> "amerikansk patent"
"patreq" -> "ansøgning om patent"
"patreqde" -> "ansøgning om tysk patent"
"patreqeu" -> "ansøgning om europæisk patent"
"patreqfr" -> "ansøgning om fransk patent"
"patrequk" -> "ansøgning om britisk patent"
"patrequs" -> "ansøgning om amerikansk patent"
"countryde" -> "Tyskland"
"countryeu" -> "Europæiske Union"
"countryep" -> "Europæiske Union"
"countryfr" -> "Frankrig"
"countryuk" -> "Storbritanien"
"countryus" -> "USA"
"newseries" -> "ny serie"
"oldseries" -> "gammel serie"
_ -> k
resolveKey' (Lang "de" "DE") k =
case T.toLower k of
"inpreparation" -> "in Vorbereitung"
"submitted" -> "eingereicht"
"forthcoming" -> "im Erscheinen"
"inpress" -> "im Druck"
"prepublished" -> "Vorveröffentlichung"
"mathesis" -> "Magisterarbeit"
"phdthesis" -> "Dissertation"
"techreport" -> "Technischer Bericht"
"resreport" -> "Forschungsbericht"
"software" -> "Computer-Software"
"datacd" -> "CD-ROM"
"audiocd" -> "Audio-CD"
"patent" -> "Patent"
"patentde" -> "deutsches Patent"
"patenteu" -> "europäisches Patent"
"patentfr" -> "französisches Patent"
"patentuk" -> "britisches Patent"
"patentus" -> "US-Patent"
"patreq" -> "Patentanmeldung"
"patreqde" -> "deutsche Patentanmeldung"
"patreqeu" -> "europäische Patentanmeldung"
"patreqfr" -> "französische Patentanmeldung"
"patrequk" -> "britische Patentanmeldung"
"patrequs" -> "US-Patentanmeldung"
"countryde" -> "Deutschland"
"countryeu" -> "Europäische Union"
"countryep" -> "Europäische Union"
"countryfr" -> "Frankreich"
"countryuk" -> "Großbritannien"
"countryus" -> "USA"
"newseries" -> "neue Folge"
"oldseries" -> "alte Folge"
_ -> k
resolveKey' (Lang "en" "US") k =
case T.toLower k of
"audiocd" -> "audio CD"
"by" -> "by"
"candthesis" -> "Candidate thesis"
"countryde" -> "Germany"
"countryep" -> "European Union"
"countryeu" -> "European Union"
"countryfr" -> "France"
"countryuk" -> "United Kingdom"
"countryus" -> "United States of America"
"datacd" -> "data CD"
"edition" -> "ed."
"forthcoming" -> "forthcoming"
"inpreparation" -> "in preparation"
"inpress" -> "in press"
"introduction" -> "introduction"
"jourser" -> "ser."
"mathesis" -> "Master’s thesis"
"newseries" -> "new series"
"nodate" -> "n. d."
"number" -> "no."
"numbers" -> "nos."
"oldseries" -> "old series"
"patent" -> "patent"
"patentde" -> "German patent"
"patenteu" -> "European patent"
"patentfr" -> "French patent"
"patentuk" -> "British patent"
"patentus" -> "U.S. patent"
"patreq" -> "patent request"
"patreqde" -> "German patent request"
"patreqeu" -> "European patent request"
"patreqfr" -> "French patent request"
"patrequk" -> "British patent request"
"patrequs" -> "U.S. patent request"
"phdthesis" -> "PhD thesis"
"prepublished" -> "pre-published"
"pseudonym" -> "pseud."
"recorded" -> "recorded"
"resreport" -> "research report"
"reviewof" -> "Review of"
"revisededition" -> "rev. ed."
"software" -> "computer software"
"submitted" -> "submitted"
"techreport" -> "technical report"
"volume" -> "vol."
_ -> k
resolveKey' (Lang "es" "ES") k =
case T.toLower k of
"forthcoming" -> "previsto"
"inpress" -> "en imprenta"
"mathesis" -> "Tesis de licenciatura"
"phdthesis" -> "Tesis doctoral"
"techreport" -> "informe técnico"
"patent" -> "patente"
"patentde" -> "patente alemana"
"patenteu" -> "patente europea"
"patentfr" -> "patente francesa"
"patentuk" -> "patente británica"
"patentus" -> "patente americana"
"patreq" -> "solicitud de patente"
"patreqde" -> "solicitud de patente alemana"
"patreqeu" -> "solicitud de patente europea"
"patreqfr" -> "solicitud de patente francesa"
"patrequk" -> "solicitud de patente británica"
"patrequs" -> "solicitud de patente americana"
"countryde" -> "Alemania"
"countryeu" -> "Unión Europea"
"countryep" -> "Unión Europea"
"countryfr" -> "Francia"
"countryuk" -> "Reino Unido"
"countryus" -> "Estados Unidos de América"
"newseries" -> "nueva época"
"oldseries" -> "antigua época"
_ -> k
resolveKey' (Lang "fi" "FI") k =
case T.toLower k of
"forthcoming" -> "tulossa"
"inpress" -> "painossa"
"mathesis" -> "tutkielma"
"phdthesis" -> "tohtorinväitöskirja"
"candthesis" -> "kandidat"
"techreport" -> "tekninen raportti"
"resreport" -> "tutkimusraportti"
"software" -> "ohjelmisto"
"datacd" -> "data-CD"
"audiocd" -> "ääni-CD"
"patent" -> "patentti"
"patentde" -> "saksalainen patentti"
"patenteu" -> "Euroopan Unionin patentti"
"patentfr" -> "ranskalainen patentti"
"patentuk" -> "englantilainen patentti"
"patentus" -> "yhdysvaltalainen patentti"
"patreq" -> "patenttihakemus"
"patreqde" -> "saksalainen patenttihakemus"
"patreqeu" -> "Euroopan Unionin patenttihakemus"
"patreqfr" -> "ranskalainen patenttihakemus"
"patrequk" -> "englantilainen patenttihakemus"
"patrequs" -> "yhdysvaltalainen patenttihakemus"
"countryde" -> "Saksa"
"countryeu" -> "Euroopan Unioni"
"countryep" -> "Euroopan Unioni"
"countryfr" -> "Ranska"
"countryuk" -> "Iso-Britannia"
"countryus" -> "Yhdysvallat"
"newseries" -> "uusi sarja"
"oldseries" -> "vanha sarja"
_ -> k
resolveKey' (Lang "fr" "FR") k =
case T.toLower k of
"inpreparation" -> "en préparation"
"submitted" -> "soumis"
"forthcoming" -> "à paraître"
"inpress" -> "sous presse"
"prepublished" -> "prépublié"
"mathesis" -> "mémoire de master"
"phdthesis" -> "thèse de doctorat"
"candthesis" -> "thèse de candidature"
"techreport" -> "rapport technique"
"resreport" -> "rapport scientifique"
"software" -> "logiciel"
"datacd" -> "cédérom"
"audiocd" -> "disque compact audio"
"patent" -> "brevet"
"patentde" -> "brevet allemand"
"patenteu" -> "brevet européen"
"patentfr" -> "brevet français"
"patentuk" -> "brevet britannique"
"patentus" -> "brevet américain"
"patreq" -> "demande de brevet"
"patreqde" -> "demande de brevet allemand"
"patreqeu" -> "demande de brevet européen"
"patreqfr" -> "demande de brevet français"
"patrequk" -> "demande de brevet britannique"
"patrequs" -> "demande de brevet américain"
"countryde" -> "Allemagne"
"countryeu" -> "Union européenne"
"countryep" -> "Union européenne"
"countryfr" -> "France"
"countryuk" -> "Royaume-Uni"
"countryus" -> "États-Unis"
"newseries" -> "nouvelle série"
"oldseries" -> "ancienne série"
_ -> k
resolveKey' (Lang "it" "IT") k =
case T.toLower k of
"forthcoming" -> "futuro"
"inpress" -> "in stampa"
"mathesis" -> "tesi di laurea magistrale"
"phdthesis" -> "tesi di dottorato"
"techreport" -> "rapporto tecnico"
"resreport" -> "rapporto di ricerca"
"patent" -> "brevetto"
"patentde" -> "brevetto tedesco"
"patenteu" -> "brevetto europeo"
"patentfr" -> "brevetto francese"
"patentuk" -> "brevetto britannico"
"patentus" -> "brevetto americano"
"patreq" -> "brevetto richiesto"
"patreqde" -> "brevetto tedesco richiesto"
"patreqeu" -> "brevetto europeo richiesto"
"patreqfr" -> "brevetto francese richiesto"
"patrequk" -> "brevetto britannico richiesto"
"patrequs" -> "brevetto U.S.A. richiesto"
"countryde" -> "Germania"
"countryeu" -> "Unione Europea"
"countryep" -> "Unione Europea"
"countryfr" -> "Francia"
"countryuk" -> "Regno Unito"
"countryus" -> "Stati Uniti d’America"
"newseries" -> "nuova serie"
"oldseries" -> "vecchia serie"
_ -> k
resolveKey' (Lang "nl" "NL") k =
case T.toLower k of
"inpreparation" -> "in voorbereiding"
"submitted" -> "ingediend"
"forthcoming" -> "onderweg"
"inpress" -> "in druk"
"prepublished" -> "voorpublicatie"
"mathesis" -> "masterscriptie"
"phdthesis" -> "proefschrift"
"techreport" -> "technisch rapport"
"resreport" -> "onderzoeksrapport"
"software" -> "computersoftware"
"datacd" -> "cd-rom"
"audiocd" -> "audio-cd"
"patent" -> "patent"
"patentde" -> "Duits patent"
"patenteu" -> "Europees patent"
"patentfr" -> "Frans patent"
"patentuk" -> "Brits patent"
"patentus" -> "Amerikaans patent"
"patreq" -> "patentaanvraag"
"patreqde" -> "Duitse patentaanvraag"
"patreqeu" -> "Europese patentaanvraag"
"patreqfr" -> "Franse patentaanvraag"
"patrequk" -> "Britse patentaanvraag"
"patrequs" -> "Amerikaanse patentaanvraag"
"countryde" -> "Duitsland"
"countryeu" -> "Europese Unie"
"countryep" -> "Europese Unie"
"countryfr" -> "Frankrijk"
"countryuk" -> "Verenigd Koninkrijk"
"countryus" -> "Verenigde Staten van Amerika"
"newseries" -> "nieuwe reeks"
"oldseries" -> "oude reeks"
_ -> k
resolveKey' (Lang "pl" "PL") k =
case T.toLower k of
"inpreparation" -> "przygotowanie"
"submitted" -> "prezentacja"
"forthcoming" -> "przygotowanie"
"inpress" -> "wydrukowane"
"prepublished" -> "przedwydanie"
"mathesis" -> "praca magisterska"
"phdthesis" -> "praca doktorska"
"techreport" -> "sprawozdanie techniczne"
"resreport" -> "sprawozdanie naukowe"
"software" -> "oprogramowanie"
"datacd" -> "CD-ROM"
"audiocd" -> "audio CD"
"patent" -> "patent"
"patentde" -> "patent Niemiec"
"patenteu" -> "patent Europy"
"patentfr" -> "patent Francji"
"patentuk" -> "patent Wielkiej Brytanji"
"patentus" -> "patent USA"
"patreq" -> "podanie na patent"
"patreqeu" -> "podanie na patent Europy"
"patrequs" -> "podanie na patent USA"
"countryde" -> "Niemcy"
"countryeu" -> "Unia Europejska"
"countryep" -> "Unia Europejska"
"countryfr" -> "Francja"
"countryuk" -> "Wielka Brytania"
"countryus" -> "Stany Zjednoczone Ameryki"
"newseries" -> "nowa serja"
"oldseries" -> "stara serja"
_ -> k
resolveKey' (Lang "pt" "PT") k =
case T.toLower k of
"techreport" -> "relatório técnico"
"resreport" -> "relatório de pesquisa"
"software" -> "software"
"datacd" -> "CD-ROM"
"patent" -> "patente"
"patentde" -> "patente alemã"
"patenteu" -> "patente européia"
"patentfr" -> "patente francesa"
"patentuk" -> "patente britânica"
"patentus" -> "patente americana"
"patreq" -> "pedido de patente"
"patreqde" -> "pedido de patente alemã"
"patreqeu" -> "pedido de patente européia"
"patreqfr" -> "pedido de patente francesa"
"patrequk" -> "pedido de patente britânica"
"patrequs" -> "pedido de patente americana"
"countryde" -> "Alemanha"
"countryeu" -> "União Europeia"
"countryep" -> "União Europeia"
"countryfr" -> "França"
"countryuk" -> "Reino Unido"
"countryus" -> "Estados Unidos da América"
"newseries" -> "nova série"
"oldseries" -> "série antiga"
"forthcoming" -> "a publicar"
"inpress" -> "na imprensa"
"mathesis" -> "tese de mestrado"
"phdthesis" -> "tese de doutoramento"
"audiocd" -> "CD áudio"
_ -> k
resolveKey' (Lang "pt" "BR") k =
case T.toLower k of
"techreport" -> "relatório técnico"
"resreport" -> "relatório de pesquisa"
"software" -> "software"
"datacd" -> "CD-ROM"
"patent" -> "patente"
"patentde" -> "patente alemã"
"patenteu" -> "patente européia"
"patentfr" -> "patente francesa"
"patentuk" -> "patente britânica"
"patentus" -> "patente americana"
"patreq" -> "pedido de patente"
"patreqde" -> "pedido de patente alemã"
"patreqeu" -> "pedido de patente européia"
"patreqfr" -> "pedido de patente francesa"
"patrequk" -> "pedido de patente britânica"
"patrequs" -> "pedido de patente americana"
"countryde" -> "Alemanha"
"countryeu" -> "União Europeia"
"countryep" -> "União Europeia"
"countryfr" -> "França"
"countryuk" -> "Reino Unido"
"countryus" -> "Estados Unidos da América"
"newseries" -> "nova série"
"oldseries" -> "série antiga"
"inpreparation" -> "em preparação"
"forthcoming" -> "aceito para publicação"
"inpress" -> "no prelo"
"prepublished" -> "pré-publicado"
"mathesis" -> "dissertação de mestrado"
"phdthesis" -> "tese de doutorado"
"audiocd" -> "CD de áudio"
_ -> k
resolveKey' (Lang "sv" "SE") k =
case T.toLower k of
"forthcoming" -> "kommande"
"inpress" -> "i tryck"
"mathesis" -> "examensarbete"
"phdthesis" -> "doktorsavhandling"
"candthesis" -> "kandidatavhandling"
"techreport" -> "teknisk rapport"
"resreport" -> "forskningsrapport"
"software" -> "datorprogram"
"datacd" -> "data-cd"
"audiocd" -> "ljud-cd"
"patent" -> "patent"
"patentde" -> "tyskt patent"
"patenteu" -> "europeiskt patent"
"patentfr" -> "franskt patent"
"patentuk" -> "brittiskt patent"
"patentus" -> "amerikanskt patent"
"patreq" -> "patentansökan"
"patreqde" -> "ansökan om tyskt patent"
"patreqeu" -> "ansökan om europeiskt patent"
"patreqfr" -> "ansökan om franskt patent"
"patrequk" -> "ansökan om brittiskt patent"
"patrequs" -> "ansökan om amerikanskt patent"
"countryde" -> "Tyskland"
"countryeu" -> "Europeiska unionen"
"countryep" -> "Europeiska unionen"
"countryfr" -> "Frankrike"
"countryuk" -> "Storbritannien"
"countryus" -> "USA"
"newseries" -> "ny följd"
"oldseries" -> "gammal följd"
_ -> k
resolveKey' _ k = resolveKey' (Lang "en" "US") k
parseMonth :: Text -> Maybe Int
parseMonth s =
case T.toLower s of
"jan" -> Just 1
"feb" -> Just 2
"mar" -> Just 3
"apr" -> Just 4
"may" -> Just 5
"jun" -> Just 6
"jul" -> Just 7
"aug" -> Just 8
"sep" -> Just 9
"oct" -> Just 10
"nov" -> Just 11
"dec" -> Just 12
_ -> safeRead s
data BibState = BibState{
untitlecase :: Bool
, localeLanguage :: Lang
}
type Bib = RWST Item () BibState Maybe
notFound :: Text -> Bib a
notFound f = Prelude.fail $ T.unpack f ++ " not found"
getField :: Text -> Bib Formatted
getField f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> latex x
Nothing -> notFound f
getPeriodicalTitle :: Text -> Bib Formatted
getPeriodicalTitle f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> blocksToFormatted $ onBlocks protectCase $ latex' $ trim x
Nothing -> notFound f
getTitle :: Text -> Bib Formatted
getTitle f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> latexTitle x
Nothing -> notFound f
getShortTitle :: Bool -> Text -> Bib Formatted
getShortTitle requireColon f = do
fs <- asks fields
utc <- gets untitlecase
let processTitle = if utc then onBlocks unTitlecase else id
case Map.lookup f fs of
Just x -> case processTitle $ latex' x of
bs | not requireColon || containsColon bs ->
blocksToFormatted $ upToColon bs
| otherwise -> return mempty
Nothing -> notFound f
containsColon :: [Block] -> Bool
containsColon [Para xs] = Str ":" `elem` xs
containsColon [Plain xs] = containsColon [Para xs]
containsColon _ = False
upToColon :: [Block] -> [Block]
upToColon [Para xs] = [Para $ takeWhile (/= Str ":") xs]
upToColon [Plain xs] = upToColon [Para xs]
upToColon bs = bs
getDates :: Text -> Bib [RefDate]
getDates f = parseEDTFDate <$> getRawField f
isNumber :: Text -> Bool
isNumber t = case T.uncons t of
Just ('-', ds) -> T.all isDigit ds
Just _ -> T.all isDigit t
Nothing -> False
fixLeadingDash :: Text -> Text
fixLeadingDash t = case T.uncons t of
Just (c, ds) | (c == '–' || c == '—') && firstIsDigit ds -> T.cons '–' ds
_ -> t
where firstIsDigit = maybe False (isDigit . fst) . T.uncons
getOldDates :: Text -> Bib [RefDate]
getOldDates prefix = do
year' <- fixLeadingDash <$> getRawField (prefix <> "year")
<|> return ""
month' <- (parseMonth <$> getRawField (prefix <> "month"))
<|> return Nothing
day' <- (safeRead <$> getRawField (prefix <> "day"))
<|> return Nothing
endyear' <- (fixLeadingDash <$> getRawField (prefix <> "endyear"))
<|> return ""
endmonth' <- (parseMonth <$> getRawField (prefix <> "endmonth"))
<|> return Nothing
endday' <- (safeRead <$> getRawField (prefix <> "endday"))
<|> return Nothing
let start' = RefDate { year = safeRead year'
, month = month'
, season = Nothing
, day = day'
, other = Literal $ if isNumber year' then "" else year'
, circa = False
}
let end' = RefDate { year = safeRead endyear'
, month = endmonth'
, day = endday'
, season = Nothing
, other = Literal $ if isNumber endyear' then "" else endyear'
, circa = False
}
let hasyear r = isJust (year r)
return $ filter hasyear [start', end']
getRawField :: Text -> Bib Text
getRawField f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> return x
Nothing -> notFound f
getAuthorList :: Options -> Text -> Bib [Agent]
getAuthorList opts f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> latexAuthors opts x
Nothing -> notFound f
getLiteralList :: Text -> Bib [Formatted]
getLiteralList f = do
fs <- asks fields
case Map.lookup f fs of
Just x -> toLiteralList $ latex' x
Nothing -> notFound f
getLiteralList' :: Text -> Bib Formatted
getLiteralList' f = Formatted . intercalate [Str ";", Space] . map unFormatted
<$> getLiteralList f
splitByAnd :: [Inline] -> [[Inline]]
splitByAnd = splitOn [Space, Str "and", Space]
toLiteralList :: [Block] -> Bib [Formatted]
toLiteralList [Para xs] =
mapM inlinesToFormatted $ splitByAnd xs
toLiteralList [Plain xs] = toLiteralList [Para xs]
toLiteralList _ = mzero
toAuthorList :: Options -> [Block] -> Bib [Agent]
toAuthorList opts [Para xs] =
filter (/= emptyAgent) <$> mapM (toAuthor opts . addSpaceAfterPeriod)
(splitByAnd xs)
toAuthorList opts [Plain xs] = toAuthorList opts [Para xs]
toAuthorList _ _ = mzero
toAuthor :: Options -> [Inline] -> Bib Agent
toAuthor _ [Str "others"] =
return
Agent { givenName = []
, droppingPart = mempty
, nonDroppingPart = mempty
, familyName = mempty
, nameSuffix = mempty
, literal = Formatted [Str "others"]
, commaSuffix = False
, parseNames = False
}
toAuthor _ [Span ("",[],[]) ils] =
return
Agent { givenName = []
, droppingPart = mempty
, nonDroppingPart = mempty
, familyName = mempty
, nameSuffix = mempty
, literal = Formatted ils
, commaSuffix = False
, parseNames = False
}
toAuthor _ ils@(Str ys:_) | T.any (== '=') ys = do
let commaParts = Data.List.Split.splitWhen (== Str ",")
. splitStrWhen (\c -> c == ',' || c == '=' || c == '\160')
$ ils
let addPart ag (Str "given" : Str "=" : xs) =
ag{ givenName = givenName ag ++ [Formatted xs] }
addPart ag (Str "family" : Str "=" : xs) =
ag{ familyName = Formatted xs }
addPart ag (Str "prefix" : Str "=" : xs) =
ag{ droppingPart = Formatted xs }
addPart ag (Str "useprefix" : Str "=" : Str "true" : _) =
ag{ nonDroppingPart = droppingPart ag, droppingPart = mempty }
addPart ag (Str "suffix" : Str "=" : xs) =
ag{ nameSuffix = Formatted xs }
addPart ag (Space : xs) = addPart ag xs
addPart ag _ = ag
return $ foldl' addPart emptyAgent commaParts
toAuthor opts ils = do
let useprefix = optionSet "useprefix" opts
let usecomma = optionSet "juniorcomma" opts
let bibtex = optionSet "bibtex" opts
let words' = wordsBy (\x -> x == Space || x == Str "\160")
let commaParts = map words' $ Data.List.Split.splitWhen (== Str ",")
$ splitStrWhen (\c -> c == ',' || c == '\160') ils
let (first, vonlast, jr) =
case commaParts of
[fvl] -> let (caps', rest') = span isCapitalized fvl
in if null rest' && not (null caps')
then (init caps', [last caps'], [])
else (caps', rest', [])
[vl,f] -> (f, vl, [])
(vl:j:f:_) -> (f, vl, j )
[] -> ([], [], [])
let (von, lastname) =
if bibtex
then case span isCapitalized $ reverse vonlast of
([],w:ws) -> (reverse ws, [w])
(vs, ws) -> (reverse ws, reverse vs)
else case break isCapitalized vonlast of
(vs@(_:_), []) -> (init vs, [last vs])
(vs, ws) -> (vs, ws)
let prefix = Formatted $ intercalate [Space] von
let family = Formatted $ intercalate [Space] lastname
let suffix = Formatted $ intercalate [Space] jr
let givens = map Formatted first
return Agent
{ givenName = givens
, droppingPart = if useprefix then mempty else prefix
, nonDroppingPart = if useprefix then prefix else mempty
, familyName = family
, nameSuffix = suffix
, literal = mempty
, commaSuffix = usecomma
, parseNames = False
}
isCapitalized :: [Inline] -> Bool
isCapitalized (Str (T.uncons -> Just (c,cs)) : rest)
| isUpper c = True
| isDigit c = isCapitalized (Str cs : rest)
| otherwise = False
isCapitalized (_:rest) = isCapitalized rest
isCapitalized [] = True
optionSet :: Text -> Options -> Bool
optionSet key opts = case lookup key opts of
Just "true" -> True
Just s -> s == mempty
_ -> False
latex' :: Text -> [Block]
latex' s = Walk.walk removeSoftBreak bs
where Pandoc _ bs = readLaTeX s
removeSoftBreak :: Inline -> Inline
removeSoftBreak SoftBreak = Space
removeSoftBreak x = x
latex :: Text -> Bib Formatted
latex s = blocksToFormatted $ latex' $ trim s
latexTitle :: Text -> Bib Formatted
latexTitle s = do
utc <- gets untitlecase
let processTitle = if utc then onBlocks unTitlecase else id
blocksToFormatted $ processTitle $ latex' s
latexAuthors :: Options -> Text -> Bib [Agent]
latexAuthors opts = toAuthorList opts . latex' . trim
bib :: Bib Reference -> Item -> Maybe Reference
bib m entry = fst Control.Applicative.<$> evalRWST m entry (BibState True (Lang "en" "US"))
toLocale :: Text -> Text
toLocale "english" = "en-US"
toLocale "usenglish" = "en-US"
toLocale "american" = "en-US"
toLocale "british" = "en-GB"
toLocale "ukenglish" = "en-GB"
toLocale "canadian" = "en-US"
toLocale "australian" = "en-GB"
toLocale "newzealand" = "en-GB"
toLocale "afrikaans" = "af-ZA"
toLocale "arabic" = "ar"
toLocale "basque" = "eu"
toLocale "bulgarian" = "bg-BG"
toLocale "catalan" = "ca-AD"
toLocale "croatian" = "hr-HR"
toLocale "czech" = "cs-CZ"
toLocale "danish" = "da-DK"
toLocale "dutch" = "nl-NL"
toLocale "estonian" = "et-EE"
toLocale "finnish" = "fi-FI"
toLocale "canadien" = "fr-CA"
toLocale "acadian" = "fr-CA"
toLocale "french" = "fr-FR"
toLocale "francais" = "fr-FR"
toLocale "austrian" = "de-AT"
toLocale "naustrian" = "de-AT"
toLocale "german" = "de-DE"
toLocale "germanb" = "de-DE"
toLocale "ngerman" = "de-DE"
toLocale "greek" = "el-GR"
toLocale "polutonikogreek" = "el-GR"
toLocale "hebrew" = "he-IL"
toLocale "hungarian" = "hu-HU"
toLocale "icelandic" = "is-IS"
toLocale "italian" = "it-IT"
toLocale "japanese" = "ja-JP"
toLocale "latvian" = "lv-LV"
toLocale "lithuanian" = "lt-LT"
toLocale "magyar" = "hu-HU"
toLocale "mongolian" = "mn-MN"
toLocale "norsk" = "nb-NO"
toLocale "nynorsk" = "nn-NO"
toLocale "farsi" = "fa-IR"
toLocale "polish" = "pl-PL"
toLocale "brazil" = "pt-BR"
toLocale "brazilian" = "pt-BR"
toLocale "portugues" = "pt-PT"
toLocale "portuguese" = "pt-PT"
toLocale "romanian" = "ro-RO"
toLocale "russian" = "ru-RU"
toLocale "serbian" = "sr-RS"
toLocale "serbianc" = "sr-RS"
toLocale "slovak" = "sk-SK"
toLocale "slovene" = "sl-SL"
toLocale "spanish" = "es-ES"
toLocale "swedish" = "sv-SE"
toLocale "thai" = "th-TH"
toLocale "turkish" = "tr-TR"
toLocale "ukrainian" = "uk-UA"
toLocale "vietnamese" = "vi-VN"
toLocale "latin" = "la"
toLocale x = x
concatWith :: Char -> [Formatted] -> Formatted
concatWith sep = Formatted . foldl' go mempty . map unFormatted
where go :: [Inline] -> [Inline] -> [Inline]
go accum [] = accum
go accum s = case reverse accum of
[] -> s
(Str x:_)
| not (T.null x) && T.last x `elem` ("!?.,:;" :: String)
-> accum ++ (Space : s)
_ -> accum ++ (Str (T.singleton sep) : Space : s)
type Options = [(Text, Text)]
parseOptions :: Text -> Options
parseOptions = map breakOpt . T.splitOn ","
where breakOpt x = case T.break (=='=') x of
(w,v) -> (T.toLower $ trim w,
T.toLower $ trim $ T.drop 1 v)
ordinalize :: Locale -> Text -> Text
ordinalize locale n =
case [termSingular c | c <- terms, cslTerm c == ("ordinal-" <> pad0 n)] ++
[termSingular c | c <- terms, cslTerm c == "ordinal"] of
(suff:_) -> n <> suff
[] -> n
where pad0 s = case T.uncons s of
Just (c,"") -> T.pack ['0', c]
_ -> s
terms = localeTerms locale
itemToReference :: Lang -> Locale -> Bool -> Bool -> Item -> Maybe Reference
itemToReference lang locale bibtex caseTransform = bib $ do
modify $ \st -> st{ localeLanguage = lang,
untitlecase = case lang of
Lang "en" _ -> caseTransform
_ -> False }
id' <- asks identifier
otherIds <- (map trim . T.splitOn "," <$> getRawField "ids")
<|> return []
et <- asks entryType
guard $ et /= "xdata"
opts <- (parseOptions <$> getRawField "options") <|> return []
let getAuthorList' = getAuthorList
(("bibtex", T.toLower . T.pack $ show bibtex):opts)
st <- getRawField "entrysubtype" <|> return mempty
isEvent <- (True <$ (getRawField "eventdate"
<|> getRawField "eventtitle"
<|> getRawField "venue")) <|> return False
reftype' <- resolveKey lang <$> getField "type" <|> return mempty
let (reftype, refgenre) = case et of
"article"
| st == "magazine" -> (ArticleMagazine,mempty)
| st == "newspaper" -> (ArticleNewspaper,mempty)
| otherwise -> (ArticleJournal,mempty)
"book" -> (Book,mempty)
"booklet" -> (Pamphlet,mempty)
"bookinbook" -> (Chapter,mempty)
"collection" -> (Book,mempty)
"dataset" -> (Dataset,mempty)
"electronic" -> (Webpage,mempty)
"inbook" -> (Chapter,mempty)
"incollection" -> (Chapter,mempty)
"inreference" -> (EntryEncyclopedia,mempty)
"inproceedings" -> (PaperConference,mempty)
"manual" -> (Book,mempty)
"mastersthesis" -> (Thesis, if reftype' == mempty
then Formatted [Str $ resolveKey' lang "mathesis"]
else reftype')
"misc" -> (NoType,mempty)
"mvbook" -> (Book,mempty)
"mvcollection" -> (Book,mempty)
"mvproceedings" -> (Book,mempty)
"mvreference" -> (Book,mempty)
"online" -> (Webpage,mempty)
"patent" -> (Patent,mempty)
"periodical"
| st == "magazine" -> (ArticleMagazine,mempty)
| st == "newspaper" -> (ArticleNewspaper,mempty)
| otherwise -> (ArticleJournal,mempty)
"phdthesis" -> (Thesis, if reftype' == mempty
then Formatted [Str $ resolveKey' lang "phdthesis"]
else reftype')
"proceedings" -> (Book,mempty)
"reference" -> (Book,mempty)
"report" -> (Report,mempty)
"software" -> (Book,mempty)
"suppbook" -> (Chapter,mempty)
"suppcollection" -> (Chapter,mempty)
"suppperiodical"
| st == "magazine" -> (ArticleMagazine,mempty)
| st == "newspaper" -> (ArticleNewspaper,mempty)
| otherwise -> (ArticleJournal,mempty)
"techreport" -> (Report,mempty)
"thesis" -> (Thesis,mempty)
"unpublished" -> (if isEvent then Speech else Manuscript,mempty)
"www" -> (Webpage,mempty)
"artwork" -> (Graphic,mempty)
"audio" -> (Song,mempty)
"commentary" -> (Book,mempty)
"image" -> (Graphic,mempty)
"jurisdiction" -> (LegalCase,mempty)
"legislation" -> (Legislation,mempty)
"legal" -> (Treaty,mempty)
"letter" -> (PersonalCommunication,mempty)
"movie" -> (MotionPicture,mempty)
"music" -> (Song,mempty)
"performance" -> (Speech,mempty)
"review" -> (Review,mempty)
"standard" -> (Legislation,mempty)
"video" -> (MotionPicture,mempty)
"data" -> (Dataset,mempty)
"letters" -> (PersonalCommunication,mempty)
"newsarticle" -> (ArticleNewspaper,mempty)
_ -> (NoType,mempty)
let defaultHyphenation = case lang of
Lang x y -> x <> "-" <> y
let getLangId = do
langid <- trim . T.toLower <$> getRawField "langid"
idopts <- trim . T.toLower <$>
getRawField "langidopts" <|> return ""
case (langid, idopts) of
("english","variant=british") -> return "british"
("english","variant=american") -> return "american"
("english","variant=us") -> return "american"
("english","variant=usmax") -> return "american"
("english","variant=uk") -> return "british"
("english","variant=australian") -> return "australian"
("english","variant=newzealand") -> return "newzealand"
(x,_) -> return x
hyphenation <- (toLocale . T.toLower <$>
(getLangId <|> getRawField "hyphenation"))
<|> return mempty
author' <- getAuthorList' "author" <|> return []
containerAuthor' <- getAuthorList' "bookauthor" <|> return []
translator' <- getAuthorList' "translator" <|> return []
editortype <- getRawField "editortype" <|> return mempty
editor'' <- getAuthorList' "editor" <|> return []
director'' <- getAuthorList' "director" <|> return []
let (editor', director') = case editortype of
"director" -> ([], editor'')
_ -> (editor'', director'')
let isArticle = et `elem` ["article", "periodical", "suppperiodical", "review"]
let isPeriodical = et == "periodical"
let isChapterlike = et `elem`
["inbook","incollection","inproceedings","inreference","bookinbook"]
hasMaintitle <- (True <$ getRawField "maintitle") <|> return False
let hyphenation' = if T.null hyphenation
then defaultHyphenation
else hyphenation
let la = case T.splitOn "-" hyphenation' of
(x:_) -> x
[] -> mempty
modify $ \s -> s{ untitlecase = caseTransform && la == "en" }
title' <- (guard isPeriodical >> getTitle "issuetitle")
<|> (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "maintitle")
<|> getTitle "title"
<|> return mempty
subtitle' <- (guard isPeriodical >> getTitle "issuesubtitle")
<|> (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "mainsubtitle")
<|> getTitle "subtitle"
<|> return mempty
titleaddon' <- (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "maintitleaddon")
<|> getTitle "titleaddon"
<|> return mempty
volumeTitle' <- (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "title")
<|> (guard hasMaintitle >> guard isChapterlike >> getTitle "booktitle")
<|> return mempty
volumeSubtitle' <- (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "subtitle")
<|> (guard hasMaintitle >> guard isChapterlike >> getTitle "booksubtitle")
<|> return mempty
volumeTitleAddon' <- (guard hasMaintitle >> guard (not isChapterlike) >> getTitle "titleaddon")
<|> (guard hasMaintitle >> guard isChapterlike >> getTitle "booktitleaddon")
<|> return mempty
containerTitle' <- (guard isPeriodical >> getPeriodicalTitle "title")
<|> (guard isChapterlike >> getTitle "maintitle")
<|> (guard isChapterlike >> getTitle "booktitle")
<|> getPeriodicalTitle "journaltitle"
<|> getPeriodicalTitle "journal"
<|> return mempty
containerSubtitle' <- (guard isPeriodical >> getPeriodicalTitle "subtitle")
<|> (guard isChapterlike >> getTitle "mainsubtitle")
<|> (guard isChapterlike >> getTitle "booksubtitle")
<|> getPeriodicalTitle "journalsubtitle"
<|> return mempty
containerTitleAddon' <- (guard isPeriodical >> getPeriodicalTitle "titleaddon")
<|> (guard isChapterlike >> getTitle "maintitleaddon")
<|> (guard isChapterlike >> getTitle "booktitleaddon")
<|> return mempty
containerTitleShort' <- (guard isPeriodical >> guard (not hasMaintitle)
>> getField "shorttitle")
<|> getPeriodicalTitle "shortjournal"
<|> return mempty
let fixSeriesTitle (Formatted [Str xs]) | T.all isDigit xs =
Formatted [Str (ordinalize locale xs),
Space, Str (resolveKey' lang "ser.")]
fixSeriesTitle x = x
seriesTitle' <- fixSeriesTitle . resolveKey lang <$>
getTitle "series" <|> return mempty
shortTitle' <- (guard (not hasMaintitle || isChapterlike) >>
getTitle "shorttitle")
<|> if (subtitle' /= mempty || titleaddon' /= mempty) &&
not hasMaintitle
then getShortTitle False "title"
else getShortTitle True "title"
<|> return mempty
eventTitle' <- getTitle "eventtitle" <|> return mempty
origTitle' <- getTitle "origtitle" <|> return mempty
pubfields <- mapM (\f -> Just `fmap`
(if bibtex || f == "howpublished"
then getField f
else getLiteralList' f)
<|> return Nothing)
["school","institution","organization", "howpublished","publisher"]
let publisher' = concatWith ';' $ catMaybes pubfields
origpublisher' <- getField "origpublisher" <|> return mempty
venue' <- getField "venue" <|> return mempty
address' <- (if bibtex
then getField "address"
else getLiteralList' "address"
<|> (guard (et /= "patent") >>
getLiteralList' "location"))
<|> return mempty
origLocation' <- (if bibtex
then getField "origlocation"
else getLiteralList' "origlocation")
<|> return mempty
jurisdiction' <- if et == "patent"
then (concatWith ';' . map (resolveKey lang) <$>
getLiteralList "location") <|> return mempty
else return mempty
pages' <- getField "pages" <|> return mempty
volume' <- getField "volume" <|> return mempty
part' <- getField "part" <|> return mempty
volumes' <- getField "volumes" <|> return mempty
pagetotal' <- getField "pagetotal" <|> return mempty
chapter' <- getField "chapter" <|> return mempty
edition' <- getField "edition" <|> return mempty
version' <- getField "version" <|> return mempty
(number', collectionNumber', issue') <-
(getField "number" <|> return mempty) >>= \x ->
if et `elem` ["book","collection","proceedings","reference",
"mvbook","mvcollection","mvproceedings", "mvreference",
"bookinbook","inbook", "incollection","inproceedings",
"inreference", "suppbook","suppcollection"]
then return (mempty,x,mempty)
else if isArticle
then (getField "issue" >>= \y ->
return (mempty,mempty,concatWith ',' [x,y]))
<|> return (mempty,mempty,x)
else return (x,mempty,mempty)
issued' <- getDates "date" <|> getOldDates mempty <|> return []
eventDate' <- getDates "eventdate" <|> getOldDates "event"
<|> return []
origDate' <- getDates "origdate" <|> getOldDates "orig"
<|> return []
accessed' <- getDates "urldate" <|> getOldDates "url" <|> return []
url' <- (guard (et == "online" || lookup "url" opts /= Just "false")
>> getRawField "url")
<|> (do etype <- getRawField "eprinttype"
eprint <- getRawField "eprint"
let baseUrl =
case T.toLower etype of
"arxiv" -> "http://arxiv.org/abs/"
"jstor" -> "http://www.jstor.org/stable/"
"pubmed" -> "http://www.ncbi.nlm.nih.gov/pubmed/"
"googlebooks" -> "http://books.google.com?id="
_ -> ""
if T.null baseUrl
then mzero
else return $ baseUrl <> eprint)
<|> return mempty
doi' <- (guard (lookup "doi" opts /= Just "false") >> getRawField "doi")
<|> return mempty
isbn' <- getRawField "isbn" <|> return mempty
issn' <- getRawField "issn" <|> return mempty
pmid' <- getRawField "pmid" <|> return mempty
pmcid' <- getRawField "pmcid" <|> return mempty
callNumber' <- getRawField "library" <|> return mempty
annotation' <- getField "annotation" <|> getField "annote"
<|> return mempty
abstract' <- getField "abstract" <|> return mempty
keywords' <- getField "keywords" <|> return mempty
note' <- if et == "periodical"
then return mempty
else getField "note" <|> return mempty
addendum' <- if bibtex
then return mempty
else getField "addendum"
<|> return mempty
pubstate' <- resolveKey lang `fmap`
( getField "pubstate"
<|> case issued' of
(x:_) | other x == Literal "forthcoming" ->
return (Formatted [Str "forthcoming"])
_ -> return mempty
)
let convertEnDash (Str s) = Str (T.map (\c -> if c == '–' then '-' else c) s)
convertEnDash x = x
let takeDigits (Str xs : _) =
case T.takeWhile isDigit xs of
"" -> []
ds -> [Str ds]
takeDigits x = x
return $ emptyReference
{ refId = Literal id'
, refOtherIds = map Literal otherIds
, refType = reftype
, author = author'
, editor = editor'
, translator = translator'
, director = director'
, containerAuthor = containerAuthor'
, issued = issued'
, eventDate = eventDate'
, accessed = accessed'
, originalDate = origDate'
, title = concatWith '.' [
concatWith ':' [title', subtitle']
, titleaddon' ]
, titleShort = shortTitle'
, containerTitle = concatWith '.' [
concatWith ':' [ containerTitle'
, containerSubtitle']
, containerTitleAddon' ]
, collectionTitle = seriesTitle'
, volumeTitle = concatWith '.' [
concatWith ':' [ volumeTitle'
, volumeSubtitle']
, volumeTitleAddon' ]
, containerTitleShort = containerTitleShort'
, collectionNumber = collectionNumber'
, originalTitle = origTitle'
, publisher = publisher'
, originalPublisher = origpublisher'
, publisherPlace = address'
, originalPublisherPlace = origLocation'
, jurisdiction = jurisdiction'
, event = eventTitle'
, eventPlace = venue'
, page = Formatted $
Walk.walk convertEnDash $ unFormatted pages'
, pageFirst = Formatted $ takeDigits $ unFormatted pages'
, numberOfPages = pagetotal'
, version = version'
, volume = Formatted $ intercalate [Str "."]
$ filter (not . null)
[unFormatted volume', unFormatted part']
, numberOfVolumes = volumes'
, issue = issue'
, chapterNumber = chapter'
, status = pubstate'
, edition = edition'
, genre = if refgenre == mempty
then reftype'
else refgenre
, note = concatWith '.' [note', addendum']
, annote = annotation'
, abstract = abstract'
, keyword = keywords'
, number = number'
, url = Literal url'
, doi = Literal doi'
, isbn = Literal isbn'
, issn = Literal issn'
, pmcid = Literal pmcid'
, pmid = Literal pmid'
, language = Literal hyphenation
, callNumber = Literal callNumber'
}