{-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2014 Michal Antkiewicz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -} -- | Creates JSON outputs for different kinds of metadata. module Language.Clafer.JSONMetaData ( generateJSONnameUIDMap, generateJSONScopes, parseJSONScopes, writeCfrScopeFile, readCfrScopeFile ) where import Control.Lens hiding (element) import Data.Aeson.Lens import qualified Data.List as List import Data.Maybe import Data.Json.Builder import Data.String.Conversions import qualified Data.Text as T import System.FilePath import System.Directory import Language.Clafer.QNameUID -- | Generate a JSON list of triples containing a fully-qualified-, least-partially-qualified name, and unique id. -- | Both the FQNames and UIDs are brittle. LPQNames are the least brittle. generateJSONnameUIDMap :: QNameMaps -> String generateJSONnameUIDMap qNameMaps = prettyPrintJSON $ convertString $ toJsonBS $ foldl generateQNameUIDArrayEntry mempty sortedTriples where sortedTriples :: [(FQName, PQName, UID)] sortedTriples = List.sortBy (\(fqName1, _, _) (fqName2, _, _) -> compare fqName1 fqName2) $ getQNameUIDTriples qNameMaps generateQNameUIDArrayEntry :: Array -> (FQName, PQName, UID) -> Array generateQNameUIDArrayEntry array (fqName, lpqName, uid) = mappend array $ element $ mconcat [ row ("fqName" :: String) fqName, row ("lpqName" :: String) lpqName, row ("uid" :: String) uid ] -- | Generate a JSON list of tuples containing a least-partially-qualified name and a scope generateJSONScopes :: QNameMaps -> [(UID, Integer)] -> String generateJSONScopes qNameMaps scopes = prettyPrintJSON $ convertString $ toJsonBS $ foldl generateLpqNameScopeArrayEntry mempty sortedLpqNameScopeList where lpqNameScopeList = map (\(uid, scope) -> (fromMaybe uid $ getLPQName qNameMaps uid, scope)) scopes sortedLpqNameScopeList :: [(PQName, Integer)] sortedLpqNameScopeList = List.sortBy (\(lpqName1, _) (lpqName2, _) -> compare lpqName1 lpqName2) lpqNameScopeList generateLpqNameScopeArrayEntry :: Array -> (PQName, Integer) -> Array generateLpqNameScopeArrayEntry array (lpqName, scope) = mappend array $ element $ mconcat [ row ("lpqName" :: String) lpqName, row ("scope" :: String) scope ] -- insert a new line after [, {, and , prettyPrintJSON :: String -> String prettyPrintJSON ('[':line) = '[':'\n':(prettyPrintJSON line) prettyPrintJSON ('{':line) = '{':'\n':(prettyPrintJSON line) prettyPrintJSON (',':line) = ',':'\n':(prettyPrintJSON line) -- insert a new line before ], } prettyPrintJSON (']':line) = '\n':']':(prettyPrintJSON line) prettyPrintJSON ('}':line) = '\n':'}':(prettyPrintJSON line) -- just rewrite and continue prettyPrintJSON (c:line) = c:(prettyPrintJSON line) prettyPrintJSON "" = "" -- | given the QNameMaps, parse the JSON scopes and return list of scopes parseJSONScopes :: QNameMaps -> String -> [ (UID, Integer) ] parseJSONScopes qNameMaps scopesJSON = foldl (\uidScopes qScope -> (qNameToUIDs qScope) ++ uidScopes) [] decodedScopes where -- QName decodedScopes :: [ (T.Text, Integer) ] decodedScopes = scopesJSON ^.. _Array . traverse . to (\o -> ( o ^?! key "lpqName" . _String , o ^?! key "scope" . _Integer) ) -- a QName may resolve to potentially multiple UIDs qNameToUIDs :: (T.Text, Integer) -> [ (UID, Integer) ] qNameToUIDs (qName, scope) = if T.null qName then [ ("", scope) ] else [ (uid, scope) | uid <- getUIDs qNameMaps $ convertString qName] -- | Write a .cfr-scope file writeCfrScopeFile :: [ (UID, Integer) ] -> QNameMaps -> FilePath -> IO () writeCfrScopeFile uidScopes qNameMaps modelName = do let scopesInJSON = generateJSONScopes qNameMaps uidScopes writeFile (replaceExtension modelName ".cfr-scope") scopesInJSON -- | Read a .cfr-scope file readCfrScopeFile :: QNameMaps -> FilePath -> IO (Maybe [ (UID, Integer) ]) readCfrScopeFile qNameMaps modelName = do let cfrScopeFileName = replaceExtension modelName ".cfr-scope" exists <- doesFileExist cfrScopeFileName if exists then do scopesInJSON <- readFile cfrScopeFileName return $ Just $ parseJSONScopes qNameMaps scopesInJSON else return Nothing