# -*- coding: utf-8 -*-

#     Copyright 2012-2020 Jean-Pierre LEDURE

# =====================================================================================================================
# ===                   The Access2Base library is a part of the LibreOffice project.                               ===
# ===                   Full documentation is available on http://www.access2base.com                               ===
# =====================================================================================================================

# Access2Base is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# Access2Base is free software; you can redistribute it and/or modify it under the terms of either (at your option):

# 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
# distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .

# 2) The GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. If a copy of the LGPL was not
# distributed with this file, see http://www.gnu.org/licenses/ .

"""
The access2base.py module implements an interface between Python (user) scripts and the Access2Base Basic library.

Usage:
    from access2base import *
Additionally, if Python and LibreOffice are started in separate processes:
    If LibreOffice started from console ... (example for Linux)
        ./soffice --accept='socket,host=localhost,port=2019;urp;'
    then insert next statement
        A2BConnect(hostname = 'localhost', port = 2019)

Specific documentation about Access2Base and Python:
    http://www.access2base.com/access2base.html#%5B%5BAccess2Base%20and%20Python%5D%5D
"""

import uno
XSCRIPTCONTEXT = uno

from platform import system as _opsys
import datetime
import os
import sys
import traceback

_LIBRARY = ''               # Should be 'Access2Base' or 'Access2BaseDev'
_VERSION = '7.4'            # Actual version number
_WRAPPERMODULE = 'Python'   # Module name in the Access2Base library containing Python interfaces

# CallByName types
_vbGet, _vbLet, _vbMethod, _vbSet, _vbUNO = 2, 4, 1, 8, 16


class _Singleton(type):
    """
    A Singleton design pattern
    Credits: « Python in a Nutshell » by Alex Martelli, O'Reilly
    """
    instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls.instances:
            cls.instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls.instances[cls]


