Esempio n. 1
0
func parseStruct(fd *model.FieldDescriptor, value string, fileD *model.FileDescriptor, node *model.Node) bool {

	p := newStructParser(value)

	// 检查字段有没有重复
	fieldByFD := make(map[*model.FieldDescriptor]bool)

	return p.Run(fd, func(key, value string) bool {

		bnField := fd.Complex.FieldByValueAndMeta(key)
		if bnField == nil {

			log.Errorf("%s, '%s'", i18n.String(i18n.StructParser_FieldNotFound), key)

			return false
		}

		if _, ok := fieldByFD[bnField]; ok {
			log.Errorf("%s, '%s'", i18n.String(i18n.StructParser_DuplicateFieldInCell), key)
			return false
		}

		fieldByFD[bnField] = true

		// 添加类型节点
		fieldNode := node.AddKey(bnField)

		// 在类型节点下添加值节点
		_, ok := ConvertValue(bnField, value, fileD, fieldNode)

		return ok
	})

}
Esempio n. 2
0
func dataProcessor(file *File, fd *model.FieldDescriptor, raw string, node *model.Node) bool {

	// 列表
	if fd.IsRepeated {

		spliter := fd.ListSpliter()

		// 使用多格子实现的repeated
		if spliter == "" {

			if _, ok := filter.ConvertValue(fd, raw, file.GlobalFD, node); !ok {
				goto ConvertError
			}

		} else {
			// 一个格子切割的repeated

			valueList := strings.Split(raw, spliter)

			for _, v := range valueList {

				if _, ok := filter.ConvertValue(fd, v, file.GlobalFD, node); !ok {
					goto ConvertError
				}
			}

		}

	} else {

		// 单值
		if cv, ok := filter.ConvertValue(fd, raw, file.GlobalFD, node); !ok {
			goto ConvertError

		} else {

			// 值重复检查
			if fd.Meta.RepeatCheck && !file.checkValueRepeat(fd, cv) {
				log.Errorf("%s, %s raw: '%s'", i18n.String(i18n.DataSheet_ValueRepeated), fd.String(), cv)
				return false
			}
		}

	}

	return true

ConvertError:

	log.Errorf("%s, %s raw: '%s'", i18n.String(i18n.DataSheet_ValueConvertError), fd.String(), raw)

	return false
}
Esempio n. 3
0
// bool表示是否有错
func findlocalFieldType(localFD *model.FileDescriptor, rawFieldType string) (model.FieldType, *model.Descriptor, bool) {

	// 解析普通类型
	if ft, ok := model.ParseFieldType(rawFieldType); ok {

		return ft, nil, true

	}

	// 解析内建类型
	if desc, ok := localFD.DescriptorByName[rawFieldType]; ok {

		// 只有枚举( 结构体不允许再次嵌套, 增加理解复杂度 )
		if desc.Kind != model.DescriptorKind_Enum {
			log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_StructFieldCanNotBeStruct), rawFieldType)

			return model.FieldType_None, nil, false
		}

		return model.FieldType_Enum, desc, true

	}

	// 没找到类型, 待二次pass
	return model.FieldType_None, nil, true

}
Esempio n. 4
0
func (self *typeModelRoot) ParsePragma(localFD *model.FileDescriptor) bool {

	if err := proto.UnmarshalText(self.pragma, &localFD.Pragma); err != nil {
		log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_PragmaParseFailed), self.pragma)
		return false
	}

	if localFD.Pragma.TableName == "" {
		log.Errorf("%s", i18n.String(i18n.TypeSheet_TableNameIsEmpty))
		return false
	}

	if localFD.Pragma.Package == "" {
		log.Errorf("%s", i18n.String(i18n.TypeSheet_PackageIsEmpty))
		return false
	}

	return true
}
Esempio n. 5
0
func (self *File) ExportLocalType() bool {

	var sheetCount int
	// 解析类型表
	for _, rawSheet := range self.coreFile.Sheets {

		if isTypeSheet(rawSheet.Name) {
			if sheetCount > 0 {
				log.Errorf("%s", i18n.String(i18n.File_TypeSheetKeepSingleton))
				return false
			}

			typeSheet := newTypeSheet(NewSheet(self, rawSheet))

			// 从cell添加类型
			if !typeSheet.Parse(self.LocalFD, self.GlobalFD) {
				return false
			}

			sheetCount++

		}
	}

	// 解析表头
	for _, rawSheet := range self.coreFile.Sheets {

		// 是数据表
		if !isTypeSheet(rawSheet.Name) {
			dSheet := newDataSheet(NewSheet(self, rawSheet))

			if !dSheet.Valid() {
				continue
			}

			log.Infof("            %s", rawSheet.Name)

			dataHeader := newDataHeadSheet()

			// 检查引导头
			if !dataHeader.ParseProtoField(len(self.dataSheets), dSheet.Sheet, self.LocalFD, self.GlobalFD) {
				return false
			}

			if self.Header == nil {
				self.Header = dataHeader
			}

			self.dataSheets = append(self.dataSheets, dSheet)

		}
	}

	return true
}
Esempio n. 6
0
func (self *Globals) AddTypes(localFD *model.FileDescriptor) bool {

	// 将行定义结构也添加到文件中
	for _, d := range localFD.Descriptors {
		if !self.FileDescriptor.Add(d) {
			log.Errorf("%s, %s", i18n.String(i18n.Globals_DuplicateTypeName), d.Name)
			return false
		}
	}

	return true
}
Esempio n. 7
0
func (self *TypeSheet) ParseTable(root *typeModelRoot) bool {

	var readingLine bool = true

	root.pragma = self.GetCellData(TypeSheetRow_Pragma, 0)

	for row := TypeSheetRow_DataBegin; readingLine; row++ {

		tm := newTypeModel()
		tm.row = row

		for col := 0; ; col++ {

			// 头
			typeDeclare := self.GetCellData(TypeSheetRow_FieldDesc, col)

			// 头已经读完
			if typeDeclare == "" {
				break
			}

			if _, ok := typeHeader[typeDeclare]; !ok {
				self.Row = TypeSheetRow_FieldDesc
				self.Column = col
				log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_UnexpectedTypeHeader), typeDeclare)
				return false
			}

			// 值
			typeValue := self.GetCellData(row, col)

			// 类型空表示停止解析
			if typeDeclare == "ObjectType" && typeValue == "" {
				readingLine = false
				break
			}

			tm.colData[typeDeclare] = &typeCell{
				value: typeValue,
				col:   col,
			}

		}

		if len(tm.colData) > 0 {
			root.models = append(root.models, tm)
		}

	}

	return true
}
Esempio n. 8
0
func (self *structParser) Run(fd *model.FieldDescriptor, callback func(string, string) bool) (ok bool) {

	defer golexer.ErrorCatcher(func(err error) {

		log.Errorf("%s, '%s' '%v'", i18n.String(i18n.StructParser_LexerError), fd.Name, err.Error())
	})

	self.NextToken()

	for self.TokenID() != Token_EOF {

		if self.TokenID() != Token_Identifier {
			log.Errorf("%s, '%s'", i18n.String(i18n.StructParser_ExpectField), fd.Name)
			return false
		}

		key := self.TokenValue()

		self.NextToken()

		if self.TokenID() != Token_Comma {
			log.Errorf("%s, '%s'", i18n.String(i18n.StructParser_UnexpectedSpliter), key)
			return false
		}

		self.NextToken()

		value := self.TokenValue()

		if !callback(key, value) {
			return false
		}

		self.NextToken()

	}

	return true
}
Esempio n. 9
0
func (self *Globals) Print() bool {

	log.Infof("==========%s==========", i18n.String(i18n.Globals_OutputCombineData))

	for _, p := range self.Printers {

		if !p.Start(self) {
			return false
		}
	}

	return true

}
Esempio n. 10
0
// 检查protobuf兼容性
func (self *TypeSheet) checkProtobufCompatibility(fileD *model.FileDescriptor) bool {

	for _, bt := range fileD.Descriptors {
		if bt.Kind == model.DescriptorKind_Enum {

			// proto3 需要枚举有0值
			if _, ok := bt.FieldByNumber[0]; !ok {
				log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_FirstEnumValueShouldBeZero), bt.Name)
				return false
			}
		}
	}

	return true
}
Esempio n. 11
0
func (self *Globals) PreExport() bool {

	// 当合并结构名没有指定时, 对于代码相关的输出器, 要报错
	if self.CombineStructName == "" && self.hasAnyPrinter(".proto", ".cs") {
		log.Errorf("%s", i18n.String(i18n.Globals_CombineNameLost))
		return false
	}

	// 添加XXConfig全局结构
	self.CombineStruct.Name = self.CombineStructName
	self.CombineStruct.Kind = model.DescriptorKind_Struct
	self.CombineStruct.Usage = model.DescriptorUsage_CombineStruct
	self.FileDescriptor.Name = self.CombineStructName
	self.FileDescriptor.Add(self.CombineStruct)
	return true
}
Esempio n. 12
0
func parseFieldValue(rawFieldValue string) (model.DescriptorKind, int32, bool) {

	// 非空值是枚举
	if rawFieldValue != "" {

		v, err := strconv.Atoi(rawFieldValue)
		// 解析枚举值
		if err != nil {

			log.Errorf("%s, %s", i18n.String(i18n.TypeSheet_EnumValueParseFailed), err.Error())
			return model.DescriptorKind_None, 0, false
		}

		return model.DescriptorKind_Enum, int32(v), true
	}

	return model.DescriptorKind_Struct, 0, true
}
Esempio n. 13
0
func (self *TypeSheet) Parse(localFD *model.FileDescriptor, globalFD *model.FileDescriptor) bool {

	var root typeModelRoot

	if !self.ParseTable(&root) {
		goto ErrorStop
	}

	if !root.ParsePragma(localFD) {
		self.Row = TypeSheetRow_Pragma
		self.Column = 0
		log.Errorf("%s", i18n.String(i18n.TypeSheet_PackageIsEmpty))
		goto ErrorStop
	}

	if !root.ParseData(localFD, globalFD) {
		self.Row = root.Row
		self.Column = root.Col
		goto ErrorStop
	}

	if !root.SolveUnknownModel(localFD, globalFD) {
		self.Row = root.Row
		self.Column = root.Col
		goto ErrorStop
	}

	return self.checkProtobufCompatibility(localFD)

ErrorStop:

	r, c := self.GetRC()

	log.Errorf("%s|%s(%s)", self.file.FileName, self.Name, util.ConvR1C1toA1(r, c))
	return false
}
Esempio n. 14
0
func (self *typeModelRoot) SolveUnknownModel(localFD *model.FileDescriptor, globalFD *model.FileDescriptor) bool {

	for _, m := range self.unknownModel {

		self.Row = m.row
		self.Col = self.fieldTypeCol

		fieldType, complexType, ok := findFieldType(localFD, globalFD, m.rawFieldType)
		if !ok {
			return false
		}

		// 实在是找不到了, 没辙了
		if fieldType == model.FieldType_None {
			log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_FieldTypeNotFound), m.rawFieldType)
			return false
		}

		m.fd.Type = fieldType
		m.fd.Complex = complexType
	}

	return true
}
Esempio n. 15
0
func (self *DataHeader) makeRowDescriptor(fileD *model.FileDescriptor, rootField []*model.FieldDescriptor) bool {

	rowType := model.NewDescriptor()
	rowType.Usage = model.DescriptorUsage_RowType
	rowType.Name = fmt.Sprintf("%sDefine", fileD.Pragma.TableName)
	rowType.Kind = model.DescriptorKind_Struct

	// 类型已经存在, 说明是自己定义的 XXDefine, 不允许
	if _, ok := fileD.DescriptorByName[rowType.Name]; ok {
		log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_UseReservedTypeName), rowType.Name)
		return false
	}

	fileD.Add(rowType)

	// 将表格中的列添加到类型中, 方便导出
	for _, field := range rootField {

		rowType.Add(field)
	}

	return true

}
Esempio n. 16
0
// 从单元格原始数据到最终输出的数值, 检查并转换, 处理默认值及根据meta转换情况
func ConvertValue(fd *model.FieldDescriptor, value string, fileD *model.FileDescriptor, node *model.Node) (ret string, ok bool) {

	// 空格, 且有默认值时, 使用默认值
	if value == "" {
		value = fd.DefaultValue()
	}

	switch fd.Type {
	case model.FieldType_Int32:
		_, err := strconv.ParseInt(value, 10, 32)
		if err != nil {
			log.Debugln(err)
			return "", false
		}

		ret = value
		node.AddValue(ret)
	case model.FieldType_Int64:
		_, err := strconv.ParseInt(value, 10, 64)

		if err != nil {
			log.Debugln(err)
			return "", false
		}

		ret = value
		node.AddValue(ret)
	case model.FieldType_UInt32:
		_, err := strconv.ParseUint(value, 10, 32)
		if err != nil {
			log.Debugln(err)
			return "", false
		}

		ret = value
		node.AddValue(ret)
	case model.FieldType_UInt64:
		_, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			log.Debugln(err)
			return "", false
		}

		ret = value
		node.AddValue(ret)
	case model.FieldType_Float:
		_, err := strconv.ParseFloat(value, 32)
		if err != nil {
			log.Debugln(err)
			return "", false
		}

		ret = value
		node.AddValue(ret)
	case model.FieldType_Bool:

		for {
			if value == "是" {
				ret = "true"
				break
			} else if value == "否" {
				ret = "false"
				break
			}

			v, err := strconv.ParseBool(value)

			if err != nil {
				log.Debugln(err)
				return "", false
			}

			if v {
				ret = "true"
			} else {
				ret = "false"
			}

			break
		}

		node.AddValue(ret)

	case model.FieldType_String:
		ret = value
		node.AddValue(ret)
	case model.FieldType_Enum:
		if fd.Complex == nil {
			log.Errorf("%s, '%s'", i18n.String(i18n.ConvertValue_EnumTypeNil), fd.Name)
			return "", false
		}

		evd := fd.Complex.FieldByValueAndMeta(value)
		if evd == nil {
			log.Errorf("%s, '%s' '%s'", i18n.String(i18n.ConvertValue_EnumValueNotFound), value, fd.Complex.Name)
			return "", false
		}

		// 使用枚举的英文字段名输出
		ret = evd.Name
		node.AddValue(ret).EnumValue = evd.EnumValue

	case model.FieldType_Struct:

		if fd.Complex == nil {
			log.Errorf("%s, '%s'", i18n.String(i18n.ConvertValue_StructTypeNil), fd.Name)
			return "", false
		}

		if !parseStruct(fd, value, fileD, node) {
			return "", false
		}

	default:
		log.Errorf("%s, '%s' '%s'", i18n.String(i18n.ConvertValue_UnknownFieldType), fd.Name, fd.Name)
		return "", false
	}

	ok = true

	return
}
Esempio n. 17
0
// 检查字段行的长度
func (self *DataHeader) ParseProtoField(index int, sheet *Sheet, localFD *model.FileDescriptor, globalFD *model.FileDescriptor) bool {

	var def *model.FieldDescriptor

	// 遍历列
	for sheet.Column = 0; ; sheet.Column++ {

		def = new(model.FieldDescriptor)

		// ====================解析字段====================
		def.Name = sheet.GetCellData(DataSheetRow_FieldName, sheet.Column)
		if def.Name == "" {
			break
		}

		// #开头表示注释, 跳过
		if strings.Index(def.Name, "#") != 0 {

			// ====================解析类型====================

			testFileD := localFD

			rawFieldType := sheet.GetCellData(DataSheetRow_FieldType, sheet.Column)

			for {
				if def.ParseType(testFileD, rawFieldType) {
					break
				}

				if testFileD == localFD {
					testFileD = globalFD
					continue
				}

				break
			}

			// 依然找不到, 报错
			if def.Type == model.FieldType_None {
				sheet.Row = DataSheetRow_FieldType
				log.Errorf("%s, '%s' (%s) raw: %s", i18n.String(i18n.DataHeader_TypeNotFound), def.Name, model.FieldTypeToString(def.Type), rawFieldType)
				goto ErrorStop
			}

			// ====================解析特性====================
			metaString := sheet.GetCellData(DataSheetRow_FieldMeta, sheet.Column)

			if err := proto.UnmarshalText(metaString, &def.Meta); err != nil {
				sheet.Row = DataSheetRow_FieldMeta
				log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_MetaParseFailed), err)
				goto ErrorStop
			}

			def.Comment = sheet.GetCellData(DataSheetRow_Comment, sheet.Column)

			// 根据字段名查找, 处理repeated字段case
			exist, ok := self.HeaderByName[def.Name]

			if ok {

				// 多个同名字段只允许repeated方式的字段
				if !exist.IsRepeated {
					sheet.Row = DataSheetRow_FieldName
					log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_DuplicateFieldName), def.Name)
					goto ErrorStop
				}

				// 多个repeated描述类型不一致
				if exist.Type != def.Type {
					sheet.Row = DataSheetRow_FieldType

					log.Errorf("%s '%s' '%s' '%s'", i18n.String(i18n.DataHeader_RepeatedFieldTypeNotSameInMultiColumn),
						def.Name,
						model.FieldTypeToString(exist.Type),
						model.FieldTypeToString(def.Type))

					goto ErrorStop
				}

				// 多个repeated描述内建类型不一致
				if exist.Complex != def.Complex {
					sheet.Row = DataSheetRow_FieldType

					log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_RepeatedFieldTypeNotSameInMultiColumn),
						def.Name)

					goto ErrorStop
				}

				// 多个repeated描述的meta不一致
				if proto.CompactTextString(&exist.Meta) != proto.CompactTextString(&def.Meta) {
					sheet.Row = DataSheetRow_FieldMeta

					log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_RepeatedFieldMetaNotSameInMultiColumn),
						def.Name)

					goto ErrorStop
				}

				def = exist

			} else {
				self.HeaderByName[def.Name] = def
				self.headerFields = append(self.headerFields, def)
			}
		}

		// 有注释字段, 但是依然要放到这里来进行索引
		self.rawHeaderFields = append(self.rawHeaderFields, def)
	}

	if len(self.rawHeaderFields) == 0 {
		return false
	}

	if index == 0 {
		// 添加第一个数据表的定义
		if !self.makeRowDescriptor(localFD, self.headerFields) {
			goto ErrorStop
		}
	}

	return true

