gl_XML.py revision 848b8605
1#!/usr/bin/env python
2
3# (C) Copyright IBM Corporation 2004, 2005
4# All Rights Reserved.
5#
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:
12#
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
15# Software.
16#
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
23# IN THE SOFTWARE.
24#
25# Authors:
26#    Ian Romanick <idr@us.ibm.com>
27
28from decimal import Decimal
29import xml.etree.ElementTree as ET
30import re, sys, string
31import os.path
32import typeexpr
33
34
35def parse_GL_API( file_name, factory = None ):
36
37    if not factory:
38        factory = gl_item_factory()
39
40    api = factory.create_api()
41    api.parse_file( file_name )
42
43    # After the XML has been processed, we need to go back and assign
44    # dispatch offsets to the functions that request that their offsets
45    # be assigned by the scripts.  Typically this means all functions
46    # that are not part of the ABI.
47
48    for func in api.functionIterateByCategory():
49        if func.assign_offset:
50            func.offset = api.next_offset;
51            api.next_offset += 1
52
53    return api
54
55
56def is_attr_true( element, name, default = "false" ):
57    """Read a name value from an element's attributes.
58
59    The value read from the attribute list must be either 'true' or
60    'false'.  If the value is 'false', zero will be returned.  If the
61    value is 'true', non-zero will be returned.  An exception will be
62    raised for any other value."""
63
64    value = element.get( name, default )
65    if value == "true":
66        return 1
67    elif value == "false":
68        return 0
69    else:
70        raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
71
72
73class gl_print_base(object):
74    """Base class of all API pretty-printers.
75
76    In the model-view-controller pattern, this is the view.  Any derived
77    class will want to over-ride the printBody, printRealHader, and
78    printRealFooter methods.  Some derived classes may want to over-ride
79    printHeader and printFooter, or even Print (though this is unlikely).
80    """
81
82    def __init__(self):
83        # Name of the script that is generating the output file.
84        # Every derived class should set this to the name of its
85        # source file.
86
87        self.name = "a"
88
89
90        # License on the *generated* source file.  This may differ
91        # from the license on the script that is generating the file.
92        # Every derived class should set this to some reasonable
93        # value.
94        #
95        # See license.py for an example of a reasonable value.
96
97        self.license = "The license for this file is unspecified."
98
99
100        # The header_tag is the name of the C preprocessor define
101        # used to prevent multiple inclusion.  Typically only
102        # generated C header files need this to be set.  Setting it
103        # causes code to be generated automatically in printHeader
104        # and printFooter.
105
106        self.header_tag = None
107
108
109        # List of file-private defines that must be undefined at the
110        # end of the file.  This can be used in header files to define
111        # names for use in the file, then undefine them at the end of
112        # the header file.
113
114        self.undef_list = []
115        return
116
117
118    def Print(self, api):
119        self.printHeader()
120        self.printBody(api)
121        self.printFooter()
122        return
123
124
125    def printHeader(self):
126        """Print the header associated with all files and call the printRealHeader method."""
127
128        print '/* DO NOT EDIT - This file generated automatically by %s script */' \
129                % (self.name)
130        print ''
131        print '/*'
132        print ' * ' + self.license.replace('\n', '\n * ')
133        print ' */'
134        print ''
135        if self.header_tag:
136            print '#if !defined( %s )' % (self.header_tag)
137            print '#  define %s' % (self.header_tag)
138            print ''
139        self.printRealHeader();
140        return
141
142
143    def printFooter(self):
144        """Print the header associated with all files and call the printRealFooter method."""
145
146        self.printRealFooter()
147
148        if self.undef_list:
149            print ''
150            for u in self.undef_list:
151                print "#  undef %s" % (u)
152
153        if self.header_tag:
154            print ''
155            print '#endif /* !defined( %s ) */' % (self.header_tag)
156
157
158    def printRealHeader(self):
159        """Print the "real" header for the created file.
160
161        In the base class, this function is empty.  All derived
162        classes should over-ride this function."""
163        return
164
165
166    def printRealFooter(self):
167        """Print the "real" footer for the created file.
168
169        In the base class, this function is empty.  All derived
170        classes should over-ride this function."""
171        return
172
173
174    def printPure(self):
175        """Conditionally define `PURE' function attribute.
176
177        Conditionally defines a preprocessor macro `PURE' that wraps
178        GCC's `pure' function attribute.  The conditional code can be
179        easilly adapted to other compilers that support a similar
180        feature.
181
182        The name is also added to the file's undef_list.
183        """
184        self.undef_list.append("PURE")
185        print """#  if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
186#    define PURE __attribute__((pure))
187#  else
188#    define PURE
189#  endif"""
190        return
191
192
193    def printFastcall(self):
194        """Conditionally define `FASTCALL' function attribute.
195
196        Conditionally defines a preprocessor macro `FASTCALL' that
197        wraps GCC's `fastcall' function attribute.  The conditional
198        code can be easilly adapted to other compilers that support a
199        similar feature.
200
201        The name is also added to the file's undef_list.
202        """
203
204        self.undef_list.append("FASTCALL")
205        print """#  if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
206#    define FASTCALL __attribute__((fastcall))
207#  else
208#    define FASTCALL
209#  endif"""
210        return
211
212
213    def printVisibility(self, S, s):
214        """Conditionally define visibility function attribute.
215
216        Conditionally defines a preprocessor macro name S that wraps
217        GCC's visibility function attribute.  The visibility used is
218        the parameter s.  The conditional code can be easilly adapted
219        to other compilers that support a similar feature.
220
221        The name is also added to the file's undef_list.
222        """
223
224        self.undef_list.append(S)
225        print """#  if (defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) && defined(__ELF__))
226#    define %s  __attribute__((visibility("%s")))
227#  else
228#    define %s
229#  endif""" % (S, s, S)
230        return
231
232
233    def printNoinline(self):
234        """Conditionally define `NOINLINE' function attribute.
235
236        Conditionally defines a preprocessor macro `NOINLINE' that
237        wraps GCC's `noinline' function attribute.  The conditional
238        code can be easilly adapted to other compilers that support a
239        similar feature.
240
241        The name is also added to the file's undef_list.
242        """
243
244        self.undef_list.append("NOINLINE")
245        print """#  if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
246#    define NOINLINE __attribute__((noinline))
247#  else
248#    define NOINLINE
249#  endif"""
250        return
251
252
253def real_function_name(element):
254    name = element.get( "name" )
255    alias = element.get( "alias" )
256
257    if alias:
258        return alias
259    else:
260        return name
261
262
263def real_category_name(c):
264    if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
265        return "GL_VERSION_" + c.replace(".", "_")
266    else:
267        return c
268
269
270def classify_category(name, number):
271    """Based on the category name and number, select a numerical class for it.
272
273    Categories are divided into four classes numbered 0 through 3.  The
274    classes are:
275
276            0. Core GL versions, sorted by version number.
277            1. ARB extensions, sorted by extension number.
278            2. Non-ARB extensions, sorted by extension number.
279            3. Un-numbered extensions, sorted by extension name.
280    """
281
282    try:
283        core_version = float(name)
284    except Exception,e:
285        core_version = 0.0
286
287    if core_version > 0.0:
288        cat_type = 0
289        key = name
290    elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"):
291        cat_type = 1
292        key = int(number)
293    else:
294        if number != None:
295            cat_type = 2
296            key = int(number)
297        else:
298            cat_type = 3
299            key = name
300
301
302    return [cat_type, key]
303
304
305def create_parameter_string(parameters, include_names):
306    """Create a parameter string from a list of gl_parameters."""
307
308    list = []
309    for p in parameters:
310        if p.is_padding:
311            continue
312
313        if include_names:
314            list.append( p.string() )
315        else:
316            list.append( p.type_string() )
317
318    if len(list) == 0: list = ["void"]
319
320    return string.join(list, ", ")
321
322
323class gl_item(object):
324    def __init__(self, element, context, category):
325        self.context = context
326        self.name = element.get( "name" )
327        self.category = real_category_name( category )
328
329        return
330
331
332class gl_type( gl_item ):
333    def __init__(self, element, context, category):
334        gl_item.__init__(self, element, context, category)
335        self.size = int( element.get( "size" ), 0 )
336
337        te = typeexpr.type_expression( None )
338        tn = typeexpr.type_node()
339        tn.size = int( element.get( "size" ), 0 )
340        tn.integer = not is_attr_true( element, "float" )
341        tn.unsigned = is_attr_true( element, "unsigned" )
342        tn.pointer = is_attr_true( element, "pointer" )
343        tn.name = "GL" + self.name
344        te.set_base_type_node( tn )
345
346        self.type_expr = te
347        return
348
349
350    def get_type_expression(self):
351        return self.type_expr
352
353
354class gl_enum( gl_item ):
355    def __init__(self, element, context, category):
356        gl_item.__init__(self, element, context, category)
357        self.value = int( element.get( "value" ), 0 )
358
359        temp = element.get( "count" )
360        if not temp or temp == "?":
361            self.default_count = -1
362        else:
363            try:
364                c = int(temp)
365            except Exception,e:
366                raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
367
368            self.default_count = c
369
370        return
371
372
373    def priority(self):
374        """Calculate a 'priority' for this enum name.
375
376        When an enum is looked up by number, there may be many
377        possible names, but only one is the 'prefered' name.  The
378        priority is used to select which name is the 'best'.
379
380        Highest precedence is given to core GL name.  ARB extension
381        names have the next highest, followed by EXT extension names.
382        Vendor extension names are the lowest.
383        """
384
385        if self.name.endswith( "_BIT" ):
386            bias = 1
387        else:
388            bias = 0
389
390        if self.category.startswith( "GL_VERSION_" ):
391            priority = 0
392        elif self.category.startswith( "GL_ARB_" ):
393            priority = 2
394        elif self.category.startswith( "GL_EXT_" ):
395            priority = 4
396        else:
397            priority = 6
398
399        return priority + bias
400
401
402
403class gl_parameter(object):
404    def __init__(self, element, context):
405        self.name = element.get( "name" )
406
407        ts = element.get( "type" )
408        self.type_expr = typeexpr.type_expression( ts, context )
409
410        temp = element.get( "variable_param" )
411        if temp:
412            self.count_parameter_list = temp.split( ' ' )
413        else:
414            self.count_parameter_list = []
415
416        # The count tag can be either a numeric string or the name of
417        # a variable.  If it is the name of a variable, the int(c)
418        # statement will throw an exception, and the except block will
419        # take over.
420
421        c = element.get( "count" )
422        try:
423            count = int(c)
424            self.count = count
425            self.counter = None
426        except Exception,e:
427            count = 1
428            self.count = 0
429            self.counter = c
430
431        self.count_scale = int(element.get( "count_scale", "1" ))
432
433        elements = (count * self.count_scale)
434        if elements == 1:
435            elements = 0
436
437        #if ts == "GLdouble":
438        #	print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
439        #	print '/* # elements = %u */' % (elements)
440        self.type_expr.set_elements( elements )
441        #if ts == "GLdouble":
442        #	print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
443
444        self.is_client_only = is_attr_true( element, 'client_only' )
445        self.is_counter     = is_attr_true( element, 'counter' )
446        self.is_output      = is_attr_true( element, 'output' )
447
448
449        # Pixel data has special parameters.
450
451        self.width      = element.get('img_width')
452        self.height     = element.get('img_height')
453        self.depth      = element.get('img_depth')
454        self.extent     = element.get('img_extent')
455
456        self.img_xoff   = element.get('img_xoff')
457        self.img_yoff   = element.get('img_yoff')
458        self.img_zoff   = element.get('img_zoff')
459        self.img_woff   = element.get('img_woff')
460
461        self.img_format = element.get('img_format')
462        self.img_type   = element.get('img_type')
463        self.img_target = element.get('img_target')
464
465        self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
466        self.img_null_flag      = is_attr_true( element, 'img_null_flag' )
467        self.img_send_null      = is_attr_true( element, 'img_send_null' )
468
469        self.is_padding = is_attr_true( element, 'padding' )
470        return
471
472
473    def compatible(self, other):
474        return 1
475
476
477    def is_array(self):
478        return self.is_pointer()
479
480
481    def is_pointer(self):
482        return self.type_expr.is_pointer()
483
484
485    def is_image(self):
486        if self.width:
487            return 1
488        else:
489            return 0
490
491
492    def is_variable_length(self):
493        return len(self.count_parameter_list) or self.counter
494
495
496    def is_64_bit(self):
497        count = self.type_expr.get_element_count()
498        if count:
499            if (self.size() / count) == 8:
500                return 1
501        else:
502            if self.size() == 8:
503                return 1
504
505        return 0
506
507
508    def string(self):
509        return self.type_expr.original_string + " " + self.name
510
511
512    def type_string(self):
513        return self.type_expr.original_string
514
515
516    def get_base_type_string(self):
517        return self.type_expr.get_base_name()
518
519
520    def get_dimensions(self):
521        if not self.width:
522            return [ 0, "0", "0", "0", "0" ]
523
524        dim = 1
525        w = self.width
526        h = "1"
527        d = "1"
528        e = "1"
529
530        if self.height:
531            dim = 2
532            h = self.height
533
534        if self.depth:
535            dim = 3
536            d = self.depth
537
538        if self.extent:
539            dim = 4
540            e = self.extent
541
542        return [ dim, w, h, d, e ]
543
544
545    def get_stack_size(self):
546        return self.type_expr.get_stack_size()
547
548
549    def size(self):
550        if self.is_image():
551            return 0
552        else:
553            return self.type_expr.get_element_size()
554
555
556    def get_element_count(self):
557        c = self.type_expr.get_element_count()
558        if c == 0:
559            return 1
560
561        return c
562
563
564    def size_string(self, use_parens = 1):
565        s = self.size()
566        if self.counter or self.count_parameter_list:
567            list = [ "compsize" ]
568
569            if self.counter and self.count_parameter_list:
570                list.append( self.counter )
571            elif self.counter:
572                list = [ self.counter ]
573
574            if s > 1:
575                list.append( str(s) )
576
577            if len(list) > 1 and use_parens :
578                return "(%s)" % (string.join(list, " * "))
579            else:
580                return string.join(list, " * ")
581
582        elif self.is_image():
583            return "compsize"
584        else:
585            return str(s)
586
587
588    def format_string(self):
589        if self.type_expr.original_string == "GLenum":
590            return "0x%x"
591        else:
592            return self.type_expr.format_string()
593
594
595class gl_function( gl_item ):
596    def __init__(self, element, context):
597        self.context = context
598        self.name = None
599
600        self.entry_points = []
601        self.return_type = "void"
602        self.parameters = []
603        self.offset = -1
604        self.initialized = 0
605        self.images = []
606        self.exec_flavor = 'mesa'
607        self.desktop = True
608        self.deprecated = None
609
610        # self.entry_point_api_map[name][api] is a decimal value
611        # indicating the earliest version of the given API in which
612        # each entry point exists.  Every entry point is included in
613        # the first level of the map; the second level of the map only
614        # lists APIs which contain the entry point in at least one
615        # version.  For example,
616        # self.entry_point_api_map['ClipPlanex'] == { 'es1':
617        # Decimal('1.1') }.
618        self.entry_point_api_map = {}
619
620        # self.api_map[api] is a decimal value indicating the earliest
621        # version of the given API in which ANY alias for the function
622        # exists.  The map only lists APIs which contain the function
623        # in at least one version.  For example, for the ClipPlanex
624        # function, self.entry_point_api_map == { 'es1':
625        # Decimal('1.1') }.
626        self.api_map = {}
627
628        self.assign_offset = 0
629
630        self.static_entry_points = []
631
632        # Track the parameter string (for the function prototype)
633        # for each entry-point.  This is done because some functions
634        # change their prototype slightly when promoted from extension
635        # to ARB extension to core.  glTexImage3DEXT and glTexImage3D
636        # are good examples of this.  Scripts that need to generate
637        # code for these differing aliases need to real prototype
638        # for each entry-point.  Otherwise, they may generate code
639        # that won't compile.
640
641        self.entry_point_parameters = {}
642
643        self.process_element( element )
644
645        return
646
647
648    def process_element(self, element):
649        name = element.get( "name" )
650        alias = element.get( "alias" )
651
652        if is_attr_true(element, "static_dispatch", "true"):
653            self.static_entry_points.append(name)
654
655        self.entry_points.append( name )
656
657        self.entry_point_api_map[name] = {}
658        for api in ('es1', 'es2'):
659            version_str = element.get(api, 'none')
660            assert version_str is not None
661            if version_str != 'none':
662                version_decimal = Decimal(version_str)
663                self.entry_point_api_map[name][api] = version_decimal
664                if api not in self.api_map or \
665                        version_decimal < self.api_map[api]:
666                    self.api_map[api] = version_decimal
667
668        exec_flavor = element.get('exec')
669        if exec_flavor:
670            self.exec_flavor = exec_flavor
671
672        deprecated = element.get('deprecated', 'none')
673        if deprecated != 'none':
674            self.deprecated = Decimal(deprecated)
675
676        if not is_attr_true(element, 'desktop', 'true'):
677            self.desktop = False
678
679        if alias:
680            true_name = alias
681        else:
682            true_name = name
683
684            # Only try to set the offset when a non-alias entry-point
685            # is being processed.
686
687            offset = element.get( "offset" )
688            if offset:
689                try:
690                    o = int( offset )
691                    self.offset = o
692                except Exception, e:
693                    self.offset = -1
694                    if offset == "assign":
695                        self.assign_offset = 1
696
697
698        if not self.name:
699            self.name = true_name
700        elif self.name != true_name:
701            raise RuntimeError("Function true name redefined.  Was %s, now %s." % (self.name, true_name))
702
703
704        # There are two possible cases.  The first time an entry-point
705        # with data is seen, self.initialized will be 0.  On that
706        # pass, we just fill in the data.  The next time an
707        # entry-point with data is seen, self.initialized will be 1.
708        # On that pass we have to make that the new values match the
709        # valuse from the previous entry-point.
710
711        parameters = []
712        return_type = "void"
713        for child in element.getchildren():
714            if child.tag == "return":
715                return_type = child.get( "type", "void" )
716            elif child.tag == "param":
717                param = self.context.factory.create_parameter(child, self.context)
718                parameters.append( param )
719
720
721        if self.initialized:
722            if self.return_type != return_type:
723                raise RuntimeError( "Return type changed in %s.  Was %s, now %s." % (name, self.return_type, return_type))
724
725            if len(parameters) != len(self.parameters):
726                raise RuntimeError( "Parameter count mismatch in %s.  Was %d, now %d." % (name, len(self.parameters), len(parameters)))
727
728            for j in range(0, len(parameters)):
729                p1 = parameters[j]
730                p2 = self.parameters[j]
731                if not p1.compatible( p2 ):
732                    raise RuntimeError( 'Parameter type mismatch in %s.  "%s" was "%s", now "%s".' % (name, p2.name, p2.type_expr.original_string, p1.type_expr.original_string))
733
734
735        if true_name == name or not self.initialized:
736            self.return_type = return_type
737            self.parameters = parameters
738
739            for param in self.parameters:
740                if param.is_image():
741                    self.images.append( param )
742
743        if element.getchildren():
744            self.initialized = 1
745            self.entry_point_parameters[name] = parameters
746        else:
747            self.entry_point_parameters[name] = []
748
749        return
750
751    def filter_entry_points(self, entry_point_list):
752        """Filter out entry points not in entry_point_list."""
753        if not self.initialized:
754            raise RuntimeError('%s is not initialized yet' % self.name)
755
756        entry_points = []
757        for ent in self.entry_points:
758            if ent not in entry_point_list:
759                if ent in self.static_entry_points:
760                    self.static_entry_points.remove(ent)
761                self.entry_point_parameters.pop(ent)
762            else:
763                entry_points.append(ent)
764
765        if not entry_points:
766            raise RuntimeError('%s has no entry point after filtering' % self.name)
767
768        self.entry_points = entry_points
769        if self.name not in entry_points:
770            # use the first remaining entry point
771            self.name = entry_points[0]
772            self.parameters = self.entry_point_parameters[entry_points[0]]
773
774    def get_images(self):
775        """Return potentially empty list of input images."""
776        return self.images
777
778
779    def parameterIterator(self, name = None):
780        if name is not None:
781            return self.entry_point_parameters[name].__iter__();
782        else:
783            return self.parameters.__iter__();
784
785
786    def get_parameter_string(self, entrypoint = None):
787        if entrypoint:
788            params = self.entry_point_parameters[ entrypoint ]
789        else:
790            params = self.parameters
791
792        return create_parameter_string( params, 1 )
793
794    def get_called_parameter_string(self):
795        p_string = ""
796        comma = ""
797
798        for p in self.parameterIterator():
799            if p.is_padding:
800                continue
801            p_string = p_string + comma + p.name
802            comma = ", "
803
804        return p_string
805
806
807    def is_abi(self):
808        return (self.offset >= 0 and not self.assign_offset)
809
810    def is_static_entry_point(self, name):
811        return name in self.static_entry_points
812
813    def dispatch_name(self):
814        if self.name in self.static_entry_points:
815            return self.name
816        else:
817            return "_dispatch_stub_%u" % (self.offset)
818
819    def static_name(self, name):
820        if name in self.static_entry_points:
821            return name
822        else:
823            return "_dispatch_stub_%u" % (self.offset)
824
825    def entry_points_for_api_version(self, api, version = None):
826        """Return a list of the entry point names for this function
827        which are supported in the given API (and optionally, version).
828
829        Use the decimal.Decimal type to precisely express non-integer
830        versions.
831        """
832        result = []
833        for entry_point, api_to_ver in self.entry_point_api_map.iteritems():
834            if api not in api_to_ver:
835                continue
836            if version is not None and version < api_to_ver[api]:
837                continue
838            result.append(entry_point)
839        return result
840
841
842class gl_item_factory(object):
843    """Factory to create objects derived from gl_item."""
844
845    def create_function(self, element, context):
846        return gl_function(element, context)
847
848    def create_type(self, element, context, category):
849        return gl_type(element, context, category)
850
851    def create_enum(self, element, context, category):
852        return gl_enum(element, context, category)
853
854    def create_parameter(self, element, context):
855        return gl_parameter(element, context)
856
857    def create_api(self):
858        return gl_api(self)
859
860
861class gl_api(object):
862    def __init__(self, factory):
863        self.functions_by_name = {}
864        self.enums_by_name = {}
865        self.types_by_name = {}
866
867        self.category_dict = {}
868        self.categories = [{}, {}, {}, {}]
869
870        self.factory = factory
871
872        self.next_offset = 0
873
874        typeexpr.create_initial_types()
875        return
876
877    def filter_functions(self, entry_point_list):
878        """Filter out entry points not in entry_point_list."""
879        functions_by_name = {}
880        for func in self.functions_by_name.itervalues():
881            entry_points = [ent for ent in func.entry_points if ent in entry_point_list]
882            if entry_points:
883                func.filter_entry_points(entry_points)
884                functions_by_name[func.name] = func
885
886        self.functions_by_name = functions_by_name
887
888    def filter_functions_by_api(self, api, version = None):
889        """Filter out entry points not in the given API (or
890        optionally, not in the given version of the given API).
891        """
892        functions_by_name = {}
893        for func in self.functions_by_name.itervalues():
894            entry_points = func.entry_points_for_api_version(api, version)
895            if entry_points:
896                func.filter_entry_points(entry_points)
897                functions_by_name[func.name] = func
898
899        self.functions_by_name = functions_by_name
900
901
902    def parse_file(self, file_name):
903        doc = ET.parse( file_name )
904        self.process_element(file_name, doc)
905
906
907    def process_element(self, file_name, doc):
908        element = doc.getroot()
909        if element.tag == "OpenGLAPI":
910            self.process_OpenGLAPI(file_name, element)
911        return
912
913
914    def process_OpenGLAPI(self, file_name, element):
915        for child in element.getchildren():
916            if child.tag == "category":
917                self.process_category( child )
918            elif child.tag == "OpenGLAPI":
919                self.process_OpenGLAPI( file_name, child )
920            elif child.tag == '{http://www.w3.org/2001/XInclude}include':
921                href = child.get('href')
922                href = os.path.join(os.path.dirname(file_name), href)
923                self.parse_file(href)
924
925        return
926
927
928    def process_category(self, cat):
929        cat_name = cat.get( "name" )
930        cat_number = cat.get( "number" )
931
932        [cat_type, key] = classify_category(cat_name, cat_number)
933        self.categories[cat_type][key] = [cat_name, cat_number]
934
935        for child in cat.getchildren():
936            if child.tag == "function":
937                func_name = real_function_name( child )
938
939                temp_name = child.get( "name" )
940                self.category_dict[ temp_name ] = [cat_name, cat_number]
941
942                if self.functions_by_name.has_key( func_name ):
943                    func = self.functions_by_name[ func_name ]
944                    func.process_element( child )
945                else:
946                    func = self.factory.create_function( child, self )
947                    self.functions_by_name[ func_name ] = func
948
949                if func.offset >= self.next_offset:
950                    self.next_offset = func.offset + 1
951
952
953            elif child.tag == "enum":
954                enum = self.factory.create_enum( child, self, cat_name )
955                self.enums_by_name[ enum.name ] = enum
956            elif child.tag == "type":
957                t = self.factory.create_type( child, self, cat_name )
958                self.types_by_name[ "GL" + t.name ] = t
959
960        return
961
962
963    def functionIterateByCategory(self, cat = None):
964        """Iterate over functions by category.
965
966        If cat is None, all known functions are iterated in category
967        order.  See classify_category for details of the ordering.
968        Within a category, functions are sorted by name.  If cat is
969        not None, then only functions in that category are iterated.
970        """
971        lists = [{}, {}, {}, {}]
972
973        for func in self.functionIterateAll():
974            [cat_name, cat_number] = self.category_dict[func.name]
975
976            if (cat == None) or (cat == cat_name):
977                [func_cat_type, key] = classify_category(cat_name, cat_number)
978
979                if not lists[func_cat_type].has_key(key):
980                    lists[func_cat_type][key] = {}
981
982                lists[func_cat_type][key][func.name] = func
983
984
985        functions = []
986        for func_cat_type in range(0,4):
987            keys = lists[func_cat_type].keys()
988            keys.sort()
989
990            for key in keys:
991                names = lists[func_cat_type][key].keys()
992                names.sort()
993
994                for name in names:
995                    functions.append(lists[func_cat_type][key][name])
996
997        return functions.__iter__()
998
999
1000    def functionIterateByOffset(self):
1001        max_offset = -1
1002        for func in self.functions_by_name.itervalues():
1003            if func.offset > max_offset:
1004                max_offset = func.offset
1005
1006
1007        temp = [None for i in range(0, max_offset + 1)]
1008        for func in self.functions_by_name.itervalues():
1009            if func.offset != -1:
1010                temp[ func.offset ] = func
1011
1012
1013        list = []
1014        for i in range(0, max_offset + 1):
1015            if temp[i]:
1016                list.append(temp[i])
1017
1018        return list.__iter__();
1019
1020
1021    def functionIterateAll(self):
1022        return self.functions_by_name.itervalues()
1023
1024
1025    def enumIterateByName(self):
1026        keys = self.enums_by_name.keys()
1027        keys.sort()
1028
1029        list = []
1030        for enum in keys:
1031            list.append( self.enums_by_name[ enum ] )
1032
1033        return list.__iter__()
1034
1035
1036    def categoryIterate(self):
1037        """Iterate over categories.
1038
1039        Iterate over all known categories in the order specified by
1040        classify_category.  Each iterated value is a tuple of the
1041        name and number (which may be None) of the category.
1042        """
1043
1044        list = []
1045        for cat_type in range(0,4):
1046            keys = self.categories[cat_type].keys()
1047            keys.sort()
1048
1049            for key in keys:
1050                list.append(self.categories[cat_type][key])
1051
1052        return list.__iter__()
1053
1054
1055    def get_category_for_name( self, name ):
1056        if self.category_dict.has_key(name):
1057            return self.category_dict[name]
1058        else:
1059            return ["<unknown category>", None]
1060
1061
1062    def typeIterate(self):
1063        return self.types_by_name.itervalues()
1064
1065
1066    def find_type( self, type_name ):
1067        if type_name in self.types_by_name:
1068            return self.types_by_name[ type_name ].type_expr
1069        else:
1070            print "Unable to find base type matching \"%s\"." % (type_name)
1071            return None
1072