class acConstants(object, metaclass = _Singleton):
    """
    VBA constants used in the Access2Base API.
    Values derived from MSAccess, except when conflicts
    """
    # Python special constants (used in the protocol between Python and Basic)
    # -----------------------------------------------------------------
    Empty = '+++EMPTY+++'
    Null = '+++NULL+++'
    Missing = '+++MISSING+++'
    FromIsoFormat = '%Y-%m-%d %H:%M:%S' # To be used with datetime.datetime.strptime()

    # AcCloseSave
    # -----------------------------------------------------------------
    acSaveNo = 2
    acSavePrompt = 0
    acSaveYes = 1

    # AcFormView
    # -----------------------------------------------------------------
    acDesign = 1
    acNormal = 0
    acPreview = 2

    # AcFormOpenDataMode
    # -----------------------------------------------------------------
    acFormAdd = 0
    acFormEdit = 1
    acFormPropertySettings = -1
    acFormReadOnly = 2

    # acView
    # -----------------------------------------------------------------
    acViewDesign = 1
    acViewNormal = 0
    acViewPreview = 2

    # acOpenDataMode
    # -----------------------------------------------------------------
    acAdd = 0
    acEdit = 1
    acReadOnly = 2

    # AcObjectType
    # -----------------------------------------------------------------
    acDefault = -1
    acDiagram = 8
    acForm = 2
    acQuery = 1
    acReport = 3
    acTable = 0
    #   Unexisting in MS/Access
    acBasicIDE = 101
    acDatabaseWindow = 102
    acDocument = 111
    acWelcome = 112
    #   Subtype if acDocument
    docWriter = "Writer"
    docCalc = "Calc"
    docImpress = "Impress"
    docDraw = "Draw"
    docMath = "Math"

    # AcWindowMode
    # -----------------------------------------------------------------
    acDialog = 3
    acHidden = 1
    acIcon = 2
    acWindowNormal = 0

    # VarType constants
    # -----------------------------------------------------------------
    vbEmpty = 0
    vbNull = 1
    vbInteger = 2
    vbLong = 3
    vbSingle = 4
    vbDouble = 5
    vbCurrency = 6
    vbDate = 7
    vbString = 8
    vbObject = 9
    vbBoolean = 11
    vbVariant = 12
    vbByte = 17
    vbUShort = 18
    vbULong = 19
    vbBigint = 35
    vbDecimal = 37
    vbArray = 8192

    # MsgBox constants
    # -----------------------------------------------------------------
    vbOKOnly = 0  # OK button only (default)
    vbOKCancel = 1  # OK and Cancel buttons
    vbAbortRetryIgnore = 2  # Abort, Retry, and Ignore buttons
    vbYesNoCancel = 3  # Yes, No, and Cancel buttons
    vbYesNo = 4  # Yes and No buttons
    vbRetryCancel = 5  # Retry and Cancel buttons
    vbCritical = 16  # Critical message
    vbQuestion = 32  # Warning query
    vbExclamation = 48  # Warning message
    vbInformation = 64  # Information message
    vbDefaultButton1 = 128  # First button is default (default) (VBA: 0)
    vbDefaultButton2 = 256  # Second button is default
    vbDefaultButton3 = 512  # Third button is default
    vbApplicationModal = 0  # Application modal message box (default)
    # MsgBox Return Values
    # -----------------------------------------------------------------
    vbOK = 1  # OK button pressed
    vbCancel = 2  # Cancel button pressed
    vbAbort = 3  # Abort button pressed
    vbRetry = 4  # Retry button pressed
    vbIgnore = 5  # Ignore button pressed
    vbYes = 6  # Yes button pressed
    vbNo = 7  # No button pressed

    # Dialogs Return Values
    # ------------------------------------------------------------------
    dlgOK = 1  # OK button pressed
    dlgCancel = 0  # Cancel button pressed

    # Control Types
    # -----------------------------------------------------------------
    acCheckBox = 5
    acComboBox = 7
    acCommandButton = 2
    acToggleButton = 122
    acCurrencyField = 18
    acDateField = 15
    acFileControl = 12
    acFixedLine = 24  # FREE ENTRY (USEFUL IN DIALOGS)
    acFixedText = 10
    acLabel = 10
    acFormattedField = 1  # FREE ENTRY TAKEN TO NOT CONFUSE WITH acTextField
    acGridControl = 11
    acGroupBox = 8
    acOptionGroup = 8
    acHiddenControl = 13
    acImageButton = 4
    acImageControl = 14
    acImage = 14
    acListBox = 6
    acNavigationBar = 22
    acNumericField = 17
    acPatternField = 19
    acProgressBar = 23  # FREE ENTRY (USEFUL IN DIALOGS)
    acRadioButton = 3
    acOptionButton = 3
    acScrollBar = 20
    acSpinButton = 21
    acSubform = 112
    acTextField = 9
    acTextBox = 9
    acTimeField = 16

    # AcRecord
    # -----------------------------------------------------------------
    acFirst = 2
    acGoTo = 4
    acLast = 3
    acNewRec = 5
    acNext = 1
    acPrevious = 0

    # FindRecord
    # -----------------------------------------------------------------
    acAnywhere = 0
    acEntire = 1
    acStart = 2
    acDown = 1
    acSearchAll = 2
    acUp = 0
    acAll = 0
    acCurrent = -1

    # AcDataObjectType
    # -----------------------------------------------------------------
    acActiveDataObject = -1
    acDataForm = 2
    acDataQuery = 1
    acDataServerView = 7
    acDataStoredProcedure = 9
    acDataTable = 0

    # AcQuitOption
    # -----------------------------------------------------------------
    acQuitPrompt = 0
    acQuitSaveAll = 1
    acQuitSaveNone = 2

    # AcCommand
    # -----------------------------------------------------------------
    acCmdAboutMicrosoftAccess = 35
    acCmdAboutOpenOffice = 35
    acCmdAboutLibreOffice = 35
    acCmdVisualBasicEditor = 525
    acCmdBringToFront = 52
    acCmdClose = 58
    acCmdToolbarsCustomize = 165
    acCmdChangeToCommandButton = 501
    acCmdChangeToCheckBox = 231
    acCmdChangeToComboBox = 230
    acCmdChangeToTextBox = 227
    acCmdChangeToLabel = 228
    acCmdChangeToImage = 234
    acCmdChangeToListBox = 229
    acCmdChangeToOptionButton = 233
    acCmdCopy = 190
    acCmdCut = 189
    acCmdCreateRelationship = 150
    acCmdDelete = 337
    acCmdDatabaseProperties = 256
    acCmdSQLView = 184
    acCmdRemove = 366
    acCmdDesignView = 183
    acCmdFormView = 281
    acCmdNewObjectForm = 136
    acCmdNewObjectTable = 134
    acCmdNewObjectView = 350
    acCmdOpenDatabase = 25
    acCmdNewObjectQuery = 135
    acCmdShowAllRelationships = 149
    acCmdNewObjectReport = 137
    acCmdSelectAll = 333
    acCmdRemoveTable = 84
    acCmdOpenTable = 221
    acCmdRename = 143
    acCmdDeleteRecord = 223
    acCmdApplyFilterSort = 93
    acCmdSnapToGrid = 62
    acCmdViewGrid = 63
    acCmdInsertHyperlink = 259
    acCmdMaximumRecords = 508
    acCmdObjectBrowser = 200
    acCmdPaste = 191
    acCmdPasteSpecial = 64
    acCmdPrint = 340
    acCmdPrintPreview = 54
    acCmdSaveRecord = 97
    acCmdFind = 30
    acCmdUndo = 292
    acCmdRefresh = 18
    acCmdRemoveFilterSort = 144
    acCmdRunMacro = 31
    acCmdSave = 20
    acCmdSaveAs = 21
    acCmdSelectAllRecords = 109
    acCmdSendToBack = 53
    acCmdSortDescending = 164
    acCmdSortAscending = 163
    acCmdTabOrder = 41
    acCmdDatasheetView = 282
    acCmdZoomSelection = 371

    # AcSendObjectType
    # -----------------------------------------------------------------
    acSendForm = 2
    acSendNoObject = -1
    acSendQuery = 1
    acSendReport = 3
    acSendTable = 0

    # AcOutputObjectType
    # -----------------------------------------------------------------
    acOutputTable = 0
    acOutputQuery = 1
    acOutputForm = 2
    acOutputArray = -1

    # AcEncoding
    # -----------------------------------------------------------------
    acUTF8Encoding = 76

    # AcFormat
    # -----------------------------------------------------------------
    acFormatPDF = "writer_pdf_Export"
    acFormatODT = "writer8"
    acFormatDOC = "MS Word 97"
    acFormatHTML = "HTML"
    acFormatODS = "calc8"
    acFormatXLS = "MS Excel 97"
    acFormatXLSX = "Calc MS Excel 2007 XML"
    acFormatTXT = "Text - txt - csv (StarCalc)"

    # AcExportQuality
    # -----------------------------------------------------------------
    acExportQualityPrint = 0
    acExportQualityScreen = 1

    # AcSysCmdAction
    # -----------------------------------------------------------------
    acSysCmdAccessDir = 9
    acSysCmdAccessVer = 7
    acSysCmdClearHelpTopic = 11
    acSysCmdClearStatus = 5
    acSysCmdGetObjectState = 10
    acSysCmdGetWorkgroupFile = 13
    acSysCmdIniFile = 8
    acSysCmdInitMeter = 1
    acSysCmdProfile = 12
    acSysCmdRemoveMeter = 3
    acSysCmdRuntime = 6
    acSysCmdSetStatus = 4
    acSysCmdUpdateMeter = 2

    # Type property
    # -----------------------------------------------------------------
    dbBigInt = 16
    dbBinary = 9
    dbBoolean = 1
    dbByte = 2
    dbChar = 18
    dbCurrency = 5
    dbDate = 8
    dbDecimal = 20
    dbDouble = 7
    dbFloat = 21
    dbGUID = 15
    dbInteger = 3
    dbLong = 4
    dbLongBinary = 11  # (OLE Object)
    dbMemo = 12
    dbNumeric = 19
    dbSingle = 6
    dbText = 10
    dbTime = 22
    dbTimeStamp = 23
    dbVarBinary = 17
    dbUndefined = -1

    # Attributes property
    # -----------------------------------------------------------------
    dbAutoIncrField = 16
    dbDescending = 1
    dbFixedField = 1
    dbHyperlinkField = 32768
    dbSystemField = 8192
    dbUpdatableField = 32
    dbVariableField = 2

    # OpenRecordset
    # -----------------------------------------------------------------
    dbOpenForwardOnly = 8
    dbSQLPassThrough = 64
    dbReadOnly = 4

    # Query types
    # -----------------------------------------------------------------
    dbQAction = 240
    dbQAppend = 64
    dbQDDL = 4  # 96
    dbQDelete = 32
    dbQMakeTable = 128  # 80
    dbQSelect = 0
    dbQSetOperation = 8  # 128
    dbQSQLPassThrough = 1  # 112
    dbQUpdate = 16  # 48

    # Edit mode
    # -----------------------------------------------------------------
    dbEditNone = 0
    dbEditInProgress = 1
    dbEditAdd = 2

    # Toolbars
    # -----------------------------------------------------------------
    msoBarTypeNormal = 0  # Usual toolbar
    msoBarTypeMenuBar = 1  # Menu bar
    msoBarTypePopup = 2  # Shortcut menu
    msoBarTypeStatusBar = 11  # Status bar
    msoBarTypeFloater = 12  # Floating window

    msoControlButton = 1  # Command button
    msoControlPopup = 10  # Popup, submenu

    # New Lines
    # -----------------------------------------------------------------
    vbCr = chr(13)
    vbLf = chr(10)

    def _NewLine():
        if _opsys == 'Windows':
            return chr(13) + chr(10)
        return chr(10)

    vbNewLine = _NewLine()
    vbTab = chr(9)

    # Module types
    # -----------------------------------------------------------------
    acClassModule = 1
    acStandardModule = 0

    # (Module) procedure types
    # -----------------------------------------------------------------
    vbext_pk_Get = 1  # A Property Get procedure
    vbext_pk_Let = 2  # A Property Let procedure
    vbext_pk_Proc = 0  # A Sub or Function procedure
    vbext_pk_Set = 3  # A Property Set procedure


COMPONENTCONTEXT, DESKTOP, SCRIPTPROVIDER, THISDATABASEDOCUMENT = None, None, None, None

def _ErrorHandler(type, value, tb):
    '''
    Is the function to be set as new sys.excepthook to bypass the standard error handler
        Derived from https://stackoverflow.com/questions/31949760/how-to-limit-python-traceback-to-specific-files
    Handler removes traces pointing to methods located in access2base.py when error is due to a user programming error
        sys.excepthook = _ErrorHandler
    NOT APPLIED YET
    '''

    def check_file(name):
        return 'access2base.py' not in name

    show = (fs for fs in traceback.extract_tb(tb) if check_file(fs.filename))
    fmt = traceback.format_list(show) + traceback.format_exception_only(type, value)
    print(''.join(fmt), end = '', file = sys.stderr)
    # Reset to standard handler
    sys.excepthook = sys.__excepthook__


