func (obj *Object) HasDims() bool {
	return obj.ListObject() != nil || (obj.HyperCube() != nil && len(obj.HyperCube().DimensionInfo) > 0)
}
HasDims returns true if object has at least one dimension
package enigmahandlers
import (
	"fmt"
	"sync"
	"github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/qlik-oss/enigma-go/v2"
	"github.com/qlik-oss/gopherciser/helpers"
)
type (
	ObjectType int
	HyperCube struct {
		*enigma.HyperCube
		Binned bool
	}
	objectData struct {
		properties    *enigma.GenericObjectProperties
		childlist     *enigma.ChildList
		children      *[]ObjChild
		listobject    *enigma.ListObject
		hypercube     *HyperCube
		treeDataPages []*enigma.NxTreeNode
	}
	Object struct {
		Handle int
		ID string
		Type ObjectType
		EnigmaObject interface{}
		closefuncs   []func() error
		data         *objectData
		lockData     sync.Mutex
	}
	ObjChild struct {
		ExternalReference struct {
			MasterID string `json:"masterId"`
			App      string `json:"app"`
			ViewID   string `json:"viewId"`
		} `json:"externalReference"`
		Label string `json:"label"`
		RefID string `json:"refId"`
		Type  string `json:"type"`
	}
	ObjectNotFound int
	ObjectIDNotFound string
)
const (
	ObjTypeApp ObjectType = iota
	ObjTypeSheet
	ObjTypeGenericObject
)
func (obj *Object) SetProperties(properties *enigma.GenericObjectProperties) {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		obj.data = &objectData{
			properties: properties,
		}
		return
	}
	obj.data.properties = properties
}
func (obj *Object) Properties() *enigma.GenericObjectProperties {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		return nil
	}
	return obj.data.properties
}
func (obj *Object) SetChildList(children *enigma.ChildList) {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		obj.data = &objectData{
			childlist: children,
		}
		return
	}
	obj.data.childlist = children
}
func (obj *Object) SetChildren(children *[]ObjChild) {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		obj.data = &objectData{
			children: children,
		}
		return
	}
	obj.data.children = children
}
func (obj *Object) ChildList() *enigma.ChildList {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		return nil
	}
	return obj.data.childlist
}
func (obj *Object) SetListObject(listobject *enigma.ListObject) {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		obj.data = &objectData{
			listobject: listobject,
		}
		return
	}
	obj.data.listobject = listobject
}
func (obj *Object) ListObject() *enigma.ListObject {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		return nil
	}
	return obj.data.listobject
}
func (obj *Object) SetHyperCube(hypercube *enigma.HyperCube) {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		obj.data = &objectData{
			hypercube: &HyperCube{
				hypercube,
				false,
			},
		}
	}
	obj.data.hypercube = &HyperCube{hypercube, false}
}
func (obj *Object) ExternalReferenceApps() []string {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.children == nil {
		return nil
	}
	apps := make([]string, 0, len(*obj.data.children))
	for _, item := range *obj.data.children {
		apps = append(apps, item.ExternalReference.App)
	}
	return apps
}
func (obj *Object) HyperCube() *HyperCube {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		return nil
	}
	return obj.data.hypercube
}
func (obj *Object) SetListObjectDataPages(datapages []*enigma.NxDataPage) error {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.listobject == nil {
		return errors.Errorf("object<%s> has no listobject", obj.ID)
	}
	obj.data.listobject.DataPages = datapages
	return nil
}
func (obj *Object) ListObjectDataPages() []*enigma.NxDataPage {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.listobject == nil {
		return nil
	}
	return obj.data.listobject.DataPages
}
func (obj *Object) SetHyperCubeDataPages(datapages []*enigma.NxDataPage, binned bool) error {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return errors.Errorf("object<%s> has no hypercube", obj.ID)
	}
	obj.data.hypercube.DataPages = datapages
	obj.data.hypercube.Binned = binned
	return nil
}
func (obj *Object) SetStackHyperCubePages(datapages []*enigma.NxStackPage) error {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return errors.Errorf("object<%s> has no hypercube", obj.ID)
	}
	obj.data.hypercube.StackedDataPages = datapages
	return nil
}
func (obj *Object) SetPivotHyperCubePages(datapages []*enigma.NxPivotPage) error {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return errors.Errorf("object<%s> has no hypercube", obj.ID)
	}
	obj.data.hypercube.PivotDataPages = datapages
	return nil
}
func (obj *Object) SetTreeDataPages(treeDataPages []*enigma.NxTreeNode) error {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil {
		return errors.Errorf("object<%s> has no data structure", obj.ID)
	}
	obj.data.treeDataPages = treeDataPages
	return nil
}
func (obj *Object) HyperCubeDataPages() []*enigma.NxDataPage {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return nil
	}
	return obj.data.hypercube.DataPages
}
func (obj *Object) HyperCubeStackPages() []*enigma.NxStackPage {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return nil
	}
	return obj.data.hypercube.StackedDataPages
}
func (obj *Object) HyperPivotPages() []*enigma.NxPivotPage {
	obj.lockData.Lock()
	defer obj.lockData.Unlock()
	if obj.data == nil || obj.data.hypercube == nil {
		return nil
	}
	return obj.data.hypercube.PivotDataPages
}
func (obj *Object) Close() error {
	if obj == nil {
		return nil
	}
	if obj.closefuncs == nil {
		return nil
	}
	var mErr *multierror.Error
	for _, f := range obj.closefuncs {
		err := f()
		if err != nil {
			mErr = multierror.Append(mErr, err)
		}
	}
	return errors.WithStack(helpers.FlattenMultiError(mErr))
}
func (obj *Object) RegisterCloseFunc(f func() error) {
	if obj.closefuncs == nil {
		obj.closefuncs = make([]func() error, 1)
		obj.closefuncs[0] = f
		return
	}
	obj.closefuncs = append(obj.closefuncs, f)
}