diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/hub.pc.h b/cmd/protoc-gen-cpp-tableau-loader/embed/hub.pc.h index de86bf43..6253b7e8 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/hub.pc.h +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/hub.pc.h @@ -3,10 +3,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go index 9f69f728..112826bb 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/index.go @@ -141,6 +141,30 @@ func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, descriptor *inde generateOneCppMulticolumnIndex(g, depth, descriptor, parentDataName, keys) } } + if depth == 1 && len(descriptor.KeyFields) != 0 { + g.P(strings.Repeat(" ", depth), "for (auto&& item : ", indexContainerName, ") {") + g.P(strings.Repeat(" ", depth+1), "std::sort(item.second.begin(), item.second.end(),") + g.P(strings.Repeat(" ", depth+6), "[](const ", helper.ParseCppClassType(descriptor.MD), "* a, const ", helper.ParseCppClassType(descriptor.MD), "* b) {") + for i, field := range descriptor.KeyFields { + fieldName := "" + for i, leveledFd := range field.LeveledFDList { + accessOperator := "." + if i == 0 { + accessOperator = "->" + } + fieldName += accessOperator + helper.ParseIndexFieldName(leveledFd) + "()" + } + if i == len(descriptor.KeyFields)-1 { + g.P(strings.Repeat(" ", depth+7), "return a", fieldName, " < b", fieldName, ";") + } else { + g.P(strings.Repeat(" ", depth+7), "if (a", fieldName, " != b", fieldName, ") {") + g.P(strings.Repeat(" ", depth+8), "return a", fieldName, " < b", fieldName, ";") + g.P(strings.Repeat(" ", depth+7), "}") + } + } + g.P(strings.Repeat(" ", depth+6), "});") + g.P(strings.Repeat(" ", depth), "}") + } } func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, descriptor *index.IndexDescriptor, parentDataName string, keys []string) []string { diff --git a/cmd/protoc-gen-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go index 18ea75db..b5e32833 100644 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ b/cmd/protoc-gen-go-tableau-loader/index.go @@ -98,6 +98,25 @@ func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth in generateOneMulticolumnIndex(gen, g, depth, descriptor, parentDataName, keys) } } + if depth == 1 && len(descriptor.KeyFields) != 0 { + g.P("for _, item := range x.", indexContainerName, " {") + g.P(sortPackage.Ident("Slice"), "(item, func(i, j int) bool {") + for i, field := range descriptor.KeyFields { + fieldName := "" + for _, leveledFd := range field.LeveledFDList { + fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" + } + if i == len(descriptor.KeyFields)-1 { + g.P("return item[i]", fieldName, " < item[j]", fieldName) + } else { + g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") + g.P("return item[i]", fieldName, " < item[j]", fieldName) + g.P("}") + } + } + g.P("})") + g.P("}") + } } func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile, diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index 7f855e99..442db51a 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -22,6 +22,7 @@ const ( treeMapPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/treemap") pairPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/pair") timePackage = protogen.GoImportPath("time") + sortPackage = protogen.GoImportPath("sort") protoPackage = protogen.GoImportPath("google.golang.org/protobuf/proto") ) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index bad4947b..c1c64045 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -13,9 +13,11 @@ import ( type IndexDescriptor struct { *Index - MD protoreflect.MessageDescriptor // deepest level message descriptor - Name string // index name, e.g.: name of (ID, Name)@ItemInfo is "ItemInfo" - Fields []*LevelField // index fields in the same struct (protobuf message), refer to the deepest level message's Fields. + MD protoreflect.MessageDescriptor // deepest level message descriptor + Name string // index name, e.g.: name of (ID, Name)@ItemInfo is "ItemInfo" + + Fields []*LevelField // index fields in the same struct (protobuf message), refer to the deepest level message's Fields. + KeyFields []*LevelField // key fields in the same struct (protobuf message), refer to the deepest level message's Fields. LevelMessage *LevelMessage // message hierarchy to the deepest level message which contains all index fields. } @@ -59,10 +61,14 @@ type LevelMessage struct { // Deepest level message fields corresponding to index fields // NOTE: Fields is valid only when this level is the deepest level. Fields []*LevelField + + // Deepest level message fields corresponding to key fields + // NOTE: Fields is valid only when this level is the deepest level. + KeyFields []*LevelField } -// ParseRecursively parses multi-column index related info. -func ParseRecursively(gen *protogen.Plugin, cols []string, prefix string, md protoreflect.MessageDescriptor) *LevelMessage { +// parseRecursively parses multi-column index related info. +func parseRecursively(gen *protogen.Plugin, cols, keys []string, prefix string, md protoreflect.MessageDescriptor) *LevelMessage { levelInfo := &LevelMessage{} for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) @@ -71,27 +77,28 @@ func ParseRecursively(gen *protogen.Plugin, cols []string, prefix string, md pro fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) fieldOptName := fdOpts.GetName() if fd.IsMap() && fd.MapValue().Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = ParseRecursively(gen, cols, prefix+fieldOptName, fd.MapValue().Message()) + levelInfo.NextLevel = parseRecursively(gen, cols, keys, prefix+fieldOptName, fd.MapValue().Message()) if levelInfo.NextLevel != nil { levelInfo.FD = fd return levelInfo } } else if fd.IsList() && fd.Kind() == protoreflect.MessageKind { - levelInfo.NextLevel = ParseRecursively(gen, cols, prefix+fieldOptName, fd.Message()) + levelInfo.NextLevel = parseRecursively(gen, cols, keys, prefix+fieldOptName, fd.Message()) if levelInfo.NextLevel != nil { levelInfo.FD = fd return levelInfo } } } - levelInfo.Fields = ParseInSameLevel(gen, cols, prefix, md, nil) + levelInfo.Fields = parseInSameLevel(gen, cols, prefix, md, nil) + levelInfo.KeyFields = parseInSameLevel(gen, keys, prefix, md, nil) if len(levelInfo.Fields) != 0 { return levelInfo } return nil } -func ParseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md protoreflect.MessageDescriptor, leveledFDList []protoreflect.FieldDescriptor) []*LevelField { +func parseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md protoreflect.MessageDescriptor, leveledFDList []protoreflect.FieldDescriptor) []*LevelField { levelFields := []*LevelField{} for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) @@ -110,7 +117,7 @@ func ParseInSameLevel(gen *protogen.Plugin, cols []string, prefix string, md pro break } else if fd.Kind() == protoreflect.MessageKind && strings.HasPrefix(columnName, prefix+fieldOptName) { levelFields = append(levelFields, - ParseInSameLevel( + parseInSameLevel( gen, cols, prefix+fieldOptName, fd.Message(), append(leveledFDList, fd), )..., @@ -128,7 +135,7 @@ func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescripto if len(index.Cols) == 0 { continue } - levelInfo := ParseRecursively(gen, index.Cols, "", md) + levelInfo := parseRecursively(gen, index.Cols, index.Keys, "", md) if levelInfo == nil { continue } @@ -146,6 +153,7 @@ func ParseIndexDescriptor(gen *protogen.Plugin, md protoreflect.MessageDescripto deepestLevelMessage = deepestLevelMessage.NextLevel } descriptor.Fields = deepestLevelMessage.Fields + descriptor.KeyFields = deepestLevelMessage.KeyFields descriptor.Name = index.Name if descriptor.Name == "" { // use index field's parent message name if not set. diff --git a/internal/index/index.go b/internal/index/index.go index f140305e..350d10c3 100644 --- a/internal/index/index.go +++ b/internal/index/index.go @@ -1,6 +1,7 @@ package index import ( + "regexp" "strings" "github.com/tableauio/tableau/proto/tableaupb" @@ -9,21 +10,27 @@ import ( "google.golang.org/protobuf/types/descriptorpb" ) -// Single-column index: -// - ID -// - ID@Item -// -// Multi-column index (composite index): -// - (ID, Name) -// - (ID, Name)@Item +var indexRegexp *regexp.Regexp -const colAndNameSep = "@" -const multiColSep = "," -const multiColGroupCutset = "()" +func init() { + // Single-column index: + // - ID + // - ID@Item + // - ID@Item + // - ID@Item + // + // Multi-column index (composite index): + // - (ID, Name) + // - (ID, Name)@Item + // - (ID, Name)@Item + // - (ID, Name)@Item + indexRegexp = regexp.MustCompile(`^(?P\([^)]+\)|[^<@]+)?(<(?P[^>]+)>)?(@(?P.+))?$`) +} type Index struct { Cols []string // column names in CamelCase (single-column or multi-column) Name string // index name in CamelCase + Keys []string // key names in CamelCase (single-column or multi-column) } func (index *Index) String() string { @@ -34,10 +41,12 @@ func (index *Index) String() string { syntax += "," } } - if len(index.Cols) > 1 { syntax = "(" + syntax + ")" } + if len(index.Keys) != 0 { + syntax += "<" + strings.Join(index.Keys, ",") + ">" + } if index.Name != "" { syntax += "@" + index.Name } @@ -51,33 +60,50 @@ func parseWSOptionIndex(md protoreflect.MessageDescriptor) []*Index { return parseIndexFrom(wsOpts.Index) } -func parseColsFrom(multiColGroup string) []string { - trimmedStr := strings.Trim(multiColGroup, multiColGroupCutset) - cols := strings.Split(trimmedStr, multiColSep) - for i, col := range cols { - cols[i] = strings.TrimSpace(col) - } - return cols -} - func parseIndex(indexStr string) *Index { - var cols []string - var name string - splits := strings.SplitN(indexStr, colAndNameSep, 2) - switch len(splits) { - case 1: - cols = parseColsFrom(splits[0]) - case 2: - cols = parseColsFrom(splits[0]) - name = splits[1] - default: + index := &Index{} + matches := indexRegexp.FindStringSubmatch(indexStr) + // Extract columns + if cols := matches[indexRegexp.SubexpIndex("cols")]; cols != "" { + if strings.HasPrefix(cols, "(") && strings.HasSuffix(cols, ")") { + // Multi-column index + cols = cols[1 : len(cols)-1] + splitCols := strings.Split(cols, ",") + if len(splitCols) <= 1 { + return nil + } + for _, col := range splitCols { + col = strings.TrimSpace(col) + if col != "" { + index.Cols = append(index.Cols, col) + } + } + } else { + // Single-column index + if len(strings.Split(cols, ",")) > 1 { + return nil + } + col := strings.TrimSpace(cols) + if col != "" { + index.Cols = append(index.Cols, col) + } + } + } + if len(index.Cols) == 0 { return nil } - - return &Index{ - Cols: cols, - Name: name, + // Extract keys + if keys := matches[indexRegexp.SubexpIndex("keys")]; keys != "" { + index.Keys = strings.Split(keys, ",") + for i, key := range index.Keys { + index.Keys[i] = strings.TrimSpace(key) + } + } + // Extract name + if name := matches[indexRegexp.SubexpIndex("name")]; name != "" { + index.Name = name } + return index } func parseIndexFrom(indexList []string) []*Index { diff --git a/internal/index/index_test.go b/internal/index/index_test.go index 4067179e..f46061c5 100644 --- a/internal/index/index_test.go +++ b/internal/index/index_test.go @@ -6,47 +6,80 @@ import ( ) func Test_parseColsFrom(t *testing.T) { - type args struct { - multiColGroup string - } tests := []struct { - name string - args args - want []string + name string + input string + want *Index }{ - // TODO: Add test cases. { - name: "single-column", - args: args{ - multiColGroup: "ID", + name: "Single column with single key and name", + input: "Column1@IndexName", + want: &Index{ + Cols: []string{"Column1"}, + Keys: []string{"Key1"}, + Name: "IndexName", + }, + }, + { + name: "Multi-column with multi-key and name", + input: "( Column1 , Column2 )< Key1 , Key2 >@IndexName", + want: &Index{ + Cols: []string{"Column1", "Column2"}, + Keys: []string{"Key1", "Key2"}, + Name: "IndexName", + }, + }, + { + name: "Single column with name only", + input: "Column3@IndexName", + want: &Index{ + Cols: []string{"Column3"}, + Name: "IndexName", }, - want: []string{"ID"}, }, { - name: "multi-column without with parentheses", - args: args{ - multiColGroup: "ID, Type", + name: "Multi-column without keys or name", + input: "(Column4, Column5)", + want: &Index{ + Cols: []string{"Column4", "Column5"}, }, - want: []string{"ID", "Type"}, }, { - name: "multi-column with parentheses", - args: args{ - multiColGroup: "(ID,Type)", + name: "Single column with single key only", + input: "Column6", + want: &Index{ + Cols: []string{"Column6"}, + Keys: []string{"Key6"}, }, - want: []string{"ID", "Type"}, }, { - name: "multi-column with space", - args: args{ - multiColGroup: "(ID, Type)", + name: "Multi-column with spaces around commas", + input: "(Column7, Column8, Column9)@IndexName", + want: &Index{ + Cols: []string{"Column7", "Column8", "Column9"}, + Keys: []string{"Key7", "Key8", "Key9"}, + Name: "IndexName", }, - want: []string{"ID", "Type"}, + }, + { + name: "Invalid format (multi-column without parentheses)", + input: "Column10, Column11@IndexName", + want: nil, + }, + { + name: "Invalid format (single column with parentheses)", + input: "(Column12)@IndexName", + want: nil, + }, + { + name: "Invalid format (empty columns)", + input: "@IndexName", + want: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := parseColsFrom(tt.args.multiColGroup); !reflect.DeepEqual(got, tt.want) { + if got := parseIndex(tt.input); !reflect.DeepEqual(got, tt.want) { t.Errorf("parseColsFrom() = %v, want %v", got, tt.want) } }) diff --git a/pkg/treemap/treemap.go b/pkg/treemap/treemap.go index 069b9a2e..552eec31 100644 --- a/pkg/treemap/treemap.go +++ b/pkg/treemap/treemap.go @@ -16,25 +16,22 @@ func New[K constraints.Ordered, V any]() *TreeMap[K, V] { } // Put inserts key-value pair into the map. -// Key should adhere to the comparator's type assertion, otherwise method panics. func (m *TreeMap[K, V]) Put(key K, value V) { m.tree.Put(key, value) } -// Get searches the element in the map by key and returns its value or nil if key is not found in tree. +// Get searches the element in the map by key and returns its value or empty value if key is not found in tree. // Second return parameter is true if key was found, otherwise false. -// Key should adhere to the comparator's type assertion, otherwise method panics. func (m *TreeMap[K, V]) Get(key K) (value V, found bool) { return m.tree.Get(key) } // Remove removes the element from the map by key. -// Key should adhere to the comparator's type assertion, otherwise method panics. func (m *TreeMap[K, V]) Remove(key K) { m.tree.Remove(key) } -// Empty returns true if map does not contain any elements +// Empty returns true if map does not contain any elements. func (m *TreeMap[K, V]) Empty() bool { return m.tree.Empty() } @@ -44,7 +41,7 @@ func (m *TreeMap[K, V]) Size() int { return m.tree.Size() } -// Keys returns all keys in-order +// Keys returns all keys in-order. func (m *TreeMap[K, V]) Keys() []K { return m.tree.Keys() } @@ -77,20 +74,37 @@ func (m *TreeMap[K, V]) Max() (key K, value V, ok bool) { return key, value, false } -// LowerBound returns an iterator pointing to the first element that is not less than key. -// If no such element is found, a past-the-end iterator is returned. -func (m *TreeMap[K, V]) LowerBound(key K) TreeMapIterator[K, V] { - iter := m.tree.Iterator() - iter.End() +// Floor finds the floor key-value pair for the input key. +// In case that no floor is found, then both returned values will be corresponding type's empty value. +// +// Floor key is defined as the greatest key that is less than or equal to the given key. +// A floor key may not be found, either because the map is empty, or because +// all keys in the map are greater than the given key. +func (m *TreeMap[K, V]) Floor(key K) (foundKey K, foundValue V, ok bool) { + node, found := m.tree.Floor(key) + if found { + return node.Key, node.Value, true + } + return foundKey, foundValue, false +} + +// Ceiling finds the ceiling key-value pair for the input key. +// In case that no ceiling is found, then both returned values will be corresponding type's empty value. +// +// Ceiling key is defined as the least key that is greater than or equal to the given key. +// A ceiling key may not be found, either because the map is empty, or because +// all keys in the map are less than the given key. +func (m *TreeMap[K, V]) Ceiling(key K) (foundKey K, foundValue V, ok bool) { node, found := m.tree.Ceiling(key) if found { - iter = m.tree.IteratorAt(node) + return node.Key, node.Value, true } - return TreeMapIterator[K, V]{iter} + return foundKey, foundValue, false } // UpperBound returns an iterator pointing to the first element that is greater than key. // If no such element is found, a past-the-end iterator is returned. +// See: https://en.cppreference.com/w/cpp/container/map/upper_bound func (m *TreeMap[K, V]) UpperBound(key K) TreeMapIterator[K, V] { iter := m.tree.Iterator() iter.Begin() @@ -102,6 +116,19 @@ func (m *TreeMap[K, V]) UpperBound(key K) TreeMapIterator[K, V] { return TreeMapIterator[K, V]{iter} } +// LowerBound returns an iterator pointing to the first element that is not less than key. +// If no such element is found, a past-the-end iterator is returned. +// See: https://en.cppreference.com/w/cpp/container/map/lower_bound +func (m *TreeMap[K, V]) LowerBound(key K) TreeMapIterator[K, V] { + iter := m.tree.Iterator() + iter.End() + node, found := m.tree.Ceiling(key) + if found { + iter = m.tree.IteratorAt(node) + } + return TreeMapIterator[K, V]{iter} +} + // String returns a string representation of container func (m *TreeMap[K, V]) String() string { str := "TreeMap\nmap[" diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.h b/test/cpp-tableau-loader/src/protoconf/hub.pc.h index c471fbe8..31be2f60 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.h @@ -8,10 +8,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc index bf9a81cf..0ac2db8e 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -29,12 +29,18 @@ bool ItemConf::ProcessAfterLoad() { for (auto&& item1 : data_.item_map()) { index_item_map_[item1.second.type()].push_back(&item1.second); } - // Index: Param@ItemInfo + // Index: Param@ItemInfo for (auto&& item1 : data_.item_map()) { for (auto&& item2 : item1.second.param_list()) { index_item_info_map_[item2].push_back(&item1.second); } } + for (auto&& item : index_item_info_map_) { + std::sort(item.second.begin(), item.second.end(), + [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { + return a->id() < b->id(); + }); + } // Index: Default@ItemDefaultInfo for (auto&& item1 : data_.item_map()) { index_item_default_info_map_[item1.second.default_()].push_back(&item1.second); @@ -45,11 +51,20 @@ bool ItemConf::ProcessAfterLoad() { index_item_ext_info_map_[static_cast(item2)].push_back(&item1.second); } } - // Index: (ID,Name)@AwardItem + // Index: (ID,Name)@AwardItem for (auto&& item1 : data_.item_map()) { Index_AwardItemKey key{item1.second.id(), item1.second.name()}; index_award_item_map_[key].push_back(&item1.second); } + for (auto&& item : index_award_item_map_) { + std::sort(item.second.begin(), item.second.end(), + [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { + if (a->type() != b->type()) { + return a->type() < b->type(); + } + return a->use_effect().type() < b->use_effect().type(); + }); + } // Index: (ID,Type,Param,ExtType)@SpecialItem for (auto&& item1 : data_.item_map()) { for (auto&& index_item2 : item1.second.param_list()) { @@ -111,7 +126,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItem(protoconf::FruitType ty return (*conf)[0]; } -// Index: Param@ItemInfo +// Index: Param@ItemInfo const ItemConf::Index_ItemInfoMap& ItemConf::FindItemInfo() const { return index_item_info_map_ ;} const ItemConf::Index_ItemInfoVector* ItemConf::FindItemInfo(int32_t param) const { @@ -168,7 +183,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemExtInfo(protoconf::Fruit return (*conf)[0]; } -// Index: (ID,Name)@AwardItem +// Index: (ID,Name)@AwardItem const ItemConf::Index_AwardItemMap& ItemConf::FindAwardItem() const { return index_award_item_map_ ;} const ItemConf::Index_AwardItemVector* ItemConf::FindAwardItem(const Index_AwardItemKey& key) const { diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h index 81311faa..70ac4e9a 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h @@ -48,7 +48,7 @@ class ItemConf : public Messager { private: Index_ItemMap index_item_map_; - // Index: Param@ItemInfo + // Index: Param@ItemInfo public: using Index_ItemInfoVector = std::vector; using Index_ItemInfoMap = std::unordered_map; @@ -81,7 +81,7 @@ class ItemConf : public Messager { private: Index_ItemExtInfoMap index_item_ext_info_map_; - // Index: (ID,Name)@AwardItem + // Index: (ID,Name)@AwardItem public: struct Index_AwardItemKey { uint32_t id; diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc index 12acd671..b3178430 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -43,12 +43,18 @@ bool ActivityConf::ProcessAfterLoad() { index_chapter_map_[item2.second.chapter_id()].push_back(&item2.second); } } - // Index: ChapterName@NamedChapter + // Index: ChapterName@NamedChapter for (auto&& item1 : data_.activity_map()) { for (auto&& item2 : item1.second.chapter_map()) { index_named_chapter_map_[item2.second.chapter_name()].push_back(&item2.second); } } + for (auto&& item : index_named_chapter_map_) { + std::sort(item.second.begin(), item.second.end(), + [](const protoconf::ActivityConf::Activity::Chapter* a, const protoconf::ActivityConf::Activity::Chapter* b) { + return a->award_id() < b->award_id(); + }); + } return true; } @@ -155,7 +161,7 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstChapter return (*conf)[0]; } -// Index: ChapterName@NamedChapter +// Index: ChapterName@NamedChapter const ActivityConf::Index_NamedChapterMap& ActivityConf::FindNamedChapter() const { return index_named_chapter_map_ ;} const ActivityConf::Index_NamedChapterVector* ActivityConf::FindNamedChapter(const std::string& chapter_name) const { diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h index 33843dbb..16641dc4 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h @@ -63,7 +63,7 @@ class ActivityConf : public Messager { private: Index_ChapterMap index_chapter_map_; - // Index: ChapterName@NamedChapter + // Index: ChapterName@NamedChapter public: using Index_NamedChapterVector = std::vector; using Index_NamedChapterMap = std::unordered_map; diff --git a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go index 036f218b..219dac9e 100644 --- a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go @@ -15,6 +15,7 @@ import ( load "github.com/tableauio/tableau/load" store "github.com/tableauio/tableau/store" proto "google.golang.org/protobuf/proto" + sort "sort" time "time" ) @@ -25,7 +26,7 @@ type ProtoconfItemConfItemMap_OrderedMap = treemap.TreeMap[uint32, *protoconf.It // Index: Type type ItemConf_Index_ItemMap = map[protoconf.FruitType][]*protoconf.ItemConf_Item -// Index: Param@ItemInfo +// Index: Param@ItemInfo type ItemConf_Index_ItemInfoMap = map[int32][]*protoconf.ItemConf_Item // Index: Default@ItemDefaultInfo @@ -34,7 +35,7 @@ type ItemConf_Index_ItemDefaultInfoMap = map[string][]*protoconf.ItemConf_Item // Index: ExtType@ItemExtInfo type ItemConf_Index_ItemExtInfoMap = map[protoconf.FruitType][]*protoconf.ItemConf_Item -// Index: (ID,Name)@AwardItem +// Index: (ID,Name)@AwardItem type ItemConf_Index_AwardItemKey struct { Id uint32 Name string @@ -157,7 +158,7 @@ func (x *ItemConf) processAfterLoad() error { key := item1.GetType() x.indexItemMap[key] = append(x.indexItemMap[key], item1) } - // Index: Param@ItemInfo + // Index: Param@ItemInfo x.indexItemInfoMap = make(ItemConf_Index_ItemInfoMap) for _, item1 := range x.data.GetItemMap() { for _, item2 := range item1.GetParamList() { @@ -165,6 +166,11 @@ func (x *ItemConf) processAfterLoad() error { x.indexItemInfoMap[key] = append(x.indexItemInfoMap[key], item1) } } + for _, item := range x.indexItemInfoMap { + sort.Slice(item, func(i, j int) bool { + return item[i].GetId() < item[j].GetId() + }) + } // Index: Default@ItemDefaultInfo x.indexItemDefaultInfoMap = make(ItemConf_Index_ItemDefaultInfoMap) for _, item1 := range x.data.GetItemMap() { @@ -179,12 +185,20 @@ func (x *ItemConf) processAfterLoad() error { x.indexItemExtInfoMap[key] = append(x.indexItemExtInfoMap[key], item1) } } - // Index: (ID,Name)@AwardItem + // Index: (ID,Name)@AwardItem x.indexAwardItemMap = make(ItemConf_Index_AwardItemMap) for _, item1 := range x.data.GetItemMap() { key := ItemConf_Index_AwardItemKey{item1.GetId(), item1.GetName()} x.indexAwardItemMap[key] = append(x.indexAwardItemMap[key], item1) } + for _, item := range x.indexAwardItemMap { + sort.Slice(item, func(i, j int) bool { + if item[i].GetType() != item[j].GetType() { + return item[i].GetType() < item[j].GetType() + } + return item[i].GetUseEffect().GetType() < item[j].GetUseEffect().GetType() + }) + } // Index: (ID,Type,Param,ExtType)@SpecialItem x.indexSpecialItemMap = make(ItemConf_Index_SpecialItemMap) for _, item1 := range x.data.GetItemMap() { @@ -263,9 +277,9 @@ func (x *ItemConf) FindFirstItem(type_ protoconf.FruitType) *protoconf.ItemConf_ return nil } -// Index: Param@ItemInfo +// Index: Param@ItemInfo -// FindItemInfoMap returns the index(Param@ItemInfo) to value(protoconf.ItemConf_Item) map. +// FindItemInfoMap returns the index(Param@ItemInfo) to value(protoconf.ItemConf_Item) map. // One key may correspond to multiple values, which are contained by a slice. func (x *ItemConf) FindItemInfoMap() ItemConf_Index_ItemInfoMap { return x.indexItemInfoMap @@ -332,9 +346,9 @@ func (x *ItemConf) FindFirstItemExtInfo(extType protoconf.FruitType) *protoconf. return nil } -// Index: (ID,Name)@AwardItem +// Index: (ID,Name)@AwardItem -// FindAwardItemMap returns the index((ID,Name)@AwardItem) to value(protoconf.ItemConf_Item) map. +// FindAwardItemMap returns the index((ID,Name)@AwardItem) to value(protoconf.ItemConf_Item) map. // One key may correspond to multiple values, which are contained by a slice. func (x *ItemConf) FindAwardItemMap() ItemConf_Index_AwardItemMap { return x.indexAwardItemMap diff --git a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go index bac8ab57..66d42415 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -16,6 +16,7 @@ import ( load "github.com/tableauio/tableau/load" store "github.com/tableauio/tableau/store" proto "google.golang.org/protobuf/proto" + sort "sort" time "time" ) @@ -35,7 +36,7 @@ type ProtoconfActivityConfActivityMap_OrderedMap = treemap.TreeMap[uint64, *Prot // Index: ChapterID type ActivityConf_Index_ChapterMap = map[uint32][]*protoconf.ActivityConf_Activity_Chapter -// Index: ChapterName@NamedChapter +// Index: ChapterName@NamedChapter type ActivityConf_Index_NamedChapterMap = map[string][]*protoconf.ActivityConf_Activity_Chapter // ActivityConf is a wrapper around protobuf message: protoconf.ActivityConf. @@ -151,7 +152,7 @@ func (x *ActivityConf) processAfterLoad() error { x.indexChapterMap[key] = append(x.indexChapterMap[key], item2) } } - // Index: ChapterName@NamedChapter + // Index: ChapterName@NamedChapter x.indexNamedChapterMap = make(ActivityConf_Index_NamedChapterMap) for _, item1 := range x.data.GetActivityMap() { for _, item2 := range item1.GetChapterMap() { @@ -159,6 +160,11 @@ func (x *ActivityConf) processAfterLoad() error { x.indexNamedChapterMap[key] = append(x.indexNamedChapterMap[key], item2) } } + for _, item := range x.indexNamedChapterMap { + sort.Slice(item, func(i, j int) bool { + return item[i].GetAwardId() < item[j].GetAwardId() + }) + } return nil } @@ -285,9 +291,9 @@ func (x *ActivityConf) FindFirstChapter(chapterId uint32) *protoconf.ActivityCon return nil } -// Index: ChapterName@NamedChapter +// Index: ChapterName@NamedChapter -// FindNamedChapterMap returns the index(ChapterName@NamedChapter) to value(protoconf.ActivityConf_Activity_Chapter) map. +// FindNamedChapterMap returns the index(ChapterName@NamedChapter) to value(protoconf.ActivityConf_Activity_Chapter) map. // One key may correspond to multiple values, which are contained by a slice. func (x *ActivityConf) FindNamedChapterMap() ActivityConf_Index_NamedChapterMap { return x.indexNamedChapterMap diff --git a/test/proto/item_conf.proto b/test/proto/item_conf.proto index 147a1386..218deb5f 100644 --- a/test/proto/item_conf.proto +++ b/test/proto/item_conf.proto @@ -17,10 +17,10 @@ message ItemConf { name: "ItemConf" ordered_map: true index: "Type" - index: "Param@ItemInfo" + index: "Param@ItemInfo" index: "Default@ItemDefaultInfo" // For testing programming language keyword conflicts index: "ExtType@ItemExtInfo" - index: "(ID,Name)@AwardItem" + index: "(ID,Name)@AwardItem" index: "(ID,Type,Param,ExtType)@SpecialItem" index: "PathDir@ItemPathDir" index: "PathName@ItemPathName" diff --git a/test/proto/test_conf.proto b/test/proto/test_conf.proto index 16c4a887..7cedbb8b 100644 --- a/test/proto/test_conf.proto +++ b/test/proto/test_conf.proto @@ -15,7 +15,7 @@ message ActivityConf { name: "Activity" ordered_map: true index: "ChapterID" - index: "ChapterName@NamedChapter" + index: "ChapterName@NamedChapter" index: "SectionItemId@Award" };