Example #1
0
            //*********************************************************************
            //  Constructor: ExcelTableRow()
            //
            /// <summary>
            /// Initializes a new instance of the <see cref="ExcelTableRow" />
            /// class.
            /// </summary>
            ///
            /// <param name="excelTableReader">
            /// The table reader object that owns this object.
            /// </param>
            //*********************************************************************

            public ExcelTableRow
            (
                ExcelTableReader excelTableReader
            )
            {
                m_oExcelTableReader = excelTableReader;

                AssertValid();
            }
        //*************************************************************************
        //  Method: ReadLayoutOrder()
        //
        /// <summary>
        /// If a layout order has been specified for a vertex, sets the vertex's
        /// layout order.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the layout order on.
        /// </param>
        ///
        /// <returns>
        /// true if a layout order was specified.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadLayoutOrder(
            ExcelTableReader.ExcelTableRow oRow,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            String sOrder;

            if ( !oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.LayoutOrder, out sOrder) )
            {
            return (false);
            }

            Single fOrder;

            if ( !Single.TryParse(sOrder, out fOrder) )
            {
            Range oInvalidCell = oRow.GetRangeForCell(
                VertexTableColumnNames.LayoutOrder);

            OnWorkbookFormatError( String.Format(

                "The cell {0} contains an invalid layout order.  The layout"
                + " order, which is optional, must be a number."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell)
                ),

                oInvalidCell
            );
            }

            oVertex.SetValue( ReservedMetadataKeys.SortableLayoutOrder, fOrder);

            return (true);
        }
        //*************************************************************************
        //  Method: FilterColumnNames()
        //
        /// <summary>
        /// Returns an array of table column names with some names filtered out.
        /// </summary>
        ///
        /// <param name="oExcelTableReader">
        /// Object that is reading the edge or vertex table.
        /// </param>
        ///
        /// <param name="oColumnNamesToExclude">
        /// HashSet of zero or more columns to exclude.
        /// </param>
        //*************************************************************************
        protected String[] FilterColumnNames(
            ExcelTableReader oExcelTableReader,
            HashSet<String> oColumnNamesToExclude
            )
        {
            Debug.Assert(oExcelTableReader != null);
            Debug.Assert(oColumnNamesToExclude != null);
            AssertValid();

            List<String> oFilteredColumnNames = new List<String>();

            foreach (String sColumnName in oExcelTableReader.ColumnNames)
            {
            if ( !oColumnNamesToExclude.Contains(sColumnName) )
            {
                oFilteredColumnNames.Add(sColumnName);
            }
            }

            return ( oFilteredColumnNames.ToArray() );
        }
        //*************************************************************************
        //  Method: ReadGroupVertexTable()
        //
        /// <summary>
        /// Reads the group vertex table.
        /// </summary>
        ///
        /// <param name="oGroupVertexTable">
        /// The group vertex table.
        /// </param>
        ///
        /// <param name="oReadWorkbookContext">
        /// Provides access to objects needed for converting an Excel workbook to a
        /// NodeXL graph.
        /// </param>
        ///
        /// <param name="oGroupNameDictionary">
        /// The key is the group name and the value is the GroupInformation object
        /// for the group.
        /// </param>
        ///
        /// <param name="oGraph">
        /// Graph to add group data to.
        /// </param>
        //*************************************************************************
        protected void ReadGroupVertexTable(
            ListObject oGroupVertexTable,
            ReadWorkbookContext oReadWorkbookContext,
            Dictionary<String, GroupInformation> oGroupNameDictionary,
            IGraph oGraph
            )
        {
            Debug.Assert(oGroupVertexTable != null);
            Debug.Assert(oReadWorkbookContext != null);
            Debug.Assert(oGroupNameDictionary != null);
            Debug.Assert(oGraph != null);
            AssertValid();

            Dictionary<String, IVertex> oVertexNameDictionary =
            oReadWorkbookContext.VertexNameDictionary;

            ExcelTableReader oExcelTableReader =
            new ExcelTableReader(oGroupVertexTable);

            foreach ( ExcelTableReader.ExcelTableRow oRow in
            oExcelTableReader.GetRows() )
            {
            // Get the group vertex information from the row.

            String sGroupName, sVertexName;

            if (
                !oRow.TryGetNonEmptyStringFromCell(
                    GroupVertexTableColumnNames.GroupName, out sGroupName)
                ||
                !oRow.TryGetNonEmptyStringFromCell(
                    GroupVertexTableColumnNames.VertexName, out sVertexName)
                )
            {
                continue;
            }

            // Get the group information for the vertex and store the group
            // information in the vertex.

            GroupInformation oGroupInformation;
            IVertex oVertex;

            if (
                !oGroupNameDictionary.TryGetValue(sGroupName,
                    out oGroupInformation)
                ||
                !oVertexNameDictionary.TryGetValue(sVertexName,
                    out oVertex)
                )
            {
                continue;
            }

            oVertex.SetValue(ReservedMetadataKeys.PerColor,
                oGroupInformation.VertexColor);

            oVertex.SetValue(ReservedMetadataKeys.PerVertexShape,
                oGroupInformation.VertexShape);

            if (oReadWorkbookContext.SaveGroupVertices)
            {
                Debug.Assert(oGroupInformation.Vertices != null);

                oGroupInformation.Vertices.Add(oVertex);
            }
            }
        }
        //*************************************************************************
        //  Method: ReadClusterVertexTable()
        //
        /// <summary>
        /// Reads the cluster vertex table.
        /// </summary>
        ///
        /// <param name="oClusterVertexTable">
        /// The cluster vertex table.
        /// </param>
        ///
        /// <param name="oVertexNameDictionary">
        /// The key is the vertex name from the edge or vertex worksheet and the
        /// value is the IVertex object.
        /// </param>
        ///
        /// <param name="oClusterNameDictionary">
        /// The key is the cluster name and the value is the ClusterInformation
        /// object for the cluster.
        /// </param>
        ///
        /// <param name="oGraph">
        /// Graph to add cluster data to.
        /// </param>
        ///
        /// <returns>
        /// A dictionary.  The key is the cluster name and the value is the
        /// ClusterInformation object for the cluster.
        /// </returns>
        //*************************************************************************
        protected void ReadClusterVertexTable(
            ListObject oClusterVertexTable,
            Dictionary<String, ClusterInformation> oClusterNameDictionary,
            Dictionary<String, IVertex> oVertexNameDictionary,
            IGraph oGraph
            )
        {
            Debug.Assert(oClusterVertexTable != null);
            Debug.Assert(oClusterNameDictionary != null);
            Debug.Assert(oVertexNameDictionary != null);
            Debug.Assert(oGraph != null);
            AssertValid();

            ExcelTableReader oExcelTableReader =
            new ExcelTableReader(oClusterVertexTable);

            foreach ( ExcelTableReader.ExcelTableRow oRow in
            oExcelTableReader.GetRows() )
            {
            // Get the cluster vertex information from the row.

            String sClusterName, sVertexName;

            if (
                !oRow.TryGetNonEmptyStringFromCell(
                    ClusterVertexTableColumnNames.ClusterName,
                    out sClusterName)
                ||
                !oRow.TryGetNonEmptyStringFromCell(
                    ClusterVertexTableColumnNames.VertexName, out sVertexName)
                )
            {
                continue;
            }

            // Get the cluster information for the vertex and store the cluster
            // information in the vertex.

            ClusterInformation oClusterInformation;
            IVertex oVertex;

            if (
                !oClusterNameDictionary.TryGetValue(sClusterName,
                    out oClusterInformation)
                ||
                !oVertexNameDictionary.TryGetValue(sVertexName,
                    out oVertex)
                )
            {
                continue;
            }

            oVertex.SetValue(ReservedMetadataKeys.PerColor,
                oClusterInformation.VertexColor);

            oVertex.SetValue(ReservedMetadataKeys.PerVertexShape,
                oClusterInformation.VertexShape);
            }
        }
        //*************************************************************************
        //  Method: ReadMarked()
        //
        /// <summary>
        /// If a marked flag has been specified for a vertex, sets the vertex's
        /// marked flag.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oBooleanConverter">
        /// Object that converts a Boolean between values used in the Excel
        /// workbook and values used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the marked flag on.
        /// </param>
        ///
        /// <remarks>
        /// "Marking" is something the user does for himself.  A marked vertex
        /// doesn't behave differently from an unmarked vertex.)
        /// </remarks>
        //*************************************************************************
        protected void ReadMarked(
            ExcelTableReader.ExcelTableRow oRow,
            BooleanConverter oBooleanConverter,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oBooleanConverter != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            Boolean bMarked;

            if ( !TryGetBoolean(oRow, VertexTableColumnNames.Marked,
            oBooleanConverter, out bMarked) )
            {
            return;
            }

            oVertex.SetValue(ReservedMetadataKeys.Marked, bMarked);

            return;
        }
        //*************************************************************************
        //  Method: ReadRadius()
        //
        /// <summary>
        /// If a radius has been specified for a vertex, sets the vertex's radius.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertexRadiusConverter">
        /// Object that converts a vertex radius between values used in the Excel
        /// workbook and values used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the radius on.
        /// </param>
        ///
        /// <returns>
        /// If a radius has been specified for the vertex, the radius in workbook
        /// units is returned.  Otherwise, a Nullable that has no value is
        /// returned.
        /// </returns>
        //*************************************************************************
        protected Nullable<Single> ReadRadius(
            ExcelTableReader.ExcelTableRow oRow,
            VertexRadiusConverter oVertexRadiusConverter,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            Debug.Assert(oVertexRadiusConverter != null);
            AssertValid();

            String sRadius;

            if ( !oRow.TryGetNonEmptyStringFromCell(VertexTableColumnNames.Radius,
            out sRadius) )
            {
            return ( new Nullable<Single>() );
            }

            Single fRadius;

            if ( !Single.TryParse(sRadius, out fRadius) )
            {
            Range oInvalidCell = oRow.GetRangeForCell(
                VertexTableColumnNames.Radius);

            OnWorkbookFormatError( String.Format(

                "The cell {0} contains an invalid size.  The vertex size,"
                + " which is optional, must be a number.  Any number is"
                + " acceptable, although {1} is used for any number less than"
                + " {1} and {2} is used for any number greater than {2}."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell),
                VertexRadiusConverter.MinimumRadiusWorkbook,
                VertexRadiusConverter.MaximumRadiusWorkbook
                ),

                oInvalidCell
            );
            }

            oVertex.SetValue( ReservedMetadataKeys.PerVertexRadius,
            oVertexRadiusConverter.WorkbookToGraph(fRadius) );

            return ( new Nullable<Single>(fRadius) );
        }
        //*************************************************************************
        //  Method: ReadColor()
        //
        /// <summary>
        /// If a color has been specified for an edge or vertex, sets the edge's
        /// or vertex's color.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row to check.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column to check.
        /// </param>
        ///
        /// <param name="oEdgeOrVertex">
        /// Edge or vertex to set the color on.
        /// </param>
        ///
        /// <param name="sColorKey">
        /// Name of the metadata key that stores the color on the edge or vertex.
        /// </param>
        ///
        /// <param name="oColorConverter2">
        /// Object for converting the color from a string to a Color.
        /// </param>
        //*************************************************************************
        protected void ReadColor(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            IMetadataProvider oEdgeOrVertex,
            String sColorKey,
            ColorConverter2 oColorConverter2
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            Debug.Assert(oEdgeOrVertex != null);
            Debug.Assert( !String.IsNullOrEmpty(sColorKey) );
            Debug.Assert(oColorConverter2 != null);
            AssertValid();

            Color oColor;

            if ( TryGetColor(oRow, sColumnName, oColorConverter2, out oColor) )
            {
            oEdgeOrVertex.SetValue(sColorKey, oColor);
            }
        }
        //*************************************************************************
        //  Method: TryGetBoolean()
        //
        /// <summary>
        /// Attempts to get a Boolean value from a worksheet cell.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the data.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column containing the Boolean value.
        /// </param>
        ///
        /// <param name="oBooleanConverter">
        /// Object for converting the Boolean value from a string to a Boolean.
        /// </param>
        ///
        /// <param name="bBoolean">
        /// Where the Boolean value gets stored if true is returned.
        /// </param>
        ///
        /// <returns>
        /// true if the specified cell contains a valid Boolean value.
        /// </returns>
        ///
        /// <remarks>
        /// If the specified cell is empty, false is returned.  If the cell
        /// contains a valid Boolean value, the value gets stored at <paramref
        /// name="bBoolean" /> and true is returned.  If the cell contains an
        /// invalid Boolean, a <see cref="WorkbookFormatException" /> is thrown.
        /// </remarks>
        //*************************************************************************
        protected Boolean TryGetBoolean(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            BooleanConverter oBooleanConverter,
            out Boolean bBoolean
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            Debug.Assert(oBooleanConverter != null);
            AssertValid();

            bBoolean = false;
            String sBoolean;

            if ( !oRow.TryGetNonEmptyStringFromCell(sColumnName, out sBoolean) )
            {
            return (false);
            }

            if ( !oBooleanConverter.TryWorkbookToGraph(sBoolean, out bBoolean) )
            {
            OnWorkbookFormatErrorWithDropDown(oRow, sColumnName, "value");
            }

            return (true);
        }
        //*************************************************************************
        //  Method: ReadAlpha()
        //
        /// <summary>
        /// If an alpha has been specified for an edge or vertex, sets the alpha
        /// value on the edge or vertex.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the edge or vertex data.
        /// </param>
        ///
        /// <param name="oEdgeOrVertex">
        /// Edge or vertex to set the alpha on.
        /// </param>
        ///
        /// <returns>
        /// true if the edge or vertex was hidden.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadAlpha(
            ExcelTableReader.ExcelTableRow oRow,
            IMetadataProvider oEdgeOrVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oEdgeOrVertex != null);

            AssertValid();

            String sString;

            if ( !oRow.TryGetNonEmptyStringFromCell(CommonTableColumnNames.Alpha,
            out sString) )
            {
            return (false);
            }

            Single fAlpha;

            if ( !Single.TryParse(sString, out fAlpha) )
            {
            Range oInvalidCell = oRow.GetRangeForCell(
                CommonTableColumnNames.Alpha);

            OnWorkbookFormatError( String.Format(

                "The cell {0} contains an invalid opacity.  The opacity,"
                + " which is optional, must be a number.  Any number is"
                + " acceptable, although {1} (transparent) is used for any"
                + " number less than {1} and {2} (opaque) is used for any"
                + " number greater than {2}."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell),
                AlphaConverter.MinimumAlphaWorkbook,
                AlphaConverter.MaximumAlphaWorkbook
                ),

                oInvalidCell
            );
            }

            fAlpha = m_oAlphaConverter.WorkbookToGraph(fAlpha);

            oEdgeOrVertex.SetValue(ReservedMetadataKeys.PerAlpha, fAlpha);

            return (fAlpha == 0);
        }
        //*************************************************************************
        //  Method: ReadCellAndSetMetadata()
        //
        /// <summary>
        /// If a cell is not empty, sets a metadata value on an edge or vertex to
        /// the cell contents.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row to check.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column to check.
        /// </param>
        ///
        /// <param name="oEdgeOrVertex">
        /// Edge or vertex to set the metadata value on.
        /// </param>
        ///
        /// <param name="sKeyName">
        /// Name of the metadata key to set.
        /// </param>
        ///
        /// <returns>
        /// true if the metadata value was set on the edge or vertex.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadCellAndSetMetadata(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            IMetadataProvider oEdgeOrVertex,
            String sKeyName
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            Debug.Assert(oEdgeOrVertex != null);
            Debug.Assert( !String.IsNullOrEmpty(sKeyName) );
            AssertValid();

            String sNonEmptyString;

            if ( !oRow.TryGetNonEmptyStringFromCell(sColumnName,
            out sNonEmptyString) )
            {
            return (false);
            }

            oEdgeOrVertex.SetValue(sKeyName, sNonEmptyString);

            return (true);
        }
        //*************************************************************************
        //  Method: ReadAllColumns()
        //
        /// <summary>
        /// Reads all columns in a table row and stores the cell values as metadata
        /// on an edge or vertex.
        /// </summary>
        ///
        /// <param name="oExcelTableReader">
        /// Object that is reading the edge or vertex table.
        /// </param>
        ///
        /// <param name="oRow">
        /// Row containing the edge or vertex data.
        /// </param>
        ///
        /// <param name="oEdgeOrVertex">
        /// Edge or vertex to set the metadata on.
        /// </param>
        ///
        /// <param name="oColumnNamesToExclude">
        /// HashSet of zero or more columns to exclude.
        /// </param>
        //*************************************************************************
        protected void ReadAllColumns(
            ExcelTableReader oExcelTableReader,
            ExcelTableReader.ExcelTableRow oRow,
            IMetadataProvider oEdgeOrVertex,
            HashSet<String> oColumnNamesToExclude
            )
        {
            Debug.Assert(oExcelTableReader != null);
            Debug.Assert(oRow != null);
            Debug.Assert(oEdgeOrVertex != null);
            Debug.Assert(oColumnNamesToExclude != null);
            AssertValid();

            foreach (String sColumnName in oExcelTableReader.ColumnNames)
            {
            String sValue;

            if ( !oColumnNamesToExclude.Contains(sColumnName) &&
                oRow.TryGetNonEmptyStringFromCell(sColumnName, out sValue) )
            {
                oEdgeOrVertex.SetValue(sColumnName, sValue);
            }
            }
        }
        //*************************************************************************
        //  Method: OnWorkbookFormatErrorWithDropDown()
        //
        /// <summary>
        /// Handles a workbook format error that prevents a graph from being
        /// created, where the invalid cell has a drop-down list.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the invalid cell.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column containing the invalid cell.
        /// </param>
        ///
        /// <param name="sInvalidCellDescription">
        /// Description of the invalid cell.  Sample: "shape".
        /// </param>
        //*************************************************************************
        protected void OnWorkbookFormatErrorWithDropDown(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            String sInvalidCellDescription
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            Debug.Assert( !String.IsNullOrEmpty(sInvalidCellDescription) );
            AssertValid();

            Range oInvalidCell = oRow.GetRangeForCell(sColumnName);

            OnWorkbookFormatError( String.Format(

            "The cell {0} contains an invalid {1}.  Try selecting"
            + " from the cell's drop-down list instead."
            ,
            ExcelUtil.GetRangeAddress(oInvalidCell),
            sInvalidCellDescription
            ),

            oInvalidCell
            );
        }
        //*************************************************************************
        //  Method: OnInvalidVisibility()
        //
        /// <summary>
        /// Handles an invalid edge or vertex visibility.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the invalid visibility.
        /// </param>
        ///
        /// <remarks>
        /// This method throws a <see cref="WorkbookFormatException" />.
        /// </remarks>
        //*************************************************************************
        protected void OnInvalidVisibility(
            ExcelTableReader.ExcelTableRow oRow
            )
        {
            Debug.Assert(oRow != null);
            AssertValid();

            OnWorkbookFormatErrorWithDropDown(oRow,
            CommonTableColumnNames.Visibility, "visibility");
        }
        //*************************************************************************
        //  Method: ReadLocation()
        //
        /// <summary>
        /// If a location has been specified for a vertex, sets the vertex's
        /// location.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertexLocationConverter">
        /// Object that converts a vertex location between coordinates used in the
        /// Excel workbook and coordinates used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the location on.
        /// </param>
        ///
        /// <returns>
        /// true if a location was specified.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadLocation(
            ExcelTableReader.ExcelTableRow oRow,
            VertexLocationConverter oVertexLocationConverter,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertexLocationConverter != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            String sX;

            Boolean bHasX = oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.X, out sX);

            String sY;

            Boolean bHasY = oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.Y, out sY);

            if (bHasX != bHasY)
            {
            // X or Y alone won't do.

            goto Error;
            }

            if (!bHasX && !bHasY)
            {
            return (false);
            }

            Single fX, fY;

            if ( !Single.TryParse(sX, out fX) || !Single.TryParse(sY, out fY) )
            {
            goto Error;
            }

            // Transform the location from workbook coordinates to graph
            // coordinates.

            oVertex.Location = oVertexLocationConverter.WorkbookToGraph(fX, fY);

            return (true);

            Error:

            Range oInvalidCell = oRow.GetRangeForCell(
                VertexTableColumnNames.X);

            OnWorkbookFormatError( String.Format(

                "There is a problem with the vertex location at {0}.  If you"
                + " enter a vertex location, it must include both X and Y"
                + " numbers.  Any numbers are acceptable, although {1} is used"
                + " for any number less than {1} and and {2} is used for any"
                + " number greater than {2}."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell),

                VertexLocationConverter.MinimumXYWorkbook.ToString(
                    ExcelTemplateForm.Int32Format),

                VertexLocationConverter.MaximumXYWorkbook.ToString(
                    ExcelTemplateForm.Int32Format)
                ),

                oInvalidCell
                );

            // Make the compiler happy.

            return (false);
        }
        //*************************************************************************
        //  Method: TryGetColor()
        //
        /// <summary>
        /// Attempts to get a color from a worksheet cell.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row to check.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column to check.
        /// </param>
        ///
        /// <param name="oColorConverter2">
        /// Object for converting the color from a string to a Color.
        /// </param>
        ///
        /// <param name="oColor">
        /// Where the color gets stored if true is returned.
        /// </param>
        ///
        /// <returns>
        /// true if the specified cell contains a valid color.
        /// </returns>
        ///
        /// <remarks>
        /// If the specified cell is empty, false is returned.  If the cell
        /// contains a valid color, the color gets stored at <paramref
        /// name="oColor" /> and true is returned.  If the cell contains an invalid
        /// color, a <see cref="WorkbookFormatException" /> is thrown.
        /// </remarks>
        //*************************************************************************
        protected Boolean TryGetColor(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            ColorConverter2 oColorConverter2,
            out Color oColor
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            Debug.Assert(oColorConverter2 != null);
            AssertValid();

            oColor = Color.Empty;

            String sColor;

            if ( !oRow.TryGetNonEmptyStringFromCell(sColumnName, out sColor) )
            {
            return (false);
            }

            if ( !oColorConverter2.TryWorkbookToGraph(sColor, out oColor) )
            {
            Range oInvalidCell = oRow.GetRangeForCell(sColumnName);

            OnWorkbookFormatError( String.Format(

                "The cell {0} contains an unrecognized color.  Right-click the"
                + " cell and select Select Color on the right-click menu."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell)
                ),

                oInvalidCell
            );
            }

            return (true);
        }
        //*************************************************************************
        //  Method: ReadLocked()
        //
        /// <summary>
        /// If a locked flag has been specified for a vertex, sets the vertex's
        /// locked flag.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oBooleanConverter">
        /// Object that converts a Boolean between values used in the Excel
        /// workbook and values used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="bLocationSpecified">
        /// true if a location was specified for the vertex.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the lock flag on.
        /// </param>
        ///
        /// <remarks>
        /// "Locked" means "prevent the layout algorithm from moving the vertex."
        /// </remarks>
        //*************************************************************************
        protected void ReadLocked(
            ExcelTableReader.ExcelTableRow oRow,
            BooleanConverter oBooleanConverter,
            Boolean bLocationSpecified,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oBooleanConverter != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            Boolean bLocked;

            if ( !TryGetBoolean(oRow, VertexTableColumnNames.Locked,
            oBooleanConverter, out bLocked) )
            {
            return;
            }

            if (bLocked && !bLocationSpecified)
            {
            Range oInvalidCell = oRow.GetRangeForCell(
                VertexTableColumnNames.Locked);

            OnWorkbookFormatError( String.Format(

                "The cell {0} indicates that the vertex should be locked,"
                + " but the vertex has no X and Y location values.  Either"
                + " clear the lock or specify a vertex location."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell)
                ),

                oInvalidCell
            );
            }

            oVertex.SetValue(ReservedMetadataKeys.LockVertexLocation, bLocked);
        }
        //*************************************************************************
        //  Method: TryGetVertexShape()
        //
        /// <summary>
        /// Attempts to get a vertex shape from a worksheet cell.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="sColumnName">
        /// Name of the column containing the vertex shape.
        /// </param>
        ///
        /// <param name="eShape">
        /// Where the vertex shape gets stored if true is returned.
        /// </param>
        ///
        /// <returns>
        /// true if the specified cell contains a valid vertex shape.
        /// </returns>
        ///
        /// <remarks>
        /// If the specified shape cell is empty, false is returned.  If the cell
        /// contains a valid vertex shape, the shape gets stored at <paramref
        /// name="eShape" /> and true is returned.  If the cell contains an invalid
        /// shape, a <see cref="WorkbookFormatException" /> is thrown.
        /// </remarks>
        //*************************************************************************
        protected Boolean TryGetVertexShape(
            ExcelTableReader.ExcelTableRow oRow,
            String sColumnName,
            out VertexShape eShape
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert( !String.IsNullOrEmpty(sColumnName) );
            AssertValid();

            eShape = VertexShape.Circle;
            String sShape;

            if ( !oRow.TryGetNonEmptyStringFromCell(sColumnName, out sShape) )
            {
            return (false);
            }

            VertexShapeConverter oVertexShapeConverter =
            new VertexShapeConverter();

            if ( !oVertexShapeConverter.TryWorkbookToGraph(sShape, out eShape) )
            {
            OnWorkbookFormatErrorWithDropDown(oRow, sColumnName, "shape");
            }

            return (true);
        }
        //*************************************************************************
        //  Method: ReadPolarCoordinates()
        //
        /// <summary>
        /// If polar coordinates have been specified for a vertex, sets the
        /// vertex's polar coordinates.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the polar coordinates on.
        /// </param>
        ///
        /// <returns>
        /// true if a location was specified.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadPolarCoordinates(
            ExcelTableReader.ExcelTableRow oRow,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            String sR;

            Boolean bHasR = oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.PolarR, out sR);

            String sAngle;

            Boolean bHasAngle = oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.PolarAngle, out sAngle);

            if (bHasR != bHasAngle)
            {
            // R or Angle alone won't do.

            goto Error;
            }

            if (!bHasR && !bHasAngle)
            {
            return (false);
            }

            Single fR, fAngle;

            if ( !Single.TryParse(sR, out fR) ||
            !Single.TryParse(sAngle, out fAngle) )
            {
            goto Error;
            }

            oVertex.SetValue(ReservedMetadataKeys.PolarLayoutCoordinates,
            new SinglePolarCoordinates(fR, fAngle) );

            return (true);

            Error:

            Range oInvalidCell = oRow.GetRangeForCell(
                VertexTableColumnNames.PolarR);

            OnWorkbookFormatError( String.Format(

                "There is a problem with the vertex polar coordinates at {0}."
                + " If you enter polar coordinates, they must include both"
                + " {1} and {2} numbers.  Any numbers are acceptable."
                + "\r\n\r\n"
                + "Polar coordinates are used only when a Layout of Polar"
                + " or Polar Absolute is selected in the graph pane."
                ,
                ExcelUtil.GetRangeAddress(oInvalidCell),
                VertexTableColumnNames.PolarR,
                VertexTableColumnNames.PolarAngle
                ),

                oInvalidCell
                );

            // Make the compiler happy.

            return (false);
        }
        //*************************************************************************
        //  Method: ReadVertexTable()
        //
        /// <summary>
        /// Reads the vertex table and adds the contents to a graph.
        /// </summary>
        ///
        /// <param name="oVertexTable">
        /// Table that contains the vertex data.
        /// </param>
        ///
        /// <param name="oReadWorkbookContext">
        /// Provides access to objects needed for converting an Excel workbook to a
        /// NodeXL graph.
        /// </param>
        ///
        /// <param name="oGraph">
        /// Graph to add vertices to.
        /// </param>
        ///
        /// <param name="bLayoutOrderSet">
        /// Gets set to true if a vertex layout order was specified for at least
        /// one vertex, or false otherwise.
        /// </param>
        ///
        /// <param name="bToolTipSet">
        /// Gets set to true if a tooltip was specified for at least one vertex, or
        /// false otherwise.
        /// </param>
        //*************************************************************************
        protected void ReadVertexTable(
            ListObject oVertexTable,
            ReadWorkbookContext oReadWorkbookContext,
            IGraph oGraph,
            out Boolean bLayoutOrderSet,
            out Boolean bToolTipSet
            )
        {
            Debug.Assert(oVertexTable != null);
            Debug.Assert(oReadWorkbookContext != null);
            Debug.Assert(oGraph != null);
            AssertValid();

            bLayoutOrderSet = bToolTipSet = false;

            if (GetTableColumnIndex(oVertexTable,
            VertexTableColumnNames.VertexName, false) == NoSuchColumn)
            {
            // Nothing can be done without vertex names.

            return;
            }

            Boolean bReadAllEdgeAndVertexColumns =
            oReadWorkbookContext.ReadAllEdgeAndVertexColumns;

            if (oReadWorkbookContext.FillIDColumns)
            {
            FillIDColumn(oVertexTable);
            }

            // Get the names of all the column pairs that are used to add custom
            // menu items to the vertex context menu in the graph.

            TableColumnAdder oTableColumnAdder = new TableColumnAdder();

            ICollection< KeyValuePair<String, String> > aoCustomMenuItemPairNames =
            oTableColumnAdder.GetColumnPairNames(oVertexTable,
                VertexTableColumnNames.CustomMenuItemTextBase,
                VertexTableColumnNames.CustomMenuItemActionBase);

            IVertexCollection oVertices = oGraph.Vertices;

            Dictionary<String, IVertex> oVertexNameDictionary =
            oReadWorkbookContext.VertexNameDictionary;

            Dictionary<Int32, IIdentityProvider> oEdgeRowIDDictionary =
            oReadWorkbookContext.EdgeRowIDDictionary;

            BooleanConverter oBooleanConverter =
            oReadWorkbookContext.BooleanConverter;

            VertexVisibilityConverter oVertexVisibilityConverter =
            new VertexVisibilityConverter();

            VertexLabelPositionConverter oVertexLabelPositionConverter =
            new VertexLabelPositionConverter();

            ExcelTableReader oExcelTableReader =
            new ExcelTableReader(oVertexTable);

            HashSet<String> oColumnNamesToExclude = new HashSet<String>(
            new String[] {
                VertexTableColumnNames.VertexName
                } );

            foreach ( ExcelTableReader.ExcelTableRow oRow in
            oExcelTableReader.GetRows() )
            {
            // Get the name of the vertex.

            String sVertexName;

            if ( !oRow.TryGetNonEmptyStringFromCell(
                VertexTableColumnNames.VertexName, out sVertexName) )
            {
                continue;
            }

            // If the vertex was added to the graph as part of an edge,
            // retrieve the vertex.

            IVertex oVertex;

            if ( !oVertexNameDictionary.TryGetValue(sVertexName, out oVertex) )
            {
                oVertex = null;
            }

            // Assume a default visibility.

            Visibility eVisibility = Visibility.ShowIfInAnEdge;
            String sVisibility;

            if ( oRow.TryGetNonEmptyStringFromCell(
                    CommonTableColumnNames.Visibility, out sVisibility) )
            {
                if ( !oVertexVisibilityConverter.TryWorkbookToGraph(
                    sVisibility, out eVisibility) )
                {
                    OnInvalidVisibility(oRow);
                }
            }

            switch (eVisibility)
            {
                case Visibility.ShowIfInAnEdge:

                    // If the vertex is part of an edge, show it using the
                    // specified vertex attributes.  Otherwise, skip the vertex
                    // row.

                    if (oVertex == null)
                    {
                        continue;
                    }

                    break;

                case Visibility.Skip:

                    // Skip the vertex row and any edge rows that include the
                    // vertex.  Do not read them into the graph.

                    if (oVertex != null)
                    {
                        // Remove the vertex and its incident edges from the
                        // graph and dictionaries.

                        foreach (IEdge oIncidentEdge in oVertex.IncidentEdges)
                        {
                            if (oIncidentEdge.Tag is Int32)
                            {
                                oEdgeRowIDDictionary.Remove(
                                    (Int32)oIncidentEdge.Tag);
                            }
                        }

                        oVertexNameDictionary.Remove(sVertexName);

                        oVertices.Remove(oVertex);

                        // (The vertex doesn't get added to
                        // ReadWorkbookContext.VertexIDDictionary until after
                        // this switch statement, so it doesn't need to be
                        // removed from that dictionary.)
                    }

                    continue;

                case Visibility.Hide:

                    // If the vertex is part of an edge, hide it and its
                    // incident edges.  Otherwise, skip the vertex row.

                    if (oVertex == null)
                    {
                        continue;
                    }

                    // Hide the vertex and its incident edges.

                    oVertex.SetValue(ReservedMetadataKeys.Visibility,
                        VisibilityKeyValue.Hidden);

                    foreach (IEdge oIncidentEdge in oVertex.IncidentEdges)
                    {
                        oIncidentEdge.SetValue(ReservedMetadataKeys.Visibility,
                            VisibilityKeyValue.Hidden);
                    }

                    break;

                case Visibility.Show:

                    // Show the vertex using the specified attributes
                    // regardless of whether it is part of an edge.

                    if (oVertex == null)
                    {
                        oVertex = CreateVertex(sVertexName, oVertices,
                            oVertexNameDictionary);
                    }

                    break;

                default:

                    Debug.Assert(false);
                    break;
            }

            Debug.Assert(oVertex != null);

            // If there is an ID column, add the vertex to the vertex ID
            // dictionary and set the vertex's Tag to the ID.

            AddToRowIDDictionary(oRow, oVertex,
                oReadWorkbookContext.VertexRowIDDictionary);

            if (bReadAllEdgeAndVertexColumns)
            {
                // All columns except the vertex name should be read and stored
                // as metadata on the vertex.

                ReadAllColumns( oExcelTableReader, oRow, oVertex,
                    oColumnNamesToExclude);

                continue;
            }

            // Layout order.

            if ( ReadLayoutOrder(oRow, oVertex) )
            {
                bLayoutOrderSet = true;
            }

            // Location and Locked.

            if (!oReadWorkbookContext.IgnoreVertexLocations)
            {
                Boolean bLocationSpecified = false;

                bLocationSpecified = ReadLocation(oRow,
                    oReadWorkbookContext.VertexLocationConverter, oVertex);

                ReadLocked(oRow, oBooleanConverter, bLocationSpecified,
                    oVertex);
            }

            // Polar coordinates.

            ReadPolarCoordinates(oRow, oVertex);

            // Marked.

            ReadMarked(oRow, oBooleanConverter, oVertex);

            // Custom menu items.

            if (aoCustomMenuItemPairNames.Count > 0)
            {
                ReadCustomMenuItems(oRow, aoCustomMenuItemPairNames, oVertex);
            }

            // Alpha.

            ReadAlpha(oRow, oVertex);

            // Tooltip.

            if ( ReadCellAndSetMetadata(oRow, VertexTableColumnNames.ToolTip,
                oVertex, ReservedMetadataKeys.VertexToolTip) )
            {
                bToolTipSet = true;
            }

            // Label.

            if (oReadWorkbookContext.ReadVertexLabels)
            {
                ReadCellAndSetMetadata(oRow, VertexTableColumnNames.Label,
                    oVertex, ReservedMetadataKeys.PerVertexLabel);
            }

            // Label fill color.

            ReadColor(oRow, VertexTableColumnNames.LabelFillColor, oVertex,
                ReservedMetadataKeys.PerVertexLabelFillColor,
                oReadWorkbookContext.ColorConverter2);

            // Label position.

            ReadLabelPosition(oRow, oVertexLabelPositionConverter, oVertex);

            // Radius.

            Nullable<Single> oRadiusWorkbook = new Nullable<Single>();

            oRadiusWorkbook = ReadRadius(oRow,
                oReadWorkbookContext.VertexRadiusConverter, oVertex);

            // Shape.

            VertexShape eVertexShape;

            if ( !ReadShape(oRow, oVertex, out eVertexShape) )
            {
                eVertexShape = oReadWorkbookContext.DefaultVertexShape;
            }

            // Label font size.

            if (eVertexShape == VertexShape.Label && oRadiusWorkbook.HasValue)
            {
                // The vertex radius is used to specify font size when the
                // shape is Label.

                oVertex.SetValue( ReservedMetadataKeys.PerVertexLabelFontSize,
                    oReadWorkbookContext.VertexRadiusConverter.
                        WorkbookToLabelFontSize(oRadiusWorkbook.Value) );
            }

            // Image URI.

            if (eVertexShape == VertexShape.Image &&
                oReadWorkbookContext.ReadVertexImages)
            {
                ReadImageUri(oRow, oVertex,
                    oReadWorkbookContext.VertexRadiusConverter,

                    oRadiusWorkbook.HasValue ? oRadiusWorkbook :
                        oReadWorkbookContext.DefaultVertexImageSize
                    );
            }

            // Color

            ReadColor(oRow, VertexTableColumnNames.Color, oVertex,
                ReservedMetadataKeys.PerColor,
                oReadWorkbookContext.ColorConverter2);
            }

            if (bReadAllEdgeAndVertexColumns)
            {
            // Store the vertex column names on the graph.

            oGraph.SetValue( ReservedMetadataKeys.AllVertexMetadataKeys,
                FilterColumnNames(oExcelTableReader, oColumnNamesToExclude) );
            }
        }
        //*************************************************************************
        //  Method: ReadShape()
        //
        /// <summary>
        /// If a shape has been specified for a vertex, sets the vertex's shape.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the shape on.
        /// </param>
        ///
        /// <param name="eShape">
        /// Where the shape gets stored if true is returned.
        /// </param>
        ///
        /// <returns>
        /// true if a shape has been specified for the vertex.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadShape(
            ExcelTableReader.ExcelTableRow oRow,
            IVertex oVertex,
            out VertexShape eShape
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            if ( TryGetVertexShape(oRow, VertexTableColumnNames.Shape,
            out eShape) )
            {
            oVertex.SetValue(ReservedMetadataKeys.PerVertexShape, eShape);
            return (true);
            }

            return (false);
        }
        //*************************************************************************
        //  Method: ReadCustomMenuItems()
        //
        /// <summary>
        /// If custom menu items have been specified for a vertex, stores the
        /// custom menu item information in the vertex.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="aoCustomMenuItemPairNames">
        /// Collection of pairs of column names, one element for each pair of
        /// columns that are used to add custom menu items to the vertex context
        /// menu in the graph.  They key is the name of the custom menu item text
        /// and the value is the name of the custom menu item action.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to add custom menu item information to.
        /// </param>
        //*************************************************************************
        protected void ReadCustomMenuItems(
            ExcelTableReader.ExcelTableRow oRow,
            ICollection< KeyValuePair<String, String> > aoCustomMenuItemPairNames,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(aoCustomMenuItemPairNames != null);
            Debug.Assert(oVertex != null);
            AssertValid();

            // List of string pairs, one pair for each custom menu item to add to
            // the vertex's context menu in the graph.  The key is the custom menu
            // item text and the value is the custom menu item action.

            List<KeyValuePair<String, String>> oCustomMenuItemInformation =
            new List<KeyValuePair<String, String>>();

            foreach (KeyValuePair<String, String> oPairNames in
            aoCustomMenuItemPairNames)
            {
            String sCustomMenuItemText, sCustomMenuItemAction;

            // Both the menu item text and menu item action must be specified.
            // Skip the pair if either is missing.

            if (
                !oRow.TryGetNonEmptyStringFromCell(oPairNames.Key,
                    out sCustomMenuItemText)
                ||
                !oRow.TryGetNonEmptyStringFromCell(oPairNames.Value,
                    out sCustomMenuItemAction)
                )
            {
                continue;
            }

            Int32 iCustomMenuItemTextLength = sCustomMenuItemText.Length;

            if (iCustomMenuItemTextLength > MaximumCustomMenuItemTextLength)
            {
                Range oInvalidCell = oRow.GetRangeForCell(oPairNames.Key);

                OnWorkbookFormatError( String.Format(

                    "The cell {0} contains custom menu item text that is {1}"
                    + " characters long.  Custom menu item text can't be"
                    + " longer than {2} characters."
                    ,
                    ExcelUtil.GetRangeAddress(oInvalidCell),
                    iCustomMenuItemTextLength,
                    MaximumCustomMenuItemTextLength
                    ),

                    oInvalidCell
                    );
            }

            oCustomMenuItemInformation.Add( new KeyValuePair<String, String>(
                sCustomMenuItemText, sCustomMenuItemAction) );
            }

            if (oCustomMenuItemInformation.Count > 0)
            {
            oVertex.SetValue( ReservedMetadataKeys.CustomContextMenuItems,
                oCustomMenuItemInformation.ToArray() );
            }
        }
        //*************************************************************************
        //  Method: ReadClusterTable()
        //
        /// <summary>
        /// Reads the cluster table.
        /// </summary>
        ///
        /// <param name="oClusterTable">
        /// The cluster table.
        /// </param>
        ///
        /// <param name="oReadWorkbookContext">
        /// Provides access to objects needed for converting an Excel workbook to a
        /// NodeXL graph.
        /// </param>
        ///
        /// <returns>
        /// A dictionary.  The key is the cluster name and the value is a
        /// ClusterInformation object for the cluster.
        /// </returns>
        //*************************************************************************
        protected Dictionary<String, ClusterInformation> ReadClusterTable(
            ListObject oClusterTable,
            ReadWorkbookContext oReadWorkbookContext
            )
        {
            Debug.Assert(oClusterTable != null);
            Debug.Assert(oReadWorkbookContext != null);
            AssertValid();

            Dictionary<String, ClusterInformation> oClusterNameDictionary =
            new Dictionary<String, ClusterInformation>();

            ColorConverter2 oColorConverter2 =
            oReadWorkbookContext.ColorConverter2;

            ExcelTableReader oExcelTableReader =
            new ExcelTableReader(oClusterTable);

            foreach ( ExcelTableReader.ExcelTableRow oRow in
            oExcelTableReader.GetRows() )
            {
            // Get the cluster information.

            String sClusterName;
            Color oVertexColor;
            VertexShape eVertexShape;

            if (
                !oRow.TryGetNonEmptyStringFromCell(
                    ClusterTableColumnNames.Name, out sClusterName)
                ||
                !TryGetColor(oRow, ClusterTableColumnNames.VertexColor,
                    oColorConverter2, out oVertexColor)
                ||
                !TryGetVertexShape(oRow, ClusterTableColumnNames.VertexShape,
                    out eVertexShape)
                )
            {
                continue;
            }

            // Add the cluster information to the dictionary.

            ClusterInformation oClusterInformation =
                new ClusterInformation();

            oClusterInformation.VertexColor = oVertexColor;
            oClusterInformation.VertexShape = eVertexShape;

            try
            {
                oClusterNameDictionary.Add(
                    sClusterName, oClusterInformation);
            }
            catch (ArgumentException)
            {
                Range oInvalidCell = oRow.GetRangeForCell(
                    ClusterTableColumnNames.Name);

                OnWorkbookFormatError( String.Format(

                    "The cell {0} contains a duplicate cluster name.  There"
                    + " can't be two rows with the same cluster name."
                    ,
                    ExcelUtil.GetRangeAddress(oInvalidCell)
                    ),

                    oInvalidCell
                );
            }
            }

            return (oClusterNameDictionary);
        }
        //*************************************************************************
        //  Method: ReadImageUri()
        //
        /// <summary>
        /// If an image URI has been specified for a vertex, sets the vertex's
        /// image.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the image on.
        /// </param>
        ///
        /// <param name="oVertexRadiusConverter">
        /// Object that converts a vertex radius between values used in the Excel
        /// workbook and values used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="oVertexImageSize">
        /// The size to use for the image (in workbook units), or a Nullable that
        /// has no value to use the image's actual size.
        /// </param>
        ///
        /// <returns>
        /// true if an image key was specified.
        /// </returns>
        //*************************************************************************
        protected Boolean ReadImageUri(
            ExcelTableReader.ExcelTableRow oRow,
            IVertex oVertex,
            VertexRadiusConverter oVertexRadiusConverter,
            Nullable<Single> oVertexImageSize
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            Debug.Assert(oVertexRadiusConverter != null);
            AssertValid();

            String sImageUri;

            if ( !oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.ImageUri, out sImageUri) )
            {
            return (false);
            }

            if ( sImageUri.ToLower().StartsWith("www.") )
            {
            // The Uri class thinks that "www.somewhere.com" is a relative
            // path.  Fix that.

            sImageUri= "http://" + sImageUri;
            }

            Uri oUri;

            // Is the URI either an URL or a full file path?

            if ( !Uri.TryCreate(sImageUri, UriKind.Absolute, out oUri) )
            {
            // No.  It appears to be a relative path.

            Range oCell = oRow.GetRangeForCell(
                VertexTableColumnNames.ImageUri);

            String sWorkbookPath =
                ( (Workbook)(oCell.Worksheet.Parent) ).Path;

            if ( !String.IsNullOrEmpty(sWorkbookPath) )
            {
                sImageUri = Path.Combine(sWorkbookPath, sImageUri);
            }
            else
            {
                OnWorkbookFormatError( String.Format(

                    "The image file path specified in cell {0} is a relative"
                    + " path.  Relative paths must be relative to the saved"
                    + " workbook file, but the workbook hasn't been saved yet."
                    + "  Either save the workbook or change the image file to"
                    + " an absolute path, such as \"C:\\MyImages\\Image.jpg\"."
                    ,
                    ExcelUtil.GetRangeAddress(oCell)
                    ),

                    oCell
                    );
            }
            }

            // Note that sImageUri may or may not be a valid URI string.  If it is
            // not, GetImageSynchronousIgnoreDpi() will return an error image.

            ImageSource oImage =
            ( new WpfImageUtil() ).GetImageSynchronousIgnoreDpi(sImageUri);

            if (oVertexImageSize.HasValue)
            {
            // Resize the image.

            Double dLongerDimension =
                oVertexRadiusConverter.WorkbookToLongerImageDimension(
                    oVertexImageSize.Value);

            Debug.Assert(dLongerDimension >= 1);

            oImage = ( new WpfImageUtil() ).ResizeImage(oImage,
                (Int32)dLongerDimension);
            }

            oVertex.SetValue(ReservedMetadataKeys.PerVertexImage, oImage);

            return (true);
        }
        //*************************************************************************
        //  Method: ReadGroupTable()
        //
        /// <summary>
        /// Reads the group table.
        /// </summary>
        ///
        /// <param name="oGroupTable">
        /// The group table.
        /// </param>
        ///
        /// <param name="oReadWorkbookContext">
        /// Provides access to objects needed for converting an Excel workbook to a
        /// NodeXL graph.
        /// </param>
        ///
        /// <returns>
        /// A dictionary.  The key is the group name and the value is a
        /// GroupInformation object for the group.
        /// </returns>
        //*************************************************************************
        protected Dictionary<String, GroupInformation> ReadGroupTable(
            ListObject oGroupTable,
            ReadWorkbookContext oReadWorkbookContext
            )
        {
            Debug.Assert(oGroupTable != null);
            Debug.Assert(oReadWorkbookContext != null);
            AssertValid();

            if (oReadWorkbookContext.FillIDColumns)
            {
            FillIDColumn(oGroupTable);
            }

            Dictionary<String, GroupInformation> oGroupNameDictionary =
            new Dictionary<String, GroupInformation>();

            ColorConverter2 oColorConverter2 =
            oReadWorkbookContext.ColorConverter2;

            BooleanConverter oBooleanConverter =
            oReadWorkbookContext.BooleanConverter;

            ExcelTableReader oExcelTableReader = new ExcelTableReader(oGroupTable);

            foreach ( ExcelTableReader.ExcelTableRow oRow in
            oExcelTableReader.GetRows() )
            {
            // Get the group information.

            String sGroupName;
            Color oVertexColor;
            VertexShape eVertexShape;

            if (
                !oRow.TryGetNonEmptyStringFromCell(GroupTableColumnNames.Name,
                    out sGroupName)
                ||
                !TryGetColor(oRow, GroupTableColumnNames.VertexColor,
                    oColorConverter2, out oVertexColor)
                ||
                !TryGetVertexShape(oRow, GroupTableColumnNames.VertexShape,
                    out eVertexShape)
                )
            {
                continue;
            }

            Boolean bCollapsed = false;
            Boolean bCollapsedCellValue;

            if (
                TryGetBoolean(oRow, GroupTableColumnNames.Collapsed,
                    oBooleanConverter, out bCollapsedCellValue)
                &&
                bCollapsedCellValue
                )
            {
                bCollapsed = true;
            }

            Int32 iRowIDAsInt32;
            Nullable<Int32> iRowID = null;

            if ( oRow.TryGetInt32FromCell(CommonTableColumnNames.ID,
                out iRowIDAsInt32) )
            {
                iRowID = iRowIDAsInt32;
            }

            GroupInformation oGroupInformation = new GroupInformation(
                sGroupName, iRowID, oVertexColor, eVertexShape, bCollapsed);

            if (oReadWorkbookContext.SaveGroupVertices)
            {
                // ReadGroupVertexTable() will save the group's vertices in
                // this LinkedList.

                oGroupInformation.Vertices = new LinkedList<IVertex>();
            }

            try
            {
                oGroupNameDictionary.Add(sGroupName, oGroupInformation);
            }
            catch (ArgumentException)
            {
                Range oInvalidCell = oRow.GetRangeForCell(
                    GroupTableColumnNames.Name);

                OnWorkbookFormatError( String.Format(

                    "The cell {0} contains a duplicate group name.  There"
                    + " can't be two rows with the same group name."
                    ,
                    ExcelUtil.GetRangeAddress(oInvalidCell)
                    ),

                    oInvalidCell
                );
            }
            }

            return (oGroupNameDictionary);
        }
        //*************************************************************************
        //  Method: ReadLabelPosition()
        //
        /// <summary>
        /// If a label position has been specified for a vertex, sets the vertex's
        /// label position.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the vertex data.
        /// </param>
        ///
        /// <param name="oVertexLabelPositionConverter">
        /// Object that converts a vertex label position between values used in the
        /// Excel workbook and values used in the NodeXL graph.
        /// </param>
        ///
        /// <param name="oVertex">
        /// Vertex to set the label position on.
        /// </param>
        //*************************************************************************
        protected void ReadLabelPosition(
            ExcelTableReader.ExcelTableRow oRow,
            VertexLabelPositionConverter oVertexLabelPositionConverter,
            IVertex oVertex
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oVertex != null);
            Debug.Assert(oVertexLabelPositionConverter != null);
            AssertValid();

            String sLabelPosition;

            if ( !oRow.TryGetNonEmptyStringFromCell(
            VertexTableColumnNames.LabelPosition, out sLabelPosition) )
            {
            return;
            }

            VertexLabelPosition eLabelPosition;

            if ( !oVertexLabelPositionConverter.TryWorkbookToGraph(sLabelPosition,
            out eLabelPosition) )
            {
            OnWorkbookFormatErrorWithDropDown(oRow,
                VertexTableColumnNames.LabelPosition, "label position");
            }

            oVertex.SetValue( ReservedMetadataKeys.PerVertexLabelPosition,
            eLabelPosition);
        }
