Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

using System;
using System.Windows.Forms;
using System.Collections;
using System.IO;

namespace EA_ReqPro
{
   /// <summary>
   /// The EA_ElementSorter class has been created to allow an element collection belonging to a
   /// package to be sorted and traversed in the order implied by the view of those elements
   /// in the project browser. This is needed since the ordering of elements in the collections
   /// does not seem to match that of the project browser. Furthermore, an element can be in more
   /// than one collection, because elements themselves can have sub-elements and associated 
   /// collections yet such sub-elements also appear in the parent package collection. Confusing? 
   /// Until Sparx Systems changes the way it organises elements, we need to use this class to
   /// sort them and traverse them, unfortunately.
   /// </summary>
   public class EA_ElementSorter
        {
      private ArrayList sorted_elements;
      private ArrayList sorted_element_levels;
      private int i_sorted_elements;

      public EA_ElementSorter(EA.Package thePackage)
      {
         i_sorted_elements = 0;
         sorted_elements = new ArrayList();
         sorted_element_levels = new ArrayList();

         sortElements(thePackage); // this is where most of the work is done
      }

      /// <summary>
      /// Return the first element in the sorted and flattened list of EA elements
      /// </summary>
      /// <param name="theElement"></param>
      /// <param name="recurse_level"></param>
      /// <returns></returns>
      public bool getFirst(ref EA.Element theElement, ref int recurse_level)
      {
         i_sorted_elements = 0;
         if (i_sorted_elements < sorted_elements.Count)
         {
            theElement    = (EA.Element)sorted_elements[i_sorted_elements];
            recurse_level =  (int)sorted_element_levels[i_sorted_elements];
            return true;
         }
         return false;
      }

      /// <summary>
      /// Return the next element in the sorted and flattened list of EA elements
      /// </summary>
      /// <param name="theElement"></param>
      /// <param name="recurse_level"></param>
      /// <returns></returns>
      public bool getNext(ref EA.Element theElement, ref int recurse_level)
      {
         i_sorted_elements++;
         if (i_sorted_elements < sorted_elements.Count)
         {
            theElement    = (EA.Element)sorted_elements[i_sorted_elements];
            recurse_level =  (int)sorted_element_levels[i_sorted_elements];
            return true;
         }
         return false;
      }
   

