crashanalysercmd/Libraries/Engine/CrashItemLib/Crash/Base/DataBinding/CIDBModel.cs
author Matti Laitinen <matti.t.laitinen@nokia.com>
Thu, 11 Feb 2010 15:50:58 +0200
changeset 0 818e61de6cd1
permissions -rw-r--r--
Add initial version of Crash Analyser cmdline under EPL

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
* 
* Description:
*
*/
using System;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;

namespace CrashItemLib.Crash.Base.DataBinding
{
    public class CIDBModel : IEnumerable<CIDBRow>
	{
		#region Constructors
        public CIDBModel( CIElement aElement, bool aAutoPopulate )
        {
            iElement = aElement;
            iAutoPopulate = aAutoPopulate;
        }
		#endregion

        #region API
        public void Add( CIDBColumn aColumn )
        {
            aColumn.Model = this;
            iColumns.Add( aColumn );
        }

        public void Add( CIDBRow aRow )
        {
            aRow.Model = this;
            iRows.Add( aRow );
        }

        public void Add( params CIDBCell[] aCells )
        {
            CIDBRow row = new CIDBRow( aCells );
            Add( row );
        }

        public void ClearRows()
        {
            iRows.Clear();
        }

        public void ClearColumns()
        {
            iColumns.Clear();
        }

        public void TryAutoPopulateColumns( CIElement aElement )
        {
            Type customAttributeType = typeof( CIDBAttributeColumn );
            Type thisObjectType = aElement.GetType();

            object[] attribs = thisObjectType.GetCustomAttributes( customAttributeType, true );
            if ( attribs != null && attribs.Length > 0 )
            {
                ExtractAttributeColumns( attribs, aElement );
            }
        }

        public void TryAutoPopulateCells( CIElement aElement )
        {
            if ( AutoPopulate )
            {
                SortedDictionary<int, CIDBRow> rows = new SortedDictionary<int, CIDBRow>();
                
                Type customAttributeType = typeof( CIDBAttributeCell );
                Type thisObjectType = aElement.GetType();

                // Get properties featuring the CIDBAttribute 
                PropertyInfo[] propertyInfo = thisObjectType.GetProperties();
                foreach ( PropertyInfo p in propertyInfo )
                {
                    object[] attribs = p.GetCustomAttributes( customAttributeType, true );
                    if ( attribs != null && attribs.Length > 0 )
                    {
                        object propertyValue = p.GetValue( aElement, null );
                        ExtractAttributeCells( p.ToString(), aElement, attribs, propertyValue, rows );
                    }
                }

                // Same, but get methods featuring the CIDBAttribute 
                MethodInfo[] methodInfo = thisObjectType.GetMethods();
                foreach ( MethodInfo m in methodInfo )
                {
                    object[] attribs = m.GetCustomAttributes( customAttributeType, true );
                    if ( attribs != null && attribs.Length > 0 )
                    {
                        // We only support this attribute on methods that don't contain
                        // any arguments
                        int paramCount = m.GetParameters().Length;
                        if ( paramCount != 0 )
                        {
                            throw new NotSupportedException( "Method: " + m.ToString() + " has CIDBAttribute but non-empty parameter list -> Not supported" );
                        }

                        // Get property value 
                        object propertyValue = m.Invoke( aElement, null );
                        ExtractAttributeCells( m.ToString(), aElement, attribs, propertyValue, rows );
                    }
                }

                // Since the list is already sorted for us, just add the items in order
                foreach ( KeyValuePair<int, CIDBRow> kvp in rows )
                {
                    this.Add( kvp.Value );
                }
            }
        }
        #endregion

        #region Properties
        public bool AutoPopulate
        {
            get { return iAutoPopulate; }
        }

        public CIElement Element
        {
            get { return iElement; }
        }

        public List<CIDBColumn> Columns
        {
            get { return iColumns; }
        }

        public List<CIDBRow> Rows
        {
            get { return iRows; }
        }
        #endregion

        #region Internal methods
        private void ExtractAttributeColumns( object[] aColumnAttributes, CIElement aElement )
        {
            bool foundColumnAttributes = false;
            SortedDictionary<int, CIDBColumn> columns = new SortedDictionary<int,CIDBColumn>();

            foreach ( object obj in aColumnAttributes )
            {
                if ( obj is CIDBAttributeColumn )
                {
                    CIDBAttributeColumn attribute = (CIDBAttributeColumn) obj;

                    // Make a column
                    CIDBColumn col = new CIDBColumn( attribute.Caption );
                    col.Width = attribute.Width;
                    col.WidthSet = attribute.WidthSet;
                    col.TakesUpSlack = attribute.TakesUpSlack;

                    if ( columns.ContainsKey( attribute.Order ) )
                    {
                        throw new Exception( string.Format( "Column: [{0}] in element: [{1}] specifies order: {2} which is already in use",
                                                            attribute.Caption, aElement, attribute.Order ) );
                    }

                    columns.Add( attribute.Order, col );
                    foundColumnAttributes = true;
                }
            }

            // Since the list is already sorted for us, just add the items in order
            foreach ( KeyValuePair<int, CIDBColumn> kvp in columns )
            {
                this.Add( kvp.Value );
            }

            // We'll override the auto populate feature if we find a valid
            // column attribute
            iAutoPopulate = foundColumnAttributes;
        }