Example #27
0
            //*********************************************************************
            //  Constructor: ExcelTableRow()
            //
            /// <summary>
            /// Initializes a new instance of the <see cref="ExcelTableRow" />
            /// class.
            /// </summary>
            ///
            /// <param name="excelTableReader">
            /// The table reader object that owns this object.
            /// </param>
            //*********************************************************************
            public ExcelTableRow(
                ExcelTableReader excelTableReader
                )
            {
                m_oExcelTableReader = excelTableReader;

                AssertValid();
            }
        //*************************************************************************
        //  Method: AddToRowIDDictionary()
        //
        /// <summary>
        /// Adds an edge or vertex to a dictionary and sets the edge or vertex's
        /// Tag to the row ID.
        /// </summary>
        ///
        /// <param name="oRow">
        /// Row containing the edge or vertex data.
        /// </param>
        ///
        /// <param name="oEdgeOrVertex">
        /// Edge or vertex to get the row ID for.
        /// </param>
        ///
        /// <param name="oRowIDDictionary">
        /// Keeps track of the edges or vertices that have been added to the graph,
        /// depending on which derived class this method is being called from.  The
        /// key is the edge or vertex row ID read from the edge or vertex worksheet
        /// and the value is the edge or vertex.
        /// </param>
        //*************************************************************************
        protected void AddToRowIDDictionary(
            ExcelTableReader.ExcelTableRow oRow,
            IIdentityProvider oEdgeOrVertex,
            Dictionary<Int32, IIdentityProvider> oRowIDDictionary
            )
        {
            Debug.Assert(oRow != null);
            Debug.Assert(oEdgeOrVertex != null);
            Debug.Assert(oRowIDDictionary != null);
            AssertValid();

            // Because the derived class fills in its ID column if the column
            // exists, each cell in the column should be valid.

            String sRowID;
            Int32 iRowID = Int32.MinValue;

            if (
            oRow.TryGetNonEmptyStringFromCell(CommonTableColumnNames.ID,
                out sRowID)
            &&
            Int32.TryParse(sRowID, out iRowID)
            )
            {
            oRowIDDictionary.Add(iRowID, oEdgeOrVertex);

            Debug.Assert(oEdgeOrVertex is IMetadataProvider);

            // Store the row ID in the edge or vertex tag.

            ( (IMetadataProvider)oEdgeOrVertex ).Tag = iRowID;
            }
        }