ErrorStop:

	r, c := sheet.GetRC()

	log.Errorf("%s|%s(%s)", sheet.file.FileName, sheet.Name, util.ConvR1C1toA1(r, c))
	return false
}
Esempio n. 18
0
func Run(g *printer.Globals) bool {

	if !g.PreExport() {
		return false
	}

	fileObjList := make([]interface{}, 0)

	log.Infof("==========%s==========", i18n.String(i18n.Run_CollectTypeInfo))

	// 合并类型
	for _, in := range g.InputFileList {

		inputFile := in.(string)

		file := NewFile(inputFile)

		if file == nil {
			return false
		}

		log.Infoln(filepath.Base(inputFile))

		file.GlobalFD = g.FileDescriptor

		// 电子表格数据导出到Table对象
		if !file.ExportLocalType() {
			return false
		}

		// 整合类型信息和数据
		if !g.AddTypes(file.LocalFD) {
			return false
		}

		// 没有
		if file.Header != nil {
			fileObjList = append(fileObjList, file)
		}

	}

	log.Infof("==========%s==========", i18n.String(i18n.Run_ExportSheetData))
	// 导出表格
	if !util.ParallelWorker(fileObjList, false, func(in interface{}) bool {

		file := in.(*File)

		log.Infoln(filepath.Base(file.FileName))

		// 电子表格数据导出到Table对象
		tab := file.ExportData()
		if tab == nil {
			return false
		}

		// 整合类型信息和数据
		return g.AddContent(tab)

	}) {
		return false
	}

	// 根据各种导出类型, 调用各导出器导出
	return g.Print()
}
Esempio n. 19
0
// 合并每个表带的类型
func (self *Globals) AddContent(tab *model.Table) bool {

	localFD := tab.LocalFD

	self.guard.Lock()

	defer self.guard.Unlock()

	// 有表格里描述的包名不一致, 无法合成最终的文件
	if self.Pragma.Package == "" {
		self.Pragma.Package = localFD.Pragma.Package
	} else if self.Pragma.Package != localFD.Pragma.Package {

		log.Errorf("%s, '%s' '%s'", i18n.String(i18n.Globals_PackageNameDiff), self.Pragma.Package, localFD.Pragma.Package)
		return false
	}

	if _, ok := self.tableByName[localFD.Name]; ok {

		log.Errorf("%s, '%s'", i18n.String(i18n.Globals_TableNameDuplicated), localFD.Name)
		return false
	}

	// 表的全局类型信息与合并信息一致
	tab.GlobalFD = self.FileDescriptor

	self.tableByName[localFD.Name] = tab
	self.Tables = append(self.Tables, tab)

	// 每个表在结构体里的字段
	var rowFD model.FieldDescriptor
	rowFD.Name = localFD.Name
	rowFD.Type = model.FieldType_Struct
	rowFD.Complex = localFD.RowDescriptor()
	rowFD.IsRepeated = true
	rowFD.Order = int32(len(self.CombineStruct.Fields) + 1)
	rowFD.Comment = localFD.Name
	self.CombineStruct.Add(&rowFD)

	if localFD.RowDescriptor() == nil {
		panic("row field null:" + localFD.Name)
	}

	for _, d := range localFD.Descriptors {

		// 非行类型的, 全部忽略
		if d.Usage != model.DescriptorUsage_RowType {
			continue
		}

		for _, indexFD := range d.Indexes {

			key := TableIndex{
				Row:   &rowFD,
				Index: indexFD,
			}

			self.GlobalIndexes = append(self.GlobalIndexes, key)

		}

	}

	return true
}
Esempio n. 20
0
func (self *typeModelRoot) ParseData(localFD *model.FileDescriptor, globalFD *model.FileDescriptor) bool {

	var td *model.Descriptor

	reservedRowFieldTypeName := localFD.Pragma.TableName + "Define"

	// 每一行
	for _, m := range self.models {

		self.Row = m.row

		var rawTypeName string

		rawTypeName, self.Col = m.getValue("ObjectType")

		if rawTypeName == reservedRowFieldTypeName {
			log.Errorf("%s '%s'", i18n.String(i18n.DataHeader_UseReservedTypeName), rawTypeName)
			return false
		}

		existType, ok := localFD.DescriptorByName[rawTypeName]

		if ok {

			td = existType

		} else {

			td = model.NewDescriptor()
			td.Name = rawTypeName
			localFD.Add(td)
		}

		// 字段名
		m.fd.Name, self.Col = m.getValue("FieldName")

		// 解析类型
		m.rawFieldType, self.Col = m.getValue("FieldType")
		self.fieldTypeCol = self.Col

		fieldType, complexType, ok := findFieldType(localFD, globalFD, m.rawFieldType)
		if !ok {
			return false
		}

		if fieldType == model.FieldType_None {
			self.unknownModel = append(self.unknownModel, m)
		}

		m.fd.Type = fieldType
		m.fd.Complex = complexType

		var rawFieldValue string
		// 解析值
		rawFieldValue, self.Col = m.getValue("Value")

		kind, enumvalue, ok := parseFieldValue(rawFieldValue)
		if !ok {
			return false
		}

		if td.Kind == model.DescriptorKind_None {
			td.Kind = kind
			// 一些字段有填值, 一些没填值
		} else if td.Kind != kind {
			log.Errorf("%s", i18n.String(i18n.TypeSheet_DescriptorKindNotSame))
			return false
		}

		if td.Kind == model.DescriptorKind_Enum {
			if _, ok := td.FieldByNumber[enumvalue]; ok {
				log.Errorf("%s %d", i18n.String(i18n.TypeSheet_DuplicatedEnumValue), enumvalue)
				return false
			}
		}

		m.fd.EnumValue = enumvalue

		m.fd.Comment, self.Col = m.getValue("Comment")

		var rawMeta string
		rawMeta, self.Col = m.getValue("Meta")

		if err := proto.UnmarshalText(rawMeta, &m.fd.Meta); err != nil {
			log.Errorf("%s, '%s'", i18n.String(i18n.TypeSheet_FieldMetaParseFailed), err.Error())
			return false
		}

		td.Add(&m.fd)

	}

	return true
}