        private void ExtractAttributeCells( string aEntityName, CIElement aElement, object[] aCIDBAttributeEntries, object aValue, SortedDictionary<int, CIDBRow> aRows )
        {
            foreach ( object obj in aCIDBAttributeEntries )
            {
                if ( obj is CIDBAttributeCell )
                {
                    CIDBAttributeCell attribute = (CIDBAttributeCell) obj;

                    // If the property is an 'auto expand' entry, then don't look at this
                    // property, but instead look at the object itself
                    if ( attribute.Options == CIDBAttributeCell.TOptions.EAutoExpand )
                    {
                        // The object must be an element
                        CIElement element = aValue as CIElement;
                        if ( element == null )
                        {
                            throw new ArgumentException( "CIDBAttributeCell(TOptions.EAutoExpand) may only be applied to CIElement objects" );
                        }

                        TryAutoPopulateCells( element );
                    }
                    else
                    {
                        // Whether or not to create a row
                        bool makeRow = true;

                        // Convert attribute value to string. If the object is an enum, we'll check if it has
                        // a System.ComponentModel.Description attached to it, and use that in preference to a raw
                        // value.
                        string defaultValueString = ( attribute.DefaultValue != null ) ? attribute.DefaultValue.ToString() : null;
                        string propertyValueString = aValue.ToString();
                        if ( aValue.GetType().BaseType == typeof( Enum ) )
                        {
                            // Check if it supports System.ComponentModel.Description
                            FieldInfo fi = aValue.GetType().GetField( propertyValueString );
                            if ( fi != null )
                            {
                                DescriptionAttribute[] attributes = (DescriptionAttribute[]) fi.GetCustomAttributes( typeof( DescriptionAttribute ), false );
                                if ( attributes != null && attributes.Length > 0 )
                                {
                                    propertyValueString = attributes[ 0 ].Description;
                                }
                            }
                        }
                        else if ( attribute.Format.Length > 0 && aValue is IFormattable )
                        {
                            string formatSpec = attribute.Format;
                            IFormattable formattable = (IFormattable) aValue;
                            propertyValueString = formattable.ToString( formatSpec, null );

                            // Also get the default value if available
                            bool defaultIsFormattable = attribute.DefaultValue is IFormattable;
                            if ( attribute.DefaultValue != null && defaultIsFormattable )
                            {
                                formattable = (IFormattable) attribute.DefaultValue;
                                defaultValueString = formattable.ToString( formatSpec, null );
                            }
                        }

                        // If the value of the property is the same as the default, then don't
                        // show it.
                        if ( defaultValueString != null )
                        {
                            makeRow = ( defaultValueString.CompareTo( propertyValueString ) != 0 );
                        }

                        // Make a row
                        if ( makeRow )
                        {
                            CIDBRow row = new CIDBRow();
                            row.Add( new CIDBCell( attribute.Caption ) );
                            row.Add( new CIDBCell( propertyValueString, attribute.ForeColor, attribute.BackColor, attribute.Format ) );

                            // Add the row if it doesn't exist. If it does, then that implies
                            // duplicate ordering, which we treat as a programming error.
                            if ( aRows.ContainsKey( attribute.Order ) )
                            {
                                throw new Exception( string.Format( "Entity: [{0}] in element: [{1}] specifies order: {2} which is already in use",
                                                                    aEntityName, aElement, attribute.Order ) );
                            }

                            aRows.Add( attribute.Order, row );
                        }
                    }
                }
            }
        }
        #endregion

        #region From IEnumerable<CIDBRow>
        public IEnumerator<CIDBRow> GetEnumerator()
        {
            foreach ( CIDBRow row in iRows )
            {
                yield return row;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            foreach ( CIDBRow row in iRows )
            {
                yield return row;
            }
        }
        #endregion

        #region Data members
        private readonly CIElement iElement;
        private bool iAutoPopulate = false;
        private List<CIDBColumn> iColumns = new List<CIDBColumn>();
        private List<CIDBRow> iRows = new List<CIDBRow>();
		#endregion
    }
}