27 |
# NOTES: |
# NOTES: |
28 |
# |
# |
29 |
|
|
30 |
import sys |
import sys, copy, types |
31 |
|
from gnue.common.GObjects import GObj |
32 |
|
from gnue.common.GTrigger import GTrigger |
33 |
|
from gnue.common.FileUtils import openResource |
34 |
|
|
35 |
try: |
try: |
36 |
from xml.sax import saxutils |
from xml.sax import saxutils |
83 |
|
|
84 |
def loadXMLObject(stream, handler, rootType, xmlFileType, |
def loadXMLObject(stream, handler, rootType, xmlFileType, |
85 |
initialize=1, attributes={}, initParameters={}): |
initialize=1, attributes={}, initParameters={}): |
86 |
|
|
87 |
|
|
88 |
# Create a parser |
# Create a parser |
89 |
parser = xml.sax.make_parser() |
parser = xml.sax.make_parser() |
90 |
|
|
91 |
# Set up some namespace-related stuff for the parsers |
# Set up some namespace-related stuff for the parsers |
92 |
parser.setFeature(xml.sax.handler.feature_namespaces, 1) |
parser.setFeature(xml.sax.handler.feature_namespaces, 1) |
93 |
|
|
94 |
|
# Allow for parameter external entities |
95 |
|
## Does not work with expat!!! ## |
96 |
|
##parser.setFeature(xml.sax.handler.feature_external_pes, 1) |
97 |
|
|
98 |
# Create a stack for the parsing routine |
# Create a stack for the parsing routine |
99 |
object = None |
object = None |
100 |
|
|
101 |
# Create the handler |
# Create the handler |
102 |
dh = handler() |
dh = handler() |
103 |
|
dh.initValidation() |
104 |
|
|
105 |
# Tell the parser to use our handler |
# Tell the parser to use our handler |
106 |
parser.setContentHandler(dh) |
parser.setContentHandler(dh) |
114 |
raise MarkupError, _("Error loading %s: not a valid %s definition (expected: %s, got: %s)") % (xmlFileType, |
raise MarkupError, _("Error loading %s: not a valid %s definition (expected: %s, got: %s)") % (xmlFileType, |
115 |
xmlFileType, rootType, object._type) |
xmlFileType, rootType, object._type) |
116 |
|
|
117 |
|
dh.finalValidation() |
118 |
|
|
119 |
# Set the object's attributes |
# Set the object's attributes |
120 |
object.__dict__.update(attributes) |
object.__dict__.update(attributes) |
121 |
|
|
122 |
|
|
123 |
if initialize: |
if initialize: |
124 |
GDebug.printMesg(10,"Initializing the object tree starting at %s" %(object)) |
GDebug.printMesg(10,"Initializing the object tree starting at %s" %(object)) |
125 |
object.phaseInit(dh._phaseInitCount) |
object.phaseInit(dh._phaseInitCount) |
126 |
|
|
|
return object |
|
|
|
|
127 |
|
|
128 |
|
|
129 |
####################################################### |
return object |
|
# |
|
|
# char |
|
|
# |
|
|
# This is for typecasting strings |
|
|
# |
|
|
# NOTE: This is a redefinition of GTypecast.text. |
|
|
# This redefinition will be removed as soon as all |
|
|
# references to it are changes. You should be using |
|
|
# GTypecast from now on. |
|
|
# |
|
|
####################################################### |
|
|
char = GTypecast.text |
|
|
|
|
|
|
|
|
####################################################### |
|
|
# |
|
|
# bool |
|
|
# |
|
|
# This is for typecasting booleans |
|
|
# |
|
|
# NOTE: This is a redefinition of GTypecast.boolean. |
|
|
# This redefinition will be removed as soon as all |
|
|
# references to it are changes. You should be using |
|
|
# GTypecast from now on. |
|
|
# |
|
|
####################################################### |
|
|
bool = GTypecast.boolean |
|
130 |
|
|
131 |
|
|
132 |
####################################################### |
####################################################### |
167 |
self.uniqueIDs = {} |
self.uniqueIDs = {} |
168 |
self.root = None |
self.root = None |
169 |
self._phaseInitCount = 0 |
self._phaseInitCount = 0 |
170 |
|
|
171 |
|
self._requiredTags = [] |
172 |
|
self._singleInstanceTags = [] |
173 |
|
self._tagCounts = {} |
174 |
|
|
175 |
|
|
176 |
# |
# |
177 |
# Called by client code to get the "root" node |
# Called by client code to get the "root" node |
178 |
# |
# |
180 |
return self.root |
return self.root |
181 |
|
|
182 |
# |
# |
183 |
|
# Builds structures need to verify requirements in the file |
184 |
|
# |
185 |
|
def initValidation(self): |
186 |
|
# |
187 |
|
# Build list of tags along with a list of |
188 |
|
# require tags |
189 |
|
# |
190 |
|
for element in self.xmlElements.keys(): |
191 |
|
self._tagCounts[element] = 0 |
192 |
|
try: |
193 |
|
if self.xmlElements[element]['Required'] == 1: |
194 |
|
self._requiredTags.append(element) |
195 |
|
except KeyError: |
196 |
|
pass |
197 |
|
|
198 |
|
try: |
199 |
|
if self.xmlElements[element]['SingleInstance'] == 1: |
200 |
|
self._singleInstanceTags.append(element) |
201 |
|
except KeyError: |
202 |
|
pass |
203 |
|
|
204 |
|
def finalValidation(self): |
205 |
|
for element in self._singleInstanceTags: |
206 |
|
if self._tagCounts[element] > 1: |
207 |
|
raise MarkupError, _("File has multiple instances of <%s> when only one allowed") % (element) |
208 |
|
|
209 |
|
for element in self._requiredTags: |
210 |
|
if self._tagCounts[element] < 1: |
211 |
|
raise MarkupError, _("File is missing required tag <%s>") % (element) |
212 |
|
|
213 |
|
|
214 |
|
# |
215 |
# Called by the internal SAX parser whenever |
# Called by the internal SAX parser whenever |
216 |
# a starting XML element/tag is encountered. |
# a starting XML element/tag is encountered. |
217 |
# |
# |
224 |
# |
# |
225 |
# No namespace qualifier |
# No namespace qualifier |
226 |
# |
# |
227 |
|
self._tagCounts[name] += 1 |
228 |
GDebug.printMesg(50, "<%s>" % name) |
GDebug.printMesg(50, "<%s>" % name) |
229 |
|
|
230 |
try: |
try: |
231 |
baseAttrs = self.xmlElements[name].get('Attributes',{}) # default(self.xmlElements[name],'Attributes',{}) |
baseAttrs = self.xmlElements[name].get('Attributes',{}) |
232 |
except KeyError: |
except KeyError: |
233 |
raise MarkupError, _('Error processing <%s> tag [I do not know what a <%s> tag does]') % (name, name) |
raise MarkupError, _('Error processing <%s> tag [I do not know what a <%s> tag does]') % (name, name) |
234 |
|
|
235 |
|
|
236 |
for qattr in saxattrs.keys(): |
for qattr in saxattrs.keys(): |
237 |
attrns, attr = qattr |
attrns, attr = qattr |
238 |
|
encoding= sys.getdefaultencoding() |
239 |
|
if encoding == 'ascii': |
240 |
|
encoding = 'iso8859-1' # TODO: fix this when we have [common] section |
241 |
|
try: # in gnue.conf |
242 |
|
encoding = gConfig('formFontEncoding') |
243 |
|
except: |
244 |
|
pass |
245 |
|
|
246 |
# Typecasting, anyone? If attribute should be int, make it an int |
# Typecasting, anyone? If attribute should be int, make it an int |
247 |
try: |
try: |
248 |
attrs[attr] = baseAttrs[attr].get('Typecast',char)(saxattrs[qattr]) # default(baseAttrs[attr],'Typecast',char)(saxattrs[qattr]) |
attrs[attr] = baseAttrs[attr].get('Typecast',GTypecast.text)(saxattrs[qattr].encode(encoding)) # default(baseAttrs[attr],'Typecast',GTypecast.text)(saxattrs[qattr]) |
249 |
loadedxmlattrs[attr] = attrs[attr] |
loadedxmlattrs[attr] = attrs[attr] |
250 |
except KeyError: |
except KeyError: |
251 |
raise MarkupError, _('Error processing <%s> tag [I do not recognize the "%s" attribute') % (name, attr) |
raise MarkupError, _('Error processing <%s> tag [I do not recognize the "%s" attribute') % (name, attr) |
262 |
|
|
263 |
# Pull default values for missing attributes |
# Pull default values for missing attributes |
264 |
if baseAttrs[attr].has_key ('Default'): |
if baseAttrs[attr].has_key ('Default'): |
265 |
attrs[attr] = baseAttrs[attr].get('Typecast', char) (baseAttrs[attr]['Default'])# default(baseAttrs[attr],'Typecast', char) (baseAttrs[attr]['Default']) |
attrs[attr] = baseAttrs[attr].get('Typecast', GTypecast.text) (baseAttrs[attr]['Default'])# default(baseAttrs[attr],'Typecast', GTypecast.text) (baseAttrs[attr]['Default']) |
266 |
|
|
267 |
# Check for missing required attributes |
# Check for missing required attributes |
268 |
elif baseAttrs[attr].get('Required', 0): #default(baseAttrs[attr], 'Required', 0): |
elif baseAttrs[attr].get('Required', 0): #default(baseAttrs[attr], 'Required', 0): |
357 |
GDebug.printMesg(50, "</%s>" % name) |
GDebug.printMesg(50, "</%s>" % name) |
358 |
|
|
359 |
|
|
360 |
|
class GImportItem(GObj): |
361 |
|
def __init__(self, parent=None, type="GCImport-Item"): |
362 |
|
GObj.__init__(self, parent, type=type) |
363 |
|
self._loadedxmlattrs = {} # Set by parser |
364 |
|
self._inits = [self.primaryInit] |
365 |
|
self._xmlParser = self.findParentOfType(None)._xmlParser |
366 |
|
|
367 |
|
def _buildObject(self): |
368 |
|
if hasattr(self,'_xmltag'): |
369 |
|
self._type = 'GC%s' % self._xmltag |
370 |
|
if not hasattr(self,'_importclass'): |
371 |
|
self._importclass = self._xmlParser\ |
372 |
|
.getXMLelements()[string.lower(self._type[9:])]['BaseClass'] |
373 |
|
return GObj._buildObject(self) |
374 |
|
|
375 |
|
def primaryInit(self): |
376 |
|
# |
377 |
|
# Open the library and convert it into objects |
378 |
|
# |
379 |
|
handle = openResource(self.library) |
380 |
|
form = self._xmlParser.loadFile(handle, self.findParentOfType(None)._app, initialize=0) |
381 |
|
handle.close() |
382 |
|
id = 'id' |
383 |
|
if hasattr(self,'name'): |
384 |
|
id = 'name' |
385 |
|
# |
386 |
|
# Configure the imported object, assign as a child of self |
387 |
|
# |
388 |
|
rv = self.__findImportItem(self, form, id) |
389 |
|
if rv != None: |
390 |
|
rv._parent = self |
391 |
|
rv._IMPORTED = 1 |
392 |
|
self._children.append(rv) |
393 |
|
# |
394 |
|
# transfer attributes reassigned during the import |
395 |
|
# |
396 |
|
for key in self._loadedxmlattrs.keys(): |
397 |
|
if key[0] != '_': |
398 |
|
rv.__dict__[key] = self._loadedxmlattrs[key] |
399 |
|
GDebug.printMesg (5, ">>> Moving %s" % key) |
400 |
|
rv._buildObject() |
401 |
|
|
402 |
|
# |
403 |
|
# __findImportItem |
404 |
|
# |
405 |
|
# finds the item in the object tree with the |
406 |
|
# same name and instance type |
407 |
|
# |
408 |
|
def __findImportItem(self, find, object, id): |
409 |
|
if isinstance(object, find._importclass) and \ |
410 |
|
hasattr(object, id) and \ |
411 |
|
object.__dict__[id] == find.__dict__[id]: |
412 |
|
return object |
413 |
|
elif hasattr(object,'_children'): |
414 |
|
rv = None |
415 |
|
for child in object._children: |
416 |
|
rv = self.__findImportItem(find, child, id) |
417 |
|
if rv: |
418 |
|
break |
419 |
|
return rv |
420 |
|
else: |
421 |
|
return None |
422 |
|
|
423 |
|
|
424 |
|
class GImport(GObj): |
425 |
|
def __init__(self, parent=None): |
426 |
|
GObj.__init__(self, parent, type="GCImport") |
427 |
|
self.library = "" |
428 |
|
self._form = None |
429 |
|
self._inits = [self.primaryInit] |
430 |
|
self._xmlParser = self.findParentOfType(None)._xmlParser |
431 |
|
|
432 |
|
def primaryInit(self): |
433 |
|
handle = openResource(self.library) |
434 |
|
form = self._xmlParser.loadFile(handle, self.findParentOfType(None)._app, initialize=0) |
435 |
|
handle.close() |
436 |
|
|
437 |
|
for attribute in self._loadedxmlattrs.keys(): |
438 |
|
if attribute != 'library': |
439 |
|
importAll = self._loadedxmlattrs[attribute] == "*" |
440 |
|
importNames = string.split(string.replace(self._loadedxmlattrs[attribute],' ',''),',') |
441 |
|
|
442 |
|
instanceType = self._xmlParser.getXMLelements()[string.lower(attribute)]['BaseClass'] |
443 |
|
|
444 |
|
if importAll or len(importNames): |
445 |
|
for child in form._children: |
446 |
|
if isinstance(child,instanceType) and \ |
447 |
|
(importAll or child.name in importNames): |
448 |
|
child._parent = self |
449 |
|
child._IMPORTED = 1 |
450 |
|
self._children.append(child) |
451 |
|
child._buildObject() |
452 |
|
|
453 |
|
def buildImportableTags(rootTag, elements): |
454 |
|
# |
455 |
|
# Scans xml elements and looks for Importable = 1 |
456 |
|
# Items with this set can be imported |
457 |
|
# If an object needs to be importable, |
458 |
|
# simply add its tag name to the tuple below |
459 |
|
# and make sure it has a "name" attribute |
460 |
|
# (otherwise we don't know how to reference |
461 |
|
# it in the imported file). |
462 |
|
# |
463 |
|
importElement = {'BaseClass': GImport, |
464 |
|
'Attributes': {'library': { |
465 |
|
'Required': 1, |
466 |
|
'Typecast': GTypecast.name }, |
467 |
|
}, |
468 |
|
'ParentTags': rootTag, |
469 |
|
} |
470 |
|
|
471 |
|
for key in elements.keys(): |
472 |
|
if elements[key].has_key('Importable') and elements[key]['Importable']: |
473 |
|
name = "import-%s" % key |
474 |
|
copy._deepcopy_dispatch[types.FunctionType] = copy._deepcopy_atomic |
475 |
|
copy._deepcopy_dispatch[types.ClassType] = copy._deepcopy_atomic |
476 |
|
copy._deepcopy_dispatch[type(int)] = copy._deepcopy_atomic |
477 |
|
|
478 |
|
p = copy.deepcopy(elements[key]) |
479 |
|
p['BaseClass'] = GImportItem |
480 |
|
|
481 |
|
if not p.has_key('Attributes'): |
482 |
|
p['Attributes'] = {} |
483 |
|
|
484 |
|
p['Attributes']['library'] = { |
485 |
|
'Required': 1, |
486 |
|
'Typecast': GTypecast.name } |
487 |
|
p['MixedContent'] = 0 |
488 |
|
p['Required'] = 0 |
489 |
|
elements[name] = p |
490 |
|
|
491 |
|
importElement['Attributes'][key] = { |
492 |
|
'Typecast': GTypecast.name, |
493 |
|
'Default': "" } |
494 |
|
|
495 |
|
if len(importElement['Attributes'].keys()): |
496 |
|
elements['import'] = importElement |
497 |
|
return elements |