1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
module Database.ERD
( ERD(..), ERDName, Entity(..), EName, Entity(..)
, Attribute(..), AName, Key(..), Null, Domain(..)
, Relationship(..), REnd(..), RName, Role, Cardinality(..), MaxValue(..)
, readERDTermFile, writeERDTermFile
) where
import Char (isSpace)
import Directory (getAbsolutePath)
import IO
import ReadShowTerm (readUnqualifiedTerm)
import Time
data ERD = ERD ERDName [Entity] [Relationship]
deriving Show
type ERDName = String
data Entity = Entity EName [Attribute]
deriving Show
type EName = String
data Attribute = Attribute AName Domain Key Null
deriving Show
type AName = String
data Key = NoKey
| PKey
| Unique
deriving (Eq, Show)
type Null = Bool
data Domain = IntDom (Maybe Int)
| FloatDom (Maybe Float)
| CharDom (Maybe Char)
| StringDom (Maybe String)
| BoolDom (Maybe Bool)
| DateDom (Maybe CalendarTime)
| UserDefined String (Maybe String)
| KeyDom String
deriving Show
data Relationship = Relationship RName [REnd]
deriving Show
type RName = String
data REnd = REnd EName Role Cardinality
deriving Show
type Role = String
data Cardinality = Exactly Int
| Between Int MaxValue
deriving Show
data MaxValue = Max Int | Infinite
deriving Show
readERDTermFile :: String -> IO ERD
readERDTermFile termfilename = do
putStrLn $ "Reading ERD term from file '" ++ termfilename ++ "'..."
handle <- openFile termfilename ReadMode
line <- skipCommentLines handle
termstring <- hGetContents handle
return (updateERDTerm (readUnqualifiedTerm ["Database.ERD","Prelude"]
(unlines [line,termstring])))
where
h = do
line <- hGetLine h >>= return . dropWhile isSpace
if null line || take 2 line == "--"
then skipCommentLines h
else if take 2 line == "{-"
then skipBracketComment h (drop 2 line)
else return line
h [] = hGetLine h >>= skipBracketComment h
skipBracketComment h [_] = hGetLine h >>= skipBracketComment h
skipBracketComment h (c1:c2:cs) =
if c1=='-' && c2=='}' then return cs
else skipBracketComment h (c2:cs)
updateERDTerm :: ERD -> ERD
updateERDTerm (ERD name es rs) = ERD name es (map updateRel rs)
where
updateRel (Relationship r ends) = Relationship r (map updateEnd ends)
updateEnd (REnd n r c) = REnd n r (updateCard c)
updateCard (Exactly n) = Exactly n
updateCard (Between min (Max m)) =
if min<=m
then Between min (Max m)
else error ("ERD: Illegal cardinality " ++ show (Between min (Max m)))
updateCard (Between min Infinite) = Between min Infinite
writeERDTermFile :: ERD -> IO String
writeERDTermFile erd@(ERD name _ _) = do
let termfile = name ++ ".erdterm"
writeFile termfile (show erd)
getAbsolutePath termfile
|