1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    classman.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16
17# python classman.py
18
19# TODO:
20# - store old values in attrcons and recover with undo
21
22
23# To be or not to be.  -- Shakespeare
24# To do is to be.  -- Nietzsche
25# To be is to do.  -- Sartre
26# Do be do be do.  -- Sinatra
27
28# save with is saved flag
29# xml mixin
30# different attrconfig classe (numbers, strings, lists, colors,...)
31
32import types
33import os
34import pickle
35import sys
36import string
37from collections import OrderedDict
38from datetime import datetime
39#import numpy as np
40import xmlman as xm
41from logger import Logger
42from misc import get_inversemap
43
44##########
45
46
47# event triggers
48#       plugtype    plugcode
49EVTDEL = 0  # delete attribute
50EVTSET = 1  # set attribute
51EVTGET = 2  # get attribute
52EVTADD = 3  # add/create attribute
53
54EVTDELITEM = 20  # delete attribute
55EVTSETITEM = 21  # set attribute
56EVTGETITEM = 22  # get attribute
57EVTADDITEM = 23  # add/create attribute
58
59
60ATTRS_NOSAVE = ('value', 'plugin', '_obj', '_manager', 'get', 'set', 'add',
61                'del', 'delete', 'childs', 'parent', '_attrconfig_id_tab')
62ATTRS_SAVE = ('ident', '_name', 'managertype', '_info', 'xmltag', '_attrs_nosave')
63ATTRS_SAVE_TABLE = ('_is_localvalue', 'attrname', '_colconfigs', '_ids', '_inds', '_attrconfigs',
64                    '_groups', 'plugin', '_is_indexing', '_index_to_id', '_id_to_index')
65
66STRUCTS_COL = ('odict', 'array')
67STRUCTS_SCALAR = ('scalar', 'list', 'matrix', 'scalar.func')
68
69NUMERICTYPES = (types.BooleanType, types.FloatType, types.IntType, types.LongType, types.ComplexType)
70STRINGTYPES = (types.StringType, types.UnicodeType)
71
72
73def save_obj(obj, filename, is_not_save_parent=False):
74    """
75    Saves python object to a file with filename.
76    Filename may also include absolute or relative path.
77    If operation fails a False is returned and True otherwise.
78    """
79    # print 'save_obj',is_not_save_parent,filename,obj.parent
80    try:
81        file = open(filename, 'wb')
82    except:
83        print 'WARNING in save: could not open', filename
84        return False
85
86    if is_not_save_parent:
87        parent = obj.parent
88        obj.parent = None
89    # print '  before',is_not_save_parent,parent,obj.parent
90    pickle.dump(obj, file, protocol=2)
91    file.close()
92    #  set all objects and attrubutes to unsaved again
93    # obj.set_unsaved()
94    # no, decided to eliminate _is_saved restriction
95    # print '  after',is_not_save_parent,parent,obj.parent
96    if is_not_save_parent:
97        obj.parent = parent
98    return True
99
100
101def load_obj(filename, parent=None, is_throw_error=False):
102    """
103    Reads python object from a file with filename and returns object.
104    Filename may also include absolute or relative path.
105    If operation fails a None object is returned.
106    """
107    print 'load_obj', filename
108
109    if is_throw_error:
110        f = open(filename, 'rb')
111    else:
112        try:
113            f = open(filename, 'rb')
114        except:
115            print 'WARNING in load_obj: could not open', filename
116            return None
117
118    # try:
119    # print '  pickle.load...'
120    obj = pickle.load(f)
121    f.close()
122
123    # init_postload_internal is to restore INTERNAL states from INTERNAL states
124    # print 'load_obj->init_postload_internal',obj.ident
125    obj.init_postload_internal(parent)
126    # print '  after init_postload_internal  obj.parent',obj.parent
127
128    # init_postload_external is to restore INTERNAL states from EXTERNAL states
129    # such as linking
130    #obj.init_postload_external(is_root = True)
131    obj.init_postload_external()
132
133    # _init4_ is to do misc stuff when everything is set
134    # obj._init4_config()
135
136    return obj
137
138# class ObjXmlMixin:
139
140
141# class AttrXmlMixin:
142
143
144class Plugin:
145
146    def __init__(self, obj, is_enabled=True):
147        self._obj = obj  # this can be either attrconf or main object
148        self._events = {}
149        self._has_events = False
150        self._is_enabled = is_enabled
151
152    def get_obj(self):
153        return self._obj
154
155    def unplug(self):
156        del self._events
157        self._events = {}
158        self._has_events = False
159
160    def add_event(self, trigger, function):
161        """
162        Standard plug types are automatically set but the system:
163
164        """
165        if not self._events.has_key(trigger):
166            self._events[trigger] = []
167        self._events[trigger].append(function)
168        self._has_events = True
169
170    def del_event(self, trigger):
171        del self._events[trigger]
172        if len(self._events) == 0:
173            self._has_events = False
174
175    def enable(self, is_enabled=True):
176        self._is_enabled = is_enabled
177
178    def exec_events(self, trigger):
179        if self._has_events & self._is_enabled:
180            # print '**PuginMixin.exec_events',trigger,(EVTGETITEM,EVTGET)
181            # if trigger!=EVTGET:
182            #    print '  call set_modified',self._obj
183            #    self._obj.set_modified(True)
184
185            for function in self._events.get(trigger, []):
186                function(self._obj)
187
188    def exec_events_attr(self, trigger, attrconfig):
189        if self._has_events & self._is_enabled:
190            # print '**PuginMixin.exec_events',trigger,(EVTGETITEM,EVTGET)
191            # if trigger!=EVTGET:
192            #    print '  call set_modified',self._obj
193            #    self._obj.set_modified(True)
194
195            for function in self._events.get(trigger, []):
196                function(self._obj, attrconfig)
197
198    def exec_events_ids(self, trigger, ids):
199        """
200        Executes all functions assigned for this trigger for multiple ids.
201        """
202        if self._has_events & self._is_enabled:
203            # print '**ArrayConf._execute_events_keys',trigger,ids
204            # print '  _events',self._events
205
206            for function in self._events.get(trigger, []):
207                function(self._obj, ids)
208
209
210class AttrConf:
211    """
212    Contains additional information on the object's attribute.
213    """
214
215    def __init__(self, attrname, default,
216                 groupnames=[], perm='rw',
217                 is_save=True,
218                 # is_link = False, # define link class
219                 is_copy=True,
220                 name='', info='',
221                 unit='',
222                 xmltag=None,
223                 xmlsep=' ',
224                 is_plugin=False,
225                 struct='scalar',
226                 metatype='',
227                 is_returnval=True,
228                 **attrs):
229        # if struct == 'None':
230        #    if hasattr(default, '__iter__'):
231        #        struct = 'scalar'
232        #    else:
233        #        struct = 'list'
234
235        # these states will be saved and reloaded
236        self.attrname = attrname
237        self.groupnames = groupnames
238        self.metatype = metatype
239        self.struct = struct
240
241        self._default = default
242
243        self._is_save = is_save  # will be set properly in set_manager
244        self._is_copy = is_copy
245        self._is_localvalue = True  # value stored locally, set in set_manager
246        self._is_returnval = is_returnval
247        self._unit = unit
248        self._info = info
249        self._name = name
250        self._perm = perm
251
252        # states below need to be resored after load
253        self._manager = None  # set later by attrsman , necessary?
254        self._obj = None  # parent object, set later by attrsman
255
256        self._is_modified = False
257        self._is_saved = False
258
259        self.init_plugin(is_plugin)
260        # self._init_xml(xmltag)
261        self.set_xmltag(xmltag, xmlsep)
262
263        # set rest of attributes passed as keyword args
264        # no matter what they are used for
265        for attr, value in attrs.iteritems():
266            setattr(self, attr, value)
267
268    def is_save(self):
269        return self._is_save
270
271    def set_save(self, is_save):
272        if is_save:
273            self._manager.do_save_attr(self.attrname)
274        else:
275            self._manager.do_not_save_attr(self.attrname)
276
277    def add_groupnames(self, groupnames):
278        self.groupnames = list(set(self.groupnames+groupnames))
279        self._manager.insert_groupnames(self)
280
281    def has_group(self, groupname):
282        return groupname in self.groupnames
283
284    def enable_plugin(self, is_enabled=True):
285        if self.plugin is not None:
286            self.plugin.enable(is_enabled)
287
288    def get_metatype(self):
289        return self.metatype
290
291    def init_plugin(self, is_plugin):
292        if is_plugin:
293            self.plugin = Plugin(self)
294            self.set = self.set_plugin
295            self.get = self.get_plugin
296        else:
297            self.plugin = None
298
299    # def _init_xml(self,xmltag=None):
300    #    if xmltag is not None:
301    #        self.xmltag = xmltag
302    #    else:
303    #        self.xmltag = self.attrname
304
305    def set_xmltag(self, xmltag, xmlsep=' '):
306        self.xmltag = xmltag
307        self.xmlsep = xmlsep
308
309    def get_value_from_xmlattr(self, xmlattrs):
310        """
311        Return a value of the correct data type
312        from the xml attribute object
313
314        If this configuration is not found in xmlattrs
315        then None is returned.
316        """
317        if (self.xmltag is not None):
318            if xmlattrs.has_key(self.xmltag):
319                return self.get_value_from_string(xmlattrs[self.xmltag])
320            else:
321                return None
322
323    def get_value_from_string(self, s, sep=','):
324        """
325        Returns the attribute value from a string in the correct type.
326        """
327
328        if self.metatype == 'color':
329            return xm.parse_color(s, sep)
330
331        # TODO: allow arrays
332        # elif hasattr(val, '__iter__'):
333        #    if len(val)>0:
334        #        if hasattr(val[0], '__iter__'):
335        #            # matrix
336        #            fd.write(xm.mat(self.xmltag,val))
337        #        else:
338        #            # list
339        #            fd.write(xm.arr(self.xmltag,val,self.xmlsep))
340        #    else:
341        #        # empty list
342        #        #fd.write(xm.arr(self.xmltag,val))
343        #        # don't even write empty lists
344        #        pass
345
346        elif hasattr(self, 'xmlmap'):
347            imap = get_inversemap(self.xmlmap)
348            # print 'get_value_from_string',s,imap
349            if imap.has_key(s):
350                return imap[s]
351            else:
352                return self.get_numvalue_from_string(s)
353
354        else:
355            return self.get_numvalue_from_string(s)
356
357    def get_numvalue_from_string(self, s):
358        t = type(self._default)
359        if t in (types.UnicodeType, types.StringType):
360            return s
361
362        elif t in (types.UnicodeType, types.StringType):
363            return s
364
365        elif t in (types.LongType, types.IntType):
366            return int(s)
367
368        elif t in (types.FloatType, types.ComplexType):
369            return float(s)
370
371        elif t == types.BooleanType:  # use default and hope it is no a numpy bool!!!
372            if s in ('1', 'True'):
373                return True
374            else:
375                return False
376
377        else:
378            return None  # unsuccessful
379
380    def write_xml(self, fd):
381        if self.xmltag is not None:
382            self._write_xml_value(self.get_value(), fd)
383
384    def _write_xml_value(self, val, fd):
385        # print 'write_xml',self.xmltag,hasattr(val, '__iter__')
386        if self.metatype == 'color':
387            fd.write(xm.color(self.xmltag, val))
388
389        elif hasattr(val, '__iter__'):
390            if len(val) > 0:
391                if hasattr(val[0], '__iter__'):
392                    # matrix
393                    fd.write(xm.mat(self.xmltag, val))
394                else:
395                    # list
396                    fd.write(xm.arr(self.xmltag, val, self.xmlsep))
397            else:
398                # empty list
399                # fd.write(xm.arr(self.xmltag,val))
400                # don't even write empty lists
401                pass
402
403        elif hasattr(self, 'xmlmap'):
404            if self.xmlmap.has_key(val):
405                fd.write(xm.num(self.xmltag, self.xmlmap[val]))
406            else:
407                fd.write(xm.num(self.xmltag, val))
408
409        elif hasattr(self, 'choices'):
410            if type(self.choices) == types.ListType:
411                fd.write(xm.num(self.xmltag, val))
412            else:
413                # print '_write_xml_value',self.attrname
414                # print '  val,self.choices.values()',val,self.choices.values()
415                i = self.choices.values().index(val)
416                fd.write(xm.num(self.xmltag, self.choices.keys()[i]))
417
418        elif type(self._default) == types.BooleanType:  # use default and hope it is no a numpy bool!!!
419            if val:
420                fd.write(xm.num(self.xmltag, 1))
421            else:
422                fd.write(xm.num(self.xmltag, 0))
423
424        elif type(self._default) in (types.UnicodeType, types.StringType):
425            if len(val) > 0:
426                fd.write(xm.num(self.xmltag, val))
427
428        else:
429            # scalar number or string
430            fd.write(xm.num(self.xmltag, val))
431
432    def get_name(self):
433        return self._name
434
435    def is_modified(self):
436        # print 'is_modified', self.attrname, self._is_modified
437        return self._is_modified
438
439    def set_modified(self, is_modified):
440        self._is_modified = is_modified
441
442    def set_manager(self, manager):
443        """
444        Method to set manager to attribute configuration object.
445        This is either attribute manager or table manager.
446        Used by add method of AttrManager
447        """
448        self._manager = manager
449        self._is_localvalue = manager.is_localvalue()
450        self.set_save(self._is_save)
451
452    def get_manager(self):
453        """
454        Method to get manager to attribute configuration object.
455        """
456        return self._manager
457
458    def set_obj(self, obj):
459        """
460        Method to set instance of managed object.
461        Used by add method of AttrManager
462        """
463        self._obj = obj
464
465    def get_obj(self):
466        return self._obj
467
468    def get(self):
469        #  return attribute, overridden with indexing for array and dict struct
470        return self.get_value()
471
472    def set(self, value):
473        # set attribute, overridden with indexing for array and dict struct
474        if value != self.get_value():
475            self.set_value(value)
476            self._is_modified = True
477        return value
478
479    def get_plugin(self):
480        """
481        Default get method with plugin for scalar attrs
482        """
483        #  return attribute, overridden with indexing for array and dict struct
484        self.plugin.exec_events(EVTGET)
485
486        return self.get_value()
487
488    def set_plugin(self, value):
489        """
490        Default set method with plugin for scalar attrs
491        """
492        # set attribute, overridden with indexing for array and dict struct
493        if value != self.get_value():
494            self.set_value(value)
495            self._is_modified = True
496            self.plugin.exec_events(EVTSET)
497
498        return value
499
500    def set_default(self, value):
501        self._default = value
502
503    def get_default(self):
504        return self._default
505
506    def get_init(self):
507        """
508        Returns initialization of attribute.
509        Usually same as get_default for scalars.
510        Overridden by table configuration classes
511        """
512        value = self.get_default()
513        # store locally if required
514        if self._is_localvalue:
515            self.value = value
516        return value
517
518    def reset(self):
519        if self._is_localvalue:
520            self.value = self.get_default()
521        else:
522            setattr(self._obj, self.attrname, self.get_default())
523
524    def clear(self):
525        self.reset()
526
527    # def is_tableattr(self):
528    #    return self.struct in ('dict','array','list')
529
530    def set_perm(self, perm):
531        self._perm = perm
532
533    def get_perm(self):
534        return self._perm
535
536    def is_returnval(self):
537        if hasattr(self, '_is_returnval'):  # for back compatibility
538            return self._is_returnval
539        else:
540            return True
541
542    def is_readonly(self):
543        return 'w' not in self._perm
544
545    def is_writable(self):
546        return 'w' in self._perm
547
548    def is_editable(self):
549        """Can attribute be edited """
550        return 'e' in self._perm
551
552    def has_unit(self):
553        return self._unit != ''
554
555    def has_info(self):
556        return self.get_info() is not None
557
558    def is_colattr(self):
559        return hasattr(self, '__getitem__')
560
561    def get_info(self):
562        if self._info is None:
563            return self.__doc__
564        else:
565            return self._info
566
567    def format_unit(self, show_parentesis=False):
568        if self._unit in ('', None):
569            return ''
570        if show_parentesis:
571            return '[%s]' % self._unit
572        else:
573            return '%s' % self._unit
574
575    def format_value(self, show_unit=False, show_parentesis=False):
576        if show_unit:
577            unit = ' '+self.format_unit(show_parentesis)
578        else:
579            unit = ''
580        # return repr(self.get_value())+unit
581        return str(self.get_value())+unit
582
583    def format_symbol(self):
584        if hasattr(self, 'symbol'):
585            symbol = self.symbol
586        else:
587            symbol = self._name
588
589        return symbol+' '+self.format_unit(show_parentesis=True)
590
591    ####
592    def get_value(self):
593        # always return attribute, no indexing, no plugin
594        if self._is_localvalue:
595            return self.value
596        else:
597            return getattr(self._obj, self.attrname)
598
599    def set_value(self, value):
600        # set entire attribute, no indexing, no plugin
601        # print 'AttrConf.set_value',self.attrname, self._is_localvalue, value, type(value)
602        if self._is_localvalue:
603            self.value = value
604        else:
605            return setattr(self._obj, self.attrname, value)
606
607    def predelete(self):
608        """
609        Cleanup operations before deleting
610        """
611        if self._is_localvalue:
612            del self.value  # delete value
613        else:
614            del self._obj.__dict__[self.attrname]  # delete value
615
616    # def init_presave_internal(self, man, obj):
617    #    pass
618    # not a good idea to delete links, plugins here
619
620    # def save_value(self, state):
621    #    """
622    #    Save attribute value of managed object to state dict.
623    #
624    #    move this into __getstate__
625    #
626    #    restore value in _obj during postllad_external
627    #
628    #    make _getstate_ for speecific save
629    #    """
630    #    #print 'save_value',self.attrname,self._is_save, self._is_localvalue,
631    #    #
632    #    # Attention can be called fron __getstate__ of obj if _is_localvalue=False
633    #    # or from __getstate__ of attribute config if _is_localvalue=True
634
635    def _getstate_specific(self, state):
636        """
637        Called by __getstate__ to add/change specific states,
638        before returning states.
639        To be overridden.
640        """
641        pass
642
643    def __getstate__(self):
644        # print 'AttrConf.__getstate__',self.get_obj().format_ident_abs(),self.attrname
645        # print '  self.__dict__=\n',self.__dict__.keys()
646        if self._is_saved:
647            # this message indicates a loop!!
648            print 'WARNING in __getstate__: Attribute already saved:', self.get_obj().format_ident_abs(), self.attrname
649        state = {}
650        for attr in self.__dict__.keys():
651
652            if attr == 'plugin':
653                plugin = self.__dict__[attr]
654                if plugin is not None:
655                    state[attr] = True
656                else:
657                    state[attr] = False
658
659            elif attr not in ATTRS_NOSAVE:
660                state[attr] = self.__dict__[attr]
661
662        if self._is_save:
663            self._is_modified = False
664            state['value'] = self.get_value()
665
666        self._getstate_specific(state)
667        # print  '  state',state
668        return state
669
670    def __setstate__(self, state):
671        # print '__setstate__',self
672
673        # this is always required, but will not be saved
674        self.plugins = {}
675
676        for attr in state.keys():
677            # print '  state key',attr, state[attr]
678            # done in init_postload_internal...
679            # if attr=='plugin':
680            #    if state[attr]==True:
681            #        self.__dict__[attr] = Plugin(self)
682            #    else:
683            #        self.__dict__[attr]= None
684            # else:
685            self.__dict__[attr] = state[attr]
686
687    def init_postload_internal(self, man, obj):
688        # print 'AttrConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'obj:',obj.ident
689
690        self.set_manager(man)
691        self.set_obj(obj)
692        self.init_plugin(self.plugin)
693
694        # set initial values for unsafed attributes
695        if not self._is_save:
696            self.set_value(self.get_init())
697        else:
698            if self._is_localvalue:
699                # OK self.value already set in __setstate__
700                pass
701            else:
702                setattr(self._obj, self.attrname, self.value)  # TODO: could be made nicer with method
703                del self.value  # no longer needed
704
705        # print '  check',hasattr(self,'value')
706        # print '  value=',self.get_value()
707        self._is_saved = False
708
709    def init_postload_external(self):
710        pass
711
712
713class NumConf(AttrConf):
714    """
715    Contains additional information on the object's attribute.
716    Here specific number related attributes are defined.
717    """
718
719    def __init__(self, attrname, default,
720                 digits_integer=None, digits_fraction=None,
721                 minval=None, maxval=None,
722                 **kwargs):
723        self.min = minval
724        self.max = maxval
725        self.digits_integer = digits_integer
726        self.digits_fraction = digits_fraction
727
728        AttrConf.__init__(self,  attrname, default, metatype='number',
729                          **kwargs
730                          )
731
732
733class ObjConf(AttrConf):
734    """
735    Contains additional information on the object's attribute.
736    Configures Pointer to another object .
737    This other object must have an ident.
738    it can be either a child (then it will be saved)
739    or a link (then only the id will saved)
740    If it is a child the is_child = True (default value)
741    """
742
743    def __init__(self, valueobj, is_child=True, **kwargs):
744        attrname = valueobj.get_ident()
745        self._is_child = is_child
746        AttrConf.__init__(self,  attrname, valueobj,
747                          struct='scalar',
748                          metatype='obj',
749                          perm='r',
750                          **kwargs
751                          )
752
753    def set_obj(self, obj):
754        """
755        Method to set instance of managed object.
756        Used by add method of AttrManager
757        """
758        # print 'ObjConf.set_obj',self.attrname,obj.ident
759        AttrConf.set_obj(self, obj)
760
761        if self._is_child:
762            # tricky: during first initialization
763            # child instance is stored in default
764            obj.set_child(self)
765
766    def predelete(self):
767        AttrConf.predelete(self)
768        if self._is_child:
769            self.get_obj().del_child(self.attrname)
770
771    def reset(self):
772        if self._is_child:
773            self.get_value().reset()
774
775    def clear(self):
776        if self._is_child:
777            self.get_value().clear()
778
779    def is_child(self):
780        return self._is_child
781
782    def _getstate_specific(self, state):
783        """
784        Called by __getstate__ to add/change specific states,
785        before returning states.
786        To be overridden.
787        """
788        # print 'ObjConf._getstate_specific',self.attrname,self._is_child,self._is_save
789        if self._is_save:
790            if self._is_child:
791                # OK self.value already set in
792                pass
793            else:
794                # print ' remove object reference from value and create ident'
795                state['value'] = None
796                state['_ident_value'] = self.get_value().get_ident_abs()
797                # print '  ',
798
799    def is_modified(self):
800        # if self._is_child
801        #is_modified = self.get_value().is_modified()
802        # print 'is_modified', self.attrname, is_modified
803        return self.get_value().is_modified()
804
805    def set_modified(self, is_modified):
806        if self._is_child:
807            self.get_value().set_modified(is_modified)
808
809    def write_xml(self, fd):
810        """
811        Objects are not written here, but in write_xml of the parent obj.
812        """
813        pass
814
815    def init_postload_internal(self, man, obj):
816        # print 'ObjConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'parent obj:',obj.ident
817
818        AttrConf.init_postload_internal(self, man, obj)
819        if self._is_child:
820            # print '  make sure children get initialized'
821            # self.get_value().init_postload_internal(obj)
822            # print '  call init_postload_internal of',self.get_value().ident,self.get_value(),self.get_value().__class__,self.get_value().init_postload_internal
823            self.get_value().init_postload_internal(obj)
824
825    def init_postload_external(self):
826        # print 'ObjConf.init_postload_external',self.attrname,self._is_child
827        if self._is_child:
828            # restore normally
829            AttrConf.init_postload_external(self)
830            self.get_value().init_postload_external()
831        else:
832            # Substitute absolute ident with link object.
833            # Called from init_postload_external of attrsman during load_obj
834            #
835            ident_abs = self._ident_value
836            # print 'reset_linkobj',self.attrname,ident_abs
837            obj = self.get_obj()
838            rootobj = obj.get_root()
839            # print '  rootobj',rootobj.ident
840            linkobj = rootobj.get_obj_from_ident(ident_abs)
841            # print '  linkobj',linkobj.ident
842            self.set_value(linkobj)
843
844    # def get_valueobj(self):
845    #    """
846    #    This is called by get_childobj to retrive the child instance.
847    #    """
848    #    return self.get_value()
849
850    def get_name(self):
851        return self.get_value().get_name()
852
853    def get_info(self):
854        return self.get_value().__doc__
855
856    def format_value(self, show_unit=False, show_parentesis=False):
857        return repr(self.get_value())
858
859
860class FuncConf(AttrConf):
861    """
862    Configures a function.
863    The function with name funcname must be a method of the object.
864    Default value is used to specify the type of output.
865    """
866
867    def __init__(self,  attrname, funcname, exampleoutput, struct='scalar.func', perm='r', **kwargs):
868        self.funcname = funcname
869
870        AttrConf.__init__(self,  attrname, exampleoutput,
871                          struct=struct,
872                          perm=perm,
873                          is_save=False,
874                          **kwargs
875                          )
876
877    def set_obj(self, obj):
878        AttrConf.set_obj(self, obj)
879        if self._info == '':
880            self._info = getattr(self._obj, self.funcname).__doc__
881
882    def get_value(self):
883        # print 'get_value',self.attrname
884        # always return attribute, no indexing, no plugin
885        return getattr(self._obj, self.funcname)()
886        # if self._is_localvalue:
887        #    return self.value
888        # else:
889        #    return getattr(self._obj, self.attrname)
890
891    def get_function(self):
892        # print 'get_function',self.attrname
893        return getattr(self._obj, self.funcname)
894
895    def set_value(self, value):
896        # set entire attribute, no indexing, no plugin
897        # print 'AttrConf.set_value',self.attrname, self._is_localvalue, value, type(value)
898        # this will run the function and return a value
899        return self.get_function()
900
901    def is_modified(self):
902        return False
903
904    def reset(self):
905        pass
906
907    def clear(self):
908        pass
909
910
911class Indexing:
912    """
913    Mixing to allow any column attribute to be used as index.
914    """
915
916    def _init_indexing(self):
917        """
918        Init Indexing management attributes.
919        """
920
921        self._index_to_id = {}  # OrderedDict()
922
923        # this updates index if values already exist
924        if hasattr(self, 'value'):
925            ids = self.get_obj().get_ids()
926            self.add_indices(ids, self[ids])
927
928    def reset_index(self):
929        self._init_indexing()
930
931    def get_indexmap(self):
932        return self._index_to_id
933
934    def get_id_from_index(self, index):
935        return self._index_to_id[index]
936
937    def has_index(self, index):
938        return self._index_to_id.has_key(index)
939
940    def get_ids_from_indices(self, indices):
941        ids = len(indices)*[0]
942        for i in range(len(indices)):
943            # if not self._index_to_id.has_key(indices[i]):
944            #    print 'WARNING from get_ids_from_indices: no index',indices[i]
945            #    print self._index_to_id
946            ids[i] = self._index_to_id[indices[i]]
947        return ids
948
949    def get_ids_from_indices_save(self, indices):
950        ids = len(indices)*[0]
951        for i in range(len(indices)):
952            if not self._index_to_id.has_key(indices[i]):
953                ids[i] = -1
954            else:
955                ids[i] = self._index_to_id[indices[i]]
956        return ids
957
958    # use set instead of add
959    def add_indices(self, ids, indices):
960        for _id, index in zip(ids, indices):
961            self.add_index(_id, index)
962
963    def add_index(self, _id, index):
964        self._index_to_id[index] = _id
965
966    def rebuild_indices(self):
967        for idx in self._index_to_id.keys():
968            del self._index_to_id[idx]
969        ids = self.get_obj().get_ids()
970        self.add_indices(ids, self[ids])
971
972    def del_indices(self, ids):
973
974        for _id in ids:
975            self.del_index(_id)
976
977    def set_index(self, _id, index):
978        # print 'set_index',self.attrname,_id, index
979        # print '  B_index_to_id',self._index_to_id
980        self.del_index(_id)
981        self.add_index(_id, index)
982        # print '  A_index_to_id',self._index_to_id
983
984    def set_indices(self, ids, indices):
985        self.del_indices(ids)
986        self.add_indices(ids, indices)
987
988    def del_index(self, _id):
989        index = self[_id]
990        # when index is added (with set) no previous index value exists
991        if self._index_to_id.has_key(index):
992            del self._index_to_id[index]
993
994    def get_ids_sorted(self):
995        # print 'get_ids_sorted',self.value
996        # print '  _index_to_id',self._index_to_id
997        # print '  sorted',sorted(self._index_to_id.iteritems())
998        return OrderedDict(sorted(self._index_to_id.iteritems())).values()
999
1000    # def indexset(self, indices, values):
1001    # no! set made with respective attribute
1002    #    print 'indexset',indices
1003    #    print '  ids=',self.get_ids_from_indices(indices)
1004    #    print '  values=',values
1005    #    self[self.get_ids_from_indices(indices)] = values
1006
1007
1008class ColConf(Indexing, AttrConf):
1009    """
1010    Basic column configuration.
1011    Here an ordered dictionary is used to represent the data.
1012    #>>> from collections import OrderedDict
1013    #>>> spam = OrderedDict([('s',(1,2)),('p',(3,4)),('a',(5,6)),('m',(7,8))])
1014    >>> spam.values()
1015
1016    """
1017    # def __init__(self, **attrs):
1018    #    print 'ColConf',attrs
1019
1020    def __init__(self, attrname, default, is_index=False, **attrs):
1021        # print 'ColConf',attrs
1022        self._is_index = is_index
1023        AttrConf.__init__(self,  attrname, default,
1024                          struct='odict',
1025                          **attrs)
1026
1027        if is_index:
1028            self._init_indexing()
1029
1030    def is_index(self):
1031        return self._is_index
1032
1033    def get_defaults(self, ids):
1034        # create a list, should work for all types and dimensions
1035        # default can be scalar or an array of any dimension
1036        # print '\n\nget_defaults',self.attrname,ids,self.get_default()
1037        values = []
1038        for _id in ids:
1039            values.append(self.get_default())
1040        # len(ids)*self.get_default() # makes links, not copies
1041        return values
1042
1043    def get_init(self):
1044        """
1045        Returns initialization of attribute.
1046        Usually same as get_default for scalars.
1047        Overridden by table configuration classes
1048        """
1049        ids = self._manager.get_ids()
1050
1051        # print '\n\nget_init',self.attrname,ids
1052        values = self.get_defaults(ids)
1053        i = 0
1054        odict = OrderedDict()
1055        for _id in ids:
1056            odict[_id] = values[i]
1057            i += 1
1058        # store locally if required
1059        if self._is_localvalue:
1060            self.value = odict
1061        # pass on to calling instance
1062        # in this cas the data is stored under self._obj
1063        return odict
1064
1065    def reset(self):
1066        # this reset works also for np arrays!
1067        odict = self.get_init()
1068        if not self._is_localvalue:
1069            setattr(self._obj, self.attrname, odict)
1070        if self._is_index:
1071            self.reset_index()
1072
1073    def init_plugin(self, is_plugin):
1074        if is_plugin:
1075            self.plugin = Plugin(self)
1076            self.set = self.set_plugin
1077            self.get = self.get_plugin
1078            self.add = self.add_plugin
1079            self.delete = self.delete_plugin
1080        else:
1081            self.plugin = None
1082
1083    def write_xml(self, fd, _id):
1084        if self.xmltag is not None:
1085            self._write_xml_value(self[_id], fd)
1086
1087    def __delitem__(self, ids):
1088        # print '        before=\n',self.__dict__[attr]
1089        #attr = self.attrconf.get_attr()
1090        if hasattr(ids, '__iter__'):
1091            if self._is_index:
1092                self.del_indices(ids)
1093
1094            array = self.get_value()
1095            for _id in ids:
1096                del array[_id]
1097
1098        else:
1099            if self._is_index:
1100                self.del_index(ids)
1101
1102            del self.get_value()[ids]
1103
1104    def delete_item(self, _id):
1105        # print '        before=\n',self.__dict__[attr]
1106        #attr = self.attrconf.get_attr()
1107        del self.get_value()[_id]
1108
1109    def __getitem__(self, ids):
1110        # print '__getitem__',key
1111        if hasattr(ids, '__iter__'):
1112            items = len(ids)*[None]
1113            i = 0
1114            array = self.get_value()
1115            for _id in ids:
1116                items[i] = array[_id]
1117                i += 1
1118            return items
1119        else:
1120            return self.get_value()[ids]
1121
1122    def __setitem__(self, ids, values):
1123        # print '__setitem__',ids,values,type(self.get_value())
1124        if hasattr(ids, '__iter__'):
1125            if self._is_index:
1126                self.set_indices(ids, values)  # must be set before setting new value
1127            i = 0
1128            array = self.get_value()
1129            for _id in ids:
1130                array[_id] = values[i]
1131                i += 1
1132            # if self._is_index:
1133            #    self.add_indices(ids, values)
1134        else:
1135
1136            if self._is_index:
1137                self.set_index(ids, values)  # must be set before setting new value
1138            self.get_value()[ids] = values
1139
1140            # if self._is_index:
1141            #    self.add_index(ids, values)
1142
1143    def add(self, ids, values=None):
1144        if not hasattr(ids, '__iter__'):
1145            _ids = [ids]
1146            if values is not None:
1147                _values = [values]
1148        else:
1149            _ids = ids
1150            _values = values
1151
1152        if values is None:
1153            _values = self.get_defaults(_ids)
1154
1155        # print 'add ids, _values',ids, _values
1156        # trick to prevent updating index before value is added
1157        if self._is_index:
1158            is_index_backup = True
1159            self._is_index = False
1160        else:
1161            is_index_backup = False
1162
1163        self[_ids] = _values
1164
1165        if is_index_backup:
1166            self._is_index = True
1167            self.add_indices(_ids, _values)
1168
1169        self._is_modified = True
1170
1171    def add_plugin(self, ids, values=None):
1172        if not hasattr(ids, '__iter__'):
1173            _ids = [ids]
1174            if values is not None:
1175                _values = [values]
1176        else:
1177            _ids = ids
1178            _values = values
1179
1180        # print 'add ids, _values',ids, _values
1181        if values is None:
1182            _values = self.get_defaults(_ids)
1183        # trick to prevent updating index before value is added
1184        if self._is_index:
1185            is_index_backup = True
1186            self._is_index = False
1187        else:
1188            is_index_backup = False
1189
1190        self[_ids] = _values
1191
1192        if is_index_backup:
1193            self._is_index = True
1194            self.add_indices(_ids, _values)
1195
1196        self._is_modified = True
1197
1198        if self.plugin:
1199            self.plugin.exec_events_ids(EVTADDITEM, _ids)
1200
1201    def get(self, ids):
1202        """
1203        Central function to get the attribute value associated with ids.
1204        should be overridden by specific array configuration classes
1205        """
1206        return self[ids]
1207
1208    def get_plugin(self, ids):
1209        """
1210        Central function to get the attribute value associated with ids.
1211        should be overridden by specific array configuration classes
1212        """
1213        if self._plugin:
1214            if not hasattr(ids, '__iter__'):
1215                self.plugin.exec_events_ids(EVTGETITEM, [ids])
1216            else:
1217                self.plugin.exec_events_ids(EVTGETITEM, ids)
1218        return self[ids]
1219
1220    def set(self, ids, values):
1221        """
1222        Returns value of array element for all ids.
1223        """
1224
1225        self[ids] = values
1226        self._is_modified = True
1227        # print 'set',self.attrname
1228        if self._is_index:
1229            self.set_indices(ids, values)
1230
1231    def set_plugin(self, ids, values):
1232        """
1233        Returns value of array element for all ids.
1234        """
1235        self[ids] = values
1236        self._is_modified = True
1237        # print 'set',self.attrname
1238
1239        if self.plugin:
1240            if not hasattr(ids, '__iter__'):
1241                self.plugin.exec_events_ids(EVTSETITEM, [ids])
1242            else:
1243                self.plugin.exec_events_ids(EVTSETITEM, ids)
1244        if self._is_index:
1245            self.set_indices(ids, values)
1246
1247    def delete(self, ids):
1248        """
1249        removes key from array structure
1250        To be overridden
1251        """
1252        del self[ids]
1253        self._is_modified = True
1254
1255    def delete_plugin(self, ids):
1256        """
1257        removes key from array structure
1258        To be overridden
1259        """
1260        if self.plugin:
1261            if not hasattr(ids, '__iter__'):
1262                self.plugin.exec_events_ids(EVTGETITEM, [ids])
1263            else:
1264                self.plugin.exec_events_ids(EVTGETITEM, ids)
1265
1266        del self[ids]
1267        self._is_modified = True
1268
1269    def format_value(self, _id, show_unit=False, show_parentesis=False):
1270        if show_unit:
1271            unit = ' '+self.format_unit(show_parentesis)
1272        else:
1273            unit = ''
1274        # return repr(self[_id])+unit
1275
1276        #self.min = minval
1277        #self.max = maxval
1278        #self.digits_integer = digits_integer
1279        #self.digits_fraction = digits_fraction
1280        val = self[_id]
1281        tt = type(val)
1282
1283        if tt in (types.LongType, types.IntType):
1284            return str(val)+unit
1285
1286        elif tt in (types.FloatType, types.ComplexType):
1287            if hasattr(attrconf, 'digits_fraction'):
1288                digits_fraction = self.digits_fraction
1289            else:
1290                digits_fraction = 3
1291            return "%."+str(digits_fraction)+"f" % (val)+unit
1292
1293        else:
1294            return str(val)+unit
1295
1296        # return str(self[_id])+unit
1297
1298    def format(self, ids=None):
1299        # TODO: incredibly slow when calling format_value for each value
1300        text = ''
1301
1302        if ids is None:
1303            ids = self._manager.get_ids()
1304        if not hasattr(ids, '__iter__'):
1305            ids = [ids]
1306
1307        #unit =  self.format_unit()
1308        attrname = self.attrname
1309        for id in ids:
1310            text += '%s[%d] = %s\n' % (attrname, id, self.format_value(id, show_unit=True))
1311
1312        return text[:-1]  # remove last newline
1313
1314
1315class NumcolConf(ColConf):
1316    def __init__(self, attrname, default,
1317                 digits_integer=None, digits_fraction=None,
1318                 minval=None, maxval=None,
1319                 **attrs):
1320        self.min = minval
1321        self.max = maxval
1322        self.digits_integer = digits_integer
1323        self.digits_fraction = digits_fraction
1324
1325        ColConf.__init__(self, attrname, default, **attrs)
1326
1327
1328class IdsConf(ColConf):
1329    """
1330    Column, where each entry is the id of a single Table.
1331    """
1332
1333    def __init__(self, attrname, tab,  id_default=-1, is_index=False, perm='r', **kwargs):
1334        self._is_index = is_index
1335        self._tab = tab
1336
1337        AttrConf.__init__(self,  attrname,
1338                          id_default,  # default id
1339                          struct='odict',
1340                          metatype='id',
1341                          perm=perm,
1342                          **kwargs
1343                          )
1344        self.init_xml()
1345        # print 'IdsConf.__init__',attrname
1346        # print '  ',self._tab.xmltag,self._attrconfig_id_tab
1347
1348    def set_linktab(self, tab):
1349        self._tab = tab
1350
1351    def get_linktab(self):
1352        return self._tab
1353
1354    def init_xml(self):
1355        # print 'init_xml',self.attrname
1356        if self._tab.xmltag is not None:
1357
1358            # necessary?? see ObjMan.write_xml
1359            xmltag_tab, xmltag_item_tab, attrname_id_tab = self._tab.xmltag
1360            if (attrname_id_tab is None) | (attrname_id_tab is ''):
1361                self._attrconfig_id_tab = None
1362            else:
1363                self._attrconfig_id_tab = getattr(self._tab, attrname_id_tab)  # tab = tabman !
1364
1365            if not hasattr(self, 'is_xml_include_tab'):
1366                # this means that entire table rows will be included
1367                self.is_xml_include_tab = False
1368            # print '  xmltag_tab, xmltag_item_tab, attrname_id_tab',xmltag_tab, xmltag_item_tab, attrname_id_tab,self.is_xml_include_tab
1369
1370        else:
1371            self._attrconfig_id_tab = None
1372            self.is_xml_include_tab = False
1373
1374    def write_xml(self, fd, _id, indent=0):
1375        if (self.xmltag is not None) & (self[_id] >= 0):
1376            if self._attrconfig_id_tab is None:
1377                self._write_xml_value(self[_id], fd)
1378            elif self.is_xml_include_tab:
1379                # this means that entire table rows will be included
1380                self._tab.write_xml(fd, indent, ids=self[_id], is_print_begin_end=False)
1381            else:
1382                self._write_xml_value(self._attrconfig_id_tab[self[_id]], fd)
1383
1384    def _write_xml_value(self, val, fd):
1385        # print 'write_xml',self.xmltag,hasattr(val, '__iter__')
1386        if hasattr(val, '__iter__'):
1387            if len(val) > 0:
1388                if hasattr(val[0], '__iter__'):
1389                    # matrix
1390                    fd.write(xm.mat(self.xmltag, val))
1391                else:
1392                    # list
1393                    fd.write(xm.arr(self.xmltag, val, self.xmlsep))
1394            else:
1395                # empty list
1396                # fd.write(xm.arr(self.xmltag,val))
1397                # don't even write empty lists
1398                pass
1399
1400        elif type(self._default) in (types.UnicodeType, types.StringType):
1401            if len(val) > 0:
1402                fd.write(xm.num(self.xmltag, val))
1403
1404        else:
1405            # scalar number or string
1406            fd.write(xm.num(self.xmltag, val))
1407
1408    def get_defaults(self, ids):
1409        # create a list, should work for all types and dimensions
1410        # default can be scalar or an array of any dimension
1411        # print '\n\nget_defaults',self.attrname,ids,self.get_default()
1412        return len(ids)*[self.get_default()]
1413
1414    def _getstate_specific(self, state):
1415        """
1416        Called by __getstate__ to add/change specific states,
1417        before returning states.
1418        To be overridden.
1419        """
1420        if self._is_save:
1421            # if self._is_child:
1422            #    # OK self.value already set in
1423            #    pass
1424            # else:
1425            #    # remove table reference and create ident
1426            # print '_getstate_specific',self._tab.get_ident_abs()
1427            state['_tab'] = None
1428            state['_ident_tab'] = self._tab.get_ident_abs()
1429
1430    def init_postload_internal(self, man, obj):
1431        # print 'IdsConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'obj:',obj.ident
1432
1433        AttrConf.init_postload_internal(self, man, obj)
1434        # if self._is_child:
1435        #    print '  make sure children get initialized'
1436        #    print '  call init_postload_internal of',self._tab.ident
1437        #    self._tab.init_postload_internal(obj)
1438
1439    def init_postload_external(self):
1440        # if self._is_child:
1441        #    # restore normally
1442        #    AttrConf.init_postload_external(self)
1443        #    self._tab.init_postload_external()
1444        # else:
1445
1446        # Substitute absolute ident with link object.
1447        # Called from init_postload_external of attrsman during load_obj
1448        #
1449        ident_abs = self._ident_tab
1450        # print 'reset_linkobj',self.attrname,ident_abs
1451        obj = self.get_obj()
1452        rootobj = obj.get_root()
1453        # print '  rootobj',rootobj.ident
1454        linkobj = rootobj.get_obj_from_ident(ident_abs)
1455        # print '  linkobj',linkobj.ident
1456        self._tab = linkobj
1457        self.init_xml()
1458
1459    def is_modified(self):
1460        return False
1461
1462
1463class TabIdsConf(ColConf):
1464    """
1465    Column, where each entry contains a tuple with table object and id.
1466    """
1467
1468    def __init__(self, attrname,  is_index=False, **kwargs):
1469        self._is_index = is_index
1470        AttrConf.__init__(self,  attrname,
1471                          -1,  # default id
1472                          struct='odict',
1473                          metatype='tabids',
1474                          **kwargs
1475                          )
1476
1477    def get_defaults(self, ids):
1478        # create a list, should work for all types and dimensions
1479        # default can be scalar or an array of any dimension
1480        # print '\n\nget_defaults',self.attrname,ids,self.get_default()
1481        return len(ids)*[(None, -1)]
1482
1483    def reset(self):
1484        # TODO: this will reset all the tables
1485        # instead should reset only the specified ids
1486        if self._is_child:
1487            for tab, ids in self.get_value():
1488                tab.reset()
1489
1490    def clear(self):
1491        self.reset()
1492        # necessary? because tbles have been cleared from manager
1493        # if self._is_child:
1494        #    for tab, ids in self.get_value():
1495        #        tab.clear()
1496
1497    def _getstate_specific(self, state):
1498        """
1499        Called by __getstate__ to add/change specific states,
1500        before returning states.
1501        To be overridden.
1502        """
1503        if self._is_save:
1504            n = len(state['value'])
1505            state['value'] = None
1506            _tabids_save = n*[None]
1507            i = 0
1508            for tab, ids in self.get_value():
1509                _tabids_save[i] = [tab.get_ident_abs(), ids]
1510                i += 1
1511            state['_tabids_save'] = _tabids_save
1512
1513    def init_postload_internal(self, man, obj):
1514        # print 'IdsConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'obj:',obj.ident
1515
1516        AttrConf.init_postload_internal(self, man, obj)
1517        # if self._is_child:
1518        #    print '  make sure children get initialized'
1519        #    print '  call init_postload_internal of',self._tab.ident
1520        #    self._tab.init_postload_internal(obj)
1521
1522    def init_postload_external(self):
1523        # if self._is_child:
1524        #    # restore normally
1525        #    AttrConf.init_postload_external(self)
1526        #    self._tab.init_postload_external()
1527        # else:
1528
1529        # Substitute absolute ident with link object.
1530        # Called from init_postload_external of attrsman during load_obj
1531        #
1532        #ident_abs = self._ident_tab
1533        # print 'reset_linkobj',self.attrname,ident_abs
1534        #obj = self.get_obj()
1535        #rootobj = obj.get_root()
1536        # print '  rootobj',rootobj.ident
1537        #linkobj = rootobj.get_obj_from_ident(ident_abs)
1538        # print '  linkobj',linkobj.ident
1539        #self._tab = linkobj
1540
1541        # Substitute absolute ident with link object.
1542        # Called from init_postload_external of attrsman during load_obj
1543        #
1544        _tabids_save = self._tabids_save
1545        #ident_abs = self._ident_value
1546        # print 'init_postload_external',self.attrname,_tabids_save
1547        obj = self.get_obj()
1548        rootobj = obj.get_root()
1549        # print '  rootobj',rootobj.ident
1550        tabids = len(self._tabids_save)*[None]
1551        i = 0
1552        for tabident, ids in self._tabids_save:
1553            tab = rootobj.get_obj_from_ident(tabident)
1554            # print '  ',tab.get_ident_abs(), ids
1555            tabids[i] = [tab, ids]
1556            i += 1
1557
1558        self.set_value(tabids)
1559
1560    def is_modified(self):
1561        return False
1562
1563
1564class ObjsConf(ColConf):
1565    """
1566    Column, where each entry is an object of class objclass with
1567    ident= (attrname, id).
1568    """
1569    # TODO:
1570    # there is a problems with objects that are stored here
1571    # in particular if objects are Table. What is their correct ident
1572    # or absolute ident. Currently it is not correct.
1573    # This leads to incorrect referencing when linked from elsewhere
1574    # for example within TabIdListArrayConf
1575    # .get_ident_abs() needs to to be correct, such that
1576    # get_obj_from_ident can locate them
1577    # maybe it is not an issue of ObjsConf  itself,
1578    # but the Objects stored must have a special getident method
1579
1580    def __init__(self, attrname,  is_index=False, **kwargs):
1581        self._is_index = is_index
1582        self._is_child = True  # at the moment no links possible
1583        AttrConf.__init__(self,  attrname,
1584                          None,  # BaseObjman('empty'), # default id
1585                          struct='odict',
1586                          metatype='obj',
1587                          perm='r',
1588                          **kwargs
1589                          )
1590
1591    def set_obj(self, obj):
1592        """
1593        Method to set instance of managed object.
1594        Used by add method of AttrManager
1595        """
1596        # print 'set_obj',self.attrname,obj.ident
1597        AttrConf.set_obj(self, obj)
1598
1599        # if self._is_child:
1600        obj.set_child(self)
1601
1602    # def get_valueobj(self, id = None):
1603    #    """
1604    #    This is called by get_childobj to retrive the child instance.
1605    #    Here this is just the table.
1606    #    """
1607    #    return self._tab
1608
1609    def predelete(self):
1610        AttrConf.predelete(self)
1611        # if self._is_child:
1612        self.get_obj().del_child(self.attrname)
1613
1614    # def _getstate_specific(self, state):
1615    #    """
1616    #    Called by __getstate__ to add/change specific states,
1617    #    before returning states.
1618    #    To be overridden.
1619    #    """
1620    #    if self._is_save:
1621    #        if self._is_child:
1622    #            # OK self.value already set in
1623    #            pass
1624    #        else:
1625    #            # remove column reference and create column with idents
1626    #            state['value']= None
1627    #            idents_obj = OrderedDict()
1628    #            linkobjs = self.get_value()
1629    #            for _id in self.get_ids():
1630    #                idents_obj[_id] = linkobjs[_id].get_ident_abs()
1631    #            state['_idents_obj'] = idents_obj
1632
1633    def init_postload_internal(self, man, obj):
1634        # print 'ObjsConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'obj:',obj.ident
1635
1636        AttrConf.init_postload_internal(self, man, obj)
1637        # if self._is_child:
1638
1639        # make sure all children in column get initialized
1640        # print '  make sure childrenS get initialized'
1641        childobjs = self.get_value()
1642
1643        obj = self.get_obj()
1644        # print 'init_postload_internal',self.attrname,obj,obj.ident
1645        for _id in obj.get_ids():
1646            # print '  call init_postload_internal of',childobjs[_id].ident
1647            childobjs[_id].init_postload_internal(obj)  # attention obj is the parent object!
1648
1649    def reset(self):
1650        # print 'ObjsConf.reset',self.get_value(),len(self.get_obj().get_ids())
1651        #obj = self.get_obj()
1652        # print 'init_postload_internal',self.attrname,obj,obj.ident
1653        childobjs = self.get_value()
1654        for _id in self.get_obj().get_ids():
1655            # print '  call reset of',childobjs[_id].ident,_id
1656            childobjs[_id].reset()
1657
1658    def clear(self):
1659        odict = self.get_init()
1660        if not self._is_localvalue:
1661            setattr(self._obj, self.attrname, odict)
1662        if self._is_index:
1663            self.reset_index()
1664
1665    def is_modified(self):
1666        # if self._is_child
1667        #is_modified = self.get_value().is_modified()
1668        # print 'is_modified', self.attrname, is_modified
1669
1670        childobjs = self.get_value()
1671
1672        #obj = self.get_obj()
1673        # print 'init_postload_internal',self.attrname,obj,obj.ident
1674        for _id in self.get_obj().get_ids():
1675            # print '  call init_postload_internal of',childobjs[_id].ident
1676            if childobjs[_id].is_modified():
1677                return True
1678
1679    def set_modified(self, is_modified):
1680        childobjs = self.get_value()
1681
1682        obj = self.get_obj()
1683        # print 'init_postload_internal',self.attrname,obj,obj.ident
1684        for _id in self.get_obj().get_ids():
1685            # print '  call init_postload_internal of',childobjs[_id].ident
1686            childobjs[_id].set_modified(is_modified)
1687
1688    def init_postload_external(self):
1689        # if self._is_child:
1690        # restore normally
1691        AttrConf.init_postload_external(self)
1692        childobjs = self.get_value()
1693
1694        for _id in self.get_obj().get_ids():
1695            childobjs[_id].init_postload_external()
1696
1697    # def get_name(self):
1698    #    return self.'Table ID for '+self._tab.get_name()
1699    #
1700    # def get_info(self):
1701    #    return 'ID for Table:\n'+self._tab.get_info()
1702
1703
1704class Attrsman:
1705    """
1706    Manages all attributes of an object
1707
1708    if argument obj is specified with an instance
1709    then  attributes are stored under this instance.
1710    The values of attrname is then directly accessible with
1711
1712    obj.attrname
1713
1714    If nothing is specified, then column attribute will be stored under
1715    the respective config instance of this  attrsman (self).
1716    The values of attrname is then directly accessible with
1717
1718    self.attrname.value
1719    """
1720
1721    def __init__(self, obj, attrname='attrsman', is_plugin=False):
1722
1723        if obj is None:
1724            # this means that column data will be stored
1725            # in value attribute of attrconfigs
1726            obj = self
1727            self._is_localvalue = True
1728        else:
1729            # this means that column data will be stored under obj
1730            self._is_localvalue = False
1731
1732        self._obj = obj  # managed object
1733        self._attrconfigs = []  # managed attribute config instances
1734        self.attrname = attrname  # the manager's attribute name in the obj instance
1735
1736        self._attrs_nosave = set(ATTRS_NOSAVE)
1737
1738        # groupes of attributes
1739        # key=groupname, value = list of attribute config instances
1740        self._groups = {}
1741
1742        self.init_plugin(is_plugin)
1743
1744    def init_plugin(self, is_plugin):
1745        if is_plugin:
1746            self.plugin = Plugin(self)
1747        else:
1748            self.plugin = None
1749
1750    def enable_plugin(self, is_enabled=True):
1751        if self.plugin is not None:
1752            self.plugin.enable(is_enabled)
1753
1754    def is_localvalue(self):
1755        return self._is_localvalue
1756
1757    def has_attrname(self, attrname):
1758        # attention this is a trick, exploiting the fact that the
1759        # attribute object with all the attr info is an attribute
1760        # of the attr manager (=self)
1761        return hasattr(self, attrname)
1762
1763    def is_modified(self):
1764        for attrconf in self._attrconfigs:
1765            # TODO: not very clean
1766            if hasattr(attrconf, 'is_child'):
1767                if attrconf.is_child():
1768                    if attrconf.is_modified():
1769                        return True
1770            else:
1771                if attrconf.is_modified():
1772                    return True
1773
1774        return False
1775
1776    def set_modified(self, is_modified=True):
1777        for attrconf in self._attrconfigs:
1778            attrconf.set_modified(is_modified)
1779
1780    def get_modified(self):
1781        # returns a list of modified attributes
1782        modified = []
1783        for attrconf in self._attrconfigs:
1784            if attrconf.is_modified():
1785                modified.append(attrconf)
1786        return modified
1787
1788    def get_config(self, attrname):
1789        return getattr(self, attrname)  # a bit risky
1790
1791    def get_configs(self, is_all=False, structs=None, filtergroupnames=None):
1792        # print 'get_configs',self,self._obj.ident,structs,filtergroupnames,len(self._attrconfigs)
1793        if is_all:
1794            return self._attrconfigs
1795        else:
1796            attrconfigs = []
1797            for attrconf in self._attrconfigs:
1798                # print '  found',attrconf.attrname,attrconf.struct
1799                is_check = True
1800                if (structs is not None):
1801                    if (attrconf.struct not in structs):
1802                        is_check = False
1803
1804                if is_check:
1805                    # print '  **is_check',is_check
1806                    if len(attrconf.groupnames) > 0:
1807                        if '_private' not in attrconf.groupnames:
1808                            # print '    not private'
1809                            if filtergroupnames is not None:
1810                                # print '     apply filtergroupnames',filtergroupnames,attrconf.groupnames
1811                                if not set(filtergroupnames).isdisjoint(attrconf.groupnames):
1812                                    # print '       append',attrconf.attrname
1813                                    attrconfigs.append(attrconf)
1814                            else:
1815                                # print '     no filtergroupnames'
1816                                attrconfigs.append(attrconf)
1817                    else:
1818                        if filtergroupnames is None:
1819                            attrconfigs.append(attrconf)
1820
1821            return attrconfigs
1822
1823    # def get_colconfigs(self, is_all = False):
1824    #    return []
1825
1826    def get_obj(self):
1827        return self._obj
1828
1829    def add(self, attrconf, is_overwrite=False):
1830        """
1831        Add a one or several new attributes to be managed.
1832        kwargs has attribute name as key and Attribute configuration object
1833        as value.
1834        """
1835
1836        attrname = attrconf.attrname
1837        # print '\n\nAttrsman.add',self.get_obj().ident,'add',attrname,self.has_attrname(attrname)
1838        dir(self._obj)
1839        if (not self.has_attrname(attrname)) | is_overwrite:
1840            attrconf.set_obj(self._obj)
1841            attrconf.set_manager(self)
1842
1843            # set configuration object as attribute of AttrManager
1844            setattr(self, attrname, attrconf)
1845
1846            # append also to the list of managed objects
1847            self._attrconfigs.append(attrconf)
1848
1849            # insert in groups
1850            self.insert_groupnames(attrconf)
1851
1852            if self.plugin:
1853                self.plugin.exec_events_attr(EVTADD, attrconf)
1854
1855            # return default value as attribute of managed object
1856            if (attrconf.struct in STRUCTS_SCALAR) & (attrconf.is_returnval()):  # == 'scalar':
1857                return attrconf.get_init()
1858            else:
1859                return None  # table configs do their own init
1860
1861        else:
1862            # print '  attribute with this name already exists',attrname,type(attrconf)
1863            # TODO: here we could do some intelligent updating
1864            del attrconf
1865            attrconf = getattr(self, attrname)
1866            # print '  existing',attrconf,type(attrconf)
1867            if (attrconf.struct in STRUCTS_SCALAR) & (attrconf.is_returnval()):  # == 'scalar':
1868                return attrconf.get_value()
1869            else:
1870                return None  # table configs do their own init
1871
1872    def do_not_save_attr(self, attrname):
1873        self._attrs_nosave.add(attrname)
1874
1875    def do_save_attr(self, attrname):
1876        if attrname in self._attrs_nosave:
1877            self._attrs_nosave.remove(attrname)
1878
1879    def do_not_save_attrs(self, attrnames):
1880        self._attrs_nosave.update(attrnames)
1881
1882    def insert_groupnames(self, attrconf):
1883        if len(attrconf.groupnames) > 0:
1884            for groupname in attrconf.groupnames:
1885
1886                if not self._groups.has_key(groupname):
1887                    self._groups[groupname] = []
1888
1889                if attrconf not in self._groups[groupname]:
1890                    self._groups[groupname].append(attrconf)
1891
1892    def get_groups(self):
1893        return self._groups
1894
1895    def get_groupnames(self):
1896        return self._groups.keys()
1897
1898    def has_group(self, groupname):
1899        return self._groups.has_key(groupname)
1900
1901    def get_group(self, name):
1902        """
1903        Returns a list with attributes that belong to that group name.
1904        """
1905        # print 'get_group self._groups=\n',self._groups.keys()
1906        return self._groups.get(name, [])
1907
1908    def get_group_attrs(self, name):
1909        """
1910        Returns a dictionary with all attributes of a group.
1911        Key is attribute name and value is attribute value.
1912        """
1913        # print 'get_group_attrs', self._groups
1914        attrs = OrderedDict()
1915        if not self._groups.has_key(name):
1916            return attrs
1917        for attrconf in self._groups[name]:
1918            # print '  attrconf.attrname',attrconf.attrname
1919            attrs[attrconf.attrname] = getattr(self._obj, attrconf.attrname)
1920        # print '  attrs',attrs
1921        return attrs
1922
1923    def print_attrs(self, show_unit=True, show_parentesis=False, attrconfigs=None):
1924        print 'Attributes of', self._obj._name, 'ident_abs=', self._obj.get_ident_abs()
1925        if attrconfigs is None:
1926            attrconfigs = self.get_configs()
1927
1928        for attrconf in attrconfigs:
1929            print '  %s =\t %s' % (attrconf.attrname, attrconf.format_value(show_unit=True))
1930
1931    def save_values(self, state):
1932        """
1933        Called by the managed object during save to save the
1934        attribute values.
1935        """
1936        for attrconfig in self.get_configs():
1937            attrconfig.save_value(state)
1938
1939    def delete(self, attrname):
1940        """
1941        Delete attibite with respective name
1942        """
1943        # print '.__delitem__','attrname=',attrname
1944
1945        # if hasattr(self,attrname):
1946        attrconf = getattr(self, attrname)
1947
1948        if attrconf in self._attrconfigs:
1949            if self.plugin:
1950                self.plugin.exec_events_attr(EVTDEL, attrconf)
1951
1952            for groupname in attrconf.groupnames:
1953                self._groups[groupname].remove(attrconf)
1954
1955            self._attrconfigs.remove(attrconf)
1956            attrconf.predelete()  # this will remove also the value attribute
1957
1958            #attrname = attrconf.attrname
1959            del self.__dict__[attrname]    # delete config
1960            return True
1961
1962        return False  # attribute not managed
1963        # return False # attribute not existant
1964
1965    def __getstate__(self):
1966        # if hasattr(self,'attrname'):
1967        #    print 'Attrsman.__getstate__',self.attrname,'  of  obj=',self._obj.ident
1968        # else:
1969        #    print 'WARNING in Attrsman.__getstate__','attrname missing'
1970
1971        if not hasattr(self, '_obj'):
1972            print 'WARNING: unknown obj in attrman', self, type(self)
1973            # print '  dir',dir(self)
1974            # if hasattr(self,'attrname'):
1975            #    print '    No attrman but attribute',self.attrname
1976            # for attrconf in self.get_configs(is_all=True):
1977            #    print '  attrname=',attrconf.attrname
1978            return {}
1979
1980        # if not hasattr(self,'_attrs_nosave'):
1981        #    print 'WARNING: in __getstate__ of',self.attrname#,'obj',self._obj,'has no attr _attrs_nosave'
1982        #    #self.print_attrs()
1983        #    print 'dict=\n',self.__dict__
1984
1985        # print '  self.__dict__=\n',self.__dict__.keys()
1986
1987        state = {}
1988        for attr in self.__dict__.keys():
1989                # print '  attr',attr,self.__dict__[attr]
1990                # TODO: optimize and put this at the end
1991            if attr == 'plugin':
1992                plugin = self.__dict__[attr]
1993                if plugin is not None:
1994                    state[attr] = True
1995                else:
1996                    state[attr] = False
1997
1998            elif attr == '_attrconfigs':
1999                attrconfigs_save = []
2000                for attrconfig in self._attrconfigs:
2001                    if attrconfig.is_save():
2002                        attrconfigs_save.append(attrconfig)
2003                state[attr] = attrconfigs_save
2004
2005            elif attr not in self._attrs_nosave:
2006                state[attr] = self.__dict__[attr]
2007
2008        # print  '  _attrs_nosave=', self._attrs_nosave
2009        # print  '  state=', state
2010        return state
2011
2012    def __setstate__(self, state):
2013        # print '__setstate__',self
2014
2015        # this is always required, but will not be saved
2016        # self.plugins={}
2017
2018        for attr in state.keys():
2019            # print '  set state',attr
2020            # plugin set in init_postload_internal
2021            # if attr=='plugin':
2022            #    if state[attr]==True:
2023            #        self.__dict__[attr] = Plugin(self)
2024            #    else:
2025            #        self.__dict__[attr]= None
2026            # else:
2027            self.__dict__[attr] = state[attr]
2028
2029    def init_postload_internal(self, obj):
2030        """
2031        Called after set state.
2032        Link internal states.
2033        """
2034        # print 'Attrsman.init_postload_internal of obj:',obj.ident
2035
2036        if not hasattr(self, '_attrs_nosave'):
2037            self._attrs_nosave = set(ATTRS_NOSAVE)
2038
2039        # if not hasattr(self,'_attrs_nosave'):
2040        #    print 'WARNING: in init_postload_internal of',self.attrname,'obj',obj,'has no attr _attrs_nosave'
2041
2042        self._obj = obj
2043        self.init_plugin(self.plugin)
2044        for attrconfig in self.get_configs(is_all=True):
2045            # print '  call init_postload_internal of',attrconfig.attrname
2046            attrconfig.init_postload_internal(self, obj)
2047
2048    def init_postload_external(self):
2049        """
2050        Called after set state.
2051        Link external states.
2052        """
2053        # print 'init_postload_external',self._obj.get_ident()
2054
2055        for attrconfig in self.get_configs(is_all=True):
2056            # print '  ***',attrconfig.attrname,attrconfig.metatype
2057            attrconfig.init_postload_external()
2058
2059
2060class Tabman(Attrsman):
2061    """
2062    Manages all table attributes of an object.
2063
2064    if argument obj is specified with an instance
2065    then column attributes are stored under this instance.
2066    The values of attrname is then directly accessible with
2067
2068    obj.attrname
2069
2070    If nothing is specified, then column attribute will be stored under
2071    the respective config instance of this tab man (self).
2072    The values of attrname is then directly accessible with
2073
2074    self.attrname.value
2075
2076    """
2077
2078    def __init__(self, obj=None,  **kwargs):
2079        Attrsman.__init__(self, obj, **kwargs)
2080        self._colconfigs = []
2081        self._ids = []
2082
2083    def add_col(self, attrconf):
2084        # print 'add_col',attrconf.attrname,attrconf.is_index()
2085        attrname = attrconf.attrname
2086        if not self.has_attrname(attrname):
2087            Attrsman.add(self, attrconf)  # insert in common attrs database
2088            self._colconfigs.append(attrconf)
2089            # returns initial array and also create local array if self._is_localvalue == True
2090            return attrconf.get_init()
2091        else:
2092            return getattr(self, attrname).get_value()
2093
2094    def delete(self, attrname):
2095        """
2096        Delete attribute with respective name
2097        """
2098        # print '.__delitem__','attrname=',attrname
2099
2100        if hasattr(self, attrname):
2101            attrconf = getattr(self, attrname)
2102            if self.plugin:
2103                self.plugin.exec_events_attr(EVTDEL, attrconf)
2104            if Attrsman.delete(self, attrname):
2105                if attrconf in self._colconfigs:
2106                    self._colconfigs.remove(attrconf)
2107
2108    def get_colconfigs(self, is_all=False, filtergroupnames=None):
2109        if is_all:
2110            return self._colconfigs
2111        else:
2112            colconfigs = []
2113            for colconfig in self._colconfigs:
2114                if len(colconfig.groupnames) > 0:
2115                    if colconfig.groupnames[0] != '_private':
2116                        if filtergroupnames is not None:
2117                            if not set(filtergroupnames).isdisjoint(colconfig.groupnames):
2118                                colconfigs.append(colconfig)
2119                        else:
2120                            colconfigs.append(colconfig)
2121
2122                else:
2123                    if filtergroupnames is None:
2124                        colconfigs.append(colconfig)
2125
2126            return colconfigs
2127
2128    def get_ids(self):
2129        return self._ids
2130
2131    def __len__(self):
2132        """
2133        Determine current array length (same for all arrays)
2134        """
2135
2136        return len(self._ids)
2137
2138    def __contains__(self, _id):
2139        return _id in self._ids
2140
2141    def select_ids(self, mask):
2142
2143        ids_mask = []
2144        i = 0
2145        for _id in self.get_ids():
2146            if mask[i]:
2147                ids_mask.append(_id)
2148            i += 1
2149
2150        return ids_mask
2151
2152    def suggest_id(self, is_zeroid=False):
2153        """
2154        Returns a an availlable id.
2155
2156        Options:
2157            is_zeroid=True allows id to be zero.
2158
2159        """
2160        if is_zeroid:
2161            id0 = 0
2162        else:
2163            id0 = 1
2164
2165        id_set = set(self.get_ids())
2166        if len(id_set) == 0:
2167            id_max = 0
2168        else:
2169            id_max = max(id_set)
2170        # print  'suggest_id',id0,
2171        return list(id_set.symmetric_difference(xrange(id0, id_max+id0+1)))[0]
2172
2173    def suggest_ids(self, n, is_zeroid=False):
2174        """
2175        Returns a list of n availlable ids.
2176        It returns even a list for n=1.
2177
2178        Options:
2179            is_zeroid=True allows id to be zero.
2180        """
2181        if is_zeroid:
2182            id0 = 0
2183        else:
2184            id0 = 1
2185        id_set = set(self.get_ids())
2186        if len(id_set) == 0:
2187            id_max = 0
2188        else:
2189            id_max = max(id_set)
2190
2191        return list(id_set.symmetric_difference(xrange(id0, id_max+id0+n)))[:n]
2192
2193    def add_rows(self, n=None, ids=[], **attrs):
2194        if n is not None:
2195            ids = self.suggest_ids(n)
2196        elif len(ids) == 0:
2197            # get number of rows from any valye vector provided
2198            ids = self.suggest_ids(len(attrs.values()[0]))
2199        else:
2200            # ids already given , no ids to create
2201            pass
2202
2203        self._ids += ids
2204        # print 'add_rows ids', ids
2205        for colconfig in self._colconfigs:
2206            colconfig.add(ids, values=attrs.get(colconfig.attrname, None))
2207        if self.plugin:
2208            self.plugin.exec_events_ids(EVTADDITEM, ids)
2209        return ids
2210
2211    def add_row(self, _id=None, **attrs):
2212        if _id is None:
2213            _id = self.suggest_id()
2214        self._ids += [_id, ]
2215        for colconfig in self._colconfigs:
2216            colconfig.add(_id, values=attrs.get(colconfig.attrname, None))
2217        if self.plugin:
2218            self.plugin.exec_events_ids(EVTADDITEM, [_id])
2219        return _id
2220
2221    def set_row(self, _id, **attrs):
2222        for colconfig in self._colconfigs:
2223            colconfig.set(_id, values=attrs.get(colconfig.attrname, None))
2224        if self.plugin:
2225            self.plugin.exec_events_ids(EVTSETITEM, [_id])
2226
2227    def set_rows(self, ids, **attrs):
2228
2229        # print 'add_rows ids', ids
2230        for colconfig in self._colconfigs:
2231            colconfig.set(ids, values=attrs.get(colconfig.attrname, None))
2232        if self.plugin:
2233            self.plugin.exec_events_ids(SETSETITEM, ids)
2234
2235    def get_row(self, _id):
2236        attrvalues = {}
2237        if self.plugin:
2238            self.plugin.exec_events_ids(EVTGETITEM, [_id])
2239        for attrconfig in self._colconfigs:
2240            attrvalues[attrconfig.attrname] = attrconfig[_id]
2241
2242        return attrvalues
2243
2244    def del_rows(self, ids):
2245        if self.plugin:
2246            self.plugin.exec_events_ids(EVTDELITEM, ids)
2247        for colconfig in self._colconfigs:
2248            del colconfig[ids]
2249
2250        for _id in ids:
2251            self._ids.remove(_id)
2252
2253    def del_row(self, _id):
2254        if self.plugin:
2255            self.plugin.exec_events_ids(EVTDELITEM, [_id])
2256        for colconfig in self._colconfigs:
2257            del colconfig[_id]
2258        self._ids.remove(_id)
2259
2260    def __delitem__(self, ids):
2261        """
2262        remove rows correspondent to the given ids from all array and dict
2263        attributes
2264        """
2265        if hasattr(ids, '__iter__'):
2266            self.del_rows(ids)
2267        else:
2268            self.del_row(ids)
2269
2270    def print_attrs(self, **kwargs):
2271        # print 'Attributes of',self._obj._name,'(ident=%s)'%self._obj.ident
2272        Attrsman.print_attrs(self, attrconfigs=self.get_configs(structs=['scalar']), **kwargs)
2273        # print '   ids=',self._ids
2274        for _id in self.get_ids():
2275            for attrconf in self.get_configs(structs=STRUCTS_COL):
2276                print '  %s[%d] =\t %s' % (attrconf.attrname, _id, attrconf.format_value(_id, show_unit=True))
2277
2278
2279class BaseObjman:
2280    """
2281    Object management base methods to be inherited by all object managers.
2282    """
2283
2284    def __init__(self, ident, is_plugin=False,  **kwargs):
2285        # print 'BaseObjman.__init__',ident#,kwargs
2286        self._init_objman(ident, **kwargs)
2287        self.set_attrsman(Attrsman(self, is_plugin=is_plugin))
2288        # print 'BaseObjman.__init__',self.format_ident(),'parent=',self.parent
2289        self._init_attributes()
2290        self._init_constants()
2291
2292    def set_attrsman(self, attrsman):
2293        self._attrsman = attrsman
2294        return attrsman
2295
2296    def _init_objman(self, ident='no_ident', parent=None, name=None,
2297                     managertype='basic', info=None, logger=None,
2298                     xmltag=None, version=0.0):
2299        # print 'BaseObjman._init_objman',ident,logger,parent
2300        self.managertype = managertype
2301        self.ident = ident
2302        self.set_version(version)
2303        self.set_logger(logger)
2304
2305        #self._is_root = False
2306        self.parent = parent
2307        self.childs = {}  # dict with attrname as key and child instance as value
2308
2309        self._info = info
2310
2311        self._is_saved = False
2312
2313        if name is None:
2314            self._name = self.format_ident()
2315        else:
2316            self._name = name
2317
2318        self.set_xmltag(xmltag)
2319
2320        # print '  self.parent',self.parent
2321        # must be called explicitely during  __init__
2322        # self._init_attributes()
2323        # self._init_constants()
2324
2325    def _init_attributes(self):
2326        """
2327        This is the place to add all attributes.
2328        This method will be called to initialize
2329        and after loading a saved object.
2330        Use this method also to update a version.
2331        """
2332        pass
2333
2334    def _init_constants(self):
2335        """
2336        This is the place to init any costants that are outside the management.
2337        Constants are not saved.
2338        This method will be called to initialize and after loading a saved object.
2339        """
2340        pass
2341
2342    def set_version(self, version):
2343        self._version = version
2344
2345    def get_version(self):
2346        return self._version
2347
2348    # def _upgrade_version(self):
2349    #    pass
2350
2351    # def _init_xml(self,xmltag=None):
2352    #    if xmltag is not None:
2353    #        self.xmltag = xmltag
2354    #    else:
2355    #        self.xmltag = self.get_ident()
2356
2357    def reset(self):
2358        """
2359        Resets all attributes to default values
2360        """
2361        # print 'reset'
2362        for attrconfig in self.get_attrsman().get_configs(is_all=True):
2363            # print '  reset',attrconfig.attrname
2364            attrconfig.reset()
2365
2366    def clear(self):
2367        """
2368        Clear tables and reset scalars.
2369        """
2370        for attrconfig in self.get_attrsman().get_configs(is_all=True):
2371            attrconfig.clear()
2372
2373        self._init_constants()
2374
2375    def unplug(self):
2376        if self.plugin:
2377            self.plugin.unplug()
2378
2379    def set_xmltag(self, xmltag, xmlsep=' '):
2380        self.xmltag = xmltag
2381        self.xmlsep = xmlsep
2382
2383    def write_xml(self, fd, ident):
2384        if self.xmltag is not None:
2385            # figure out scalar attributes and child objects
2386            attrconfigs = []
2387            objconfigs = []
2388            for attrconfig in self.get_attrsman().get_configs(structs=STRUCTS_SCALAR):
2389                if (attrconfig.metatype == 'obj'):  # better use self.childs
2390                    if (attrconfig.get_value().xmltag is not None) & attrconfig.is_child():
2391                        objconfigs.append(attrconfig)
2392                elif attrconfig.xmltag is not None:
2393                    attrconfigs.append(attrconfig)
2394
2395            # start writing
2396            if len(attrconfigs) > 0:
2397                # there are scalar attributes
2398                fd.write(xm.start(self.xmltag, ident))
2399                for attrconfig in attrconfigs:
2400                    attrconfig.write_xml(fd)
2401
2402                # are there child objects to write
2403                if len(objconfigs) > 0:
2404                    fd.write(xm.stop())
2405                    for attrconfig in objconfigs:
2406                        attrconfig.get_value().write_xml(fd, ident+2)
2407                    fd.write(xm.end(self.xmltag, ident))
2408                else:
2409                    fd.write(xm.stopit())
2410            else:
2411                # no scalars
2412                fd.write(xm.begin(self.xmltag, ident))
2413                if len(objconfigs) > 0:
2414                    for attrconfig in objconfigs:
2415                        attrconfig.get_value().write_xml(fd, ident+2)
2416                fd.write(xm.end(self.xmltag, ident))
2417
2418    def get_logger(self):
2419        # print 'get_logger',self.ident,self._logger,self.parent
2420        if self._logger is not None:
2421            return self._logger
2422        else:
2423            return self.parent.get_logger()
2424
2425    def set_logger(self, logger):
2426        # print 'set_logger',self.ident,logger
2427        self._logger = logger
2428
2429    def __repr__(self):
2430        # return '|'+self._name+'|'
2431        return self.format_ident()
2432
2433    def is_modified(self):
2434        return self._attrsman.is_modified()
2435
2436    def set_modified(self, is_modified=True):
2437        self._attrsman.set_modified(is_modified)
2438
2439    def get_name(self):
2440        return self._name
2441
2442    def get_info(self):
2443        if self._info is None:
2444            return self.__doc__
2445        else:
2446            return self._info
2447
2448    def get_ident(self):
2449        return self.ident
2450
2451    def _format_ident(self, ident):
2452        if hasattr(ident, '__iter__'):
2453            return str(ident[0])+'#'+str(ident[1])
2454        else:
2455            return str(ident)
2456
2457    def format_ident(self):
2458        return self._format_ident(self.ident)
2459
2460    def format_ident_abs(self):
2461        s = ''
2462        # print 'format_ident_abs',self.get_ident_abs()
2463        for ident in self.get_ident_abs():
2464            s += self._format_ident(ident)+'.'
2465        return s[:-1]
2466
2467    def get_root(self):
2468        # if hasattr(self,'_is_root'):
2469        #    print 'get_root',self.ident,'is_root',self._is_root
2470        #    if self._is_root:
2471        #        return self
2472
2473        if self.parent is not None:
2474            return self.parent.get_root()
2475        else:
2476            return self
2477
2478    def get_ident_abs(self):
2479        """
2480        Returns absolute identity.
2481        This is the ident of this object in the global tree of objects.
2482        If there is a parent objecty it must also be managed by the
2483        object manager.
2484        """
2485        # print 'obj.get_ident_abs',self.ident,self.parent, type(self.parent)
2486        # if hasattr(self,'_is_root'):
2487        #    print 'get_ident_abs',self.ident,'is_root',self._is_root
2488        #    if self._is_root:
2489        #        return (self.get_ident(),)# always return tuple
2490
2491        if self.parent is not None:
2492            return self.parent.get_ident_abs()+(self.ident,)
2493        else:
2494            return (self.get_ident(),)  # always return tuple
2495
2496    def get_obj_from_ident(self, ident_abs):
2497        # print 'get_obj_from_ident',self.ident,ident_abs
2498        if len(ident_abs) == 1:
2499            # arrived at the last element
2500            # check if it corresponds to the present object
2501            if ident_abs[0] == self.ident:
2502                return self
2503            else:
2504                return None  # could throw an error
2505        else:
2506            return self.get_childobj(ident_abs[1]).get_obj_from_ident(ident_abs[1:])
2507
2508    # this is an attemt to restore objects from
2509    # root objects without childs
2510    # def search_ident_abs(self, childobj):
2511    #    """
2512    #    Returns root and absolute ident for the found root.
2513    #    """
2514    #    #if hasattr(self,'_is_root'):
2515    #    #    print 'get_root',self.ident,'is_root',self._is_root
2516    #    #    if self._is_root:
2517    #    #        return self
2518    #
2519    #    if self.parent is not None:
2520    #        if self.parent.childs.has_key(childobj.ident)
2521    #        return self.parent.get_root()
2522    #    else:
2523    #        return self
2524
2525    # def search_obj_from_ident(self, ident_abs, obj_init):
2526    #
2527    #    #print 'get_obj_from_ident',self.ident,ident_abs
2528    #    if len(ident_abs)==1:
2529    #        # arrived at the last element
2530    #        # check if it corresponds to the present object
2531    #        if ident_abs[0] == self.ident:
2532    #            return self
2533    #        else:
2534    #            return None # could throw an error
2535    #   else:
2536    #       return self.get_childobj(ident_abs[1]).get_obj_from_ident(ident_abs[1:])
2537
2538    def get_childobj(self, attrname):
2539        """
2540        Return child instance
2541        """
2542        if self.childs.has_key(attrname):
2543            config = self.childs[attrname]
2544            return config.get_value()
2545        else:
2546            return BaseObjman(self)
2547
2548    def set_child(self, childconfig):
2549        """
2550        Set child childconfig
2551        """
2552        self.childs[childconfig.attrname] = childconfig
2553
2554    def del_child(self, attrname):
2555        """
2556        Return child instance
2557        """
2558        del self.childs[attrname]
2559
2560    def get_parent(self):
2561        return self.parent
2562
2563    # def reset_parent(self, parent):
2564    #    self.parent=parent
2565
2566    # def set_attrsman(self, attrsman):
2567    #    # for quicker acces and because it is only on
2568    #    # the attribute management is public and also directly accessible
2569    #    #setattr(self, attrname,Attrsman(self))# attribute management
2570    #    self._attrsman = attrsman
2571    #    #return attrsman
2572
2573    def get_attrsman(self):
2574        return self._attrsman
2575
2576    def _getstate_specific(self, state):
2577        """
2578        Called by __getstate__ to add/change specific states,
2579        before returning states.
2580        To be overridden.
2581        """
2582        pass
2583
2584    def __getstate__(self):
2585        # print 'BaseObjman.__getstate__',self.ident,self._is_saved
2586        # print '  self.__dict__=\n',self.__dict__.keys()
2587        state = {}
2588        # if not self._is_saved:
2589
2590        # if self._is_saved:
2591        #    # this message indicates a loop!!
2592        #    print 'WARNING in __getstate__: object already saved',self.format_ident_abs()
2593
2594        # print  '  save standart values'
2595        for attr in ATTRS_SAVE:
2596            if hasattr(self, attr):
2597                state[attr] = getattr(self, attr)
2598
2599        # print '  save all scalar stuctured attributes'
2600        # attrsman knows which and how
2601        # self._attrsman.save_values(state)
2602        #
2603        # values of configured attributes are not saved here
2604        # values are now ALWAYS stored in the value attribute of the
2605        # attrconfig and reset in main obj
2606
2607        # print '  save also attrsman'
2608        state['_attrsman'] = self._attrsman
2609        self._getstate_specific(state)
2610
2611        self._is_saved = True
2612
2613        # else:
2614        #    print 'WARNING in __getstate__: object %s already saved'%self.ident
2615        return state
2616
2617    def __setstate__(self, state):
2618        # print '__setstate__',self
2619
2620        # this is always required, but will not be saved
2621        # self.plugins={}
2622
2623        for key in state.keys():
2624            # print '  set state',key
2625            self.__dict__[key] = state[key]
2626
2627        self._is_saved = False
2628        # done in init2_config...
2629        # set default values for all states tha have not been saved
2630        # for attr in self._config.keys():
2631        #    if (not self._config[attr]['save']) & (not hasattr(self,attr)):
2632        #        print '  config attr',attr
2633        #        self.config(attr,**self._config[attr])
2634
2635        # set other states
2636        # self._setstate(state)
2637
2638    def init_postload_internal(self, parent):
2639        """
2640        Called after set state.
2641        Link internal states and call constant settings.
2642        """
2643        print 'BaseObjman.init_postload_internal', self.ident, 'parent:'
2644        # if parent is not None:
2645        #    print parent.ident
2646        # else:
2647        #    print 'ROOT'
2648        self.parent = parent
2649        self.childs = {}
2650        self._attrsman.init_postload_internal(self)
2651
2652    def init_postload_external(self):
2653        """
2654        Called after set state.
2655        Link internal states.
2656        """
2657
2658        #self._is_root =  is_root
2659        print 'init_postload_external', self.ident  # ,self._is_root
2660        # set default logger
2661        self.set_logger(Logger(self))
2662        # for child in self.childs.values():
2663        #    child.reset_parent(self)
2664        self._attrsman.init_postload_external()
2665        self._init_attributes()
2666        self._init_constants()
2667
2668
2669class TableMixin(BaseObjman):
2670
2671    def format_ident_row(self, _id):
2672        # print 'format_ident_row',_id
2673        return self.format_ident()+'['+str(_id)+']'
2674
2675    def format_ident_row_abs(self, _id):
2676        return self.format_ident_abs()+'['+str(_id)+']'
2677
2678    def get_obj_from_ident(self, ident_abs):
2679        # print 'get_obj_from_ident',self.ident,ident_abs,type(ident_abs)
2680        if len(ident_abs) == 1:
2681            # arrived at the last element
2682            # check if it corresponds to the present object
2683            ident_check = ident_abs[0]
2684
2685            #  now 2 things can happen:
2686            # 1.) the ident is a simple string identical to ident of the object
2687            # in this case, return the whole object
2688            # 2.) ident is a tuple with string and id
2689            # in this case return object and ID
2690            # if hasattr(ident_check, '__iter__'):
2691            #    #if (ident_check[0] == self.ident)&(ident_check[1] in self._ids):
2692            #    if ident_check[1] in self._ids:
2693            #        return (self, ident_check[1])
2694            #    else:
2695            #       return None # could throw an error
2696            # else:
2697            if ident_check == self.ident:
2698                return self
2699        else:
2700            childobj = self.get_childobj(ident_abs[1])
2701            return childobj.get_obj_from_ident(ident_abs[1:])
2702
2703    def get_childobj(self, ident):
2704        """
2705        Return child instance.
2706        This is any object with ident
2707        """
2708        if hasattr(ident, '__iter__'):
2709            # access of ObjsConf configured child
2710            # get object from column attrname
2711            attrname, _id = ident
2712            config = self.childs[attrname]
2713            return config[_id]  # config.get_valueobj(_id)
2714        else:
2715            # access of ObjConf configured child
2716            # get object from  attrname
2717            config = self.childs[ident]
2718            return config.get_value()
2719
2720    def __getstate__(self):
2721        # print '__getstate__',self.ident,self._is_saved
2722        # print '  self.__dict__=\n',self.__dict__.keys()
2723        state = {}
2724        if 1:  # not self._is_saved:
2725
2726            # print  '  save standart values'
2727            for attr in ATTRS_SAVE+ATTRS_SAVE_TABLE:
2728                if attr == 'plugin':
2729                    plugin = self.__dict__[attr]
2730                    if plugin is not None:
2731                        state[attr] = True
2732                    else:
2733                        state[attr] = False
2734
2735                elif hasattr(self, attr):
2736                    state[attr] = getattr(self, attr)
2737
2738            # save managed attributes !!!
2739            for attrconfig in self.get_configs(is_all=True):
2740                state[attrconfig.attrname] = attrconfig
2741
2742            # print '  save all scalar stuctured attributes'
2743            # attrsman knows which and how
2744            # self.save_values(state)
2745
2746            # print '  save also attrsman'
2747            #state['attrsman'] = self._attrsman
2748            self._is_saved = True
2749
2750        else:
2751            print 'WARNING in __getstate__: object %s already saved' % self.ident
2752        return state
2753
2754    def __setstate__(self, state):
2755        # print '__setstate__',self.ident
2756
2757        # this is always required, but will not be saved
2758        self.plugins = {}
2759
2760        for attr in state.keys():
2761            # print '  set state',key
2762            if attr == 'plugin':
2763                if state[attr] == True:
2764                    self.__dict__[attr] = Plugin(self)
2765                else:
2766                    self.__dict__[attr] = None
2767            else:
2768                self.__dict__[attr] = state[attr]
2769
2770        self._is_saved = False
2771        # done in init2_config...
2772        # set default values for all states tha have not been saved
2773        # for attr in self._config.keys():
2774        #    if (not self._config[attr]['save']) & (not hasattr(self,attr)):
2775        #        print '  config attr',attr
2776        #        self.config(attr,**self._config[attr])
2777
2778        # set other states
2779        # self._setstate(state)
2780
2781    def init_postload_internal(self, parent):
2782        """
2783        Called after set state.
2784        Link internal states.
2785        """
2786        # print 'TableObjman.init_postload_internal',self.ident,'parent:',
2787        # if parent is not None:
2788        #    print parent.ident
2789        # else:
2790        #    print 'ROOT'
2791
2792        if not hasattr(self, '_attrs_nosave'):
2793            self._attrs_nosave = set(ATTRS_NOSAVE)
2794
2795        self.parent = parent
2796        self.childs = {}
2797        self.set_attrsman(self)
2798        Attrsman.init_postload_internal(self, self)
2799
2800        self._is_saved = False
2801
2802    def init_postload_external(self):
2803        """
2804        Called after set state.
2805        Link internal states.
2806        """
2807        Attrsman.init_postload_external(self)
2808        # no: BaseObjman.init_postload_external(self)
2809        self._init_attributes()
2810        self._init_constants()
2811
2812    def export_csv(self, filepath, sep=',', name_id='ID',
2813                   file=None, attrconfigs=None, ids=None, groupname=None,
2814                   is_header=True, is_ident=False, is_timestamp=True):
2815
2816        # print 'export_csv',filepath,"*"+sep+"*"
2817        fd = open(filepath, 'w')
2818
2819        if ids is None:
2820            ids = self.get_ids()
2821
2822        if groupname is not None:
2823            attrconfigs = self.get_group(groupname)
2824            is_exportall = False
2825
2826        if attrconfigs is None:
2827            attrconfigs = self.get_colconfigs(is_all=True)
2828            is_exportall = False
2829        else:
2830            is_exportall = True
2831
2832        # header
2833        if is_header:
2834
2835            row = self._clean_csv(self.get_name(), sep)
2836            if is_ident:
2837                row += sep+'(ident=%s)' % self.format_ident_abs()
2838            fd.write(row+'\n')
2839            if is_timestamp:
2840                now = datetime.now()
2841                fd.write(self._clean_csv(now.isoformat(), sep)+'\n')
2842            fd.write('\n\n')
2843
2844        # first table row
2845        row = name_id
2846        for attrconf in attrconfigs:
2847            # print '   write first row',attrconf.attrname
2848            is_private = attrconf.has_group('_private')
2849            if ((not is_private) & (attrconf.is_save())) | is_exportall:
2850                row += sep+self._clean_csv(attrconf.format_symbol(), sep)
2851        fd.write(row+'\n')
2852
2853        # rest
2854        for _id in ids:
2855            # if self._is_keyindex:
2856            #    row = str(self.get_key_from_id(id))#.__repr__()
2857            # else:
2858            row = str(_id)
2859            row = self._clean_csv(row, sep)
2860            for attrconf in attrconfigs:
2861                is_private = attrconf.has_group('_private')
2862                if ((not is_private) & (attrconf.is_save())) | is_exportall:
2863                    row += sep+self._clean_csv('%s' % (attrconf.format_value(_id, show_unit=False)), sep)
2864
2865            # make sure there is no CR in the row!!
2866            # print  row
2867            fd.write(row+'\n')
2868
2869        if filepath is not None:
2870            fd.close()
2871
2872    def _clean_csv(self, row, sep):
2873        row = row.replace('\n', ' ')
2874        #row=row.replace('\b',' ')
2875        row = row.replace('\r', ' ')
2876        #row=row.replace('\f',' ')
2877        #row=row.replace('\newline',' ')
2878        row = row.replace(sep, ' ')
2879        return row
2880
2881    def clear_rows(self):
2882        if self.plugin:
2883            self.plugin.exec_events_ids(EVTDELITEM, self.get_ids())
2884        self._ids = []
2885        for colconfig in self.get_attrsman()._colconfigs:
2886            # print 'ArrayObjman.clear_rows',colconfig.attrname,len(colconfig.get_value())
2887            colconfig.clear()
2888            # print '  done',len(colconfig.get_value())
2889
2890    def clear(self):
2891        # print 'ArrayObjman.clear',self.ident
2892        # clear/reset scalars
2893        for attrconfig in self.get_attrsman().get_configs(structs=STRUCTS_SCALAR):
2894            attrconfig.clear()
2895        self.clear_rows()
2896        self.set_modified()
2897
2898    def _write_xml_body(self, fd,  indent, objconfigs, idcolconfig_include_tab, colconfigs,
2899                        objcolconfigs, xmltag_item, attrconfig_id, xmltag_id, ids, ids_xml):
2900
2901        # print '_write_xml_body ident,ids',self.ident,ids
2902        if ids is None:
2903            ids = self.get_ids()
2904
2905        if ids_xml is None:
2906            ids_xml = ids
2907
2908        for attrconfig in objconfigs:
2909            attrconfig.get_value().write_xml(fd, indent+2)
2910
2911        # check if columns contain objects
2912        #objcolconfigs = []
2913        scalarcolconfigs = colconfigs
2914        # for attrconfig in colconfigs:
2915        #    if attrconfig.metatype == 'obj':
2916        #        objcolconfigs.append(attrconfig)
2917        #    else:
2918        #        scalarcolconfigs.append(attrconfig)
2919
2920        for _id, id_xml in zip(ids, ids_xml):
2921            fd.write(xm.start(xmltag_item, indent+2))
2922
2923            # print '   make tag and id',_id
2924            if xmltag_id == '':
2925                # no id tag will be written
2926                pass
2927            elif (attrconfig_id is None) & (xmltag_id is not None):
2928                # use specified id tag and and specified id values
2929                fd.write(xm.num(xmltag_id, id_xml))
2930
2931            elif (attrconfig_id is not None):
2932                # use id tag and values of attrconfig_id
2933                attrconfig_id.write_xml(fd, _id)
2934
2935            # print ' write columns',len(scalarcolconfigs)>0,len(idcolconfig_include_tab)>0,len(objcolconfigs)>0
2936            for attrconfig in scalarcolconfigs:
2937                # print '    scalarcolconfig',attrconfig.attrname
2938                attrconfig.write_xml(fd, _id)
2939
2940            if (len(idcolconfig_include_tab) > 0) | (len(objcolconfigs) > 0):
2941                fd.write(xm.stop())
2942
2943                for attrconfig in idcolconfig_include_tab:
2944                    # print '    include_tab',attrconfig.attrname
2945                    attrconfig.write_xml(fd, _id, indent+4)
2946
2947                for attrconfig in objcolconfigs:
2948                    # print '    objcolconfig',attrconfig.attrname
2949                    attrconfig[_id].write_xml(fd, indent+4)
2950                fd.write(xm.end(xmltag_item, indent+2))
2951            else:
2952                fd.write(xm.stopit())
2953
2954        # print '  _write_xml_body: done'
2955
2956    def write_xml(self, fd, indent, xmltag_id='id', ids=None, ids_xml=None,
2957                  is_print_begin_end=True, attrconfigs_excluded=[]):
2958        # print 'write_xml',self.ident#,ids
2959        if self.xmltag is not None:
2960            xmltag, xmltag_item, attrname_id = self.xmltag
2961
2962            if xmltag == '':  # no begin end statements
2963                is_print_begin_end = False
2964
2965            if ids is not None:
2966                if not hasattr(ids, '__iter__'):
2967                    ids = [ids]
2968
2969            if attrname_id == '':  # no id info will be written
2970                attrconfig_id = None
2971                xmltag_id = ''
2972
2973            elif attrname_id is not None:  # an attrconf for id has been defined
2974                attrconfig_id = getattr(self.get_attrsman(), attrname_id)
2975                xmltag_id = None  # this will define the id tag
2976            else:
2977                attrconfig_id = None  # native id will be written using xmltag_id from args
2978
2979            # print '  attrname_id,attrconfig_id',attrname_id,attrconfig_id
2980            # if attrconfig_id is not None:
2981            #    print '  attrconfig_id',attrconfig_id.attrname
2982
2983            # figure out scalar attributes and child objects
2984            attrconfigs = []
2985            objconfigs = []
2986            colconfigs = []
2987            objcolconfigs = []
2988            idcolconfig_include_tab = []
2989            for attrconfig in self.get_attrsman().get_configs(is_all=True):
2990                # print '  check',attrconfig.attrname,attrconfig.xmltagis not None,attrconfig.is_colattr(),attrconfig.metatype
2991                if attrconfig == attrconfig_id:
2992                    pass
2993                elif attrconfig in attrconfigs_excluded:
2994                    pass
2995                elif attrconfig.is_colattr() & (attrconfig.metatype == 'obj'):
2996                    objcolconfigs.append(attrconfig)
2997                elif (attrconfig.is_colattr()) & (attrconfig.metatype in ('ids', 'id')) & (attrconfig.xmltag is not None):
2998                    if hasattr(attrconfig, "is_xml_include_tab"):
2999                        if attrconfig.is_xml_include_tab:
3000                            idcolconfig_include_tab.append(attrconfig)
3001                        else:
3002                            colconfigs.append(attrconfig)
3003                    else:
3004                        colconfigs.append(attrconfig)
3005                elif attrconfig.is_colattr() & (attrconfig.xmltag is not None):
3006                    colconfigs.append(attrconfig)
3007                elif (attrconfig.metatype == 'obj'):  # better use self.childs
3008                    if (attrconfig.get_value().xmltag is not None) & attrconfig.is_child():
3009                        objconfigs.append(attrconfig)
3010                elif attrconfig.xmltag is not None:
3011                    attrconfigs.append(attrconfig)
3012
3013            # print '  attrconfigs',attrconfigs
3014            # print '  objconfigs',objconfigs
3015            # print '  idcolconfig_include_tab',idcolconfig_include_tab
3016            # print '  colconfigs',colconfigs
3017            # start writing
3018            if len(attrconfigs) > 0:
3019                # print '  there are scalar attributes'
3020                if is_print_begin_end:
3021                    fd.write(xm.start(xmltag, indent))
3022                for attrconfig in attrconfigs:
3023                    attrconfig.write_xml(fd)
3024
3025                # are there child objects to write
3026                if (len(objconfigs) > 0) | (len(colconfigs) > 0) | (len(idcolconfig_include_tab) > 0):
3027                    fd.write(xm.stop())
3028                    self._write_xml_body(fd, indent, objconfigs, idcolconfig_include_tab,
3029                                         colconfigs,
3030                                         objcolconfigs,
3031                                         xmltag_item, attrconfig_id,
3032                                         xmltag_id, ids, ids_xml)
3033                    fd.write(xm.end(xmltag, indent))
3034
3035                else:
3036                    fd.write(xm.stopit())
3037            else:
3038                # print '  no scalars'
3039                if is_print_begin_end:
3040                    fd.write(xm.begin(xmltag, indent))
3041                self._write_xml_body(fd, indent, objconfigs, idcolconfig_include_tab,
3042                                     colconfigs,
3043                                     objcolconfigs,
3044                                     xmltag_item, attrconfig_id,
3045                                     xmltag_id, ids, ids_xml)
3046
3047                if is_print_begin_end:
3048                    fd.write(xm.end(xmltag, indent))
3049
3050
3051class TableObjman(Tabman, TableMixin):
3052    """
3053    Table Object management manages objects with list and dict based columns.
3054    For faster operation use ArrayObjman in arrayman package, which requires numpy.
3055    """
3056
3057    def __init__(self, ident, **kwargs):
3058        self._init_objman(ident, **kwargs)
3059        self._init_attributes()
3060        self._init_constants()
3061
3062    def _init_objman(self, ident, is_plugin=False, **kwargs):
3063        BaseObjman._init_objman(self, ident, managertype='table', **kwargs)
3064        Tabman.__init__(self, is_plugin=is_plugin)
3065        # self.set_attrsman(self)
3066        self.set_attrsman(self)
3067
3068
3069
3070###############################################################################
3071if __name__ == '__main__':
3072    """
3073    Test
3074    """
3075
3076    pass
3077