3 # (C) Copyright IBM Corporation 2004, 2005
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the "Software"),
8 # to deal in the Software without restriction, including without limitation
9 # on the rights to use, copy, modify, merge, publish, distribute, sub
10 # license, and/or sell copies of the Software, and to permit persons to whom
11 # the Software is furnished to do so, subject to the following conditions:
13 # The above copyright notice and this permission notice (including the next
14 # paragraph) shall be included in all copies or substantial portions of the
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 # Ian Romanick <idr@us.ibm.com>
28 from xml
.sax
import saxutils
29 from xml
.sax
import make_parser
30 from xml
.sax
.handler
import feature_namespaces
35 """Generic class on which all other API entity types are based."""
37 def __init__(self
, tag_name
, name
, context
):
39 self
.category
= context
.get_category_define()
40 self
.context
= context
41 self
.tag_name
= tag_name
43 context
.append(tag_name
, self
)
46 def startElement(self
, name
, attrs
):
47 """Generic startElement handler.
49 The startElement handler is called for all elements except
50 the one that starts the object. For a foo element, the
51 XML "<foo><bar/></foo>" would cause the startElement handler
52 to be called once, but the endElement handler would be called
56 def endElement(self
, name
):
57 """Generic endElement handler.
59 Generic endElement handler. Returns 1 if the tag containing
60 the object is complete. Otherwise 0 is returned. All
61 derived class endElement handlers should call this method. If
62 the name of the ending tag is the same as the tag that
63 started this object, the object is assumed to be complete.
65 This fails if a tag can contain another tag with the same
66 name. The XML "<foo><foo/><bar/></foo>" would fail. The
67 object would end before the bar tag was processed.
69 The endElement handler is called for every end element
70 associated with an object, even the element that started the
71 object. See the description of startElement an example."""
73 if name
== self
.tag_name
:
78 def get_category_define(self
):
82 class glEnum( glItem
):
83 """Subclass of glItem for representing GL enumerants.
85 This class is not complete, and is not really used yet."""
87 def __init__(self
, context
, name
, attrs
):
88 self
.value
= int(attrs
.get('value', "0x0000"), 0)
90 enum_name
= "GL_" + attrs
.get('name', None)
91 glItem
.__init
__(self
, name
, enum_name
, context
)
93 temp
= attrs
.get('count', None)
95 self
.default_count
= 0
100 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
102 self
.default_count
= c
106 def process_attributes(self
, attrs
):
107 name
= attrs
.get('name', None)
109 temp
= attrs
.get('count', None)
111 c
= self
.default_count
116 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
118 mode_str
= attrs
.get('mode', "set")
119 if mode_str
== "set":
121 elif mode_str
== "get":
124 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str
, self
.context
.name
, self
.name
))
126 return [name
, c
, mode
]
129 class glType( glItem
):
130 """Subclass of glItem for representing GL types."""
132 def __init__(self
, context
, name
, attrs
):
133 self
.size
= int(attrs
.get('size', "0"))
134 self
.glx_name
= attrs
.get('glx_name', "")
136 type_name
= "GL" + attrs
.get('name', None)
137 glItem
.__init
__(self
, name
, type_name
, context
)
140 class glParameter( glItem
):
141 """Parameter of a glFunction."""
145 p_count_parameters
= None
151 def __init__(self
, context
, name
, attrs
):
152 p_name
= attrs
.get('name', None)
153 self
.p_type_string
= attrs
.get('type', None)
154 self
.p_count_parameters
= attrs
.get('variable_param', None)
156 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
157 if self
.p_type
== None:
158 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
161 # The count tag can be either a numeric string or the name of
162 # a variable. If it is the name of a variable, the int(c)
163 # statement will throw an exception, and the except block will
166 c
= attrs
.get('count', "0")
168 self
.p_count
= int(c
)
174 if attrs
.get('counter', "false") == "true":
179 if attrs
.get('output', "false") == "true":
185 # Pixel data has special parameters.
187 self
.width
= attrs
.get('img_width', None)
188 self
.height
= attrs
.get('img_height', None)
189 self
.depth
= attrs
.get('img_depth', None)
190 self
.extent
= attrs
.get('img_extent', None)
192 self
.img_xoff
= attrs
.get('img_xoff', None)
193 self
.img_yoff
= attrs
.get('img_yoff', None)
194 self
.img_zoff
= attrs
.get('img_zoff', None)
195 self
.img_woff
= attrs
.get('img_woff', None)
197 self
.img_format
= attrs
.get('img_format', None)
198 self
.img_type
= attrs
.get('img_type', None)
199 self
.img_target
= attrs
.get('img_target', None)
201 pad
= attrs
.get('img_pad_dimensions', "false")
203 self
.img_pad_dimensions
= 1
205 self
.img_pad_dimensions
= 0
208 null_flag
= attrs
.get('img_null_flag', "false")
209 if null_flag
== "true":
210 self
.img_null_flag
= 1
212 self
.img_null_flag
= 0
214 send_null
= attrs
.get('img_send_null', "false")
215 if send_null
== "true":
216 self
.img_send_null
= 1
218 self
.img_send_null
= 0
222 if self
.p_count
> 0 or self
.counter
or self
.p_count_parameters
:
228 # If there is a * anywhere in the parameter's type, then it
231 if re
.compile("[*]").search(self
.p_type_string
):
232 # We could do some other validation here. For
233 # example, an output parameter should not be const,
234 # but every non-output parameter should.
238 # If a parameter is not a pointer, then there cannot
239 # be an associated count (either fixed size or
240 # variable) and the parameter cannot be an output.
242 if has_count
or self
.is_output
:
243 raise RuntimeError("Non-pointer type has count or is output.")
246 glItem
.__init
__(self
, name
, p_name
, context
)
250 def is_variable_length_array(self
):
251 """Determine if a parameter is a variable length array.
253 A parameter is considered to be a variable length array if
254 its size depends on the value of another parameter that is
255 an enumerant. The params parameter to glTexEnviv is an
256 example of a variable length array parameter. Arrays whose
257 size depends on a count variable, such as the lists parameter
258 to glCallLists, are not variable length arrays in this
261 return self
.p_count_parameters
or self
.counter
or self
.width
265 return self
.is_pointer
268 def count_string(self
):
269 """Return a string representing the number of items
271 Returns a string representing the number of items in a
272 parameter. For scalar types this will always be "1". For
273 vector types, it will depend on whether or not it is a
274 fixed length vector (like the parameter of glVertex3fv),
275 a counted length (like the vector parameter of
276 glDeleteTextures), or a general variable length vector."""
279 if self
.p_count_parameters
!= None:
281 elif self
.counter
!= None:
284 return str(self
.p_count
)
290 if self
.p_count_parameters
or self
.counter
or self
.width
or self
.is_output
:
292 elif self
.p_count
== 0:
293 return self
.p_type
.size
295 return self
.p_type
.size
* self
.p_count
297 def size_string(self
):
301 b_prod
= self
.p_type
.size
303 if self
.p_count_parameters
== None and self
.counter
!= None:
304 a_prod
= self
.counter
305 elif self
.p_count_parameters
!= None and self
.counter
== None:
307 elif self
.p_count_parameters
!= None and self
.counter
!= None:
308 b_prod
= self
.counter
312 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
314 return "(%s * %s)" % (a_prod
, b_prod
)
319 class glParameterIterator
:
320 """Class to iterate over a list of glParameters.
322 Objects of this class are returned by the parameterIterator method of
323 the glFunction class. They are used to iterate over the list of
324 parameters to the function."""
326 def __init__(self
, data
):
334 if self
.index
== len( self
.data
):
341 class glFunction( glItem
):
345 fn_return_type
= "void"
348 def __init__(self
, context
, name
, attrs
):
349 self
.fn_alias
= attrs
.get('alias', None)
350 self
.fn_parameters
= []
353 temp
= attrs
.get('offset', None)
354 if temp
== None or temp
== "?":
357 self
.fn_offset
= int(temp
)
359 fn_name
= attrs
.get('name', None)
360 if self
.fn_alias
!= None:
361 self
.real_name
= self
.fn_alias
363 self
.real_name
= fn_name
365 glItem
.__init
__(self
, name
, fn_name
, context
)
369 def parameterIterator(self
):
370 return glParameterIterator(self
.fn_parameters
)
373 def startElement(self
, name
, attrs
):
376 self
.context
.factory
.create(self
, name
, attrs
)
378 print "Error with parameter '%s' in function '%s'." \
379 % (attrs
.get('name','(unknown)'), self
.name
)
381 elif name
== "return":
382 self
.set_return_type(attrs
.get('type', None))
385 def append(self
, tag_name
, p
):
386 if tag_name
!= "param":
387 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
392 self
.fn_parameters
.append(p
)
395 def set_return_type(self
, t
):
396 self
.fn_return_type
= t
399 def get_parameter_string(self
):
402 for p
in glFunction
.parameterIterator(self
):
403 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
413 """Factory to create objects derived from glItem."""
415 def create(self
, context
, name
, attrs
):
416 if name
== "function":
417 return glFunction(context
, name
, attrs
)
419 return glType(context
, name
, attrs
)
421 return glEnum(context
, name
, attrs
)
422 elif name
== "param":
423 return glParameter(context
, name
, attrs
)
428 class glFunctionIterator
:
429 """Class to iterate over a list of glFunctions
431 Objects of this classare returned by
432 FilterGLAPISpecBase::functionIterator. This default version
433 iterates over the functions in order of dispatch table offset. All
434 of the "true" functions are iterated first, followed by the alias
437 def __init__(self
, context
):
438 self
.context
= context
439 self
.keys
= context
.functions
.keys()
445 for self
.index
in range(0, len(self
.keys
)):
446 if self
.keys
[ self
.index
] >= 0: break
448 if self
.index
== len(self
.keys
):
452 self
.split
= self
.index
- 1
464 k
= self
.keys
[ self
.index
]
466 #if self.context.functions[k].fn_alias == None:
467 # if k != self.prevk + 1:
468 # print 'Missing offset %d' % (prevk)
469 # self.prevk = int(k)
471 self
.index
+= self
.direction
473 if self
.index
== len(self
.keys
):
474 self
.index
= self
.split
477 return self
.context
.functions
[k
]
480 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
482 license
= "The license for this file is unspecified."
487 current_object
= None
489 current_category
= ""
492 saxutils
.XMLFilterBase
.__init
__(self
)
496 self
.factory
= glItemFactory()
497 self
.header_tag
= None
500 def find_type(self
,type_name
):
502 if re
.compile(t
).search(type_name
):
504 print "Unable to find base type matching \"%s\"." % (type_name
)
508 def find_function(self
,function_name
):
509 index
= self
.xref
[function_name
]
510 return self
.functions
[index
]
513 def functionIterator(self
):
514 return glFunctionIterator(self
)
517 def printFunctions(self
):
518 for f
in self
.functionIterator():
519 self
.printFunction(f
)
523 def printHeader(self
):
524 """Print the header associated with all files and call the printRealHeader method."""
526 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
530 print ' * ' + self
.license
.replace('\n', '\n * ')
534 print '#if !defined( %s )' % (self
.header_tag
)
535 print '# define %s' % (self
.header_tag
)
537 self
.printRealHeader();
541 def printFooter(self
):
542 """Print the header associated with all files and call the printRealFooter method."""
544 self
.printFunctions()
545 self
.printRealFooter()
548 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
551 def get_category_define(self
):
552 """Convert the category name to the #define that would be found in glext.h"""
554 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
555 s
= self
.current_category
556 return "GL_VERSION_" + s
.replace(".", "_")
558 return self
.current_category
561 def append(self
, object_type
, obj
):
562 if object_type
== "function":
563 # If the function is not an alias and has a negative
564 # offset, then we do not need to track it. These are
565 # functions that don't have an assigned offset
567 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
568 if obj
.fn_offset
>= 0:
569 index
= obj
.fn_offset
571 index
= self
.next_alias
574 self
.functions
[index
] = obj
575 self
.xref
[obj
.name
] = index
576 elif object_type
== "type":
577 self
.types
[obj
.name
] = obj
582 def startElement(self
, name
, attrs
):
583 """Start a new element in the XML stream.
585 Starts a new element. There are three types of elements that
586 are specially handled by this function. When a "category"
587 element is encountered, the name of the category is saved.
588 If an element is encountered and no API object is
589 in-progress, a new object is created using the API factory.
590 Any future elements, until that API object is closed, are
591 passed to the current objects startElement method.
593 This paradigm was chosen becuase it allows subclasses of the
594 basic API types (i.e., glFunction, glEnum, etc.) to handle
595 additional XML data, GLX protocol information, that the base
596 classes do not know about."""
598 if self
.current_object
!= None:
599 self
.current_object
.startElement(name
, attrs
)
600 elif name
== "category":
601 self
.current_category
= attrs
.get('name', "")
603 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
607 def endElement(self
, name
):
608 if self
.current_object
!= None:
609 if self
.current_object
.endElement(name
):
610 self
.current_object
= None
614 def printFunction(self
,offset
):
615 """Print a single function.
617 In the base class, this function is empty. All derived
618 classes should over-ride this function."""
622 def printRealHeader(self
):
623 """Print the "real" header for the created file.
625 In the base class, this function is empty. All derived
626 classes should over-ride this function."""
630 def printRealFooter(self
):
631 """Print the "real" footer for the created file.
633 In the base class, this function is empty. All derived
634 classes should over-ride this function."""