-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathXSDcreator.py
309 lines (277 loc) · 12.3 KB
/
XSDcreator.py
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import xml.etree.ElementTree as ET
import sys
import getopt
import collections
HELP_MESSAGE = """Help:
Create .XSD grammar file for a given .XML file.
-i, --ifile <file> the input file. MUST be specified
-o, --ofile <file> the output file. Is overriden by the field
in the XML
file if specified. Defaults to schema.xsd
-e, --xml-encoding <format> the encoding formato of the xsd file.
Defaults at "utf-8"
-v, --xml-version <version> the version of the xsd file. Defaults at "1.0"
-s, --xsd-schema-url <url> the xsd schema url.
Defaults at http://www.w3.org/2001/XMLSchema
-c, --console write on stdout instead than on
the specified file.
--force-ofile-name Avoid the override of the output file name by
the field on the XML.
A file output must be present for this
--force-xs-type <type> force the use of a specific type for
the type attribute
Defaults at "xs:string".
BE CAREFUL no type check is done on this
--force-inline forces the use of inlines instead of the creation
of subtypes. DO NOT USE AS IT CAN PRODUCE WRONG
OUTPUTS, IT ALSO DOESN'T PRODUCE APPROPRIATE
ATTRIBUTES SUPPORT AND MIXED ATTRIBUTES
-h, --help display this useful help
-l Shows the license
"""
LICENSE_MESSAGE="""
#############################################################
# #
# This program is relased in the GNU GPL v3.0 license #
# you can modify/use this program as you wish. Please #
# link the original distribution of this software. If #
# you plan to redistribute your modified/copied copy #
# you need to relased the in GNU GPL v3.0 licence too #
# according to the overmentioned licence. #
# #
# "PROUDLY" MADE BY chkrr00k (i'm not THAT proud tbh) #
# #
#############################################################
# #
# #
# YEE #
# #
# #
#############################################################
"""
class StringBuffer:
def __init__(self, input : str):
self.storage = []
self.storage.append(input)
def append(self, input : str):
self.storage.append(input)
def toString(self) -> str:
return "".join(self.storage)
def printLevel(input : str, level : int, sep="\t", end="\n"):
buf.append((sep * level) + input + end)
def inspect(root : ET.Element, level : int):
printLevel("<xs:element name=\"" + root.tag + "\"",level, end="")
if len(root) > 0:
printLevel(">", 0)
printLevel("<xs:complexType>", level + 1)
printLevel("<xs:sequence>", level + 1)
for child in root:
inspect(child, level + 2)
printLevel("</xs:sequence>", level + 1)
printLevel("</xs:complexType>", level + 1)
printLevel("</xs:element>", level)
else:
printLevel(" type=\"xs:string\"/>", 0)
def getArguments(argv : list) -> dict:
ofile = False
result = {
"inputFile" : None,
"outputFile" : "schema.xsd",
"encoding" : "utf-8",
"xmlVersion" : "1.0",
"xsSchemaUrl" : "http://www.w3.org/2001/XMLSchema",
"console" : False,
"inline" : False,
"xsType" : "xs:string",
"fileOverride" : False
}
try:
opts, args = getopt.getopt(argv, "hi:o:e:v:s:cl", ["help", "ifile=", "ofile=", "xml-encoding=", "xml-version=", "xsd-schema-url=", "console", "force-inline", "force-xs-type=", "force-ofile-name"])
except getopt.GetoptError:
print("Error in arguments\n")
print(HELP_MESSAGE)
sys.exit(1)
for opt, arg in opts:
if opt in ("-h", "--help"):
print(HELP_MESSAGE)
sys.exit(0)
elif opt in ("-l"):
print(LICENSE_MESSAGE)
sys.exit(0)
elif opt in ("-i", "--ifile"):
if arg.lower().endswith(".xml"):
result["inputFile"] = arg
else:
sys.stderr.write("File must be a .xml file")
elif opt in ("-o", "--ofile"):
if arg.lower().endswith(".xsd"):
result["outputFile"] = arg
ofile = True
else:
sys.stderr.write("File must be a .xsd file")
elif opt in ("-e", "--xml-encoding"):
result["encoding"] = arg
elif opt in ("-v", "--xml-version"):
result["xmlVersion"] = arg
elif opt in ("-s", "--xsd-schema-url"):
result["xsSchemaUrl"] = arg
elif opt in ("-c", "--console"):
result["console"] = True
elif opt in ("--force-inline"):
result["inline"] = True
elif opt in ("--force-xs-type"):
result["xsType"] = arg
elif opt in ("--force-ofile-name"):
result["fileOverride"] = True
if result["fileOverride"]:
if not ofile:
print(result["fileOverride"] , ofile)
print("Error, you must specify the output file (option -o) with the --force-ofile-name\n")
print(HELP_MESSAGE)
sys.exit(3)
return result
class XSDEl:
def __init__(self, el : ET.Element, times : int = 1, obb : bool = True, mul : bool = True):
self.times = times
self.el = el
self.tag = el.tag
self.obb = obb
self.mul = False
def __eq__(self, other):
return self.tag == other.tag
def __str__(self):
return self.tag + " " + str(self.times) + " " + str(self.obb)
def __unicode__(self):
return self.tag + " " + str(self.times) + " " + str(self.obb)
def __repr__(self):
return self.tag + " " + str(self.times) + " " + str(self.obb)
class XSDCo:
def __init__(self, tag : str, attrib : dict):
self.tag = tag
self.storage = collections.OrderedDict()
self.attrib = attrib
def append(self, el : XSDEl):
if el.tag not in self.storage:
self.storage[el.tag] = el
else:
self.storage[el.tag].times += 1
self.storage[el.tag].el.attrib.update(el.el.attrib)
def __eq__(self, other):
return self.tag == other.tag
def __hash__(self):
return hash((self.tag))
def __str__(self):
return self.tag + " " + str(self.storage)
def __unicode__(self):
return self.tag + " " + str(self.storage)
def __repr__(self):
return self.tag + " " + str(self.storage)
def generateTree(root : ET.Element):
result = list()
tmp = XSDCo(root.tag, root.attrib)
for child in root:
tmp.append(XSDEl(child))
result += generateTree(child)
result.append(tmp)
return result
def subType(root : ET.Element, xsType : str):
tree = generateTree(root)
complex = [el for el in tree if len(el.storage) > 0]
newcomp = list()
for part in complex:
tmp = [el for el in complex if el.tag == part.tag]
if tmp not in newcomp:
newcomp.append(tmp)
els = collections.OrderedDict()
for elements in newcomp:
for part in elements:
if part.tag not in els:
els[part.tag] = XSDCo(part.tag, part.attrib)
for a in part.storage:
els[part.tag].append(part.storage[a])
else:
for a in part.storage:
els[part.tag].append(part.storage[a])
for name, field in els[part.tag].storage.items():
if field.times < len(elements):
field.obb = False
elif field.times > len(elements):
field.mul = True
complex = [t.tag for t in complex]
for tag, datas in els.items():
printLevel("<xs:complexType name=\"" + tag + "Type\">", 1)
printLevel("<xs:sequence>", 2)
for sub in datas.storage:
if sub not in complex:
if datas.storage[sub].el.text is not None:
type = " type=\"" + xsType + "\""
else:
type = ""
else:
type = " type=\"" + sub + "Type\""
obb = " minOccurs=\"0\"" if not datas.storage[sub].obb else ""
mul = ""
if datas.storage[sub].mul:
mul = " maxOccurs=\"" + str(datas.storage[sub].times) + "\""
if len(datas.storage[sub].el.attrib) > 0 and sub not in complex:
if datas.storage[sub].el.text is None:
printLevel("<xs:element name=\"" + sub + "\"" + obb + mul + ">", 3)
printLevel("<xs:complexType>", 4)
for at in datas.storage[sub].el.attrib:
printLevel("<xs:attribute name=\"" + at + "\" type=\"" + xsType + "\"/>", 5)
printLevel("</xs:complexType>", 4)
printLevel("</xs:element>", 3)
else:
printLevel("<xs:element name=\"" + sub + "\"" + obb + mul + ">", 3)
printLevel("<xs:complexType>", 4)
printLevel("<xs:simpleContent>", 5)
printLevel("<xs:extension base=\"" + xsType + "\">", 6)
for at in datas.storage[sub].el.attrib:
printLevel("<xs:attribute name=\"" + at + "\" type=\"" + xsType + "\"/>", 7)
printLevel("</xs:extension>", 6)
printLevel("</xs:simpleContent>", 5)
printLevel("</xs:complexType>", 4)
printLevel("</xs:element>", 3)
else:
printLevel("<xs:element name=\"" + sub + "\"" + type + obb + mul + "/>", 3)
printLevel("</xs:sequence>", 2)
for name in datas.attrib:
if not name.find("NamespaceSchemaLocation") > 0:
printLevel("<xs:attribute name=\"" + name + "\" type=\"" + xsType + "\"/>", 2)
printLevel("</xs:complexType>", 1)
printLevel("<xs:element name=\"" + root.tag + "\" type=\"" + root.tag + "Type\"/>", 1)
def main(argv):
global buf
args = getArguments(argv)
if args["inputFile"] is None:
print("Error, you must specify the input file (option -i)\n")
print(HELP_MESSAGE)
sys.exit(3)
buf = StringBuffer("<?xml version=\"" + args["xmlVersion"] +"\" encoding=\"" + args["encoding"] + "\"?>\n")
buf.append("<xs:schema xmlns:xs=\"" + args["xsSchemaUrl"] + "\">\n")
try:
tree = ET.parse(args["inputFile"])
except FileNotFoundError as err:
sys.stderr.write("The inserted file doesn't exist or have problems: " + str(err))
sys.exit(2)
root = tree.getroot()
if args["fileOverride"]:
fileName = args["outputFile"]
else:
try:
fileName = root.get(list(filter(lambda x:x.find("NamespaceSchemaLocation"), root.attrib))[0], args["outputFile"])
except:
fileName = args["outputFile"]
if args["inline"]:
print("WARNING THIS OPTION IS DEPRECATED AND IT'S SUGGESTED TO NOT USE BECAUSE OF IT'S INCORRECTED RESULTS\n(See the --help to know more)\n\n")
inspect(root, 1)
else:
subType(root, args["xsType"])
buf.append("</xs:schema>\n")
if args["console"]:
print(buf.toString())
else:
with(open(fileName, "w+")) as f:
f.write(buf.toString())
if __name__ == "__main__":
main(sys.argv[1:])