def A2BConnect(hostname = '', port = 0):
    """
    To be called explicitly by user scripts when Python process runs outside the LibreOffice process.
        LibreOffice started as (Linux):
            ./soffice --accept='socket,host=localhost,port=xxxx;urp;'
    Otherwise called implicitly by the current module without arguments
    Initializes COMPONENTCONTEXT, SCRIPTPROVIDER and DESKTOP
    :param hostname: probably 'localhost' or ''
    :param port: port number or 0
    :return: None
    """
    global XSCRIPTCONTEXT, COMPONENTCONTEXT, DESKTOP, SCRIPTPROVIDER
    # Determine COMPONENTCONTEXT, via socket or inside LibreOffice
    if len(hostname) > 0 and port > 0:      # Explicit connection request via socket
        # Code derived from Bridge.py by Alain H. Romedenne
        local_context = XSCRIPTCONTEXT.getComponentContext()
        resolver = local_context.ServiceManager.createInstanceWithContext(
            'com.sun.star.bridge.UnoUrlResolver', local_context)
        try:
            conn = 'socket,host=%s,port=%d' % (hostname, port)
            connection_url = 'uno:%s;urp;StarOffice.ComponentContext' % conn
            established_context = resolver.resolve(connection_url)
        except Exception:  # thrown when LibreOffice specified instance isn't started
            raise ConnectionError('Connection to LibreOffice failed (host = ' + hostname + ', port = ' + str(port) + ')')
        COMPONENTCONTEXT = established_context
        DESKTOP = None
    elif len(hostname) == 0 and port == 0:       # Usual interactive mode
        COMPONENTCONTEXT = XSCRIPTCONTEXT.getComponentContext()
        DESKTOP = COMPONENTCONTEXT.ServiceManager.createInstanceWithContext( 'com.sun.star.frame.Desktop', COMPONENTCONTEXT)
    else:
        raise SystemExit('The invocation of A2BConnect() has invalid arguments')
    # Determine SCRIPTPROVIDER
    servicemanager = COMPONENTCONTEXT.ServiceManager
    masterscript = servicemanager.createInstanceWithContext("com.sun.star.script.provider.MasterScriptProviderFactory", COMPONENTCONTEXT)
    SCRIPTPROVIDER = masterscript.createScriptProvider("")
    Script = _A2B.xScript('TraceLog', 'Trace')  # Don't use invokeMethod() to force reset of error stack
    Script.invoke(('===>', 'Python wrapper loaded V.' + _VERSION, False), (), ())
    return None


class _A2B(object, metaclass = _Singleton):
    """
    Collection of helper functions implementing the protocol between Python and Basic
        Read comments in PythonWrapper Basic function
    """

    @classmethod
    def BasicObject(cls, objectname):
        objs = {'COLLECTION': _Collection
            , 'COMMANDBAR': _CommandBar
            , 'COMMANDBARCONTROL': _CommandBarControl
            , 'CONTROL': _Control
            , 'DATABASE': _Database
            , 'DIALOG': _Dialog
            , 'EVENT': _Event
            , 'FIELD': _Field
            , 'FORM': _Form
            , 'MODULE': _Module
            , 'OPTIONGROUP': _OptionGroup
            , 'PROPERTY': _Property
            , 'QUERYDEF': _QueryDef
            , 'RECORDSET': _Recordset
            , 'SUBFORM': _SubForm
            , 'TABLEDEF': _TableDef
            , 'TEMPVAR': _TempVar
                }
        return objs[objectname]

    @classmethod
    def xScript(cls, script, module):
        """
        At first call checks the existence of the Access2Base library
        Initializes _LIBRARY with the found library name
        First and next calls execute the given script in the given module of the _LIBRARY library
        The script and module are presumed to exist
        :param script: name of script
        :param module: name of module
        :return: the script object. NB: the execution is done with the invoke() method applied on the returned object
        """
        global _LIBRARY
        Script = None
        def sScript(lib):
            return 'vnd.sun.star.script:' + lib + '.' + module + '.' + script + '?language=Basic&location=application'
        if _LIBRARY == '':
            # Check the availability of the Access2Base library
            for lib in ('Access2BaseDev', 'Access2Base'):
                try:
                    if Script is None:
                        Script = SCRIPTPROVIDER.getScript(sScript(lib))
                        _LIBRARY = lib
                except Exception:
                    pass
            if Script is None:
                raise SystemExit('Access2Base basic library not found')
        else:
            Script = SCRIPTPROVIDER.getScript(sScript(_LIBRARY))
        return Script

    @classmethod
    def A2BErrorCode(cls):
        """
        Return the Access2Base error stack as a tuple
            0 => error code
            1 => severity level
            2 => short error message
            3 => long error message
        """
        Script = cls.xScript('TraceErrorCode', 'Trace')
        return Script.invoke((), (), ())[0]

    @classmethod
    def invokeMethod(cls, script, module, *args):
        """
        Direct call to a named script/module pair with their arguments
        If the arguments do not match their definition at the Basic side, a TypeError is raised
        :param script: name of script
        :param module: name of module
        :param args: list of arguments to be passed to the script
        :return: the value returned by the script execution
        """
        if COMPONENTCONTEXT is None:
            A2BConnect()     #   Connection from inside LibreOffice is done at first API invocation
        Script = cls.xScript(script, module)
        try:
            Returned = Script.invoke((args), (), ())[0]
        except Exception:
            raise TypeError("Access2Base error: method '" + script + "' in Basic module '" + module + "' call error. Check its arguments.")
        else:
            if Returned is None:
                if cls.VerifyNoError():
                    return None
            return Returned

    @classmethod
    def invokeWrapper(cls, action, basic, script, *args):
        """
        Call the Basic wrapper to invite it to execute the proposed action on a Basic object
        If the arguments do not match their definition at the Basic side, a TypeError is raised
        After execution, a check is done if the execution has raised an error within Basic
            If yes, a TypeError is raised
        :param action: Property Get, Property Let, Property Set, invoke Method or return UNO object
        :param basic: the reference of the Basic object, i.e. the index in the array caching the addresses of the objects
                        conventionally Application = -1 and DoCmd = -2
        :param script: the property or method name
        :param args: the arguments of the method, if any
        :return: the value returned by the execution of the Basic routine
        """
        if COMPONENTCONTEXT is None:
            A2BConnect()     #   Connection from inside LibreOffice is done at first API invocation
        # Intercept special call to Application.Events()
        if basic == Application.basicmodule and script == 'Events':
            Script = cls.xScript('PythonEventsWrapper', _WRAPPERMODULE)
            Returned = Script.invoke((args[0],), (), ())
        else:
            Script = cls.xScript('PythonWrapper', _WRAPPERMODULE)
            NoArgs = '+++NOARGS+++'     # Conventional notation for properties/methods without arguments
            if len(args) == 0:
                args = (action,) + (basic,) + (script,) + (NoArgs,)
            else:
                args = (action,) + (basic,) + (script,) + args
            try:
                Returned = Script.invoke((args), (), ())
            except Exception:
                raise TypeError("Access2Base error: method '" + script + "' call error. Check its arguments.")

        if isinstance(Returned[0], tuple):
            # Is returned value a reference to a basic object, a scalar or a UNO object ?
            if len(Returned[0]) in (3, 4):
                if Returned[0][0] == 0:         # scalar
                    return Returned[0][1]
                elif Returned[0][0] == 1:       # reference to objects cache
                    basicobject = cls.BasicObject(Returned[0][2])
                    if len(Returned[0]) == 3:
                        return basicobject(Returned[0][1], Returned[0][2])
                    else:
                        return basicobject(Returned[0][1], Returned[0][2], Returned[0][3])
                elif Returned[0][0] == 2:       # Null value
                    return None
                else:       # Should not happen
                    return None
            else:                               # UNO object
                return Returned[0]
        elif Returned[0] is None:
            if cls.VerifyNoError():
                return None
        else: # Should not happen
            return Returned[0]

    @classmethod
    def VerifyNoError(cls):
        # has Access2Base generated an error ?
        errorstack = cls.A2BErrorCode()  # 0 = code, 1 = severity, 2 = short text, 3 = long text
        if errorstack[1] in ('ERROR', 'FATAL', 'ABORT'):
            raise TypeError('Access2Base error: ' + errorstack[3])
        return True


