module Components.Parsers.ServerSchemaJsonParser (fetchArguments) where import Control.Exception (throw) import Data.List (foldl') import Text.JSON ( JSValue(JSObject), JSObject, Result(Ok,Error), valFromObj, decode ) import Model.ServerExceptions ( QueryException( ImportSchemaServerNameException, ImportSchemaException, ImportSchemaChildrenException, ImportSchemaPseudonymsException, ImportSchemaScalarFieldsException, ImportSchemaDatabaseTablesException, ImportSchemaObjectFieldsException, ImportSchemaDatabaseRelationshipsException, ImportSchemaDuplicateException ) ) fetchArguments :: FilePath -> IO ([(String,[String])],[(String,[(String,String,[(String,[(String,String,String,String)])])])],[(String,[String])],[(String,String)],[(String,String,[String])],[(String,[String],[String])]) fetchArguments fp = do schema <- Prelude.readFile fp return $ parseSchema schema parseSchema :: String -> ([(String,[String])],[(String,[(String,String,[(String,[(String,String,String,String)])])])],[(String,[String])],[(String,String)],[(String,String,[String])],[(String,[String],[String])]) parseSchema str = parseHelper $ checkJSValueListValue (decode str :: Result (JSObject JSValue)) checkJSValueListValue :: Result (JSObject JSValue) -> (JSObject JSValue) checkJSValueListValue (Error str) = throw ImportSchemaException checkJSValueListValue (Ok a) = a parseHelper :: JSObject JSValue -> ([(String,[String])],[(String,[(String,String,[(String,[(String,String,String,String)])])])],[(String,[String])],[(String,String)],[(String,String,[String])],[(String,[String],[String])]) parseHelper json = if (isValidParentScalars soa sss)&&(isWithoutDups svrobjs sss sos sor soa) then (svrobjs,sss,sos,sdbn,sor,soa) else throw ImportSchemaChildrenException where (svrobjs,sss,sos,sdbn,sor) = parsePrimitivesIterator [] [] [] [] [] $ getObjects (valFromObj "PrimitiveObjects" json :: Result [JSObject JSValue]) soa = parseParentsIterator [] $ getObjects (valFromObj "ParentalObjects" json :: Result [JSObject JSValue]) parsePrimitivesIterator :: [(String,[String])] -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String])] -> [(String,String)] -> [(String,String,[String])] -> [JSObject JSValue] -> ([(String,[String])],[(String,[(String,String,[(String,[(String,String,String,String)])])])],[(String,[String])],[(String,String)],[(String,String,[String])]) parsePrimitivesIterator svrobjs sss sos sdbn sor [] = (svrobjs,sss,sos,sdbn,sor) parsePrimitivesIterator svrobjs sss sos sdbn sor (obj:t) = if foldl' (\acc (prev,_)->(prev==name)||acc) False sdbn then throw ImportSchemaDuplicateException else parsePrimitivesIterator ((name,pseudonyms):svrobjs) ((name,scalars):sss) ((name,nestedobjects):sos) ((name,table):sdbn) (sor++(processRelationships relationships)) t where name = getString (valFromObj "ServerName" obj :: Result String) 0 pseudonyms = getStringList (valFromObj "Pseudonyms" obj :: Result [String]) 0 scalars = checkScalars (valFromObj "ScalarFields" obj :: Result [JSValue]) nestedobjects = getStringList (valFromObj "ObjectFields" obj :: Result [String]) 1 table = getString (valFromObj "DatabaseTable" obj :: Result String) 2 relationships = getListStringList (valFromObj "DatabaseRelationships" obj :: Result [[String]]) parseParentsIterator :: [(String,[String],[String])] -> [JSObject JSValue] -> [(String,[String],[String])] parseParentsIterator soa [] = soa parseParentsIterator soa (obj:t) = if foldl' (\acc (prev,_,_)->(prev==name)||acc) False soa then throw ImportSchemaDuplicateException else parseParentsIterator ((name,pseudonyms,children):soa) t where name = getString (valFromObj "ServerName" obj :: Result String) 0 pseudonyms = getStringList (valFromObj "Pseudonyms" obj :: Result [String]) 0 children = getStringList (valFromObj "ServerChildren" obj :: Result [String]) 2 getObjects :: Result [JSObject JSValue] -> [JSObject JSValue] getObjects (Error _) = throw ImportSchemaServerNameException getObjects (Ok objects) = objects getString :: Result String -> Int -> String getString (Ok str) _ = str getString (Error str) 0 = throw ImportSchemaServerNameException getString (Error str) 1 = throw ImportSchemaScalarFieldsException getString (Error str) 2 = throw ImportSchemaDatabaseTablesException getString _ _ = throw ImportSchemaException getStringList :: Result [String] -> Int -> [String] getStringList (Ok rlt) _ = rlt getStringList _ 0 = throw ImportSchemaPseudonymsException getStringList _ 1 = throw ImportSchemaObjectFieldsException getStringList _ 2 = throw ImportSchemaChildrenException getStringList _ _ = throw ImportSchemaException getListStringList :: Result [[String]] -> [[String]] getListStringList (Error str) = throw ImportSchemaDatabaseRelationshipsException getListStringList (Ok rlt) = rlt checkScalars :: Result [JSValue] -> [(String,String,[(String,[(String,String,String,String)])])] checkScalars (Error str) = throw ImportSchemaScalarFieldsException checkScalars (Ok a) = getScalars a getScalars :: [JSValue] -> [(String,String,[(String,[(String,String,String,String)])])] getScalars [] = [] getScalars ((JSObject obj):t) = (getString (valFromObj "Name" obj :: Result String) 1,getType (valFromObj "Type" obj :: Result String),getScalarArguments $ getObjects (valFromObj "Arguments" obj :: Result [JSObject JSValue])):getScalars t getScalarArguments :: [JSObject JSValue] -> [(String,[(String,String,String,String)])] getScalarArguments [] = [] getScalarArguments (h:t) = (name,options):getScalarArguments t where name = getString (valFromObj "Name" h :: Result String) 1 options = getScalarArgumentOptions (getObjects (valFromObj "Options" h :: Result [JSObject JSValue])) getScalarArgumentOptions :: [JSObject JSValue] -> [(String,String,String,String)] getScalarArgumentOptions [] = [] getScalarArgumentOptions (h:t) = (name,typ,prefix,suffix):getScalarArgumentOptions t where name = getString (valFromObj "Name" h :: Result String) 1 typ = getType (valFromObj "Type" h :: Result String) prefix = getString (valFromObj "Prefix" h :: Result String) 1 suffix = getString (valFromObj "Suffix" h :: Result String) 1 getType :: Result String -> String getType (Ok "Text") = "Text" getType (Ok "ByteString") = "ByteString" getType (Ok "Int") = "Int" getType (Ok "Double") = "Double" getType (Ok "Rational") = "Rational" getType (Ok "Bool") = "Bool" getType (Ok "Day") = "Day" getType (Ok "TimeOfDay") = "TimeOfDay" getType (Ok "UTCTime") = "UTCTime" -- getType (Error str) = throw ImportSchemaScalarFieldsException getType _ = throw ImportSchemaScalarFieldsException processRelationships :: [[String]] -> [(String,String,[String])] processRelationships lst = foldr (\x y -> (getFirst x,getThird x, x):y) [] lst getFirst :: [String] -> String getFirst (h:t) = h getFirst _ = throw ImportSchemaException getThird :: [String] -> String getThird (h1:h2:h3:t) = h3 getThird _ = throw ImportSchemaException -- no duplicated names in schema isWithoutDups :: [(String,[String])] -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String])] -> [(String,String,[String])] -> [(String,[String],[String])] -> Bool isWithoutDups sobjs sss sos sor soa = (isNoDupsNames $ foldr (++) [] ([x | (_,x)<-sobjs]++[x | (_,x,_)<-soa]))&&(isNoDupsScalars sss)&&(isNoDupsNestedObjects sos)&&(isNoDupsRelationships sor) isNoDupsNames :: [String] -> Bool isNoDupsNames [] = True isNoDupsNames (nm:rst) = (foldl' (\y x->x/=nm&&y) True rst)&&isNoDupsNames rst isNoDupsScalars :: [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> Bool isNoDupsScalars sss = foldl' (\y (_,x)->y&&isNoDupsScalarNames x) True sss isNoDupsScalarNames :: [(String,String,[(String,[(String,String,String,String)])])] -> Bool isNoDupsScalarNames [] = True isNoDupsScalarNames ((name,_,args):rst) = (foldl' (\y (x,_,_)->name/=x&&y) True rst)&&(isNoDupsScalarArguments args)&&isNoDupsScalarNames rst isNoDupsScalarArguments :: [(String,[(String,String,String,String)])] -> Bool isNoDupsScalarArguments [] = True isNoDupsScalarArguments ((name,opts):rst) = (foldl' (\y (n,o)->name/=n&&y&&isNoDupsArgumentOptions o) True rst)&&isNoDupsScalarArguments rst isNoDupsArgumentOptions :: [(String,String,String,String)] -> Bool isNoDupsArgumentOptions [] = True isNoDupsArgumentOptions ((name,_,_,_):rst) = (foldl' (\y (n,_,_,_)->name/=n&&y) True rst)&&isNoDupsArgumentOptions rst isNoDupsNestedObjects :: [(String,[String])] -> Bool isNoDupsNestedObjects [] = True isNoDupsNestedObjects ((_,[]):rst) = isNoDupsNestedObjects rst isNoDupsNestedObjects ((obj,no:objs):rst) = (elem no objs)==False&&isNoDupsNestedObjects ((obj,objs):rst) isNoDupsRelationships :: [(String,String,[String])] -> Bool isNoDupsRelationships [] = True isNoDupsRelationships ((from,to,_):rst) = (foldl' (\y (a,b,_)->(from/=a||to/=b)&&y) True rst)&&isNoDupsRelationships rst -- shared scalars are same type isValidParentScalars :: [(String,[String],[String])] -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> Bool isValidParentScalars [] _ = True isValidParentScalars ((_,_,[]):t) sss = isValidParentScalars t sss isValidParentScalars ((_,_,_:[]):t) sss = isValidParentScalars t sss isValidParentScalars ((_,_,(h:cld)):t) sss = (isValidScalarsType (getPrimitiveScalars h sss) cld sss)&&isValidParentScalars t sss getPrimitiveScalars :: String -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,String)] getPrimitiveScalars _ [] = throw ImportSchemaChildrenException getPrimitiveScalars name ((nm,st):t) = if name==nm then foldl' (\y (n,tp,_)->(n,tp):y) [] st else getPrimitiveScalars name t isValidScalarsType :: [(String,String)] -> [String] -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> Bool isValidScalarsType _ [] _ = True isValidScalarsType [] _ _ = True isValidScalarsType ((name,typ):t) cld sss = foldl' (\y x->foldl' (\b (n,tp)->if n==name then typ==tp&&y else y) True (getPrimitiveScalars x sss)) True cld