void Sort(MemberInfoCollection list, String hierarchyUniquqName, SortDescriptor type) { // Сортируем коллекцию, если она содержит элементы, принадолежащие данной иерархии. В протипном случае идем вглубь в дочерние. if (list != null && list.Count > 0) { if (list[0].HierarchyUniqueName == hierarchyUniquqName) { list.Sort(type); // Сортируем так же элементы вложенных DrilledDown - коллекций foreach (var member in list) { Sort(member.DrilledDownChildren, hierarchyUniquqName, type); } } else { // Сортируем вглубь foreach (var member in list) { Sort(member.DrilledDownChildren, hierarchyUniquqName, type); } // Сортируем вглубь foreach (var member in list) { Sort(member.Children, hierarchyUniquqName, type); } } } }
void BuildSortedIndexes(int axisNum, MemberInfoCollection list) { foreach (var item in list) { if (item.Children.Count == 0) { if (axisNum == 0) { int x = m_Columns_Sorted_LowestMembers.Count; m_Columns_Sorted_LowestMembers.Add(x, item); item.Sorted_MemberIndexInAxis = x; } if (axisNum == 1) { int x = m_Rows_Sorted_LowestMembers.Count; m_Rows_Sorted_LowestMembers.Add(x, item); item.Sorted_MemberIndexInAxis = x; } BuildSortedIndexes(axisNum, item.DrilledDownChildren); } else { BuildSortedIndexes(axisNum, item.Children); BuildSortedIndexes(axisNum, item.DrilledDownChildren); } } }
public void Initialize(MemberInfoCollection members) { Members.Clear(); if (members != null) { int size = 0; int rowIndex = 0; int columnIndex = 0; foreach (MemberInfo member in members) { if (Type == AreaType.RowsArea) { this.AddRowMember(Members, member, columnIndex, rowIndex, out size, 0); rowIndex += size; } if (Type == AreaType.ColumnsArea) { this.AddColumnMember(Members, member, columnIndex, rowIndex, out size, 0); columnIndex += size; } } } }
public void Sort(int axisNum, String hierarchyUniquqName, SortDescriptor sortDescr) { if (axisNum == 0 || axisNum == 1) { MemberInfoCollection members = axisNum == 0 ? Columns : Rows; Dictionary <int, MemberInfo> sorted_LowestMembers = axisNum == 0 ? m_Columns_Sorted_LowestMembers : m_Rows_Sorted_LowestMembers; Dictionary <String, SortDescriptor> sortInfo = axisNum == 0 ? ColumnsSortInfo : RowsSortInfo; // Если описатель для сортировки null, то применяем сортировку None if (sortDescr == null) { sortDescr = new SortDescriptor(); } // Коллекция подлежит сортировки если: // тип сортировки - None // либо точно задано свойство по которому идет сортировка bool need_Sort = sortDescr.Type == SortTypes.None || (sortDescr.Type != SortTypes.None && !String.IsNullOrEmpty(sortDescr.SortBy)); if (need_Sort) { Sort(members, hierarchyUniquqName, sortDescr); // Формируем отсортированные индексы sorted_LowestMembers.Clear(); BuildSortedIndexes(axisNum, members); } // Если сортировка выполнялась, то информацию о ней сохраняем // Сохраняем даже сортировки None - чтобы потом при клике на элементе было понятно по чем его сортировали в прошлый раз if (need_Sort) { if (sortInfo.ContainsKey(hierarchyUniquqName)) { sortInfo[hierarchyUniquqName] = sortDescr; } else { sortInfo.Add(hierarchyUniquqName, sortDescr); } } } }
private MemberInfoCollection CreateFields(int axisNum) { if (axisNum == 0) { m_Columns_LowestMembers.Clear(); m_Columns_Sorted_LowestMembers.Clear(); } if (axisNum == 1) { m_Rows_LowestMembers.Clear(); m_Rows_Sorted_LowestMembers.Clear(); } MemberInfoCollection fields = new MemberInfoCollection(null); DateTime start = DateTime.Now; // Формируем иерархию элементов MemberInfo if (m_CellSet_Descr != null && m_CellSet_Descr.Axes.Count > axisNum) { int position_Index = 0; // Проход по позиция оси foreach (PositionData pos in m_CellSet_Descr.Axes[axisNum].Positions) { // Глубина оси (кол-во элементов в позиции) int depth = pos.Members.Count; // Предыдущий элемент в данной позиции MemberInfo prev_member_in_position = null; // Проход по элементам в каждой позици for (int i = 0; i < depth; i++) { if (i > 0 && prev_member_in_position == null) { throw new Exception(String.Format("Ошибка построения иерархии элементов. Не найден описатель для элемента {0} в позиции {1}", i, position_Index)); } // Получаем элемент из хранилища, которое является общим для всей оси MemberData member = m_CellSet_Descr.Axes[axisNum].Members[pos.Members[i].Id]; if (member == null) { throw new Exception(String.Format("Ошибка построения иерархии элементов. На оси не найден элемент с Id: {0}", pos.Members[i].Id)); } if (DataReorganizationType == DataReorganizationTypes.MergeNeighbors) { #region Правила // Правила постоения иерархии: // 1. Получаем для данного элемента PARENT_UNIQUE_NAME. // 2. Определяем КОЛЛЕКЦИЮ, в которую должен попасть элемент: // - Если это элемент нулевой линии, то ПО УМОЛЧАНИЮ он является кандидатом // на добавление в коллекцию корневых MemberInfo // - Если это элемент не нулевой линии, то ПО УМОЛЧАНИЮ он явлется кандидатом // на добавление в коллекцию Children для MemberInfo предыдущего элемента данной позиции (prev_member_in_position) // 3. Берем последний элемент в КОЛЛЕКЦИИ. Проходим вглубь по его коллекции DrilledDownChildren и строим список из последних элементов каждой из них. // Проходим по полученному списку от самого глубокого вверх. Если находим элемент, который является родителем для текущего (UNIQUE_NAME совпадает c PARENT_UNIQUE_NAME текущего), // то КОЛЛЕКЦИЯ меняется на коллецию DrilledDownChildren родителя. // Правила объединения: // 1. Объединению подлежат только следующие друг за другом одинаковые элементы. Но только в том случае, если коллекция DrilledDownChildren у предыдущего является пустой (в противном случае произойдет перемещение ячеек и может потеряться ORDER запроса) // - В следующем запросе года не должны объединяться чтобы не потерять очередность ячеек, предусмотренную запросом // select [Measures].[Internet Sales Amount] DIMENSION PROPERTIES PARENT_UNIQUE_NAME , HIERARCHY_UNIQUE_NAME , CUSTOM_ROLLUP , UNARY_OPERATOR , KEY0 on 0, // {([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Bikes]), // ([Date].[Calendar].[Calendar Semester].&[2001]&[2], [Product].[Product Categories].[Subcategory].[Mountain Bikes]), // ([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Clothing])} // DIMENSION PROPERTIES PARENT_UNIQUE_NAME , HIERARCHY_UNIQUE_NAME , CUSTOM_ROLLUP , UNARY_OPERATOR , KEY0 on 1 // from [Adventure Works] // 2. Элементы последней линии (ближайшей к ячейкам) объединению не подлежат #endregion Правила #region Определение места элемента в иерархии // Уникальное имя родителя (Правила постоения иерархии - пункт 1) String parentUniqueName = GetMemberPropertyValue(member, "PARENT_UNIQUE_NAME"); // КОЛЛЕКЦИЯ (Правила постоения иерархии - пункт 2) MemberInfoCollection container = i == 0 ? fields : prev_member_in_position.Children; // Ищем родителя (Правила постоения иерархии - пункт 3) if (!String.IsNullOrEmpty(parentUniqueName)) { if (container.Count > 0) { MemberInfo last = container[container.Count - 1]; // Теперь строим коллекцию из последних в каждой коллекции DrilledDownChildren вглубь по всей ветке List <MemberInfo> dd_last_list = new List <MemberInfo>(); dd_last_list.Add(last); while (last.DrilledDownChildren.Count > 0) { dd_last_list.Add(last.DrilledDownChildren[last.DrilledDownChildren.Count - 1]); last = last.DrilledDownChildren[last.DrilledDownChildren.Count - 1]; } for (int x = dd_last_list.Count; x > 0; x--) { var info = dd_last_list[x - 1]; if (info.UniqueName == parentUniqueName) { container = info.DrilledDownChildren; break; } } } } #endregion #region Объединение элементов if (i != (depth - 1) && container.Count > 0 && container[container.Count - 1].UniqueName == member.UniqueName && container[container.Count - 1].DrilledDownChildren.Count == 0) { // Объединение элементов prev_member_in_position = container[container.Count - 1]; } else { // Создание нового элемента prev_member_in_position = CreateMemberInfo(member); container.Add(prev_member_in_position); } // Если элементы одной линии повторяются друг за другом, то свойство DrilledDown у них верное только у последнего if (prev_member_in_position != null && !prev_member_in_position.IsCalculated) { prev_member_in_position.DrilledDown = prev_member_in_position.DrilledDown | pos.Members[i].DrilledDown; } #endregion } if (DataReorganizationType == DataReorganizationTypes.None) { #region Правила // В данном режиме никакой реорганизации и объединения не производится #endregion #region Определение места элемента в иерархии // КОЛЛЕКЦИЯ MemberInfoCollection container = i == 0 ? fields : prev_member_in_position.Children; #endregion #region Объединение элементов // Создание нового элемента prev_member_in_position = CreateMemberInfo(member); container.Add(prev_member_in_position); #endregion } if (DataReorganizationType == DataReorganizationTypes.LinkToParent) { #region Правила // В данном режиме элементы выстраиваются в иерархию используя уникальное имя родителя. При этом в иерархию // выстраивается вся ось. // Например было: // 2002 a // Q1 b // H1 c // 2002 d // H2 e // Перегруппируется в: // 2002 a // 2002 d // H1 c // Q1 b // H2 e // 1. Собираем нервы в кулак. // 2. Определяем КОЛЛЕКЦИЮ, в которую должен попасть элемент: // - Если это элемент нулевой линии, то ПО УМОЛЧАНИЮ он является кандидатом // на добавление в коллекцию корневых MemberInfo // - Если это элемент не нулевой линии, то ПО УМОЛЧАНИЮ он явлется кандидатом // на добавление в коллекцию Children для MemberInfo предыдущего элемента данной позиции (prev_member_in_position) // 3. КОЛЛЕКЦИЮ выстраиваем в плоский список рекурсивно с учетом ТОЛЬКО DrilledDownChildren. // Создаем ТЕКУЩИЙ ОПИСАТЕЛЬ (MemberInfo) для данного элемнта // Проходим с конца списка в начало: // a) Если находим элемент, который является дочерним для ТЕКУЩЕГО ОПИСАТЕЛЯ, то удаляем его из коллекции, в которой он находился и вставлем его в 0 позицию в коллекцию DriDrilledDownChildren ТЕКУЩЕГО ОПИСАТЕЛЯ // CONTINUE // b) Если находим элемент с таким же уник. именем, то: // - если это последняя линия, то они объединению не подлежат. Коллекция DriDrilledDownChildren у двойника зануляется и ТЕКУЩИЙ ОПИСАТЕЛЬ добавляется следом за ним. // - если это не последняя линия то объекты объединяются, т.е. двойнику переходит коллекция DriDrilledDownChildren текущего описателя и он будет считаться далее как ТЕКУЩИЙ ОПИСАТЕЛЬ // CТОП ЦИКЛА // c) Если находим элемент, который является родителем для данного, то добавляем ТЕКУЩИЙ ОПИСАТЕЛЬ в коллекцию DrilledDownChildren родителя. // СТОП ЦИКЛА // d) Если ни 3b) ни 3c) не отработало, то ТЕКУЩИЙ ОПИСАТЕЛЬ добавляется в КОЛЛЕКЦИЮ с учетом номера уровня. Чтобы не получилось что перед ним есть элементы с большим по глубине номером уровня. // НО если элемент вычисляемый, то уровень не учитываем, а добавляем его в конец КОЛЛЕКЦИИ. // #endregion #region Определение места элемента в иерархии // Выполняем пункт 1 :) // КОЛЛЕКЦИЯ (Правила постоения иерархии - пункт 2) MemberInfoCollection container = i == 0 ? fields : prev_member_in_position.Children; // Выстраиваем в плоский список рекурсивно с учетом ТОЛЬКО DrilledDownChildren List <MemberInfo> line = new List <MemberInfo>(); foreach (var item in container) { line.Add(item); line.AddRange(item.CollectDrilledDownChildren()); } // Создание нового элемента prev_member_in_position = CreateMemberInfo(member); // Последний из обследуемых элементов КОЛЛЕКЦИИ, чья глубина больше чем у данного MemberInfo reverse_last_leveldepth_member = null; bool isOk = false; for (int indx = line.Count - 1; indx >= 0; indx--) { var mi = line[indx]; if (mi.Container == container && mi.LevelDepth > member.LevelDepth) { reverse_last_leveldepth_member = mi; } // 3a if (mi.ParentUniqueName == member.UniqueName) { // Нашли дочерний, цепляем его и тащим за собой mi.Container.Remove(mi); prev_member_in_position.DrilledDownChildren.Insert(0, mi); continue; } // 3b if (mi.UniqueName == member.UniqueName) { // Найден дубликат if (i != (depth - 1)) { // Не последняя линия mi.DrilledDownChildren.Clear(); foreach (var x in prev_member_in_position.DrilledDownChildren) { mi.DrilledDownChildren.Add(x); } prev_member_in_position = mi; } else { // Последняя линия mi.DrilledDownChildren.Clear(); mi.IsDublicate = true; // ДОБАВЛЕНИЕ В ИЕРАРХИЮ mi.Container.Insert(mi.Container.IndexOf(mi) + 1, prev_member_in_position); } isOk = true; break; } // 3c if (mi.UniqueName == prev_member_in_position.ParentUniqueName) { // Нашли родителя, цепляемся к нему // ДОБАВЛЕНИЕ В ИЕРАРХИЮ mi.DrilledDownChildren.Add(prev_member_in_position); isOk = true; break; } } // 3d // Более глубокий элемент уровня не найден, значит добавляем хвост КОЛЛЕКЦИИ if (!isOk) { // Если Key0 == null то элемент вычисляемый if (member.IsCalculated) { // ДОБАВЛЕНИЕ В ИЕРАРХИЮ container.Add(prev_member_in_position); } else { if (reverse_last_leveldepth_member != null && container.Contains(reverse_last_leveldepth_member)) { // ДОБАВЛЕНИЕ В ИЕРАРХИЮ container.Insert(container.IndexOf(reverse_last_leveldepth_member), prev_member_in_position); } else { // ДОБАВЛЕНИЕ В ИЕРАРХИЮ container.Add(prev_member_in_position); } } } #endregion } if (i == (depth - 1)) { if (prev_member_in_position == null) { throw new Exception("Ошибка. Не создан элемент последней линии."); } if (axisNum == 0) { m_Columns_LowestMembers.Add(position_Index, prev_member_in_position); m_Columns_Sorted_LowestMembers.Add(position_Index, prev_member_in_position); } if (axisNum == 1) { m_Rows_LowestMembers.Add(position_Index, prev_member_in_position); m_Rows_Sorted_LowestMembers.Add(position_Index, prev_member_in_position); } prev_member_in_position.MemberIndexInAxis = prev_member_in_position.Sorted_MemberIndexInAxis = position_Index; } } position_Index++; } if (DataReorganizationType == DataReorganizationTypes.LinkToParent) { // Операции по вставке объектов поперепутали MemberOrder для элементов. Исправляем это. // И заодно устанавливаем флаг DrilledDown в true только если у элемента не пустая коллекция DrilledDownChildren foreach (var x in fields) { x.RefreshMemberOrder(); x.CrackDrilledDown(); } } } DateTime stop = DateTime.Now; return(fields); }
private MemberInfoCollection CreateFields(int axisNum) { if (axisNum == 0) { m_Columns_LowestMembers.Clear(); } if (axisNum == 1) { m_Rows_LowestMembers.Clear(); } MemberInfoCollection fields = new MemberInfoCollection(null); Dictionary <int, List <MemberInfo> > tmp = new Dictionary <int, List <MemberInfo> >(); if (m_CellSet_Descr != null && m_CellSet_Descr.Axes.Count > axisNum) { int position_Index = 0; foreach (PositionData pos in m_CellSet_Descr.Axes[axisNum].Positions) { MemberInfoCollection container = fields; int depth = pos.Members.Count; for (int i = 0; i < pos.Members.Count; i++) { List <MemberInfo> line = null; if (tmp.ContainsKey(i)) { line = tmp[i]; } else { line = new List <MemberInfo>(); tmp.Add(i, line); } MemberData member = m_CellSet_Descr.Axes[axisNum].Members[pos.Members[i].Id]; // Если PARENT_UNIQUE_NAME запрашивалось то берем его, иначе будем спрашивать у куба String parentUniqueName = GetMemberPropertyValue(member, "PARENT_UNIQUE_NAME"); // Если родитель является DrilledDown, то данный элемент должен попасть в коллекцию DrilledDownChildren MemberInfo parentInfo = null; try { if (!String.IsNullOrEmpty(parentUniqueName)) { int posIndex = ReversePos(line, parentUniqueName); if (posIndex > -1) { parentInfo = line[posIndex]; } if (parentInfo != null && parentInfo.DrilledDown) { // Если линия не нулевая. То объект должен попать в коллекцию DrilledDown только в том случае если и владелец данной коллекции и сам элемент, который мы проверяем пересекаются с одним и тем же элементом // Ситуацию можно увидеть в запросе /*select [Measures].[Internet Sales Amount] on 0, * {([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Bikes]), * ([Date].[Calendar].[Calendar Semester].&[2001]&[2], [Product].[Product Categories].[Subcategory].[Mountain Bikes]), * ([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Clothing])} * on 1 * from [Adventure Works]*/ if (i == 0 || (i > 0 && tmp[i - 1][posIndex] != null && tmp[i - 1][position_Index] != null && tmp[i - 1][posIndex] == tmp[i - 1][position_Index])) { container = parentInfo.DrilledDownChildren; } } } else { // Если это нулевой элемент на линии, то он однозначно должен находиться в коллекции Children // Иначе он может попасть в коллекцию DrilledDown если у предыдущего элемента установлен флаг DrilledDown if (position_Index > 0) { MemberInfo prevInLine = line[line.Count - 1]; if (prevInLine != null && prevInLine.UniqueName != member.UniqueName && prevInLine.DrilledDown) { // Для вычисляемых элементов свойство DrilledDown работает неправильно. И в этом случае считаем что для того чтобы элемент попал в коллекцию DrilledDown у него должна быть и глубина уровня больше чем у предыдущего if (prevInLine.LevelDepth < member.LevelDepth) { // Если линия не нулевая. То объект должен попать в коллекцию DrilledDown только в том случае если и владелец данной коллекции и сам элемент, который мы проверяем пересекаются с одним и тем же элементом // Ситуацию можно увидеть в запросе /*select [Measures].[Internet Sales Amount] on 0, * {([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Bikes]), * ([Date].[Calendar].[Calendar Semester].&[2001]&[2], [Product].[Product Categories].[Subcategory].[Mountain Bikes]), * ([Date].[Calendar].[Calendar Year].&[2001], [Product].[Product Categories].[Category].[Clothing])} * on 1 * from [Adventure Works]*/ if (i == 0 || (i > 0 && tmp[i - 1][line.Count - 1] != null && tmp[i - 1][position_Index] != null && tmp[i - 1][line.Count - 1] == tmp[i - 1][position_Index])) { container = prevInLine.DrilledDownChildren; } } else { // Пытаемся исправить то, что для вычисляемых элементов свойство DrilledDown работает неправильно. prevInLine.DrilledDown = false; } } } } } catch (System.NotSupportedException) { } // Если элемент существует, то он должен быть последним // Если он окажется не последним, то это значит что эти элементы объединению не подлежат и нужно создавать новый элемент MemberInfo field = null; if (line.Count > 0) { field = line[line.Count - 1]; } // Элементы последней линии объединению не подлежат if (field == null || field.UniqueName != member.UniqueName || i == (depth - 1) || (field.Parent != null && container.m_Owner != null && field.Parent != container.m_Owner)) { field = CreateMemberInfo(member); line.Add(field); // Важно чтобы добавление в контейнер было после добавления в линию, т.к. в Add идет установка Parent container.Add(field); if (i == (depth - 1)) { if (axisNum == 0) { m_Columns_LowestMembers.Add(field); } if (axisNum == 1) { m_Rows_LowestMembers.Add(field); } } } else { line.Add(field); } if (field != null && !field.IsCalculated) { field.DrilledDown = field.DrilledDown | pos.Members[i].DrilledDown; } container = field.Children; } position_Index++; } } return(fields); }