class Application(object, metaclass = _Singleton):
    """ Collection of methods located in the Application (Basic) module """
    W = _A2B.invokeWrapper
    basicmodule = -1

    @classmethod
    def AllDialogs(cls, dialog = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'AllDialogs', dialog)
    @classmethod
    def AllForms(cls, form = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'AllForms', form)
    @classmethod
    def AllModules(cls, module = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'AllModules', module)
    @classmethod
    def CloseConnection(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'CloseConnection')
    @classmethod
    def CommandBars(cls, bar = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'CommandBars', bar)
    @classmethod
    def CurrentDb(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'CurrentDb')
    @classmethod
    def CurrentUser(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'CurrentUser')
    @classmethod
    def DAvg(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DAvg', expression, domain, criteria)
    @classmethod
    def DCount(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DCount', expression, domain, criteria)
    @classmethod
    def DLookup(cls, expression, domain, criteria = '', orderclause = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DLookup', expression, domain, criteria, orderclause)
    @classmethod
    def DMax(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DMax', expression, domain, criteria)
    @classmethod
    def DMin(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DMin', expression, domain, criteria)
    @classmethod
    def DStDev(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DStDev', expression, domain, criteria)
    @classmethod
    def DStDevP(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DStDevP', expression, domain, criteria)
    @classmethod
    def DSum(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DSum', expression, domain, criteria)
    @classmethod
    def DVar(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DVar', expression, domain, criteria)
    @classmethod
    def DVarP(cls, expression, domain, criteria = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'DVarP', expression, domain, criteria)
    @classmethod
    def Events(cls, event):
        return cls.W(_vbMethod, cls.basicmodule, 'Events', event)
    @classmethod
    def Forms(cls, form = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'Forms', form)
    @classmethod
    def getObject(cls, shortcut):
        return cls.W(_vbMethod, cls.basicmodule, 'getObject', shortcut)
    GetObject = getObject
    @classmethod
    def getValue(cls, shortcut):
        return cls.W(_vbMethod, cls.basicmodule, 'getValue', shortcut)
    GetValue = getValue
    @classmethod
    def HtmlEncode(cls, string, length = 0):
        return cls.W(_vbMethod, cls.basicmodule, 'HtmlEncode', string, length)
    @classmethod
    def OpenConnection(cls, thisdatabasedocument = acConstants.Missing):
        global THISDATABASEDOCUMENT
        if COMPONENTCONTEXT is None:
            A2BConnect()     #   Connection from inside LibreOffice is done at first API invocation
        if DESKTOP is not None:
            THISDATABASEDOCUMENT = DESKTOP.getCurrentComponent()
            return _A2B.invokeMethod('OpenConnection', 'Application', THISDATABASEDOCUMENT)
    @classmethod
    def OpenDatabase(cls, connectionstring, username = '', password = '', readonly = False):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenDatabase', connectionstring, username
                           , password, readonly)
    @classmethod
    def ProductCode(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'ProductCode')
    @classmethod
    def setValue(cls, shortcut, value):
        return cls.W(_vbMethod, cls.basicmodule, 'setValue', shortcut, value)
    SetValue = setValue
    @classmethod
    def SysCmd(cls, action, text = '', value = -1):
        return cls.W(_vbMethod, cls.basicmodule, 'SysCmd', action, text, value)
    @classmethod
    def TempVars(cls, var = acConstants.Missing):
        return cls.W(_vbMethod, cls.basicmodule, 'TempVars', var)
    @classmethod
    def Version(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'Version')


class DoCmd(object, metaclass = _Singleton):
    """ Collection of methods located in the DoCmd (Basic) module """
    W = _A2B.invokeWrapper
    basicmodule = -2

    @classmethod
    def ApplyFilter(cls, filter = '', sqlwhere = '', controlname = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'ApplyFilter', filter, sqlwhere, controlname)
    @classmethod
    def Close(cls, objecttype, objectname, save = acConstants.acSavePrompt):
        return cls.W(_vbMethod, cls.basicmodule, 'Close', objecttype, objectname, save)
    @classmethod
    def CopyObject(cls, sourcedatabase, newname, sourceobjecttype, sourceobjectname):    # 1st argument must be set
        return cls.W(_vbMethod, cls.basicmodule, 'CopyObject', sourcedatabase, newname, sourceobjecttype
                           , sourceobjectname)
    @classmethod
    def FindNext(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'FindNext')
    @classmethod
    def FindRecord(cls, findwhat, match = acConstants.acEntire, matchcase = False, search = acConstants.acSearchAll
        , searchasformatted = False, onlycurrentfield = acConstants.acCurrent, findfirst = True):
        return cls.W(_vbMethod, cls.basicmodule, 'FindRecord', findwhat, match, matchcase, search
        , searchasformatted, onlycurrentfield, findfirst)
    @classmethod
    def GetHiddenAttribute(cls, objecttype, objectname = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'GetHiddenAttribute', objecttype, objectname)
    @classmethod
    def GoToControl(cls, controlname):
        return cls.W(_vbMethod, cls.basicmodule, 'GoToControl', controlname)
    @classmethod
    def GoToRecord(cls, objecttype = acConstants.acActiveDataObject, objectname = '', record = acConstants.acNext
                   , offset = 1):
        return cls.W(_vbMethod, cls.basicmodule, 'GoToRecord', objecttype, objectname, record, offset)
    @classmethod
    def Maximize(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'Maximize')
    @classmethod
    def Minimize(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'Minimize')
    @classmethod
    def MoveSize(cls, left = -1, top = -1, width = -1, height = -1):
        return cls.W(_vbMethod, cls.basicmodule, 'MoveSize', left, top, width, height)
    @classmethod
    def OpenForm(cls, formname, view = acConstants.acNormal, filter = '', wherecondition = ''
        , datamode = acConstants.acFormEdit, windowmode = acConstants.acWindowNormal, openargs = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenForm', formname, view, filter, wherecondition
        , datamode, windowmode, openargs)
    @classmethod
    def OpenQuery(cls, queryname, view = acConstants.acNormal, datamode = acConstants.acEdit):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenQuery', queryname, view, datamode)
    @classmethod
    def OpenReport(cls, queryname, view = acConstants.acNormal):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenReport', queryname, view)
    @classmethod
    def OpenSQL(cls, sql, option = -1):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenSQL', sql, option)
    @classmethod
    def OpenTable(cls, tablename, view = acConstants.acNormal, datamode = acConstants.acEdit):
        return cls.W(_vbMethod, cls.basicmodule, 'OpenTable', tablename, view, datamode)
    @classmethod
    def OutputTo(cls, objecttype, objectname = '', outputformat = '', outputfile = '', autostart = False, templatefile = ''
        , encoding = acConstants.acUTF8Encoding, quality = acConstants.acExportQualityPrint):
        if objecttype == acConstants.acOutputForm:
            encoding = 0
        return cls.W(_vbMethod, cls.basicmodule, 'OutputTo', objecttype, objectname, outputformat
                           , outputfile, autostart, templatefile, encoding, quality)
    @classmethod
    def Quit(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'Quit')
    @classmethod
    def RunApp(cls, commandline):
        return cls.W(_vbMethod, cls.basicmodule, 'RunApp', commandline)
    @classmethod
    def RunCommand(cls, command):
        return cls.W(_vbMethod, cls.basicmodule, 'RunCommand', command)
    @classmethod
    def RunSQL(cls, SQL, option = -1):
        return cls.W(_vbMethod, cls.basicmodule, 'RunSQL', SQL, option)
    @classmethod
    def SelectObject(cls, objecttype, objectname = '', indatabasewindow = False):
        return cls.W(_vbMethod, cls.basicmodule, 'SelectObject', objecttype, objectname, indatabasewindow)
    @classmethod
    def SendObject(cls, objecttype = acConstants.acSendNoObject, objectname = '', outputformat = '', to = '', cc = ''
        , bcc = '', subject = '', messagetext = '', editmessage = True, templatefile = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'SendObject', objecttype, objectname, outputformat, to, cc
        , bcc, subject, messagetext, editmessage, templatefile)
    @classmethod
    def SetHiddenAttribute(cls, objecttype, objectname = '', hidden = True):
        return cls.W(_vbMethod, cls.basicmodule, 'SetHiddenAttribute', objecttype, objectname, hidden)
    @classmethod
    def SetOrderBy(cls, orderby = '', controlname = ''):
        return cls.W(_vbMethod, cls.basicmodule, 'SetOrderBy', orderby, controlname)
    @classmethod
    def ShowAllRecords(cls):
        return cls.W(_vbMethod, cls.basicmodule, 'ShowAllRecords')


class Basic(object, metaclass = _Singleton):
    """ Collection of helper functions having the same behaviour as their Basic counterparts """
    M = _A2B.invokeMethod

    @classmethod
    def ConvertFromUrl(cls, url):
        return cls.M('PyConvertFromUrl', _WRAPPERMODULE, url)

    @classmethod
    def ConvertToUrl(cls, file):
        return cls.M('PyConvertToUrl', _WRAPPERMODULE, file)

    @classmethod
    def CreateUnoService(cls, servicename):
        return cls.M('PyCreateUnoService', _WRAPPERMODULE, servicename)

    @classmethod
    def DateAdd(cls, add, count, datearg):
        if isinstance(datearg, datetime.datetime):
            datearg = datearg.isoformat()
        dateadd = cls.M('PyDateAdd', _WRAPPERMODULE, add, count, datearg)
        return datetime.datetime.strptime(dateadd, acConstants.FromIsoFormat)

    @classmethod
    def DateDiff(cls, add, date1, date2, weekstart = 1, yearstart = 1):
        if isinstance(date1, datetime.datetime):
            date1 = date1.isoformat()
        if isinstance(date2, datetime.datetime):
            date2 = date2.isoformat()
        return cls.M('PyDateDiff', _WRAPPERMODULE, add, date1, date2, weekstart, yearstart)

    @classmethod
    def DatePart(cls, add, datearg, weekstart = 1, yearstart = 1):
        if isinstance(datearg, datetime.datetime):
            datearg = datearg.isoformat()
        return cls.M('PyDatePart', _WRAPPERMODULE, add, datearg, weekstart, yearstart)

    @classmethod
    def DateValue(cls, datestring):
        datevalue = cls.M('PyDateValue', _WRAPPERMODULE, datestring)
        return datetime.datetime.strptime(datevalue, acConstants.FromIsoFormat)

    @classmethod
    def Format(cls, value, format = None):
        if isinstance(value, (datetime.datetime, datetime.date, datetime.time, )):
            value = value.isoformat()
        return cls.M('PyFormat', _WRAPPERMODULE, value, format)

    @classmethod
    def GetGUIType(cls):
        return cls.M('PyGetGUIType', _WRAPPERMODULE)

    @staticmethod
    def GetPathSeparator():
        return os.sep

    @classmethod
    def GetSystemTicks(cls):
        return cls.M('PyGetSystemTicks', _WRAPPERMODULE)

    @classmethod
    def MsgBox(cls, text, type = None, dialogtitle = None):
        return cls.M('PyMsgBox', _WRAPPERMODULE, text, type, dialogtitle)

    class GlobalScope(object, metaclass = _Singleton):
        @classmethod
        def BasicLibraries(cls):
            return Basic.M('PyGlobalScope', _WRAPPERMODULE, 'Basic')
        @classmethod
        def DialogLibraries(self):
            return Basic.M('PyGlobalScope', _WRAPPERMODULE, 'Dialog')

    @classmethod
    def InputBox(cls, text, title = None, default = None, xpos = None, ypos = None):
        return cls.M('PyInputBox', _WRAPPERMODULE, text, title, default, xpos, ypos)

    @staticmethod
    def Now():
        return datetime.datetime.now()

    @staticmethod
    def RGB(red, green, blue):
        return int('%02x%02x%02x' % (red, green, blue), 16)

    @classmethod
    def Timer(cls):
        return cls.M('PyTimer', _WRAPPERMODULE)

    @staticmethod
    def Xray(myObject):
        xrayscript = 'vnd.sun.star.script:XrayTool._Main.Xray?language=Basic&location=application'
        xScript = SCRIPTPROVIDER.getScript(xrayscript)
        xScript.invoke((myObject,), (), ())
        return


class _BasicObject(object):
    """
    Parent class of Basic objects
    Each subclass is identified by its classProperties:
         dictionary with keys = allowed properties, value = True if editable or False
    Each instance is identified by its
        - reference in the cache managed by Basic
        - type ('DATABASE', 'COLLECTION', ...)
        - name (form, control, ... name) - may be blank
    Properties are got and set following next strategy:
        1. Property names are controlled strictly ('Value' and not 'value')
        2. Getting a property value for the first time is always done via a Basic call
        3. Next occurrences are fetched from the Python dictionary of the instance if the property is read-only, otherwise via a Basic call
        4. Methods output might force the deletion of a property from the dictionary ('MoveNext' changes 'BOF' and 'EOF' properties)
        5. Setting a property value is done via a Basic call, except if self.internal == True
    """
    W = _A2B.invokeWrapper
    internal_attributes = ('objectreference', 'objecttype', 'name', 'internal')

    def __init__(self, reference = -1, objtype = None, name = ''):
        self.objectreference = reference    # reference in the cache managed by Basic
        self.objecttype = objtype           # ('DATABASE', 'COLLECTION', ...)
        self.name = name                    # '' when no name
        self.internal = False               # True to exceptionally allow assigning a new value to a read-only property
        self.localProperties = ()

    def __getattr__(self, name):
        if name in ('classProperties', 'localProperties'):
            pass
        elif name in self.classProperties:
            # Get Property from Basic
            return self.W(_vbGet, self.objectreference, name)
        # Usual attributes getter
        return super(_BasicObject, self).__getattribute__(name)

    def __setattr__(self, name, value):
        if name in ('classProperties', 'localProperties'):
            pass
        elif name in self.classProperties:
            if self.internal:       # internal = True forces property setting even if property is read-only
                pass
            elif self.classProperties[name]: # True == Editable
                self.W(_vbLet, self.objectreference, name, value)
            else:
                raise AttributeError("type object '" + self.objecttype + "' has no editable attribute '" + name + "'")
        elif name[0:2] == '__' or name in self.internal_attributes or name in self.localProperties:
            pass
        else:
            raise AttributeError("type object '" + self.objecttype + "' has no attribute '" + name + "'")
        object.__setattr__(self, name, value)
        return

    def __repr__(self):
        repr = "Basic object (type='" + self.objecttype + "', index=" + str(self.objectreference)
        if len(self.name) > 0:
            repr += ", name='" + self.name + "'"
        return repr + ")"

    def _Reset(self, propertyname, basicreturn = None):
        """ force new value or erase properties from dictionary (done to optimize calls to Basic scripts) """
        if propertyname in ('BOF', 'EOF'):
            # After a Move method invocation on a Recordset object, BOF or EOF likely to be got soon
            if isinstance(basicreturn, int):
                self.internal = True
                # f.i. basicreturn = 0b10 means: BOF = True, EOF = False
                self.BOF = basicreturn in (2, 3, -2, -3)
                self.EOF = basicreturn in (1, 3, -1, -3)
                self.internal = False
                return ( basicreturn >= 0 )
        else:
            # Suppress possibly invalid property values: e.g. RecordCount after Delete applied on Recordset object
            if property in self.__dict__:
                del(self.propertyname)
        return basicreturn

    @property
    def Name(self): return self.name
    @property
    def ObjectType(self): return self.objecttype

    def Dispose(self):
        return self.W(_vbMethod, self.objectreference, 'Dispose')
    def getProperty(self, propertyname, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'getProperty', propertyname, index)
    GetProperty = getProperty
    def hasProperty(self, propertyname):
        return propertyname in tuple(self.classProperties.keys())
    HasProperty = hasProperty
    def Properties(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Properties', index)
    def setProperty(self, propertyname, value, index = acConstants.Missing):
        if self.hasProperty(propertyname):
            if self.W(_vbMethod, self.objectreference, 'setProperty', propertyname, value, index):
                return self.__setattr__(propertyname, value)
        raise AttributeError("type object '" + self.objecttype + "' has no editable attribute '" + propertyname + "'")
    SetProperty = setProperty


class _Collection(_BasicObject):
    """ Collection object built as a Python iterator """
    classProperties = dict(Count = False)
    def __init__(self, reference = -1, objtype = None):
        super().__init__(reference, objtype)
        self.localProperties = ('count', 'index')
        self.count = self.Count
        self.index = 0
    def __iter__(self):
        self.index = 0
        return self
    def __next__(self):
        if self.index >= self.count:
            raise StopIteration
        next = self.Item(self.index)
        self.index = self.index + 1
        return next
    def __len__(self):
        return self.count

    def Add(self, table, value = acConstants.Missing):
        if isinstance(table, _BasicObject):     # Add method applied to a TABLEDEFS collection
            return self.W(_vbMethod, self.objectreference, 'Add', table.objectreference)
        else:                                   # Add method applied to a TEMPVARS collection
            add = self.W(_vbMethod, self.objectreference, 'Add', table, value)
            self.count = self.Count
            return add
    def Delete(self, name):
        return self.W(_vbMethod, self.objectreference, 'Delete', name)
    def Item(self, index):
        return self.W(_vbMethod, self.objectreference, 'Item', index)
    def Remove(self, tempvarname):
        remove = self.W(_vbMethod, self.objectreference, 'Remove', tempvarname)
        self.count = self.Count
        return remove
    def RemoveAll(self):
        remove = self.W(_vbMethod, self.objectreference, 'RemoveAll')
        self.count = self.Count
        return remove


class _CommandBar(_BasicObject):
    classProperties = dict(BuiltIn = False, Parent = False, Visible = True)

    def CommandBarControls(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'CommandBarControls', index)
    def Reset(self):
        return self.W(_vbMethod, self.objectreference, 'Reset')


class _CommandBarControl(_BasicObject):
    classProperties = dict(BeginGroup = False, BuiltIn = False, Caption = True, Index = False, OnAction = True
                        , Parent = False, TooltipText = True, Type = False, Visible = True)

    def Execute(self):
        return self.W(_vbMethod, self.objectreference, 'Execute')


class _Control(_BasicObject):
    classProperties = dict(BackColor = True, BorderColor = True, BorderStyle = True, Cancel = True, Caption = True
                        , ControlSource = False, ControlTipText = True, ControlType = False, Default = True
                        , DefaultValue = True, Enabled = True, FontBold = True, FontItalic = True, FontName = True
                        , FontSize = True, FontUnderline = True, FontWeight = True, ForeColor = True, Form = False
                        , Format = True, ItemData = False, ListCount = False, ListIndex = True, Locked = True, MultiSelect = True
                        , OnActionPerformed = True, OnAdjustmentValueChanged = True, OnApproveAction = True
                        , OnApproveReset = True, OnApproveUpdate = True, OnChanged = True, OnErrorOccurred = True
                        , OnFocusGained = True, OnFocusLost = True, OnItemStateChanged = True, OnKeyPressed = True
                        , OnKeyReleased = True, OnMouseDragged = True, OnMouseEntered = True, OnMouseExited = True
                        , OnMouseMoved = True, OnMousePressed = True, OnMouseReleased = True, OnResetted = True, OnTextChanged = True
                        , OnUpdated = True, OptionValue = False, Page = False, Parent = False, Picture = True, Required = True
                        , RowSource = True, RowSourceType = True, Selected = True, SelLength = True, SelStart = True, SelText = True
                        , SubType = False, TabIndex = True, TabStop = True, Tag = True, Text = False, TextAlign = True
                        , TripleState = True, Value = True, Visible = True
                        )

    @property
    def BoundField(self): return self.W(_vbUNO, self.objectreference, 'BoundField')
    @property
    def ControlModel(self): return self.W(_vbUNO, self.objectreference, 'ControlModel')
    @property
    def ControlView(self): return self.W(_vbUNO, self.objectreference, 'ControlView')
    @property
    def LabelControl(self): return self.W(_vbUNO, self.objectreference, 'LabelControl')

    def AddItem(self, value, index = -1):
        basicreturn = self.W(_vbMethod, self.objectreference, 'AddItem', value, index)
        self._Reset('ItemData')
        self._Reset('ListCount')
        return basicreturn
    def Controls(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Controls', index)
    # Overrides method in parent class: list of properties is strongly control type dependent
    def hasProperty(self, propertyname):
        return self.W(_vbMethod, self.objectreference, 'hasProperty', propertyname)
    HasProperty = hasProperty
    def RemoveItem(self, index):
        basicreturn = self.W(_vbMethod, self.objectreference, 'RemoveItem', index)
        self._Reset('ItemData')
        self._Reset('ListCount')
        return basicreturn
    def Requery(self):
        return self.W(_vbMethod, self.objectreference, 'Requery')
    def SetSelected(self, value, index):
        return self.W(_vbMethod, self.objectreference, 'SetSelected', value, index)
    def SetFocus(self):
        return self.W(_vbMethod, self.objectreference, 'SetFocus')


class _Database(_BasicObject):
    classProperties = dict(Connect = False, OnCreate = True
                        , OnFocus = True, OnLoad = True, OnLoadFinished = True, OnModifyChanged = True, OnNew = True
                        , OnPrepareUnload = True, OnPrepareViewClosing = True, OnSave = True, OnSaveAs = True
                        , OnSaveAsDone = True, OnSaveAsFailed = True, OnSaveDone = True, OnSaveFailed = True
                        , OnSubComponentClosed = True, OnSubComponentOpened = True, OnTitleChanged = True, OnUnfocus = True
                        , OnUnload = True, OnViewClosed = True, OnViewCreated = True, Version = False
                        )

    @property
    def Connection(self): return self.W(_vbUNO, self.objectreference, 'Connection')
    @property
    def Document(self): return self.W(_vbUNO, self.objectreference, 'Document')
    @property
    def MetaData(self): return self.W(_vbUNO, self.objectreference, 'MetaData')

    def Close(self):
        return self.W(_vbMethod, self.objectreference, 'Close')
    def CloseAllRecordsets(self):
        return self.W(_vbMethod, self.objectreference, 'CloseAllRecordsets')
    def CreateQueryDef(self, name, sqltext, option = -1):
        return self.W(_vbMethod, self.objectreference, 'CreateQueryDef', name, sqltext, option)
    def CreateTableDef(self, name):
        return self.W(_vbMethod, self.objectreference, 'CreateTableDef', name)
    def DAvg(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DAvg', expression, domain, criteria)
    def DCount(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DCount', expression, domain, criteria)
    def DLookup(self, expression, domain, criteria = '', orderclause = ''):
        return self.W(_vbMethod, self.objectreference, 'DLookup', expression, domain, criteria, orderclause)
    def DMax(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DMax', expression, domain, criteria)
    def DMin(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DMin', expression, domain, criteria)
    def DStDev(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DStDev', expression, domain, criteria)
    def DStDevP(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DStDevP', expression, domain, criteria)
    def DVar(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DVar', expression, domain, criteria)
    def DVarP(self, expression, domain, criteria = ''):
        return self.W(_vbMethod, self.objectreference, 'DVarP', expression, domain, criteria)
    def OpenRecordset(self, source, type = -1, option = -1, lockedit = -1):
        return self.W(_vbMethod, self.objectreference, 'OpenRecordset', source, type, option, lockedit)
    def OpenSQL(self, SQL, option = -1):
        return self.W(_vbMethod, self.objectreference, 'OpenSQL', SQL, option)
    def OutputTo(self, objecttype, objectname = '', outputformat = '', outputfile = '', autostart = False, templatefile = ''
        , encoding = acConstants.acUTF8Encoding, quality = acConstants.acExportQualityPrint):
        if objecttype == acConstants.acOutputForm:
            encoding = 0
        return self.W(_vbMethod, self.objectreference, 'OutputTo', objecttype, objectname, outputformat, outputfile
                      , autostart, templatefile, encoding, quality)
    def QueryDefs(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'QueryDefs', index)
    def Recordsets(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Recordsets', index)
    def RunSQL(self, SQL, option = -1):
        return self.W(_vbMethod, self.objectreference, 'RunSQL', SQL, option)
    def TableDefs(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'TableDefs', index)


class _Dialog(_BasicObject):
    classProperties = dict(Caption = True, Height = True, IsLoaded = False, OnFocusGained = True
                        , OnFocusLost = True, OnKeyPressed = True, OnKeyReleased = True, OnMouseDragged = True
                        , OnMouseEntered = True, OnMouseExited = True, OnMouseMoved = True, OnMousePressed = True
                        , OnMouseReleased = True, Page = True, Parent = False, Visible = True, Width = True
                        )

    @property
    def UnoDialog(self): return self.W(_vbUNO, self.objectreference, 'UnoDialog')

    def EndExecute(self, returnvalue):
        return self.W(_vbMethod, self.objectreference, 'EndExecute', returnvalue)
    def Execute(self):
        return self.W(_vbMethod, self.objectreference, 'Execute')
    def Move(self, left = -1, top = -1, width = -1, height = -1):
        return self.W(_vbMethod, self.objectreference, 'Move', left, top, width, height)
    def OptionGroup(self, groupname):
        return self.W(_vbMethod, self.objectreference, 'OptionGroup', groupname)
    def Start(self):
        return self.W(_vbMethod, self.objectreference, 'Start')
    def Terminate(self):
        return self.W(_vbMethod, self.objectreference, 'Terminate')

class _Event(_BasicObject):
    classProperties = dict(ButtonLeft = False, ButtonMiddle = False, ButtonRight = False, ClickCount = False
                    , ContextShortcut = False, EventName = False, EventType = False, FocusChangeTemporary = False
                    , KeyAlt = False, KeyChar = False, KeyCode = False, KeyCtrl = False, KeyFunction = False, KeyShift = False
                    , Recommendation = False, RowChangeAction = False, Source = False, SubComponentName = False
                    , SubComponentType = False, XPos = False, YPos = False
                    )


class _Field(_BasicObject):
    classProperties = dict(DataType = False, DataUpdatable = False, DbType = False, DefaultValue = True
                        , Description = True, FieldSize = False, Size = False, Source = False
                        , SourceField = False, SourceTable = False, TypeName = False, Value = True
                        )

    @property
    def Column(self): return self.W(_vbUNO, self.objectreference, 'Column')

    def AppendChunk(self, value):
        return self.W(_vbMethod, self.objectreference, 'AppendChunk', value)
    def GetChunk(self, offset, numbytes):
        return self.W(_vbMethod, self.objectreference, 'GetChunk', offset, numbytes)
    def ReadAllBytes(self, file):
        return self.W(_vbMethod, self.objectreference, 'ReadAllBytes', file)
    def ReadAllText(self, file):
        return self.W(_vbMethod, self.objectreference, 'ReadAllText', file)
    def WriteAllBytes(self, file):
        return self.W(_vbMethod, self.objectreference, 'WriteAllBytes', file)
    def WriteAllText(self, file):
        return self.W(_vbMethod, self.objectreference, 'WriteAllText', file)


class _Form(_BasicObject):
    classProperties = dict(AllowAdditions = True, AllowDeletions = True, AllowEdits = True, Bookmark = True
                        , Caption = True, CurrentRecord = True, Filter = True, FilterOn = True, Height = True
                        , IsLoaded = False, OnApproveCursorMove = True, OnApproveParameter = True, OnApproveReset = True
                        , OnApproveRowChange = True, OnApproveSubmit = True, OnConfirmDelete = True, OnCursorMoved = True
                        , OnErrorOccurred = True, OnLoaded = True, OnReloaded = True, OnReloading = True, OnResetted = True
                        , OnRowChanged = True, OnUnloaded = True, OnUnloading = True, OpenArgs = False, OrderBy = True
                        , OrderByOn = True, Parent = False, Recordset = False, RecordSource = True, Visible = True
                        , Width = True
                        )

    @property
    def Component(self): return self.W(_vbUNO, self.objectreference, 'Component')
    @property
    def ContainerWindow(self): return self.W(_vbUNO, self.objectreference, 'ContainerWindow')
    @property
    def DatabaseForm(self): return self.W(_vbUNO, self.objectreference, 'DatabaseForm')

    def Close(self):
        return self.W(_vbMethod, self.objectreference, 'Close')
    def Controls(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Controls', index)
    def Move(self, left = -1, top = -1, width = -1, height = -1):
        return self.W(_vbMethod, self.objectreference, 'Move', left, top, width, height)
    def OptionGroup(self, groupname):
        return self.W(_vbMethod, self.objectreference, 'OptionGroup', groupname)
    def Refresh(self):
        return self.W(_vbMethod, self.objectreference, 'Refresh')
    def Requery(self):
        return self.W(_vbMethod, self.objectreference, 'Requery')
    def SetFocus(self):
        return self.W(_vbMethod, self.objectreference, 'SetFocus')


class _Module(_BasicObject):
    classProperties = dict(CountOfDeclarationLines = False, CountOfLines = False, Type = False)

    def __init__(self, reference = -1, objtype = None, name = ''):
        super().__init__(reference, objtype, name)
        self.localProperties = ('startline', 'startcolumn', 'endline', 'endcolumn', 'prockind')

    def Find(self, target, startline, startcolumn, endline, endcolumn, wholeword = False
        , matchcase = False, patternsearch = False):
        Returned = self.W(_vbMethod, self.objectreference, 'Find', target, startline, startcolumn, endline
                      , endcolumn, wholeword, matchcase, patternsearch)
        if isinstance(Returned, tuple):
            if Returned[0] and len(Returned) == 5:
                self.startline = Returned[1]
                self.startcolumn = Returned[2]
                self.endline = Returned[3]
                self.endcolumn = Returned[4]
            return Returned[0]
        return Returned
    def Lines(self, line, numlines):
        return self.W(_vbMethod, self.objectreference, 'Lines', line, numlines)
    def ProcBodyLine(self, procname, prockind):
        return self.W(_vbMethod, self.objectreference, 'ProcBodyLine', procname, prockind)
    def ProcCountLines(self, procname, prockind):
        return self.W(_vbMethod, self.objectreference, 'ProcCountLines', procname, prockind)
    def ProcOfLine(self, line, prockind):
        Returned = self.W(_vbMethod, self.objectreference, 'ProcOfLine', line, prockind)
        if isinstance(Returned, tuple):
            if len(Returned) == 2:
                self.prockind = Returned[1]
                return Returned[0]
        return Returned
    def ProcStartLine(self, procname, prockind):
        return self.W(_vbMethod, self.objectreference, 'ProcStartLine', procname, prockind)


class _OptionGroup(_BasicObject):
    classProperties = dict(Count = False, Value = True)

    def Controls(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Controls', index)


class _Property(_BasicObject):
    classProperties = dict(Value = True)


class _QueryDef(_BasicObject):
    classProperties = dict(SQL = True, Type = False)

    @property
    def Query(self): return self.W(_vbUNO, self.objectreference, 'Query')

    def Execute(self, options = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Execute', options)
    def Fields(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Fields', index)
    def OpenRecordset(self, type = -1, option = -1, lockedit = -1):
        return self.W(_vbMethod, self.objectreference, 'OpenRecordset', type, option, lockedit)


class _Recordset(_BasicObject):
    classProperties = dict(AbsolutePosition = True, BOF = False, Bookmark = True, Bookmarkable = False
                        , EditMode = False, EOF = False, Filter = True, RecordCount = False
                        )

    @property
    def RowSet(self): return self.W(_vbUNO, self.objectreference, 'RowSet')

    def AddNew(self):
        return self.W(_vbMethod, self.objectreference, 'AddNew')
    def CancelUpdate(self):
        return self.W(_vbMethod, self.objectreference, 'CancelUpdate')
    def Clone(self):
        return self.W(_vbMethod, self.objectreference, 'Clone')
    def Close(self):
        return self.W(_vbMethod, self.objectreference, 'Close')
    def Delete(self):
        return self._Reset('RecordCount',self.W(_vbMethod, self.objectreference, 'Delete'))
    def Edit(self):
        return self.W(_vbMethod, self.objectreference, 'Edit')
    def Fields(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Fields', index)
    def GetRows(self, numrows):
        return self.W(_vbMethod, self.objectreference, 'GetRows', numrows)
    def Move(self, rows, startbookmark = acConstants.Missing):
        return self._Reset('BOF', self.W(_vbMethod, self.objectreference, 'Move', rows, startbookmark))
    def MoveFirst(self):
        return self._Reset('BOF', self.W(_vbMethod, self.objectreference, 'MoveFirst'))
    def MoveLast(self):
        return self._Reset('BOF', self.W(_vbMethod, self.objectreference, 'MoveLast'))
    def MoveNext(self):
        return self._Reset('BOF', self.W(_vbMethod, self.objectreference, 'MoveNext'))
    def MovePrevious(self):
        return self._Reset('BOF', self.W(_vbMethod, self.objectreference, 'MovePrevious'))
    def OpenRecordset(self, type = -1, option = -1, lockedit = -1):
        return self.W(_vbMethod, self.objectreference, 'OpenRecordset', type, option, lockedit)
    def Update(self):
        return self._Reset('RecordCount',self.W(_vbMethod, self.objectreference, 'Update'))


class _SubForm(_Form):
    classProperties = dict(AllowAdditions = True, AllowDeletions = True, AllowEdits = True, CurrentRecord = True
                        , Filter = True, FilterOn = True, LinkChildFields = False, LinkMasterFields = False
                        , OnApproveCursorMove = True, OnApproveParameter = True, OnApproveReset = True
                        , OnApproveRowChange = True, OnApproveSubmit = True, OnConfirmDelete = True, OnCursorMoved = True
                        , OnErrorOccurred = True, OnLoaded = True, OnReloaded = True, OnReloading = True, OnResetted = True
                        , OnRowChanged = True, OnUnloaded = True, OnUnloading = True, OrderBy = True
                        , OrderByOn = True, Parent = False, Recordset = False, RecordSource = True, Visible = True
                        )

    def SetFocus(self):
        raise AttributeError("type object 'SubForm' has no method 'SetFocus'")


class _TableDef(_BasicObject):
    classProperties = dict()

    @property
    def Table(self): return self.W(_vbUNO, self.objectreference, 'Table')

    def CreateField(self, name, type, size = 0, attributes = 0):
        return self.W(_vbMethod, self.objectreference, 'CreateField', name, type, size, attributes)
    def Fields(self, index = acConstants.Missing):
        return self.W(_vbMethod, self.objectreference, 'Fields', index)
    def OpenRecordset(self, type = -1, option = -1, lockedit = -1):
        return self.W(_vbMethod, self.objectreference, 'OpenRecordset', type, option, lockedit)


class _TempVar(_BasicObject):
    classProperties = dict(Value = True)

"""
Set of directly callable error handling methods
"""
def DebugPrint(*args):
    dargs = ()
    for arg in args:
        if isinstance(arg, _BasicObject):
            arg = ('[' + arg.objecttype + '] ' + arg.name).rstrip()
        dargs = dargs + (arg,)
    return _A2B.invokeMethod('DebugPrint', _WRAPPERMODULE, *dargs)
def TraceConsole(): return _A2B.invokeMethod('TraceConsole', 'Trace')
def TraceError(tracelevel, errorcode, errorprocedure, errorline):
    return _A2B.invokeMethod('TraceError', 'Trace', tracelevel, errorcode, errorprocedure, errorline)
def TraceLevel(newtracelevel = 'ERROR'): return _A2B.invokeMethod('TraceLevel', 'Trace', newtracelevel)
def TraceLog(tracelevel, text, messagebox = True):
    return _A2B.invokeMethod('TraceLog', 'Trace', tracelevel, text, messagebox)