      /// <summary>
      /// This function sorts elements that belong to a package so that they can be traversed
      /// in the order in which they appear in the project browser. This is necessary because
      /// the order in which the elements are encountered when parsing the database, bears little
      /// relationship to the order seen in the browser, and things get considerably more 
      /// complicated if elements have sub-elements in the browser, because it seems that due to 
      /// EA's internal organisation of collections, the sub-elements can be encountered more
      /// than once.
      /// This method creates an object hierarchy using the EA_ElementWrapper class and then
      /// traverses it to flatten it out into a  sorted array list of elements.
      /// </summary>
      /// <param name="thePackage"></param>
      /// <returns></returns>
      private void sortElements(EA.Package thePackage)
      {
         // Grab a hold of all the elements under this package. This will include sub-elements to elements
         // because even those sub-elements still belong to this package and its element collection.
         ArrayList unsorted_elements = new ArrayList();
         foreach(EA.Element theElement in thePackage.Elements)
         {
            unsorted_elements.Add(theElement);
         }
      
         // Begin to build a hierarchical representation of the elements, first by getting all the elements
         // that do not have a ParentID (ie. elements that are not themselves sub-elements of other elements).
         ArrayList element_wrappers = new ArrayList();

         int i;
         for (i = 0; i < unsorted_elements.Count; )
         {
            object obj = unsorted_elements[i];

            if ( ((EA.Element)obj).ParentID == 0 )
            {
               EA_ElementWrapper element_wrapper = new EA_ElementWrapper( (EA.Element)obj );
               element_wrappers.Add( element_wrapper );
               unsorted_elements.RemoveAt(i);
               continue;
            }
            i++;
         }
   
         // Now go through the remaining elements (if any), which will all be sub-elements of other elements
         // in order to pull them out of the unsorted list and place them at the correct place in the 
         // element hierarchy. Multiple passes of this will ensure that the unsorted element list is
         // eventually exhausted, but nevertheless, we protect against an infinite loop condition anyway.
         int lastCount = -1;  // variable to ensure loop is not infinite
         while (unsorted_elements.Count > 0 && lastCount != unsorted_elements.Count)
         {
            lastCount = unsorted_elements.Count;

            // Attempt to extract more unsorted elements and add them to the hiearchically sorted data
            for (i = 0; i < unsorted_elements.Count; )
            {
               object obj = unsorted_elements[i];

               // Try and find the correct place in the hiearchy where this unsorted element can be appended.
               EA_ElementWrapper found_element_wrapper = null;
               foreach(EA_ElementWrapper element_wrapper in element_wrappers)
               {
                  found_element_wrapper = findElementWrapper(element_wrapper, ((EA.Element)obj).ParentID );
                  if (found_element_wrapper != null)
                     break;
               }

               // If we found the correct place in the hierarchy, append it, and remove it from the 
               // unsorted element list.
               if (found_element_wrapper != null)
               {
                  EA_ElementWrapper element_wrapper = new EA_ElementWrapper( (EA.Element)obj );
                  found_element_wrapper.sub_elements.Add( element_wrapper );
                  unsorted_elements.RemoveAt(i);
                  continue;
               }

               i++;
            }
         }
         // TODO
         // What should we do if unsorted_elements.Count is still > 0?

         // Now flatten the hierarchically sorted results into a simple ArrayList.
         foreach(EA_ElementWrapper element_wrapper in element_wrappers)
         {
            parseElementWrapperForFlattening( element_wrapper, 0 );
         }
      }

      /// <summary>
      /// A recursive EA_ElementWrapper parser that builds a flattened list of EA elements
      /// </summary>
      /// <param name="element_wrapper"></param>
      /// <param name="recurse_level"></param>
      private void parseElementWrapperForFlattening(EA_ElementWrapper element_wrapper, int recurse_level)
      {
         sorted_elements.Add(element_wrapper.m_element);
         sorted_element_levels.Add(recurse_level);

         foreach(object obj in element_wrapper.sub_elements)
         {
            parseElementWrapperForFlattening( (EA_ElementWrapper)obj, recurse_level+1 );
         }
      }

      /// <summary>
      /// A recursive EA_ElementWrapper parser, that matches an object to a specified elementId
      /// </summary>
      /// <param name="thisElementWrapper"></param>
      /// <param name="elementId"></param>
      /// <returns></returns>
      private EA_ElementWrapper findElementWrapper(EA_ElementWrapper thisElementWrapper, int elementId)
      {
         // If this element wrapper itself is the target, then return it
         if (thisElementWrapper.m_element.ElementID == elementId)
         {
            return thisElementWrapper;
         }

         // otherwise, look through all this element's sub-elements, and each sub-element's sub-elements, etc, etc
         foreach (object obj in thisElementWrapper.sub_elements)
         {
            EA_ElementWrapper found = findElementWrapper( (EA_ElementWrapper)obj, elementId );
            if (found != null)
               return found;
         }
         return null;
      }

   }

   /// <summary>
   /// A wrapper class for an EA element that allows that element to be the holder of a collection
   /// of sub-elements. This allows us to build a hierarchical structure of elements that mirrors 
   /// the structure (aka. indentation) of elements as seen in the project browser, where elements
   /// are implied to be sub-elements to other elements etc. 
   /// </summary>
   public class EA_ElementWrapper
   {
      public EA.Element m_element;
      public ArrayList sub_elements;

      public EA_ElementWrapper(EA.Element element)
      {
         m_element = element;
         sub_elements = new ArrayList();
      }
   }
}